From f417aba5a5c66b6eef35658dc0079fa0f5ee59a7 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 9 Feb 2024 19:27:50 +0800 Subject: [PATCH] Fix 1.11.13 for 10.6 i386 --- src/cmd/link/internal/ld/lib.go | 3 - src/crypto/x509/root_cgo_darwin.go | 278 +++++++++++----------------- src/crypto/x509/root_darwin.go | 18 +- src/internal/poll/fd_unix.go | 13 ++ src/runtime/crash_cgo_test.go | 13 ++ src/syscall/zerrors_darwin_386.go | 2 +- src/syscall/zerrors_darwin_amd64.go | 2 +- 7 files changed, 141 insertions(+), 188 deletions(-) diff --git src/cmd/link/internal/ld/lib.go src/cmd/link/internal/ld/lib.go index f2c61b60c0..6ada49c392 100644 --- src/cmd/link/internal/ld/lib.go +++ src/cmd/link/internal/ld/lib.go @@ -1091,9 +1091,6 @@ func (ctxt *Link) hostlink() { if ctxt.DynlinkingGo() { argv = append(argv, "-Wl,-flat_namespace") } - if ctxt.BuildMode == BuildModeExe && !ctxt.Arch.InFamily(sys.ARM64) { - argv = append(argv, "-Wl,-no_pie") - } case objabi.Hopenbsd: argv = append(argv, "-Wl,-nopie") case objabi.Hwindows: diff --git src/crypto/x509/root_cgo_darwin.go src/crypto/x509/root_cgo_darwin.go index 1c20f26acb..86b6831e33 100644 --- src/crypto/x509/root_cgo_darwin.go +++ src/crypto/x509/root_cgo_darwin.go @@ -7,7 +7,7 @@ package x509 /* -#cgo CFLAGS: -mmacosx-version-min=10.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300 +#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 #cgo LDFLAGS: -framework CoreFoundation -framework Security #include @@ -16,135 +16,59 @@ package x509 #include #include -static Boolean isSSLPolicy(SecPolicyRef policyRef) { - if (!policyRef) { - return false; - } - CFDictionaryRef properties = SecPolicyCopyProperties(policyRef); - if (properties == NULL) { - return false; - } - Boolean isSSL = false; - CFTypeRef value = NULL; - if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) { - isSSL = CFEqual(value, kSecPolicyAppleSSL); - } - CFRelease(properties); - return isSSL; -} - -// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value -// for a certificate in the user or admin domain, combining usage constraints -// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and -// kSecTrustSettingsAllowedError. -// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting -static SInt32 sslTrustSettingsResult(SecCertificateRef cert) { - CFArrayRef trustSettings = NULL; - OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings); - - // According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings", - // but the rules of the override are unclear. Let's assume admin trust settings are applicable - // if and only if user trust settings fail to load or are NULL. - if (err != errSecSuccess || trustSettings == NULL) { - if (trustSettings != NULL) CFRelease(trustSettings); - err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings); - } - - // > no trust settings [...] means "this certificate must be verified to a known trusted certificate” - if (err != errSecSuccess || trustSettings == NULL) { - if (trustSettings != NULL) CFRelease(trustSettings); - return kSecTrustSettingsResultUnspecified; +// FetchPEMRoots_MountainLion is the version of FetchPEMRoots from Go 1.6 +// which still works on OS X 10.8 (Mountain Lion). +// It lacks support for admin & user cert domains. +// See golang.org/issue/16473 +int FetchPEMRoots_MountainLion(CFDataRef *pemRoots) { + if (pemRoots == NULL) { + return -1; } - - // > An empty trust settings array means "always trust this certificate” with an - // > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot. - if (CFArrayGetCount(trustSettings) == 0) { - CFRelease(trustSettings); - return kSecTrustSettingsResultTrustRoot; + CFArrayRef certs = NULL; + OSStatus err = SecTrustCopyAnchorCertificates(&certs); + if (err != noErr) { + return -1; } - - // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"), - // but the Go linker's internal linking mode can't handle CFSTR relocations. - // Create our own dynamic string instead and release it below. - CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString( - NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8); - CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString( - NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8); - CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString( - NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8); - - CFIndex m; SInt32 result = 0; - for (m = 0; m < CFArrayGetCount(trustSettings); m++) { - CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m); - - // First, check if this trust setting applies to our policy. We assume - // only one will. The docs suggest that there might be multiple applying - // but don't explain how to combine them. - SecPolicyRef policyRef; - if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) { - if (!isSSLPolicy(policyRef)) { - continue; - } - } else { + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); + int i, ncerts = CFArrayGetCount(certs); + for (i = 0; i < ncerts; i++) { + CFDataRef data = NULL; + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); + if (cert == NULL) { continue; } - - if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) { - // Restricted to a hostname, not a root. + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + if (err != noErr) { continue; } - - CFNumberRef cfNum; - if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) { - CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); - } else { - // > If the value of the kSecTrustSettingsResult component is not - // > kSecTrustSettingsResultUnspecified for a usage constraints dictionary that has - // > no constraints, the default value kSecTrustSettingsResultTrustRoot is assumed. - result = kSecTrustSettingsResultTrustRoot; + if (data != NULL) { + CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); + CFRelease(data); } - - break; - } - - // If trust settings are present, but none of them match the policy... - // the docs don't tell us what to do. - // - // "Trust settings for a given use apply if any of the dictionaries in the - // certificate’s trust settings array satisfies the specified use." suggests - // that it's as if there were no trust settings at all, so we should probably - // fallback to the admin trust settings. TODO. - if (result == 0) { - result = kSecTrustSettingsResultUnspecified; } - - CFRelease(_kSecTrustSettingsPolicy); - CFRelease(_kSecTrustSettingsPolicyString); - CFRelease(_kSecTrustSettingsResult); - CFRelease(trustSettings); - - return result; + CFRelease(certs); + *pemRoots = combinedData; + return 0; } - -// isRootCertificate reports whether Subject and Issuer match. -static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) { - CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef); - if (*errRef != NULL) { - return false; - } - CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef); - if (*errRef != NULL) { - CFRelease(subjectName); - return false; - } - Boolean equal = CFEqual(subjectName, issuerName); - CFRelease(subjectName); - CFRelease(issuerName); - return equal; +// useOldCode reports whether the running machine is OS X 10.8 Mountain Lion +// or older. We only support Mountain Lion and higher, but we'll at least try our +// best on older machines and continue to use the old code path. +// +// See golang.org/issue/16473 +int useOldCode() { + char str[256]; + size_t size = sizeof(str); + memset(str, 0, size); + sysctlbyname("kern.osrelease", str, &size, NULL, 0); + // OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*. + // We never supported things before that. + return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0; } -// FetchPEMRoots fetches the system's list of trusted X.509 root certificates -// for the kSecTrustSettingsPolicy SSL. +// FetchPEMRoots fetches the system's list of trusted X.509 root certificates. // // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root // certificates of the system. On failure, the function returns -1. @@ -152,15 +76,11 @@ static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) { // // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must // be released (using CFRelease) after we've consumed its content. -int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) { +int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { int i; - if (debugDarwinRoots) { - printf("crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid); - printf("crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot); - printf("crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot); - printf("crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny); - printf("crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified); + if (useOldCode()) { + return FetchPEMRoots_MountainLion(pemRoots); } // Get certificates from all domains, not just System, this lets @@ -174,6 +94,11 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugD return -1; } + // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"), + // but the Go linker's internal linking mode can't handle CFSTR relocations. + // Create our own dynamic string instead and release it below. + CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8); + CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0); for (i = 0; i < numDomains; i++) { @@ -187,81 +112,85 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugD CFIndex numCerts = CFArrayGetCount(certs); for (j = 0; j < numCerts; j++) { CFDataRef data = NULL; + CFErrorRef errRef = NULL; CFArrayRef trustSettings = NULL; SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j); if (cert == NULL) { continue; } - SInt32 result; - if (domains[i] == kSecTrustSettingsDomainSystem) { + // We only want trusted certs. + int untrusted = 0; + int trustAsRoot = 0; + int trustRoot = 0; + if (i == 0) { + trustAsRoot = 1; + } else { + int k; + CFIndex m; + // Certs found in the system domain are always trusted. If the user // configures "Never Trust" on such a cert, it will also be found in the // admin or user domain, causing it to be added to untrustedPemRoots. The // Go code will then clean this up. - result = kSecTrustSettingsResultTrustRoot; - } else { - result = sslTrustSettingsResult(cert); - if (debugDarwinRoots) { - CFErrorRef errRef = NULL; - CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef); - if (errRef != NULL) { - printf("crypto/x509: SecCertificateCopyShortDescription failed\n"); - CFRelease(errRef); - continue; + // Trust may be stored in any of the domains. According to Apple's + // SecTrustServer.c, "user trust settings overrule admin trust settings", + // so take the last trust settings array we find. + // Skip the system domain since it is always trusted. + for (k = i; k < numDomains; k++) { + CFArrayRef domainTrustSettings = NULL; + err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings); + if (err == errSecSuccess && domainTrustSettings != NULL) { + if (trustSettings) { + CFRelease(trustSettings); + } + trustSettings = domainTrustSettings; } - CFIndex length = CFStringGetLength(summary); - CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; - char *buffer = malloc(maxSize); - if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) { - printf("crypto/x509: %s returned %d\n", buffer, (int)result); - } - free(buffer); - CFRelease(summary); } - } - - CFMutableDataRef appendTo; - // > Note the distinction between the results kSecTrustSettingsResultTrustRoot - // > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to - // > root (self-signed) certificates; the latter can only be applied to - // > non-root certificates. - if (result == kSecTrustSettingsResultTrustRoot) { - CFErrorRef errRef = NULL; - if (!isRootCertificate(cert, &errRef) || errRef != NULL) { - if (errRef != NULL) CFRelease(errRef); + if (trustSettings == NULL) { + // "this certificate must be verified to a known trusted certificate"; aka not a root. continue; } - - appendTo = combinedData; - } else if (result == kSecTrustSettingsResultTrustAsRoot) { - CFErrorRef errRef = NULL; - if (isRootCertificate(cert, &errRef) || errRef != NULL) { - if (errRef != NULL) CFRelease(errRef); - continue; + for (m = 0; m < CFArrayGetCount(trustSettings); m++) { + CFNumberRef cfNum; + CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m); + if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){ + SInt32 result = 0; + CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result); + // TODO: The rest of the dictionary specifies conditions for evaluation. + if (result == kSecTrustSettingsResultDeny) { + untrusted = 1; + } else if (result == kSecTrustSettingsResultTrustAsRoot) { + trustAsRoot = 1; + } else if (result == kSecTrustSettingsResultTrustRoot) { + trustRoot = 1; + } + } } - - appendTo = combinedData; - } else if (result == kSecTrustSettingsResultDeny) { - appendTo = combinedUntrustedData; - } else if (result == kSecTrustSettingsResultUnspecified) { - continue; - } else { - continue; + CFRelease(trustSettings); } - err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); + // Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. + // Once we support weak imports via cgo we should prefer that, and fall back to this + // for older systems. + err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); if (err != noErr) { continue; } + if (data != NULL) { + if (!trustRoot && !trustAsRoot) { + untrusted = 1; + } + CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData; CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data)); CFRelease(data); } } CFRelease(certs); } + CFRelease(policy); *pemRoots = combinedData; *untrustedPemRoots = combinedUntrustedData; return 0; @@ -278,8 +207,9 @@ func loadSystemRoots() (*CertPool, error) { var data C.CFDataRef = 0 var untrustedData C.CFDataRef = 0 - err := C.FetchPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots)) + err := C.FetchPEMRoots(&data, &untrustedData) if err == -1 { + // TODO: better error message return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo") } diff --git src/crypto/x509/root_darwin.go src/crypto/x509/root_darwin.go index b0460527b1..8de5af9cd5 100644 --- src/crypto/x509/root_darwin.go +++ src/crypto/x509/root_darwin.go @@ -22,7 +22,7 @@ import ( "sync" ) -var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1") +var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1") func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { return nil, nil @@ -58,7 +58,7 @@ func execSecurityRoots() (*CertPool, error) { if err != nil { return nil, err } - if debugDarwinRoots { + if debugExecDarwinRoots { fmt.Printf("crypto/x509: %d certs have a trust policy\n", len(hasPolicy)) } @@ -66,8 +66,8 @@ func execSecurityRoots() (*CertPool, error) { u, err := user.Current() if err != nil { - if debugDarwinRoots { - fmt.Printf("crypto/x509: can't get user home directory: %v\n", err) + if debugExecDarwinRoots { + fmt.Printf(fmt.Sprintf("crypto/x509: get current user: %v", err)) } } else { keychains = append(keychains, @@ -146,7 +146,7 @@ func execSecurityRoots() (*CertPool, error) { close(verifyCh) wg.Wait() - if debugDarwinRoots { + if debugExecDarwinRoots { fmt.Printf("crypto/x509: ran security verify-cert %d times\n", numVerified) } @@ -199,16 +199,16 @@ func verifyCertWithSystem(cert *Certificate) bool { } cmd := exec.Command("/usr/bin/security", "verify-cert", "-p", "ssl", "-c", f.Name(), "-l", "-L") var stderr bytes.Buffer - if debugDarwinRoots { + if debugExecDarwinRoots { cmd.Stderr = &stderr } if err := cmd.Run(); err != nil { - if debugDarwinRoots { + if debugExecDarwinRoots { fmt.Printf("crypto/x509: verify-cert rejected %s: %q\n", cert.Subject, bytes.TrimSpace(stderr.Bytes())) } return false } - if debugDarwinRoots { + if debugExecDarwinRoots { fmt.Printf("crypto/x509: verify-cert approved %s\n", cert.Subject) } return true @@ -241,7 +241,7 @@ func getCertsWithTrustPolicy() (map[string]bool, error) { // Rather than match on English substrings that are probably // localized on macOS, just interpret any failure to mean that // there are no trust settings. - if debugDarwinRoots { + if debugExecDarwinRoots { fmt.Printf("crypto/x509: exec %q: %v, %s\n", cmd.Args, err, stderr.Bytes()) } return nil diff --git src/internal/poll/fd_unix.go src/internal/poll/fd_unix.go index b311049ad7..7306b937fc 100644 --- src/internal/poll/fd_unix.go +++ src/internal/poll/fd_unix.go @@ -453,6 +453,19 @@ var tryDupCloexec = int32(1) func DupCloseOnExec(fd int) (int, string, error) { if atomic.LoadInt32(&tryDupCloexec) == 1 { r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) + if runtime.GOOS == "darwin" && e1 == syscall.EBADF { + // On OS X 10.6 and below (but we only support + // >= 10.6), F_DUPFD_CLOEXEC is unsupported + // and fcntl there falls back (undocumented) + // to doing an ioctl instead, returning EBADF + // in this case because fd is not of the + // expected device fd type. Treat it as + // EINVAL instead, so we fall back to the + // normal dup path. + // TODO: only do this on 10.6 if we can detect 10.6 + // cheaply. + e1 = syscall.EINVAL + } switch e1 { case 0: return int(r0), "", nil diff --git src/runtime/crash_cgo_test.go src/runtime/crash_cgo_test.go index 6da8341e84..e6f61081aa 100644 --- src/runtime/crash_cgo_test.go +++ src/runtime/crash_cgo_test.go @@ -90,6 +90,19 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) { case "plan9", "windows": t.Skipf("no pthreads on %s", runtime.GOOS) } + case "darwin": + if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" { + // static constructor needs external linking, but we don't support + // external linking on OS X 10.6. + out, err := exec.Command("uname", "-r").Output() + if err != nil { + t.Fatalf("uname -r failed: %v", err) + } + // OS X 10.6 == Darwin 10.x + if strings.HasPrefix(string(out), "10.") { + t.Skipf("no external linking on OS X 10.6") + } + } if runtime.GOARCH == "ppc64" { // TODO(austin) External linking not implemented on // ppc64 (issue #8912) diff --git src/syscall/zerrors_darwin_386.go src/syscall/zerrors_darwin_386.go index 776aecbf33..1c11de8124 100644 --- src/syscall/zerrors_darwin_386.go +++ src/syscall/zerrors_darwin_386.go @@ -232,7 +232,7 @@ const ( F_ALLOCATECONTIG = 0x2 F_CHKCLEAN = 0x29 F_DUPFD = 0x0 - F_DUPFD_CLOEXEC = 0x43 + F_DUPFD_CLOEXEC = 0x0 F_FLUSH_DATA = 0x28 F_FREEZE_FS = 0x35 F_FULLFSYNC = 0x33 diff --git src/syscall/zerrors_darwin_amd64.go src/syscall/zerrors_darwin_amd64.go index 58799fbde7..8d35537a0b 100644 --- src/syscall/zerrors_darwin_amd64.go +++ src/syscall/zerrors_darwin_amd64.go @@ -232,7 +232,7 @@ const ( F_ALLOCATECONTIG = 0x2 F_CHKCLEAN = 0x29 F_DUPFD = 0x0 - F_DUPFD_CLOEXEC = 0x43 + F_DUPFD_CLOEXEC = 0x0 F_FLUSH_DATA = 0x28 F_FREEZE_FS = 0x35 F_FULLFSYNC = 0x33