From 80e2389990082500d76eb566d4946be3e786c3ef Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 23 Sep 2014 21:24:36 +0000 Subject: [PATCH] Security-55471.14.18.tar.gz --- .../CloudKeychainProxy-Info.plist | 2 +- .../Keychain Circle Notification-Info.plist | 2 +- Keychain/Keychain-Info.plist | 2 +- Security.xcodeproj/project.pbxproj | 187 +++++++++++++ .../UserInterfaceState.xcuserstate | Bin 0 -> 140968 bytes .../xcschemes/xcschememanagement.plist | 104 ++++++++ authd/Info.plist | 2 +- codesign_tests/FatDynamicValidation.c | 34 +++ codesign_tests/validation.sh | 43 +++ lib/Info-Security.plist | 2 +- lib/plugins/csparser-Info.plist | 2 +- lib/security.exp-in | 2 + .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 24 ++ .../xcschemes/xcschememanagement.plist | 14 + libsecurity_codesigning/lib/CSCommon.h | 10 + libsecurity_codesigning/lib/SecAssessment.cpp | 17 +- libsecurity_codesigning/lib/SecAssessment.h | 9 + libsecurity_codesigning/lib/SecCodeSigner.cpp | 4 +- libsecurity_codesigning/lib/SecCodeSigner.h | 2 + libsecurity_codesigning/lib/SecStaticCode.cpp | 17 +- libsecurity_codesigning/lib/SecStaticCode.h | 5 + .../lib/SecStaticCodePriv.h | 12 + libsecurity_codesigning/lib/StaticCode.cpp | 156 ++++++++++- libsecurity_codesigning/lib/StaticCode.h | 16 +- libsecurity_codesigning/lib/bundlediskrep.cpp | 164 +++++++++++- libsecurity_codesigning/lib/bundlediskrep.h | 10 +- libsecurity_codesigning/lib/dirscanner.cpp | 198 ++++++++++++++ libsecurity_codesigning/lib/dirscanner.h | 119 +++++++++ libsecurity_codesigning/lib/diskrep.cpp | 11 + libsecurity_codesigning/lib/diskrep.h | 14 +- libsecurity_codesigning/lib/kerneldiskrep.cpp | 2 +- libsecurity_codesigning/lib/kerneldiskrep.h | 2 +- libsecurity_codesigning/lib/machorep.cpp | 18 +- libsecurity_codesigning/lib/machorep.h | 2 + .../lib/opaquewhitelist.cpp | 250 ++++++++++++++++++ libsecurity_codesigning/lib/opaquewhitelist.h | 56 ++++ libsecurity_codesigning/lib/piddiskrep.cpp | 2 +- libsecurity_codesigning/lib/piddiskrep.h | 2 +- libsecurity_codesigning/lib/policyengine.cpp | 124 +++++---- libsecurity_codesigning/lib/policyengine.h | 7 +- libsecurity_codesigning/lib/resources.cpp | 40 ++- libsecurity_codesigning/lib/resources.h | 7 +- .../lib/security_codesigning.exp | 2 + libsecurity_codesigning/lib/signer.cpp | 20 +- libsecurity_codesigning/lib/signer.h | 8 +- libsecurity_codesigning/lib/singlediskrep.cpp | 2 +- libsecurity_codesigning/lib/singlediskrep.h | 2 +- .../project.pbxproj | 20 +- .../xcschemes/xcschememanagement.plist | 54 ++++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 24 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + libsecurity_keychain/lib/SecTrust.cpp | 37 ++- libsecurity_keychain/lib/Trust.cpp | 5 +- libsecurity_keychain/lib/Trust.h | 1 + .../xcschemes/xcschememanagement.plist | 39 +++ .../xcschemes/xcschememanagement.plist | 29 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 14 + .../xcschemes/xcschememanagement.plist | 24 ++ libsecurity_ssl/lib/sslCrypto.c | 10 - .../xcschemes/xcschememanagement.plist | 39 +++ .../xcschemes/xcschememanagement.plist | 29 ++ libsecurity_utilities/lib/cfmunge.cpp | 3 +- libsecurity_utilities/lib/cfutilities.cpp | 22 ++ libsecurity_utilities/lib/cfutilities.h | 14 +- libsecurity_utilities/lib/errors.h | 3 + libsecurity_utilities/lib/macho++.cpp | 157 ++++++++++- libsecurity_utilities/lib/macho++.h | 19 +- libsecurity_utilities/lib/unix++.cpp | 10 + libsecurity_utilities/lib/unix++.h | 3 + .../xcschemes/xcschememanagement.plist | 19 ++ .../xcschemes/xcschememanagement.plist | 34 +++ .../xcschemes/xcschememanagement.plist | 89 +++++++ .../Regressions/secd-55-account-circle.c | 2 +- sec/securityd/SecItemServer.c | 2 +- 91 files changed, 2562 insertions(+), 147 deletions(-) create mode 100644 Security.xcodeproj/project.xcworkspace/xcuserdata/jkauth.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Security.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 codesign_tests/FatDynamicValidation.c create mode 100755 codesign_tests/validation.sh create mode 100644 libsecurity_apple_csp/libsecurity_apple_csp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_apple_cspdl/libsecurity_apple_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_apple_file_dl/libsecurity_apple_file_dl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_apple_x509_cl/libsecurity_apple_x509_cl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_apple_x509_tp/libsecurity_apple_x509_tp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_asn1/libsecurity_asn1.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_authorization/libsecurity_authorization.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cdsa_client/libsecurity_cdsa_client.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cdsa_plugin/libsecurity_cdsa_plugin.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cdsa_utilities/libsecurity_cdsa_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cdsa_utils/libsecurity_cdsa_utils.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_checkpw/libsecurity_checkpw.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cms/libsecurity_cms.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_codesigning/lib/dirscanner.cpp create mode 100644 libsecurity_codesigning/lib/dirscanner.h create mode 100644 libsecurity_codesigning/lib/opaquewhitelist.cpp create mode 100644 libsecurity_codesigning/lib/opaquewhitelist.h create mode 100644 libsecurity_codesigning/libsecurity_codesigning.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_comcryption/libsecurity_comcryption.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cryptkit/libsecurity_cryptkit.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_cssm/libsecurity_cssm.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_filedb/libsecurity_filedb.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_keychain/libDER/libDER.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_keychain/libsecurity_keychain.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_manifest/libsecurity_manifest.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_mds/libsecurity_mds.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_ocspd/libsecurity_ocspd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_pkcs12/libsecurity_pkcs12.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_sd_cspdl/libsecurity_sd_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_smime/libsecurity_smime.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_ssl/libsecurity_ssl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_transform/libsecurity_transform.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurity_utilities/libsecurity_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 libsecurityd/libsecurityd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 sec/sec.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/CloudKeychainProxy/CloudKeychainProxy-Info.plist b/CloudKeychainProxy/CloudKeychainProxy-Info.plist index 753b00e3..116d48a1 100644 --- a/CloudKeychainProxy/CloudKeychainProxy-Info.plist +++ b/CloudKeychainProxy/CloudKeychainProxy-Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 NSHumanReadableCopyright Copyright © 2013 Apple, Inc. All rights reserved. diff --git a/Keychain Circle Notification/Keychain Circle Notification-Info.plist b/Keychain Circle Notification/Keychain Circle Notification-Info.plist index a7c85cbd..0896253d 100644 --- a/Keychain Circle Notification/Keychain Circle Notification-Info.plist +++ b/Keychain Circle Notification/Keychain Circle Notification-Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 LSApplicationCategoryType LSMinimumSystemVersion diff --git a/Keychain/Keychain-Info.plist b/Keychain/Keychain-Info.plist index db90fc62..94b28a95 100644 --- a/Keychain/Keychain-Info.plist +++ b/Keychain/Keychain-Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSMainNibFile diff --git a/Security.xcodeproj/project.pbxproj b/Security.xcodeproj/project.pbxproj index 8e20972e..04caee63 100644 --- a/Security.xcodeproj/project.pbxproj +++ b/Security.xcodeproj/project.pbxproj @@ -53,6 +53,7 @@ ); dependencies = ( 721680B3179B4C6C00406BB4 /* PBXTargetDependency */, + 37A7CEDA197DBA8700926CE8 /* PBXTargetDependency */, 722CF218175D602F00BCE0A5 /* PBXTargetDependency */, 521470291697842500DF0DB3 /* PBXTargetDependency */, 18F235FF15CA100300060520 /* PBXTargetDependency */, @@ -362,6 +363,8 @@ 18FE688B1471A46700A2CBE3 /* SecureTransportPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 182BB372146F13BB000BF1F3 /* SecureTransportPriv.h */; settings = {ATTRIBUTES = (Private, ); }; }; 18FE688C1471A46700A2CBE3 /* TrustSettingsSchema.h in Headers */ = {isa = PBXBuildFile; fileRef = 182BB1C8146EAD5D000BF1F3 /* TrustSettingsSchema.h */; settings = {ATTRIBUTES = (Private, ); }; }; 18FE688D1471A46700A2CBE3 /* X509Templates.h in Headers */ = {isa = PBXBuildFile; fileRef = 1844609E146DFCB700B12992 /* X509Templates.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 37A7CEAE197DB8FA00926CE8 /* FatDynamicValidation.c in Sources */ = {isa = PBXBuildFile; fileRef = 37A7CEAD197DB8FA00926CE8 /* FatDynamicValidation.c */; }; + 37A7CEDD197DCEE500926CE8 /* validation.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = 37A7CEDB197DCDD700926CE8 /* validation.sh */; }; 395E7CEE16C64EA500CD82A4 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 395E7CED16C64EA500CD82A4 /* SystemConfiguration.framework */; }; 39BFB04516D304DE0022564B /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 395E7CED16C64EA500CD82A4 /* SystemConfiguration.framework */; }; 4A5C1790161A9DFB00ABF784 /* authd_private.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 18F2351A15C9FA3C00060520 /* authd_private.h */; }; @@ -1347,6 +1350,13 @@ remoteGlobalIDString = 18FE67E91471A3AA00A2CBE3; remoteInfo = copyHeaders; }; + 37A7CED9197DBA8700926CE8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 18073841146D0D4E00F05C24 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 37A7CEAA197DB8FA00926CE8; + remoteInfo = codesign_tests; + }; 4AD6F6F31651CC2500DB4CE6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 186CDD1614CA11C700AF9171 /* sec.xcodeproj */; @@ -1641,6 +1651,25 @@ name = "Copy asl module"; runOnlyForDeploymentPostprocessing = 1; }; + 37A7CEDC197DCECD00926CE8 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /AppleInternal/CoreOS/codesign_tests; + dstSubfolderSpec = 0; + files = ( + 37A7CEDD197DCEE500926CE8 /* validation.sh in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 37A7CF21197DD10900926CE8 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /AppleInternal/CoreOS; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 4A5C178F161A9DE000ABF784 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -2024,6 +2053,9 @@ 18F235FC15CA0EDB00060520 /* libstdc++.6.0.9.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libstdc++.6.0.9.dylib"; path = "/usr/lib/libstdc++.6.0.9.dylib"; sourceTree = ""; }; 18F2360015CAF41100060520 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_codesigning.a; path = /usr/local/lib/libsecurity_codesigning.a; sourceTree = ""; }; 18FE67EA1471A3AA00A2CBE3 /* Security.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Security.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 37A7CEAB197DB8FA00926CE8 /* codesign_tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = codesign_tests; sourceTree = BUILT_PRODUCTS_DIR; }; + 37A7CEAD197DB8FA00926CE8 /* FatDynamicValidation.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = FatDynamicValidation.c; sourceTree = ""; }; + 37A7CEDB197DCDD700926CE8 /* validation.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = validation.sh; sourceTree = ""; }; 395E7CED16C64EA500CD82A4 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 4C12893715FFECF3008CE3E3 /* utilities.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = utilities.xcodeproj; sourceTree = ""; }; 4C2505B616D2DF9F002CE025 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Icon.icns; sourceTree = ""; }; @@ -2264,6 +2296,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 37A7CEA8197DB8FA00926CE8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4C96F7BE16D6DF8300D3B39D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2408,6 +2447,7 @@ 4C96F7C316D6DF8400D3B39D /* Keychain Circle Notification */, 72756C00175D485D00F52070 /* cloud_keychain_diagnose */, 721680A7179B40F600406BB4 /* iCloudStats */, + 37A7CEAC197DB8FA00926CE8 /* codesign_tests */, 1807384D146D0D4E00F05C24 /* Frameworks */, 1807384C146D0D4E00F05C24 /* Products */, ); @@ -2429,6 +2469,7 @@ 4C96F7C116D6DF8300D3B39D /* Keychain Circle Notification.app */, 72756BFE175D485D00F52070 /* cloud_keychain_diagnose */, 721680A5179B40F600406BB4 /* iCloudStats */, + 37A7CEAB197DB8FA00926CE8 /* codesign_tests */, ); name = Products; sourceTree = ""; @@ -3172,6 +3213,24 @@ name = "Supporting Files"; sourceTree = ""; }; + 4C0F6FAF1985879300178101 /* sectask */ = { + isa = PBXGroup; + children = ( + 4C0F6F861985877800178101 /* SecEntitlements.h */, + ); + name = sectask; + path = ../sectask; + sourceTree = ""; + }; + 37A7CEAC197DB8FA00926CE8 /* codesign_tests */ = { + isa = PBXGroup; + children = ( + 37A7CEAD197DB8FA00926CE8 /* FatDynamicValidation.c */, + 37A7CEDB197DCDD700926CE8 /* validation.sh */, + ); + path = codesign_tests; + sourceTree = ""; + }; 4C1288F615FFECF2008CE3E3 /* utilities */ = { isa = PBXGroup; children = ( @@ -3681,6 +3740,24 @@ productReference = 18FE67EA1471A3AA00A2CBE3 /* Security.framework */; productType = "com.apple.product-type.framework"; }; + 37A7CEAA197DB8FA00926CE8 /* codesign_tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 37A7CED8197DB8FA00926CE8 /* Build configuration list for PBXNativeTarget "codesign_tests" */; + buildPhases = ( + 37A7CEA7197DB8FA00926CE8 /* Sources */, + 37A7CEA8197DB8FA00926CE8 /* Frameworks */, + 37A7CEDC197DCECD00926CE8 /* CopyFiles */, + 37A7CF21197DD10900926CE8 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = codesign_tests; + productName = codesign_tests; + productReference = 37A7CEAB197DB8FA00926CE8 /* codesign_tests */; + productType = "com.apple.product-type.tool"; + }; 4C96F7C016D6DF8300D3B39D /* Keychain Circle Notification */ = { isa = PBXNativeTarget; buildConfigurationList = 4C96F7D516D6DF8400D3B39D /* Build configuration list for PBXNativeTarget "Keychain Circle Notification" */; @@ -3797,6 +3874,11 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 0420; + TargetAttributes = { + 37A7CEAA197DB8FA00926CE8 = { + CreatedOnToolsVersion = 6.0; + }; + }; }; buildConfigurationList = 18073844146D0D4E00F05C24 /* Build configuration list for PBXProject "Security" */; compatibilityVersion = "Xcode 3.2"; @@ -3966,6 +4048,7 @@ 4CE4729E16D833FD009070D1 /* Security_temporary_UI */, 72756BFD175D485D00F52070 /* cloud_keychain_diagnose */, 721680A4179B40F600406BB4 /* iCloudStats */, + 37A7CEAA197DB8FA00926CE8 /* codesign_tests */, ); }; /* End PBXProject section */ @@ -4696,6 +4779,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 37A7CEA7197DB8FA00926CE8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37A7CEAE197DB8FA00926CE8 /* FatDynamicValidation.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4C96F7BD16D6DF8300D3B39D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -5075,6 +5166,11 @@ target = 18FE67E91471A3AA00A2CBE3 /* copyHeaders */; targetProxy = 18FE688E1471A4C900A2CBE3 /* PBXContainerItemProxy */; }; + 37A7CEDA197DBA8700926CE8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 37A7CEAA197DB8FA00926CE8 /* codesign_tests */; + targetProxy = 37A7CED9197DBA8700926CE8 /* PBXContainerItemProxy */; + }; 4AD6F6F41651CC2500DB4CE6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = libSecOtrOSX; @@ -5664,6 +5760,88 @@ }; name = Release; }; + 37A7CEAF197DB8FA00926CE8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /AppleInternal/CoreOS/codesign_tests; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + 37A7CEB0197DB8FA00926CE8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /AppleInternal/CoreOS/codesign_tests; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; 4C96F7D616D6DF8400D3B39D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6265,6 +6443,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 37A7CED8197DB8FA00926CE8 /* Build configuration list for PBXNativeTarget "codesign_tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 37A7CEAF197DB8FA00926CE8 /* Debug */, + 37A7CEB0197DB8FA00926CE8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 4C96F7D516D6DF8400D3B39D /* Build configuration list for PBXNativeTarget "Keychain Circle Notification" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Security.xcodeproj/project.xcworkspace/xcuserdata/jkauth.xcuserdatad/UserInterfaceState.xcuserstate b/Security.xcodeproj/project.xcworkspace/xcuserdata/jkauth.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..a5e3d8807838a72705c55280b70469ecc9ca64b3 GIT binary patch literal 140968 zcmcd!2YeI9(!br4uAd~-Pyq402yieqaiIiAZc<3^z0=YQ zr1#!?@4fflN&9wBI$5%1V?*Be-uu14Y39GPv$M0ayLbOATGdeB(wdw5GJ_b*u#AZ@ zGhDw=zwqoKtK-cr^-YblhcqoZB)+({WzLYg=EckF$>W+Ktxc=!4BE7LM<0GW!!rWo zV%$t$#>0$aMl)lWu}m(L$Bbk0nej{kGm)9X)G{-fS*x*iCVC6M zfM3CH;J5L6_(S{&{v3aWzr{b`pYd;a7yg@NSeE5jkyThLYiC`ohxN1l*@5g3b~rnN z9nI#l`D`IOk)6a&VT;)aJDr`uRQF1vs|m|e^+W$W1nwuxl+Q*^Agq*{$ql?3L^_?Dgyo?9J?L?49gA?EUOR?4#@x?9=RX z?2GIx?Cb1X?7Qp-?8oe9?3e5}?Dy|gA^CTudB1e0vCnEIHUroJYhsh=sw zG}tuEw6AHDX{>3SslYVBw7+SxDQF6trkM^jm7A(eQPWJ*9MgPL%(Tc9H!U}9y2*5_=?>G~ zru$3}njSGdZhFe}tmy^Q%cj>%Z<^jQy>I%+^r`6!)7Pf&Oh1}_G5v1()AWxSnN4Qi zESXia&FnC{&0cfBJit82Jk-38d8B!aInO-aTx8zQe1LhXIb<#|mzvAWmF8-5t$DV2 zo_V3U&b-9D%zTJ>rFoUP#k|_Q&b+~Vg!xGGG3MjUCz?+&pKdZW^9SY+%^#URGk<0N&iuXkSMwj{ zzs>)c|K(&(;Z)ATSvfx!;QDd>xdGfDZXa%6ZY(#Ro5UT!P3B6tY20+Kf{SwVxcS@y zF3v6GmT@b%7H%VV6n8v#GIu(6Hg^tpE_WffncL1?$z97m%00$C&OO0B$vwqA%{{|C z%RR?E&%MCC!@bMB$Gy*ez)%Db11QOEGDY6qlAuE2Kv0P^nc~ zBONAflr-rmX_Iukbdq$cbcS@cbe?pfbcwV@+AduoT`gTF?UZhkZk6tk?w0P89+V!D z9+#eyo|RsZUY1^y-jv>v-j_a-K9##<;oSxHOfxq2IVg0Zsi{30p$htG4)CH8TEPfCG}PH4fSpHJ@rHN z6ZLcTEA?CT2lZ$5H+7f#w}r8=7S1AC6pPhjx40}Gi{H}UGSD)_GTbu4GTM@B$+r|* zCR!$0rdWzC5zBPT3`>ROAWMy9mSwJGf#qP!V#`uXy`{m@WNEgvS=L(CTMoB?jf?WE*PR$2QV7#+GLrZ!5CxXFI?))fTdq*h+0>wn|&It=2Z% zHqW-uR%cscTV^}Nw$irB)?!<2TW8x~JHmFP?HJo}wi9iq*iN^dWjoh)f$d`3X4^K~ z4%<%Kt+u;t57{2JJ#Bl&_N?ti+iSMhZEx5 z!}j^3&tHB1wPU;4F4$$e#oov6wD-09?EUOH_QCdH_I>T6>|^cY>;?7-_WkXX?Lm9k zKFxlhz1&`9kJ@M2=h)}lWA;V%xP7^Og}u>!sJ+#`#(tQ6qg}HfW#42!-hPt(RQnnB zv+d{EFSK7`-(ufxzrudC{W|+j`%U&+?RVJkw%=!e(Ef=1ar;yDXYDW8U$(zyf7AYs z{eAmK_D}6!*uS=aXaCXui~V=|pZ0$o$YFBu4#}Z9Yz~LR?eIDRjscEAj-ify93vfL z9C?oMjv~i?jsqN19U(`FqtsF6sB~02Y8|s3^BfBub&e&DWsXA}D;=vGEsoWWb&d^= zBOFIMj&U64IMH#6<8;Saj&mIsI4*WJkxoO^L*z;&P$zJotHVUbYA1U-g$%b zX6J3rJDvA9?{_}reAM}b^J(XE&KI4pI^T7E=={j}ne%h!&(2?*zdC<&{_gz4xy!}5 zc$eT3U6M<7DK5Lq?dt0q=o;i2>>A=4>Kf+Cb&YowxTd(Kx`M89SB0z6RppxNn&+DD zTI5>nTH-p)wcfSCwb6CB>j;$ulr&5v+ft&AG?2a|K$GJ{k!{b_dk7wzG7dg zuiV$#cYw#~>Fe=%`gw9ZgFVAM`+7!s#(Kti3Oo}$`+FvPf}XHvn&&`Ixu?n#_006l z@yz$cJc~SW&vMTSPow8hPpfB*=P=JkkLEebv&nP3=OoXmo-;gWd(QJ*=()tR#k1XW zh39I|b)KD`n>@FA?(p30xzF>U=Mm52o~Jy|dS39n?0L=erso~c`<{-7e_1H6O0L%sWWM|#J2^StA|Mc)0q2Y9D? zL*5c^skh8q>8E5%v=Xx*jUhLiM-R8aAdzJTE?+)*c-dnu4d++kz>wUocu=g?Vlip{%&wF3;zUqC$ z`?mKz?}y$`yq|l&@_y_6!TYoKH}5X*-#*62`Z%BHQ+!sR-RJUoe12bl-$36G-*Ddu z-)LX1FW*<_o9LV5o8l|>MSRnJGkg`kgM2lpQFzN3A|`cCkj>^seOrtciz`M!&Mm-@E)F7sXKyT*6D?*`w^zT13v`tI@F?|aDi zsP75i)4u0?FZy2bz3zL<_pa{)-^aesd|&#$@%`lc*|*F0kMCbU>o@t$e$g-aWxv($ z@H_ope}DfF{|NsW|2Ti4zsNtqKgl2TNBlGV2l?mu=ld7<7y1|ZTm5bR)&4d9wf=Sf z!~E<08~mI6$NG=+pW;8me~$lL|AqdG{G0vT{g?T#@$dBC?!UwTfd4`Nlm4gtPy1i? zzv6$-|B?SQ|5yI6{lEHm1(bjq@CJMVe_&u>cwl5;R3JBy7Z?{P4D1)!KQJW_3WNhy zfrA3ofrWus;NU=AU{PRspgzzTSRGgsI3l10HU*9ioE$hKa6#b0z_!5l!1aM0ft`UH z0*?hA4?GcgGVoO3>A*9AX9Ld#o)5efcqQ<9;Pb#2fiDAJ1-=e^6Zkgpb6{8C&wl29 zTtB6st)IJ}uU}5TVg1JR8{4m_--Lcs`vvA6LV`m(i8PF48eqS3FvkfVTh#>e=X0Mn1@ z&kSI4n1LFn@tUBCnxx5^qN!IhgP9@BP-Yl0JV5?`m}b$eS|2Syx*(AM4`?G6Qe)~H zmo%*@X|7utkJ$#22bIlB;?40TDI;s@7KNG`Tbr928p!!X%r-f_2N^%c@(PN>1)&M! zN9Tr$b4QOaEDVkg<`;%W=j9cT%gZk;E-aiFjM==YR!bX8$N;n~ZjQ$r%bS)E14gBT zCroQ^ytc9a(6)H#65_G?rSeBGsi|R6U2|#6%=(u4MGd4e zPs$L|jpB@-yzAHJ737V|$sa#{ee;2kpQ!izDAcft*NM6UB9f3{4$R`Al)vjTNH1I z*+x@W7-5+)sI{&24fU<-g3a-|C^@=nomn4WlYmP>(cd}@T?Rxr-crA;F$)4UD1l!e zZ=u0R)Jp0b;#GC6%NLNpCi*}XZHz$1vY~!aOMG!#GqESu($X+y?P3x%tD2h*F+946 zS;CCm!YtMXY+>SB4)sW%@XVT&;iawdl`V-8tY;dwGKVlLw1L`iZC?YRkvWucZe^O7 zRoWnJ@K&aoY0-vgL$zTG2uyn9*~s6iE6VGZHP*MbEs0kRp1Prmq5nxl#9_?(`4!P{ zJ&Dey#=2$_0*5h^jb=77T8CziB+cxj4I4c5(A+eTP0XI18*_Ymb0;yUG9$M#Co`vLqqNamnbVllwK3XQ-96EG137DqFG-DN z%$75o`gIH$fEo3zV^TkiiMBSkEpBaVCR1m(B1TWoWiDYxUe27yoX=dqT*zF+T&(44 zdD=KFUmLF#T+Up|Y^J%1*~VLzL=yGBN=y;p>df^ z8AOmwB`ZZtdX2VWboDxB2jkqrT(1>vVRmX0=%PSHXVx{e#an_c>7q9?x6(MhMVq*V zxlP+o8!kRINg*BuRm&w&$q9Q_t{Z=-H7gL(h%+ zY6of6Mx&|BmuPg=R9!h{TQGKJQv<0BTE>R!TI=%19aC~*Vp=lMZ7seXV z(*B3E5vHkeY=^&hN|mS|8cr4;)E^B%IcOjnga)G_Xeb({)oL@fS=wxEjy6}Dr_I+E zT*VAS`=SwK37UaMqcLbK$|aL@A)t%M8srCbF`yd(-3jO}J!WW9562g^En60EradEz zQp!#GB2T7bb9T~ZIL#jro67c(fIX1RKYm6p&6(Qm1~Q&C0cwNsw6Hw zNLxz1cmWNIeKXplIV&C|3wGl&{g0{qPFBm9&6O#eOh_G{Pm4FLjJGzg(G6(>0sMYR$S;@p%cebc0EW71VFAXtK>50J1PACLQ^OCie=x z{`4A2ILmK-zg4e>=D+t6W* zb30m%)}Xa$opz|!thH#Z+tGTo0c}KwYi-&_?QlTH(Gl54Ukmd|Ql$uqzKv;VYHMB` zFC&39riIo-tM!^_R2I>y=J@LRrnZ)vy5?o^q;hy6H!`{Dk00M5Y!@gO`H z55Yt6FgzUZgZITF@JKugkH%y0Se%RV@Hm{0$KwKAh>P$9JQ44Q_s5g)0eCW=f~Vpj zF2*4o#t~eCr{U?i6d#CZ;4)l}D{v*Q!Uy4M9K|)b7SF`9@N7H>&&Bibe7pcJ#4&s@ zuEUG)V!Q;$@lw1DFUR%x5WE66;FY)$H{n(IP~41La4T-ZtMMAV7O%sH;q`a}-iQy! zM_>&DJ`x{=kH*K~P54-R96lbOfKS9H;gj(x_*8rvJ{_Nd&%|fpv++6jTznorA76kk z#24X<@g?|Dycuu7Tk$r$9bbko$5-Gh@m2V0d=0)9Ux%;9JMd0?1HKX8gm1>T;9K!+ z_;!2;z7yYt@5cAwd+~kve*6G_5I=+;#*g4f@niUL`~-dyKZT#h&){eAbK2?Jnc7*} z+1feUx!QT!`Pv2Ah1x~h#o8s>rP^j~i?&tUrft_Q(=OMp(5}?3(yrF7(XQ35)2`Qc zXgjqVv>UaXw41eCv|F{?wA-~iv^%xCw7a!?w0pJtwEMLOv4vv`4kaw8yn4 zv?sNvw5PRav}d*FwCA-Kv=_CPw3oG4v{$v)wAZyav^TZ4w70c)w0E`lwD+|Sv=6n9 zw2!qqv~RWVwC}Yav>&yfw4b$Kv|qK~wBNNqv|ZYt+F#n= z+CSRAfEYmJ#R?3F1!Mw5j;}aCJRkv(2uK1X15yB~fGmKlfNX&J0I~ye0CECy0dfQC z3&;b=3&;nE6fOZk{Q&g`GyqTzpn-q}0U8Ww2%w>Wh5;H5XdgiP0vZ8mB%o1%MgtlH zXe^*yKzV@10m=t79#8?GLO?};CIFfUXg@&v1DXWr06>!gO#w6&P!Lctpb(%ipa`H6 zK%@?t4yY8+fq-TJDg#swr~*(WpejHI0jdTR1ylp57SK#UvjEKoGzZXJK=T022ebgt zLO?M<2Lq}DvMPFKx+Z519TXm^?)`2+6ai$6Gs5j00DWi1<+A|j@I^lk&*F>_$B=E9CF;)(n>e2 z+8N|j3+Yu;D=9e0X1is>hF~~cT2oqC5iE;E!!x4#<{!U`Un3jL)aIXT&WZXq^7wWA zs44MxqlLF9uB#Jnb!a+4OBnGk73teigtS*(Sy^KUeL#hBIto=*l+B3+Lp7x{Be5XC zG^9VK(t|rnM{BF9DywTE;aF8!u%@K4x;$mWXH>!8QDJ7VtTe0xrv$&Gf?kwZW>PER zh7Hj<<>}^r(}h@N@qy`L-&3)nS^u`ip9f?Wh$gFOG!Cb}1IBtf+}p)ad^VmR3Y#(OIP>sR8|$Di80dJS|cYsSege zNY^V%O3O+srV;gUB-tX1snXPrN+~y$R)orG!;#qZNH9z~7%Qo+ERRvw)KI)|ZKx*Q zm8_Yfjp>LMtg0$24JG;;nN4~?9Tf{!mBwha8I}lCcU(u^XedZT%gAi0tgQ}3Vx$WZ zVn$^(@uF^;F*K}9^>aGv>j9VSJ~h{9%|fMzq7d1a=0wSnCYf*>Hc-dJqNUR+V#Ld( zHD!@<^3PNQeW=R7E>*~emMW$C7ZFzGd)tR z4_(5-lCt2mlxaSyF}Q;U8K9Y^)YWv%6QiFR_I^~MFAB$3H8icG=MZGR)l`>OWuWFz zp+JUEu&gFhT|xaht-7+d%JAmk?j$Q`kq9&-hfzs?7ntQ~fw?ag+@}k{sz`NtX_WSj z!~%^71AY|M8knI)=6i8z1qtEOFp0R*^5C>~@v&4qCqq0OsVb|SLkB)qR+$J|1AH8n z9@dp~Rb{k>csUdyc2!I>bPA}BFGGh++!`9u86KR_wOoe(_NQ|Fv*JS;E^fH?`l8S&=EctZ}^tF$%5H*6pS zMdp00B3Pb6oKFPqa&2lOwM?WTbKuDy79_X{njhVHIRoP?Z6lRBEfM>HO+oL?adM zmnKZsoMghIgIbj~s}7~2!`ns4vNnTc$8c$s)FE@CvGQQWv@$Zf)WxaX&`Om?wJVWD zE*hyObq=|BOg2R0G8zk3gkvPlrD_>=O^0rf!XVLkW3r?VlgV4@Svo$4DU=3Dsn%Tu4>Yyh$QPcNQ@@6;+o|nf~panh}Xqk=3?zCS4?H zwNqV@lHEdOi`r$0#Rt_^61imjk_=j_mmGS5LuVFQ+elawwDxJZoq}X`ySlc5rt4TV zLQ8hNmPoaEMF;!Z6O|rcu?X3Z7z1=Ql}&TI(ccWWUq^+!?VX4cuO-Jiy(HP$!OYr< zZX^4q4ydz&)q361Va#u(Vrkwc9!wj@s*KUQgUSv?RdtJ3)GgD~8ZAWU#1aKpX$8qG zWZB3lAMU0qW71VZ)e$<7v}D&8E8=6aIU?mN(I;g^usT)m-A8qO>ALztM{2u7zBiow zAeGBWmy67fglcPo#U$gV%40+N5h~rU3u!VwC8e_s(Z{Li$aK+mtBLPvR!eMZqLCVs z58A88r>Nq#ukFQbP!Rgkg>DJa%)si`_Z?}xb-t4BajAq&kmDFr{r<=wSqmkSy z8I8S96*4@Kh=%fDX0U!l0#-8rm>u;^An3&G&a42Y|Vl-K||*Y zsxv%Y$H-Et&0S>~P1)&{-`7-WP`XmP`G$J>Kz>K%2c*jzRbQ5(=SM2+$tIk>p8i6m zhGa<5s!w zS{bu*Ry9d7#EBgzvI$Yq;V2rXAA;r3*J9{tP}wY6t|aqzEh$GblDEl3m4`#By`_*<0dava-bS=6QwUD zH4zem)5(@EIlW8)DljfhfG!*{eV0nM-?ZW*i#%Bjf>Bai((nzImerC9)X*P5^@pJ3 z#d+d5B3UPsDWsPsnf0=15Y_0Pp^=p6RQ#BRQrV$h%O=)lQa^>t41@NeDmh)NB+{KB zJ#shF(b|&srOGs>OVeo%i_RehS-F9nM`Z_gZHK<5rOG4Icq%?6NUw}@%F1Sz=M;{~ z%^97eAM{L%H^!UwTUc_|Q%3d(q{AH#9N#a?U`yRKw-}S$Y_&YNI5BYi^*Xay{?eyEr)3~ z#R%>mBQeP;t7=NiOXnsIfQff$!H`~2n&wfsvAc((eJUZlHL|}?otBywQsoIfQ?8j# zvT`^YETLW@_Oy>}9YvYcGZbTIo;~W8P^57^L#nN)t|TXcWtEjxDc>%m`aPKA3E$EI z&gNfZqz<8IS>||KOKa0gk`-63A}>!g=ae+mEhF_v@{l>bzBaAw!ZY0>RF&0|qnO%g z2GS~ulqK@ABN;ouPzFXz_WqRBH7;wbTSgAoXpDr&WlyX$8mlEY4bv*<#R+*}cw{w2 z$m=vdnFz`08;q96W<=(YtxecaUq{tD4{xS=C`MP8TAJUCGZjOB1Jxh3o906#@sOsY zb1G_r#{SN91l1h98_m)Ry&fhrEbVN@bR<>XZ#Sy?(T08wWF-5J$3UiIC{E{zn%ViJ z@5l;9(r8tCrFk4x?z~uJRVKNYOi!|8RaKVKlX(3+YECRf&dpO3<;0%gkqVJac``{z zxsp6k2`Bdfu_%d(_F~?23dJkhjg#78C?Y9oGWUC?(<#XAhBvF`Sv_e!>0A8nLeaRYEn>r&kT}<^3*p2rR%Te-> z(YRYo)c$D$v6(`3&c%iaw3-_^(x~7rCZ;5 z(}bA3Fr`OV?R&_?zC1OlcF=Yv_M)9+M0S9=k;3fX3z(#SELdApNfVLL?^`HPs5_v< zhdAWBp`1`6`zI|T=eeW|>ogR%Q^3y4Yg%w+04AKNr$!x+9JP{S#yGe#-9^zR_t?Ay zTDpOxzBIaeFU9H{B;>u*Rn1L{!jdzc1WGH0e@>@f=8c?f3J zHPdVOoT{dV`o-iO!o+COGJt?gCpS!VKU5Y=)&(iFC$qb)8?>rOn7q?bnsWRz6k$fE z?j{jh$lMAxG~_fSON?~8LenFm8T2iT8glGTR(i5+B_)2!wC5>s#hw5Mi^<6raVT{i zK@E4#^e<8DvR+^(Vo!HxxH6>QGbAc(kJT2${G>kp0|S52qSPq=M>5O)W`;=|%l-6mr_0*le6il2;bU#Gg)X3{oj;7X_`^ z6VQa626Rbjgxt-gIVaV^-?W9^B(4sgFj^q%PE95ChkHw zIcBP;DKVnaL!tL**4L5?b@JMko~y_^xTMfdxyDaHJ6G9Vhj=P-YAa?`kQda5k-=%? zMGC{p{uHpJ7Y;}Z9&#uaA+ML{=cj3}s+tGxg^_wGY7CxXKZx*~XAL?lI1+sFoW*8?`<`H{qA9?w;HZ`9|@2&gvccIBk3Wob~DPZUGoV1`x z+otQmO}1lX38woeGJg^mBx&!IYu zp%kDIE2)T{Nb$Oj=uGPp%VB9n;*Iypk`nUPMP&&&xeiquXZhwy6tnX|M>ifMryaSo z+b@?>)=i;E-7bAucD0GUfPU3V-+l^_Bhw(+w2@Ow!->Tdw%dZUE7(}Lmgar>?hJ|P z>6Kx6W}o_;z#O5#-PT)O0VfPK@^P@HlHN*@T7X=-*R)?3o2Tzx18GGD1tvG)FTbWTRB7Bfnky*q?Cw%$hHFy5<+WusrB!4(Y~P0K zt2vUk_df8G6TDwV)rz~WW}bJzhoy@8Yb5`A$sjd zR-E?pI&%YUAg?kSOx~iAbE3pYOsQ{(64z-|BJnC``c0O(iK^#!tDaIM8K1r)Nc`9S z!Hl_?0!)fh7KFt)!N$6Vb%&9gJaSk?|46735(jDPHf$hgmNZ1^3jq41Cwaj+ST=`j zSdBT*Mv*FefRq)59xzkO3|Rg1vDBAP=Cu_3fV~1Y>|9Tw!kwY?_d19ZN?Te8KZ`M! zj9!w27!2#5_Rv3&SLEB@x-lP45sQ0`SRN^_te!)1Qw3Qp({A<6K=H=+8gE+2I4Ce5 z-GxPozLDWg&4|&dtdZ)eo;iXwWU(?1H_gXVyhvw@dX85S4VCCSDPtH;ppaR@rRR`| z<5`ROpzKegM_Bj)FE#>*H+UHJM;@#`ksOg+@3lvqR^$?cy{;D zKxz6dldzLdhw-YZ!0Mh`VULccR4*QE5MJ`zqGqZzGirL9E&vsTFGPgS$X z`{=0)0P|(EfvW$kf$|yj#W>>&AM=$IeeZ@f-AUEtUe@r;H59tLDZcw*O@}5$U8u^q z5-?v+!7KhVugoN`caw^O5I>rCG7X!xeQikptlE6D`4;kaZh|_3utHGQmyT>K+swC` zZ(}C!PK>ZLYB6+a;c%){u`jP0G$Zv96%=kI+MJRp?8szyk&6hgqtYl zm=qUait{butH+rM+ssdxpEN%O=p;a=0y>?rhnSx=KSvlezc#;MesOMlo3*+%lqcw9 zK&RN;2?xJoewBRCL)vYp21H-C|R+_sv(q()^M z(nP<$Hh)822Jh(4_D*K%OW}GfPhK)f3|K~FM%z}cYHDsBlOT;TMtw|%%uD7U zNOb*Z{?z<4x}NfzE~;yZ&m;eAjyI4haCKZ`0G+RCiC+9>{++x4MYwXdnt!7-gLH!I zB0MqXKLK5+*~s@??VNB)R?Kk|Hf|J?Va+j|nHjmALmcK<&IITZK$imA3~0-Cj^lV{ z1}6gA3g}8YGFKDgyR!IFol|m7b^WsC`k&?|=(Z9hEiU5F6uVH0^vh_?#<^%~eKVtTeu;BuG+%PAS61JnTb%Hwlp;)84PLvjed^cMiFV9?~Wtn8P_BU@3>sT*~H~> zgh1n3Ks!>bO#PRUP^N)_H`o zk#f_~ARz&^B*|3ORnC}48I+O)cC@WW06hlip0ovoJDfX$E+8DC7$p3Zk=Jn7b33@5+zs4~+)dog+%4R#+-)3*-X{T(b@XXK&j5NB z&~t#E2lN6UvU0ry=;dqB2<|SD#qQzmal>vH%INy9an9l<9HZbo5 z^9^8rMQ2YXiNsml9Isoks;R!QwMC~#CiGi`>(rMdMNGslEv4cuB~8sF*^}JWH!*|A zyEF;f<>L5q!aUg2JU_v&99_ITzLEr~Gx68zwnmcq$X6};hY_R_%7E6HN2B$J#Z%~3 zV{J-+llF4U>RMXqAxJ}<7}rj%m}sb&tWit!rU=(BSvg6jC0~6dh*~ojyQKBxP^*MY zpO(_b^7_X5R9}d}8Glb^SYvozjz>W6ZRbAczTm#(z5?_C;IDw43#K`Q z#$NttG_Ht`P5h&sxO8lKQ@MJwqHe2bgct!iCS-@^Zqrha;vNw1d- zCf>r^jM9Mb1L)IaX}~*4X~4U9H=xe|eUT~+crPDFU*yr*e1DD5XnjuUA>&iiS9E?5 zKQvvGY37G(q?Y=Uik|YiF4~=i`dY(}B5Ms#%BgSCs3iGZK9Bnz?Ev&0pl?Ym6Sl|% zi{r=9!AGY!P^OJbORQ4mFp8)-=jhL16a*s$HXC}gZ z_eO`Q(Gftuq-sfiI$zlxYa?I9A7rpL0{Rv37wNQ(30KUf6o)_ZbNIRZJbpgEfM3YR z_=EX6ei6TzU&6=vrTj8}IbY8o!mr>P_?3Jk-^8!t59OQr7QU5l<5%-*__h2x{xE($ zzk%P#AI=}aYdr8r@<;JU^T+U;_+$Cw_~ZE#_!Ie)_>=il_*41Q_|y3__%r#l__O(Q z_;dO5`1AP-_zU@q_>1{V_)GcC{1$#Izm4C{U&dd~U%_9=U&UX|U&CL^U&mk1@8Eax zH}E&|H}N;~xA3>}xAC|0ckp-ecky@g_we`f_wo1h5AYB25AhH4kMNK3kMWQ5Pw-Fj zPw`Ll&+yOk&+*UmFYqt&FYzz)ukf$(uko+*Z}4yOZ}D&Q@9^*P@A2>RAMhXYAMqda zpYWgZpYfmbU+`b@U-4h_-|*k^-|^q`Kkz^DKk+~Fzwp2Ezwy8GfAG8bKl#7-zxjXo ze+5QB0v1@oB$x$G-~~Yt1xb(vMNkEcU=?gaAHgm-1gGE<+(KW$BX|X$;1>cyKcT-c zK*$jW3WJ2f!VqDoFiaRO>?72sj4#V8C^N z7Xe-jcnRP*;H7|<0bUNc9`GT6R{(ARyb^FD;3mMU03Qmt8E^~WR={n5R|8%HcrD;{ zfDZ$_9`FXh8v!2<_z1unU?B9EfR6%vG~iBpnz6kKefG+`jDd5e3w*cM>cpKpDfG-1l zIp8Y*UkUgsz*hsl2Jp3juLFEN;2nT>0=@z8jeu_gd^6x%0N)DuHo&(7z60=`fbRl) zH{g2!-wXIY!1n`w0Pur=9|HU^;70&I3ivU=j{|-J@RNX_0{k@KX8=D7_&LDO1AYPU zi-2DO{4(HI0KW?OHNdX}egp8EfZqcAHsE&vzYF+1!0!Y80Pu%^KLY$Q;7j*Oti**G`&9Z_1NbEP{S>)?0!@ zLBAO5F~Omr-*EMm;84)7t9nOpDCn0^yC*ml^c$t@avA>9FNb!La`5(Eou)9VsWoo3q>ad$?SGFtc7CV4)(Pts!pth zqL0d^x!ve*`rCG5KPt3a)J_aeu1VW7a}(awn78zk$#E8d3~W(F~uobMd8--HBl;m*Mwzx!tf9iqoiWFIWr3 z1G{OAcr@!**vSQ5EbpeV#Asx)Scp|rA;SZS@AEsb7K%|Smk|~19%whpFk>c_&fw+S zEo-4ThwAi_wNRW-m3q!vD8{IK4_OPvMO1iqtc7BnO7(`dP+U$mGQ6`})k-fmb6#a61+E7n4B4OQtmYoT};mG1>>p}3JM^pLes)Tr=( z!CELDl?^FL#L+9(LU9vC>NpbQmHO%wn@wl(U-5XV)D3H)coG%ZbJjxfRI1+_)ePLz!FQ>Ij1)D>%?xRauFrb)?)W)N9ruoj9p zQH0JPE@nkY+0!*^p?GUA`nOxwLh+7XSVwnq)jE|2{RA#6YoT~I#p^+6QST?oWx8Z7 z6z`*8o!Ox}xvm3iq4*%x+Y8o0@evA;g*%SCWs#Fk>q&dj1#6-BIK}AvA%7?16O$|( zt6n;5q4*Sq>r95$367f7k+o2KmMZTpYoYi8Md|zjX;u@uWi1q6rbwNcva%v&VJ#G2 zqxwCV;|bsDpMqs!g)}O~Hz`_{Ii8&S}1-&)jJPw2Lrk!QWL+X`n#jG%f?zLen&O;h_z7sk*ank z_{lU{KicS)wNU(p;&h&is9Ig_4az zbAuFU9G4Sh~PTV=a^d z6k%?suawh#@}IC4N&{#+d(B!X4Wcmr0c)W&lmd1Bs64ITsjoPD#9AorLjgN4uU!Eq zR@tst3#E}1?Z06yl*Ul3&Oy?FD=?V>vauFQc@&{@xMoL4(gWxpx@NK#O5-Wc|BSUz zDx&b6hagGT(M#4sX}|1l>jtd@YoT-iMd)1DXGch|CMAm#N*$TWjU-K_VBHdZ>=^~3 z6r#Z0e&OF0aDqT3jcuYMC5u!-v3tWLmY5bg$3nLhh*Bv4*krPId}JX$i&dmM^2jfTg0eomQ+n z3PfpHZ#|r9Adw0>Qy@x*P{?jcX1cODMex&!0#RB?L2Eh{Ej^f22|EqwE-4VDRkVfP zB(4sgFj^pIKA9AVQVT`y&8+JJIgibN??Ch|+Piss9NDqI4pK-lJLH zH3gz{3I*+4vUeTgsmRGrfhe6$0lOuP>yVq$f~N}#MCq)(Fp`|PwbMu^589-2Dc~N> zmn;;B(ghT)Hw#{x4|Vf8QXonf@2!356o}I1y>(v)3Pfoe1?+q{lom8;dw#v5>y`ph zx_r0Y%_JG@mI6__s%O@v24Oc8h|;wbuiJ>uv@Wq6c1wXM?Vy;QPffb6jK(v1|U z+odmK&z&~g692Zj>=cO7Eflufg0m~w?kNzZ+bM9j^;TEF`sO#q+}0HZqIB2ZHIP5VIZntXLLA$0vl%AoG-7b9@kZChV?|ml9Uwv3m+c=_?9;0A*>&Y7 znWdorH3g!~?X_z=QXtCWUK^T)0#Q~d>fUkXbwPnBTPgH^PJt-fDeV860#SC+R{n<+ zh_Z(^@V}x!l>HQa?}jyLq1K=M4 z{{?I*u!J9`_dF!~BzQ$KC`!h(==3;c^)0PqT2?jGx2`7aZR87pETZX@B~{A_jZl)s zi@d6kMLL?~cr)mU%9_L+D1u)2DZB_#h+K&8eiG2Hh60D!Re$ba;-eOLz8plxqx>8 z{!<%PHF)X?zh{WY1=a(s7g!&#eqaN@ z_5-#*umgb20d^p@eUp5we4Ko|e1d$Ue3E>!e2RRke42c^e1?3ce3pE+e2#pse4c#1 ze1UwSe35*ye2ILiyjk8NZ|kJr06P@eVZaUtb{}B( z1$G3mBY_}X)e06P}gTwwEn9S3Yau;YO(0Jad=B4GCec7I?e0eb+jlYyNA>{MWb zz!n1=0yYe61lST_rvW>i&MZ&fgpzUjg}M281)<{n+(;-lH@7giU|g>LQ*KfIgz*#d z#utWj!{Z~lxh1&;p>gBMPr0GOP~rG-6Y~nf1;KD}L2hnwsBl6k+^#h-i`Mv1@woir z@#FFeCzcdUD9X>x%@5}0<>wiI`ME&?m^U#$G$8}9u%xJHLMU&1q+nu6IGCF|K7V{9 zuY`QGup%*GbpD|9i5gwp+|@CU*<_8IUa9pT3lCS?nI#x0Cs z+G!ZV^(~7Vng|P9McvAH3mJ)VV-j(tyS0#daKg9=B(73!&6}Wy9QmnJ$dRrV7Z*&V z(O420H<3W(7MJ9eq&cWKtAljFU>3ln7shw(1^QE#AqfMe;&H}QG8X9<<(D#7?N?<&Znt?e;b=>6iOFxuZ)HAsSAH+^!H4oknGZgd2_IZK z#+UL}i3c;+=Z?zB-DqHcCx4$Q_>=r|=7Znl-!mWlDgTxE;9rHwc%ZO~Df0oZ2$>IL zMag_%RS4%;n!$=gab`Z~t9UXW_?1BBg8@oT;(^YMr3_YvZc#{Dp8)Jc@?lJ3?av9v zSH%h0awGkhj_N#;%09}z^DCm|ZLO4FmN24iBTq&!lL?(J`AgDI$|z-YW@EWZUgm@G zN3ChIG2a}WoG9OG;f|(D(N+k2abfq-&L77sX`JhTUDDy#$Qk(f;wlXL4!F**w z=7WQky37Yllz8Gn!jsDhcbQTT?15>7Yf6K%QfVZFYiv2NWx&oLglnYA-3z)sqr^^Q z(<2OS7fra}q?!|rVQ5j-5C%7;RcTXJ16u)XC9qZ7l(ot_9YqNML3xvoC54D;!@>w^56{ROlF~Smib2WjdLrahG8TytI5BLHXdqfYiylO zp2r)PwJryi{Cnc}WA<0hQO+g8IsrF{)vd~T%;dDclMki4=(pj{3zf};HZs9ch}I2dRVAA?i?dm^xhDN8MK)p^j8XsiV~~>R2^b z%~Qvz`RaJJKrK{@)CuZDbw71~b&`63I$52fPE~_yu^LjtYD6tjr>WD`QuRP}hFYeU zs}*XcTBRPOR;y99My*w6sKt{hI!~RiE>IV$G4)`zPFL3s#mF3tJkR4s@JL4t2@-4>J93R>P_m+>MiQ6>TT-n>K*Ex z>RsyH>OJbc>V4|{>I3S7>O<Lb9$fn5shGGNI(BuViQU{?U!0PIR&8-Z;Cb``LP z0^1C13$U%gl1W5z)f!;e0=o`alBd=Ky8+mZz#b0l5x{D|0r z?WYc0ktvdo8fn0ed~LWTx%}_6A^Y z1okFiZwB@jU~dKXHehcDmdwaIfxQdZyMet2*n5E`lj(k79{~12U>^eZVPJ`i9tHL> zU>^te31FWD_94i2FkvvUU^0Qp3?>dt zJeUM9iC~hzB!fu-lL{san5|k<$$q6PGnA~9M3nmYkykPQy$q%LgnEHXK zKbQu9DF;jg!88a=gTXWeOhdsm3{1npv=5l}1=9#HjReyuFpUP&7%+_mQ!beDz%&m2 zkGS&yY~twF_KNJTk`N@V+NzmmdI!@9#TZip7-O4W9AgXHU~I>QUUWhy2_ZeC(gNwd zH_}5okluUmJ^9l9_ehqItd%U;=KlA~b8(qDGjnEUcO~yW64OZRPvQU)2a-65#K9yE zA#o^)!$>@p#Ni~4AaNv#qex6AaWsh;B#t35lfAgXK2Yak`VYA3|yfBfuBL)Lb0@b<8r1<3p&jV)_5hu1#||LOGawaGzESJ7Z*%c z;AcqTS_upM3~jl1q5?ldDwj@9;AiN>b+pBN5gF_nyIe&6dT_bK1b&9zTsEG-&(M!c zG$`;h4B%o(3H%I$qpLI^@G}gH?u>@O&oF|ErReasLRrO%CHR;R{Do*r_N@Hu@#**o zOuoA(v@FxPXfm!p!x*m7kn7Jdj!Q)@;$sS}@D7|7DA=Iu&oG$_CgS=tWXB5`=K3?_ za>2x0e}?H?HcaxR<@z(s;zD7vw1!=OhPhlNLD!!lp9?4C`ZFxxqDi{`3|_9(7}uYn zq~0*%x&91`xp0_^Exzl|P+l)~JlCJ$3@#kU^=GKz0%3MK4cDJx8J9`U^=DYgb>h1I z3~RVZJlCJW$0ZVR{TbGC$*}dP<@z&h;zD7Gj_>+2Y~|u%%Qxg6iRt%0C8=W>}a`&=B?pW%Ei+eFu&;UW$aIqW#DKf|T<`giO=73KOfT+U^~(jFXdxa-ew z6&GsI^=H^!ubc5*e};YaqSif3S(6kA>n7p)GdvfkF=RBJ>(B6F zoW=sP5$XCf9N`KP8PIh78D8OX5v$@@299+qIMuIn=?I%$gRVcrTU;ke*Pr2Cu9Udz z&+q}4Ptf&e_?QbfO(u0NxmBh{aY z23&u}letnH*PpQ!7ihfe&uHTM$+-TE7Os()>(6NCvPrrAj83kSsO!(@X`pn3>(AIG zX4j)!f5!G)HeuJFu_G6c7B@txwgPvWN53IADRX+}q`Kp*u}f0H;=BHg-8op3Tz|%1 z93uLG!l=QGgW@1`eH+X5XY9+-qU9o*(KKCu#{L{3`UySF2%-MOcKsO#B{9ARU4O=* zN%SrO*Pn4X$4g*oarWz!EHPbw#!(zB+Nl(kx_YiZV+Pl2g6q#XmIG)Q*OGAk87FXz z=qJ{rIv?0%4Y~e|lQ>+oLM$pAC$_%p&p3rEH`VoL%;6}}PfBWbA+GDsIE^Dk`=vA^ zX}JE3Gr4{OdpwZa23&u}IUG%6k8At&G+cki`7tsRH-eVy&se~bG*+H=q~I&S2-lyn zNP9d%Ra3*RKVvaRXte9kxQMGqPj9^r#B}``mvH@tWN=!pKVv1=Y?SNISj|>@xdf5vqjuZgZdWj8}4m*-_e@ zu$(lD>(980+i9}v&$ySvG=uBUxSs<>KRq2b?$G0k3Az4^*Kxq;(`&4NfkhU}^=G`1 zqcxA~&v*;Rie4nzramp#pYe8%5WQTrBgA(78Smma$IbO;yqCjAPeGlPB1zYu@d53$ z#fetW^=EvTBgBwKseCoGfNdOqXJ95SvgC3eWzdOqW89JWylHNKwD_+}GQpsnXKzQZAtu@!}7 zRa4Jre4isGvzx*r#@F*1KjPSNT@LjoEVN1!)AJcWO*X@!24d;?j9+lbxB`?|eGbWI zqV#;muQ_O5)OAJzn<_y)pYc0xA(;bLy-b8UUtiB>{E;Ikv+H6+j@0uRf8nsvZ+65^ zN!YrJ*7F&EZ{ql4>iLZSJq~|jU+`|8vp3igwH}`*{ z=QGLN)N#@CnNH%+joS6G^?asNIB4`$cI+h{S~=Q!K9i9H#+8}XJ2r(ao)~&Qler0< z)Y9{rY#gvr`$a>~XL4|~WKMWtIn>nincPk7U$~yn)VitZtEcBPwc~)%@8^Uqnxyo6 zrVb4bH&TEXSI=kaoLKKdlhA;k&(w|M#a+>ny-PsPXX?o@qu=O=lff8zK2sl#6!+8@ z@iIAVx5d-*nbJ6H+zZZFVdLxhOanP^-0Q7afkR5T2^kae@?+`wOhcO1K-fiwww}*) zYI3Owt+R&oe5R2cI8nV@WA%Kd(T&beVtPJPCN~iGJQXp+q5Q&4BM_bQl%Hn{D&Yz|hE&)BCX)3pJT=jgWyyj^lj-JmngPVx^k}fRy zv2&%R=QGXb*ohuuG-Jot^O@#x1C7@6nNH{GP0;h13OPV?==n?wIg*BqDG7sKKAvSt z&q|*(Yj$RSdImq*RQD#Bsg#2^i=NL^#-W;1&u6OOh|QuLjQ;8K~ESk z*hM+5#$}8NrmHqaj}mv^rRU|&%E}1FTh8&CRnKQy#UVA8OX6LOt>-hX<>1Y%=Q9xp zZEiiEX+x7!TVKy-+T7%hYUue)+c;{|JZ>@ce5P|abhGREOglL2@z(R1F5p&+2btuct%$SxlA#WC*a`k_inVvm?pUTR} zo)tVKUCqIdhn~;0J6z9a+Gn~BvJJTSdbjLly4Ek^`+-G#;_Q8<>rFS9ZZzFQ;v5nS zNL)ZtF*`4M2AmfWPQ7qZUZe-3&Zl9z&2)!Qr>ryGN#fi(Wu56BDCHGd*cKRIl_b)3a=w6Hn*T=bHSoxdv4nrk9}N zFdZflDvnol*Xgd;-5|6yy=;2L^eSv2;zAO=Bo_IlG8Ijb%9NEZsP-1tR+ZMQ$S(pRE-!i>dXPq%YN|43Y8L@b;>3!1&rVmLhA+eOiMa|$S z!2?TLUGv*jOrLYUz94aNNL^z3+Vm6DC8lpo-D@ z%(~E!RhAX32!u!tD^^#1#q@%*T5n{j4EW;;DxofmY-vT?jDi|(aYdEC$#4x!Fcz!M z!b17;rRe;dFYvYK7qlpnA}f03MCByqWTmBYiqc9kC`QGkD2iFJC|1R$*cDZAC{D$t zxD}6*qO?}pC~cK?N_!<$>7aB}Iw_r%E=pIWo6=qBq4ZRGDZQ0GN?)a)lBV=m1}FoS zLCRodh%!_erktt_S4Jo!l~GE%GFr({#weM}SY@0tUYVerrc6{ODU+2fWr~umOjUA} zTqRGLrc766C^MB=%4}thGFO?W%vbW2)0F~cfl{axDPCovQmm9HrOF~@u~MciQOcDH zrBXRVsZy$y8l_fQsw`8MD=U9Ws|a5*`jP!wkc;R zXDjC@+m&;b9m;vi`N{>#g~~^GP%5}>1$_>hm%1z46$}P&R%5BQ+${os`%3aFc%00@x%6-cH$^*)S%0tS-%0cB3 zxXOVa|iRX~Goy2oV+(F`bB%V*=1teZb;zcB0OyVUZUP|Il5-%h1auTl~ z@k$b}BJpYxcagZ8#62YLC2=2#*O0iM!~-N=OX77TUQgl;B;H6Oj`%l|h~wR@B;H2i z?IhkoB97U2k$5+W_mFrmi8x;0PvQe4K1kw2BtA^yK@uM!@lg^VBk^$(pCIu`5}zXR zX%Y{S_za29lK32n&y)B9i7%4)5{Wp_9wG54i7%7*3W=|h_!^0?llTUSZ<6>HiEoqm z4vFuQ_#TPxllTFNIJA94;>RR@LgJ?+en#TwB;o+{C5c~=_%(^&koYZ$-;ww|i9e8t zgU?SS{!HR8B>qa`ZzTRs;vXdbN#g%V{ENiDN&JVze@Xn0B!MIyNi9e^fh3V6i6ogM zJxM2$bP`D?lhl%=Q%Gt>l7S>6NhXpMlFTGoNV1Y-BgsyZN|J*lCrK`n+$4EON+GE= zNo`1KOHwAKN+YR1 zNdrh4NYWsZ29q>|q@g4YBk5F&_zA}O7u(IjP%G=`*1lE#uWj->GI=l9WSIE=hSLO(SVKNi#^ANzyEmW|K6Bq`4%`BWXTK`6QiA zQUOW04k#q4h$Jsb3qyhyy*!DIz3<~Cfbk%AVp z|4Ei41uf=|++uSHTFm|jS{f&4G5eoiX}qAt?0KtM8Wyye=kPw%E*Fu%`CKkBL5sP7 z%f=J5n2WeXgMt=wF&9fp&|+Q`U8Mm*i+M?OXEX#Y=1MM>kf6m}%|(+Dw3wH2jfMm* z<`rBjauFZ%9)t;6%&P+h8x*vd*Kxr_1TAL&698_VcE~V6i+Ll5Oia*X_TSeZW@gY5 zw3z+3?uQvJ8Wyye{desrC}=VJ@5@g}&|>!Ac%P)8#q7V^zA=Ipv;Y43uwld#w3z+3 z(uWB&;tN{L{yXBsV#gD-nEm&?#}Txc{r9tnDMd5{EoT2M>d6UO%>EnE;|f~L{`<@0 z30lnl`^OUzw3z*Oh=;95EkTRff1h_)qT>r%%n$H>2wT3P2S#HGTFeh~m9T8$!J7Jl z7W1QlZ8oX2d~s=c@#@ua1TE$#xa=TZs4RLsb=s7?%zTcTnS(zFj+-_)Gb=Bby?yx^ zQzmCmn1mmX$(b-cGpFwDP4m+nBy!ks1TE%g>-F#0fhtPSVt#?khNV3?-f%&S`7jr1 zP|#w2xn4Kp3tG&t)r%T@Ja5vNh{oUKVu=Y_%I+)T{=0A^6cAyh0}pxy7l!}d+C~Uk%>G+u6Be|X{rAB}bSajg z#q7VgH6ka+v?F1H7PJ4R)rhnB*?;G0jK+}Bc!Cy-|DMtqjRj^S zQqW@Y-xV5>0Zl=R#eYj@#Hu)!fn%Mjn`(>yF3ku9NrQqGi~lCfBn2%N|BaT33tBAx zn%T3-v& zfS|?FnJXnFXt8wTDoF@eEIqkGg2EL`A1>S+!WB!JR;0!VS1bcLQvJ*}AY8Ew;Yx9Y zE0$BaK;wlgmXTaP8R3e>f6rWE!WByTXF1N9SE0%jWNc48s%(13$#d1GKh(0exMu--!SRUef%_3Z}Ji?)(kHx_*1ob3= z17JMiisf;R6nnF3M2ZluSf1kgvG+}|e%J|LQ@CPzhU3JZmKbruge#WkIl}Cy2cg4q z(k#Li%S+r&lZ7jmqa3Cgge#U;IZ*WTFk#~kdn6-p^sn0^b&suNWM=d2|3L|c5Pq#Gly~WysdyBPY6H=gUZ?PIUWHJx4hhE!JTix>376w!OtVf`dk1vd3QHp_QX; zZ?UFxz__l9ddH@)#S_EcVja_jPHNd(tm8OfqxOr2y~TPOM@!~}7nVa!dy92)Q~MWg zZ?R@KHGTE$E!JEP82#o@*rG|w-eR5J;BX`TC~@sA)>(=5E;I=Z*jub~IbPfq9of4C z>@C)Ojv4)~Nt_JEu(wzjaHP1WzKGY{VY@A!y~XO~uyHRqV}*@xZ?Tqe;JDXYu>$+w zBbX4poQq{|u`X^}17Q~#+V&P}d2*=|!9H2SuE!ImplE$<8&0%k`Ue3Xr z#ol7QibFN0y~VnlBQ~GC#k!B@C*oIbO5cTdX&6NR8!^co$>a zTdcQo@MgBRSnuGV&24Y7-reNX*0;A}N9KC7N8m?Fgv$t4Znk?yLS1y1%L3m^(~?M9_t&{H%TfbY4ING+tznTT0&9{3Q0c-Ev28N&!k_hANmB+ z?IdXt2HpgFNnu6#!qVd23ks^e@RJ0-3vcGLx@O9-@|xaEP?U14Dd8mHWTBj{t*lu+&?%1TPvGU=)T(q8^Y?{HrNo>Ml3j+t!^c( z!Ml=8SP8+M7-qw%tg2Y#Ev)HPxnMbeS(j0p!{&h#$L6%TY)~((Bgsb+?QMCEt+lNU z76D0TlC++eLSj>w;JS#jviunvG$3sauL9du{iZ(@>}qFQcXX9^tqtM_-nS%e+-vJ$ z>uH1hVKYhFNZKBm&a}F8Zo(0_3F)lczeU~!wZ-*!m2IGHsCEWVwGH=Ya0^LWeceK9 z**~rw?$3G!$2HnE4vX66#uQ}QoJhZa63)VG?6ZxxO$aRNvq?IqZc#^WDu1{OESo#~ z8yGNXU}&jLwN2CR*bLiDf5*-xX-8eh`u6D?NchD!_&NkTHqVwXSa;iCvN(UY4JL~V ze4XY^7?a7Gp4^%$JoYwiMnzRwQLwnzRv}ogvn{k0+e&PuwnetZwldoi8;&v;k#sRh zmymQRNjREbM$+XZT|v^7*V!s{-Goj;CtHoJ*0$8POoy4higZ`_JQ%EWHA%Zj+D+0P z{!yn{H3e10-kRJo6K9uJXO@)~^8p7>>IHs0#=Eq%&^w`eMroC|%v)VOr?fi9TV1-c zz+cE3UT9K9VL?r4MR~r-9Qbj1RbfeK4Yo&Z6#~}%Fc5HT!IILl6;N-42Vfsk8Q%&T z)p|xnd0~~e#ye+8L19Jp^1gj~_3eXC&MjJ6Q{?Yo&oZ=K*0Zo;No6gLl0*I9mQ+;N zOkX~DVE(}V*|R1W6hes9zenF-UbouL#@WiY&4wkhkEH#3Z0FdvlXNXfH$|@Y3+h(; zHBCE>)Ra}1FZT8h{1bKr2|HuiF0E_oK)%V_gpC!h$*Qg{i!gfO`|!pC=P=vlwyU5m zvt41klBDZMx_*!CY8%$b4J6&@>ojdyX;Dqd^b&7raY;?;sI7j5nUFea(28wHmq$r@ zlB8#kb^STpOWgJ6Z7ZlX_Ga?iUZ9BFQ+RhA~-hvbQw(Whb zp?qNb(Do5Y50i9|q(^++Qbz^C2vgfm^Gncz^}n!vqgDS~+jq9_NqUT=$4PpE>j$KG zLe9{(k%zutZGUP7_@C`B+utNTMbgtG9YTPSKST`Vs9h(tw70OIFe59sS5ZMt!4##% z;supurPVck`n;lRae^qx!%m&qVX3#O8WTUWL&X9eO+}?y&~4gv zlm0}b$>sKR=-9n)|3T9_l-HJ(y?oNiEl+7>n3;vwGkmcDMK3GnlPz{9_h_+qMfGg_Ti5CytIa3+q&=#`C;LwFwVa-n zJGP>{1|?y9tEhS1eJA2+ z*k0qkWlOOI3JbElu*i9<7f&e1L2+S0p||Jgin5}kojP~vIy=zma)g)}_|nTEx#>M! zx}lZ8+kr3l=y`Il-hI%U8GIUnDe8l3&>PONNd$ zNA0b#H{00T@=mD1uW_Mu#jq*a*WqA!B#^cCc3$h;$R2xpVFVV(;NE@HdiU+$wxFi% zfT3w=ZL=qv7R@QFs9Z7LTTp~e>_0%;y9j01${*_%*}L0&*n8T0*?ZgjknU~L;qvG; zl3pk24U*m>2`@?C!}0pq>rvCIfzz&76!ybY$^$<dQP|if)iwJ5@p}PbpJJaWbidY~ZJ%n-vFF5FUav+T2R$!xUGv*+=}Gf7{P^c60cMUuYelK!jb zZu7An#u1KYQ@+f)xXikJX)4UV28YcR~@aTuK*~dU1Lm5neo^o&5g&#t!P+XKde| znSIg*_3S@z?C73@#}3Kp*)J_EJ+t4SF?jVi2+t-Xfd7ig8)`K&OjvL8Dk{nr6jV*9 zu6q|EaPbuABr1)L_?2_js($?k;f;kpgV*$EyQ**hA!$9@4(T^|&6>dWEQ6ud4!Pf_ zAw#RZ(!NsYex3acdzHP~USqGdFSReTFSoBC=^K*pPRe&A;X>gDl71xVCz5_9>6h#5 zt1vqg?Q8Ap>^`BB{Y?9Ml78jMag)B9^eaez8D8V)_mF;mX;O=u9xiT)z!E#C@8N96s(?9u%e>22G0`3%=EOH(lT6`amZX;Rn$x` z^)3s<4#f>L7csXHS;k4Zw76U&0{1QuA3j%JY^XN2w9K1bP*XA|@a?361>UmYx#L{> z^tn|+0VCMV876Qk^N%(CH710JMDNq_!~*Tlk^8kf0Fb+lKvtI*G>PB^e;*O zku2O`zruc{{VGl39pIf_N30?`#4vS&3p+?yEb>;I-${2nNq1am$HXYW;vT*vB)}mV${J5_3{z`T0 zg8LB+!aoY8_fGp=5$|!@@3rHV$!`07B%iR`{s76M|4p1gr|MQttfT~E9<)D-5V|qG zg5CDVNS5$wGw>?R{-pgG!MayBQ8!69*;nW*@_F~#pT*2PPqNI@VD)th>?O8C+Yghh zKem@%w!bZOKcJhdo2Q$POPAAi1-b>k5?`rrk#DiD%(ui>en3}bf7kw={eAoUx>EUL zUj@l0k{R6w;x6?{n9exTz#Z>*fpPuHD%_78 z*}34G7uC}Puh1Lpta=JMtF|K95}E|nq^h`*(><*_qE)n00EwU63Y?Wd-x{nY{LKy{EhSRJAcRfnmks>9V0>PU5zny!vkGt@C^raD#~ zr;b-AsHdqD)k*4PHA|hMW~)=x95q+XQ>UrZ)fwtcb(T6?oukfG=c)76eD!p-KwY2~ zszs_-U8okTC2FaYPCkKRhO#E)aB|5b)~vWU9GNB*Q)DO zpGxYP>Uwp9x>4PvZdSLbTh(pqS?by9IqG)xTy=+fo_fA|fqJ2Ok$SOuiF&EJQ@u>R zT)jfQQoTyOTHU4YR`;lT)qUzU>VEZrdaZh$dcAssdZT)idb4_qdaHVydb@gudZ&7q zdbfIydart)dcXRB`k?xd`mlOXeMEg!eN25^eL{UweM)^=J)}OPKC3>bKCix@zNo&W z9#)U2N7a|rSJYS4*VNb5H`F)Px74@Qchq;)_tf{*57ZCUkJOLVPt;G<&(zP=FVrv9 zuhg&AZ`5zq@6_+rAJiY!pVXh#U({dK-_+mLKh!_f|EYhef2;qf|Em8v1c%Pi!f}E_ zbVv@_p?93mC$wNsVM)Ij74<~s9$s&n9^e$#Y4bNAi4<^GQCP{WE_twNj`(*Dw3;7t|7UWL{XP4X_1cayw_;C zBl#MV_mg~p=QzSo4@*$F+A^BO7pCkEsl3yVC zMUr14`7p^xNIpvP%Ot--@~b4jM)K<)S+a$k3^1CFzNAmk5e?anwB!5Km z$0UD3@~0$!M)Kz*e?jt>B!5Nn*Cc;K^0y>^NAmY1|3LDOB>zP6&m{js@~#`(zexU@iS%;F!|bs0-yRe6Fgu)FIIf4; z;o%|;c$gh+xKN@VW=DH2orH(k(UB`8;9+)j;eri%m>u0?6vKUKe7jrSEohEjTr4RM zv!gFpNzB9S=+9*n@Gv_Dalu9!l^sL5a*{@6$8fIHTt;QbD2~=Rqp~A|>o(q~>=?`S z6E!M3CUEJbjLMElTqR+nvSSJtZ;Vmdk;65UF)BNzag8L5%8r>_p#h_^V-6RJ?q)pkuq9Mqq3uj3npt+b`*21gpJCMMO-{lqq1WOmrl;8?5N~A z+NR`)3|7Z37m>fETrM%AvSS68jb~JLtmYC88kHUExL8s~WyhJ(RT?lVJ2pmlM#HG= z*uuq<(Ih+0;u;NUk{#Q*ROHG$W*-gHBsM>cOiYvPxRT3;`BAkr$&OuID9kh4uqN5Dm#c(1fa|JE%gKQZHXrWS++!bj=-AJN z6VfC*uH&LfYLXo{a;3&-k{!3y8%8`$vg39x9Hz02uSs^?RWEitO|s)&E*wXb?0A3+ zgo%AMG|7&KxlD4JWXGdiC$1*h@dOu%r%84^%_S1iBs-qvl40vnOOxz)feVEtI=&{^ zahQvTE#HvlEv6>f@iJEl%O-R<^)<f4wlU{n z^O&6<=H7Mu$Yqn#Bs+fLDiH?Vgf+>I-??st5LZ)^?D!v-ixA?5iI3}Rk{$nW=?D>P zSm}VCsqV_kDR9w7Xp)^LaK(f*$xb<{OR+S`&Xc%kL{5&$f5WEAc?y?{$osK!4QP^` zMy?yfTs&o3UiP#+mX1$O559tPn&UKvjKJ8f|q3(Q8OCfVuW3K1F5)FeCITrOf& z9LvD5P6emBHJ6SM0XL{gcDCa>Notav9k^2Bnq+5ZE}x(#+1ZT?H>62+_T*B@Xp)_M zxJE>F8q_2^)3{Eunq=odt`)I6V`-9|L%3*tnQsG{Wap_|DJe~|b0k-3ugmrF4K!`} zZ&lT+SC7w39|PZhZf1H;#`yf~^t|!G2lt$#xqK2@WoIT=NKmWn9M6TDL#ynZs1>O( zT4iSzN2)&)cosdUZtH5MX)0HWqg8h1ae>Bbm7O!Vell8R=WMQ_=_bZ!$c~*7elB#* zW=u-Y8kZkm ztL&^uDp-82va^bVHA$=NtmP2V?uMwr@pHwY>!Mg%W#@8^7VUAXz1ZsuUo`pT%()zd0Fw{g8D zXqBDkZ~zUtE&jsa7Jr)e;^tM{Dj0agK5GhpSj*%oS^1+UP05%TI%(|S7}0W|sLlsA zSwmW7=LH-t+SVEsjuTs7tL(g(D>qfE?A*ywqNNm?U5Kkyc3#1eqE()nkuj+GwaU)hxO(*T*6Tn_t+Mk@uHTS%S4*qxyoYNxN~`R=pQ}bo10y@# zuvXdm5XXt$sF88%4t@2t%Fai)a`eeUvvLAjW#{9G#7kVO?0kyjHBqbVe1?NGG`*Uy z6rWF|`MTW3(keS&;vmu6T{Fj;T4m=^ju3rbii{AgRd&A0^_oSi?0kbmMIVcUT?k5u z0|&r(T4m?k94Yo@)rb_KRd&9|^<(dwVEwQYzNS{$`60)NJuNZfglUzXpKyfPQHH{> zoZ#NRT09lDdUYT#0pkwaqWo_-r03v8LB^zMxX%w?;vhynr z(+pZ==eHavT2vD@?!dzlcvPfqMgm%8=MNk(`t%wrU?7>Xw93w(Ia>2*m7TwFtms9e zEk)MSDm(w=2+_+`J3?%&vh#0_bKJDb&i^=k^c2(yz>?G|yIN?cEl#w0T4k5S5u&f_ zwIej3Rd$`o!Q#rp>Nx=U;NkZZ$JZ*mT5{mH)}UB{8`LVh3>-TdJ858B_`5x3N{0Wl z9n;xf?ti(!AM{vv{pnITa?H6-QmgE;Cfm-H*bg{AJOuC^SuQ42M`R@s%>gcN9Nm0g`UWHOe?u&ioom0evq zVlulaJYvwpS0|9oiu^;is|Ux9>)@(4VWCx;m{!@~JcAb$}??RK%fL7U6!|~#-=*ZqBpjCD) zWg^I9k$!zX_Z}TIBeVt&RAjNYn5F-4jlJ-D^}pdw92ma zO=}?RB12oN?AnxEYC`L*A+54&D+f+g%iCD3vg_ub;sDK|Rd(IV zku)Tl&7oCx-NC_|MXT((n?p6HR@rqQM{GW=vg<*P*A%U?>tKu?CGISa&?>tg<9N-g zRdzkeAvKmu;$4ibRdyZX;LWU6c0I>I$LF$Mp|GugRj>3`;ZHb;_qgtge*!LoLvNJ6 z*yPmK*DAY?G`XW1T4mQO9JOiw-56SB*Xta**|o~9w>a$a)+)Q+Aox!xBhLMf&{g_RWB;k2o5tE?;M7Th{6XCS>11?!X*XJ$PvKu;qsK))!QVT#2~rKWLVBCeglB(Nv8lL^UnkUe zz~CNj2M_79X3Z;dnY=_U7h1Y)(r0cJx&VD!_@wkI=?=Xn*T}U(OL?ihOkR%P+T>Nz z?d}v|J{M?9x~EC+2urTJjk_%;*PTlGHl%k3liNDbnT|-6yOX=Ky9?=Eq=$jU6C+i@ zfk);g_|vpNI#QBJN5n|n!==yURl0ri2bH-E*KcpdXk9Ch1f|45d z1k$I5ye{rZ?kxPuFQ0KwA$^BqzsYe=hkVAJ>&|meBYj8GcOre~z3v&pME5MxcR}Cv zr?TIrYlQ5E&q&@}=#pAPa|eQT?Bf^HwbJ)>N$sBt4(Ub2{m} z9ve@gdokQL?jpC>z0h6kE^(K-7m>aj>ARD@2kCo~z8C3xlfKUZcbR*MyWDovc2s`V zjfK&d^!-Rb0Lo4NPxl7thxxkAWuX!a_*jWIC(uQI=kZ`bz%|IezDT1HBtsFKjMa;H zA}MI_*7%(}_`A&I5G1i^@|eeKPVjfrawg&7CunPX6;wi78Zfi=s*_6B$!*+g+=Q8l zmPxzUyEm|LSf57v{=QC;isx%`o*fvz(zm*|`B(Zmq#r{1fx(qNIFR&nIq5q{KPa@m zZAbl!7Oql06x83k?o0h!S3lIhb#W|gf~_`qAP(2k9&^RH>zugyp@E+6=AOdtHJlF+ z)?){VL45}FXghE~|208TvwOe$T3+g#d`ow`uOt1)I>V0pMkr_9H@R;n{V39p4w+ls zx4G|xzZMF~N?&_l>K=Kf`)<Bo|O91LZCWmR@Xbt&Hf+kT|bYgMp~7u<)r-itbT zx5oPl_PCF@amdAX&hmAem0kv;XF2v>u=H#0x3sqJ+t|MElKwQ_zLV?_>%OKeO)86oHn3uy)1w0{!y#lpV00vq@Tj=o*r!Xcb+?( zcCwFc?Y}$_Hzl;SJ)%eQ$R52%FTdhBne@tHWf<500rbNRNZ*+|Vxb=mV#Qnf_gdgN%j}(tl?5^z`)N zGwy8C&*3v}U{oft?i3TbTkFoRomt~#e+nO~TU6PhdXw{<79T)0SaHfS61OMi6;siFEStSYU9gOIIw z5k>sb`6LwVZK21DLzbtA^ow_U(A6^Tkuqj_UTCa*fma>K?;;$>Jc~VLq+de%iqKZ} zRCuZ)j!i>6HKeU9aeFF1nnHmPdB_8qakQ!+G|54tTC5{YuiWBK_*Up6i7j zo*PNO#)k`sZ9I^a(6#i$(wbf&k7KXkM{Fp;&(sM03iBPKPUwO=IIXvP9)#!8bBAz+ z=Pu9Po_jp^dhYYw?|FdqYkmDmzmD`i(i7>C+V!O0K>Cf>>AHCy_8jya#J`Vu4ho$- zIKNy$`c0(Y%>VJ%Tk!>sPWXT5KU>W+tEwuh0yBX_U1mA2Vq9z~_!XqhWd)Vh-XgyA z!8+uhWiGFTlhIog7$UnS{p)e`ihwEDzs2enc&8q{lVRcG90q`W^c{A9_CWe2n~k3a_R9Jkno8`inVh zmj+CheobUB5xlqZV5a0;)G1FfD!4$6oSeE`Z~+(v_$L|um#($d(Sv65XK1B_63y{P zdlyzz)$JF|QXm>jWCQsNUKrL7865QY9-MA>3pe`)?e_dc`tyCAg1`CI^E+?2-$;MK zZVyhf7veK0f|pjoGJmJwgI@Lu2Ze`)2Yn-a!+pc{3J>!Ce8c?z!$Jz5nZ83XGihm| zOR5xo%1Oc2Q*a8tB-DCJs}w_oC^JP#!3j3d(G-{tcOEOMrZ^&tx>Hg@hl-Teq`$oG zP?6G3DBGLTJ|#5;n;Q!rrvY2LWA+lpa2uf3M_&KutghA1vA@B`u=p zDJcVdIHO$6MbCGHA6im|q?{U2)SfcJhbxWUT(okOzbO8m*n4_K89uqEx;MXyNb9|5 zaY1cONpF0zg}1Dtl0RplcQEtEzKHSp4;nABHO%KJnJHtZ`Cp?zel9?NE(lK09&`r- zz)%1cx$!J;9=HnZ2e*NH!9nmScpN+lo(C_2Bj6SA8u&*LOja-qj0dy8954^$g91

K&vG%? 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 reBuQzaRq%YFOU6L-ikK(|N8vzKPjI2zkj~)!T + + + + SuppressBuildableAutocreation + + 0C6C630A15D193C800BC68CD + + primary + + + 0C6C642915D5ADB500BC68CD + + primary + + + 0CC3350716C1ED8000399E53 + + primary + + + 1807384A146D0D4E00F05C24 + + primary + + + 18270ED514CF282600B05E7F + + primary + + + 182BB567146F4DCA000BF1F3 + + primary + + + 182BB598146FE295000BF1F3 + + primary + + + 186F778814E59FB200434E1F + + primary + + + 186F778C14E59FDA00434E1F + + primary + + + 18F234EA15C9F9A600060520 + + primary + + + 18FE67E91471A3AA00A2CBE3 + + primary + + + 37A7CEAA197DB8FA00926CE8 + + primary + + + 4C96F7C016D6DF8300D3B39D + + primary + + + 4CB23B45169F5873003A0131 + + primary + + + 4CC7A7B216CC2A84003E10C1 + + primary + + + 4CE4729E16D833FD009070D1 + + primary + + + 5214700516977CB800DF0DB3 + + primary + + + 721680A4179B40F600406BB4 + + primary + + + 72756BFD175D485D00F52070 + + primary + + + + + diff --git a/authd/Info.plist b/authd/Info.plist index 4169be64..45709370 100644 --- a/authd/Info.plist +++ b/authd/Info.plist @@ -19,7 +19,7 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 NSHumanReadableCopyright Copyright © 2012 Apple. All rights reserved. XPCService diff --git a/codesign_tests/FatDynamicValidation.c b/codesign_tests/FatDynamicValidation.c new file mode 100644 index 00000000..a8bfeae9 --- /dev/null +++ b/codesign_tests/FatDynamicValidation.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +int main(int argc, const char * argv[]) { + + printf("Going to sleep for 60 seconds\n"); + + sleep(60); + + return 0; +} diff --git a/codesign_tests/validation.sh b/codesign_tests/validation.sh new file mode 100755 index 00000000..c4398936 --- /dev/null +++ b/codesign_tests/validation.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +echo "[TEST] codesign dynamic validation" + +echo "[BEGIN] Dynamic validate pid 1" +codesign --verify --verbose=3 1 + +if [ $? -ne 0 ] +then + echo "[FAIL]" +else + echo "[PASS]" +fi + +echo "[BEGIN] Dynamic validate a universal binary" + +/AppleInternal/CoreOS/codesign_tests/codesign_tests & +pid=$! +codesign --verify --verbose=3 $! + +if [ $? -ne 0 ] +then + echo "[FAIL]" +else + echo "[PASS]" +fi + +echo "[BEGIN] Dynamic validate a universal binary, 32 bit slice" + +arch -i386 /AppleInternal/CoreOS/codesign_tests/codesign_tests & +pid=$! +codesign --verify --verbose=3 $! + +if [ $? -ne 0 ] +then + echo "[FAIL]" +else + echo "[PASS]" +fi + +# Will exit with status of last command. + +exit $? diff --git a/lib/Info-Security.plist b/lib/Info-Security.plist index 72270a74..4be65305 100644 --- a/lib/Info-Security.plist +++ b/lib/Info-Security.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 diff --git a/lib/plugins/csparser-Info.plist b/lib/plugins/csparser-Info.plist index 76a993e0..0d076c45 100644 --- a/lib/plugins/csparser-Info.plist +++ b/lib/plugins/csparser-Info.plist @@ -17,7 +17,7 @@ CFBundleSignature ???? CFBundleVersion - 55471.14.8 + 55471.14.18 CFBundleShortVersionString 3.0 diff --git a/lib/security.exp-in b/lib/security.exp-in index 4e0838c2..e3d3e437 100644 --- a/lib/security.exp-in +++ b/lib/security.exp-in @@ -415,6 +415,8 @@ _kSecAssessmentAssessmentFromCache _kSecAssessmentAssessmentOriginator _kSecAssessmentAssessmentSource _kSecAssessmentAssessmentVerdict +_kSecAssessmentAssessmentWeakSignature +_kSecAssessmentAssessmentCodeSigningError _kSecAssessmentRuleKeyID _kSecAssessmentRuleKeyPriority _kSecAssessmentRuleKeyAllow diff --git a/libsecurity_apple_csp/libsecurity_apple_csp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_apple_csp/libsecurity_apple_csp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..7e8cf9b3 --- /dev/null +++ b/libsecurity_apple_csp/libsecurity_apple_csp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 0539107D0A37721E00B9E848 + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_apple_cspdl/libsecurity_apple_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_apple_cspdl/libsecurity_apple_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_apple_cspdl/libsecurity_apple_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_apple_file_dl/libsecurity_apple_file_dl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_apple_file_dl/libsecurity_apple_file_dl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_apple_file_dl/libsecurity_apple_file_dl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_apple_x509_cl/libsecurity_apple_x509_cl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_apple_x509_cl/libsecurity_apple_x509_cl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..669930db --- /dev/null +++ b/libsecurity_apple_x509_cl/libsecurity_apple_x509_cl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + C207F276053B21E600FF85CB + + primary + + + + + diff --git a/libsecurity_apple_x509_tp/libsecurity_apple_x509_tp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_apple_x509_tp/libsecurity_apple_x509_tp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_apple_x509_tp/libsecurity_apple_x509_tp.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_asn1/libsecurity_asn1.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_asn1/libsecurity_asn1.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..f262dc8b --- /dev/null +++ b/libsecurity_asn1/libsecurity_asn1.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 795CA7FE0D38013D00BAE6A2 + + primary + + + + + diff --git a/libsecurity_authorization/libsecurity_authorization.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_authorization/libsecurity_authorization.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_authorization/libsecurity_authorization.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_cdsa_client/libsecurity_cdsa_client.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cdsa_client/libsecurity_cdsa_client.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_cdsa_client/libsecurity_cdsa_client.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_cdsa_plugin/libsecurity_cdsa_plugin.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cdsa_plugin/libsecurity_cdsa_plugin.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..7ebc9c56 --- /dev/null +++ b/libsecurity_cdsa_plugin/libsecurity_cdsa_plugin.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + C2C38A530535EDE600D7421F + + primary + + + + + diff --git a/libsecurity_cdsa_utilities/libsecurity_cdsa_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cdsa_utilities/libsecurity_cdsa_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..0bbe8b3a --- /dev/null +++ b/libsecurity_cdsa_utilities/libsecurity_cdsa_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 4CA2A5390523D32800978A7B + + primary + + + 4CF9C5B80535E557009B9B8D + + primary + + + + + diff --git a/libsecurity_cdsa_utils/libsecurity_cdsa_utils.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cdsa_utils/libsecurity_cdsa_utils.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_cdsa_utils/libsecurity_cdsa_utils.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_checkpw/libsecurity_checkpw.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_checkpw/libsecurity_checkpw.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..c4b3b597 --- /dev/null +++ b/libsecurity_checkpw/libsecurity_checkpw.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + 1C6C40211121FC0C00031CDE + + primary + + + 1CD90B6611011176008DD07F + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_cms/libsecurity_cms.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cms/libsecurity_cms.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_cms/libsecurity_cms.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_codesigning/lib/CSCommon.h b/libsecurity_codesigning/lib/CSCommon.h index 15f04c70..37259229 100644 --- a/libsecurity_codesigning/lib/CSCommon.h +++ b/libsecurity_codesigning/lib/CSCommon.h @@ -99,6 +99,16 @@ enum { errSecCSBadCallbackValue = -67020, /* monitor callback returned invalid value */ errSecCSHelperFailed = -67019, /* the codesign_allocate helper tool cannot be found or used */ errSecCSVetoed = -67018, + errSecCSBadLVArch = -67017, /* library validation flag cannot be used with an i386 binary */ + errSecCSResourceNotSupported = -67016, /* unsupported resource found (something not a directory, file or symlink) */ + errSecCSRegularFile = -67015, /* the main executable or Info.plist must be a regular file (no symlinks, etc.) */ + errSecCSUnsealedAppRoot = -67014, /* unsealed contents present in the bundle root */ + errSecCSWeakResourceRules = -67013, /* resource envelope is obsolete */ + errSecCSDSStoreSymlink = -67012, /* .DS_Store files cannot be a symlink */ + errSecCSAmbiguousBundleFormat = -67011, /* bundle format is ambiguous (could be app or framework) */ + errSecCSBadMainExecutable = -67010, /* main executable failed strict validation */ + errSecCSBadFrameworkVersion = -67009, /* embedded framework contains modified or invalid version */ + errSecCSUnsealedFrameworkRoot = -67008, /* unsealed contents present in the root directory of an embedded framework */ }; diff --git a/libsecurity_codesigning/lib/SecAssessment.cpp b/libsecurity_codesigning/lib/SecAssessment.cpp index 2e9862ea..68417ce6 100644 --- a/libsecurity_codesigning/lib/SecAssessment.cpp +++ b/libsecurity_codesigning/lib/SecAssessment.cpp @@ -48,9 +48,10 @@ static void esp_do_check(const char *op, CFDictionaryRef dict) // struct _SecAssessment : private CFRuntimeBase { public: - _SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef r) : path(p), type(typ), result(r) { } + _SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef c, CFDictionaryRef r) : path(p), context(c), type(typ), result(r) { } CFCopyRef path; + CFCopyRef context; AuthorityType type; CFRef result; @@ -106,6 +107,8 @@ CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute"); CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install"); CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen"); +CFStringRef kSecAssessmentContextQuarantineFlags = CFSTR("context:qtnflags"); + // // Read-only in-process access to the policy database @@ -134,6 +137,8 @@ CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:authority:r CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override"); CFStringRef kSecAssessmentAssessmentAuthorityOriginalVerdict = CFSTR("assessment:authority:verdict"); CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached"); +CFStringRef kSecAssessmentAssessmentWeakSignature = CFSTR("assessment:authority:weak"); +CFStringRef kSecAssessmentAssessmentCodeSigningError = CFSTR("assessment:cserror"); CFStringRef kDisabledOverride = CFSTR("security disabled"); @@ -161,7 +166,7 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path, // check the object cache first unless caller denied that or we need extended processing if (!(flags & (kSecAssessmentFlagRequestOrigin | kSecAssessmentFlagIgnoreCache))) { if (gDatabase().checkCache(path, type, flags, result)) - return new SecAssessment(path, type, result.yield()); + return new SecAssessment(path, type, context, result.yield()); } if (flags & kSecAssessmentFlagDirect) { @@ -196,7 +201,7 @@ SecAssessmentRef SecAssessmentCreate(CFURLRef path, __esp_notify_ns("cs-assessment-evaluate", (void *)(CFDictionaryRef)dict); } - return new SecAssessment(path, type, result.yield()); + return new SecAssessment(path, type, context, result.yield()); END_CSAPI_ERRORS1(NULL) } @@ -268,7 +273,7 @@ static void traceResult(CFURLRef target, MessageTrace &trace, std::string &sanit trace.add("signature3", "%s", sanitized.c_str()); trace.add("signature5", "%s", version.c_str()); } - + static void traceAssessment(SecAssessment &assessment, AuthorityType type, CFDictionaryRef result) { if (CFDictionaryGetValue(result, CFSTR("assessment:remote"))) @@ -355,7 +360,9 @@ CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef, result = adulterated.get(); } } - traceAssessment(assessment, assessment.type, result); + bool trace = CFDictionaryContainsKey(assessment.context, kSecAssessmentContextQuarantineFlags); + if (trace) + traceAssessment(assessment, assessment.type, result); return result.yield(); END_CSAPI_ERRORS1(NULL) diff --git a/libsecurity_codesigning/lib/SecAssessment.h b/libsecurity_codesigning/lib/SecAssessment.h index 7c9e3ba6..04a43bb4 100644 --- a/libsecurity_codesigning/lib/SecAssessment.h +++ b/libsecurity_codesigning/lib/SecAssessment.h @@ -70,6 +70,8 @@ extern CFStringRef kSecAssessmentOperationTypeExecute; // .. execute code extern CFStringRef kSecAssessmentOperationTypeInstall; // .. install software extern CFStringRef kSecAssessmentOperationTypeOpenDocument; // .. LaunchServices-level document open +extern CFStringRef kSecAssessmentContextQuarantineFlags; + /*! Operational flags for SecAssessment calls @@ -84,6 +86,9 @@ extern CFStringRef kSecAssessmentOperationTypeOpenDocument; // .. LaunchServices Any content already there is left undisturbed. Independent of kSecAssessmentFlagIgnoreCache. @constant kSecAssessmentFlagEnforce Perform normal operations even if assessments have been globally bypassed (which would usually approve anything). + @constant kSecAssessmentAllowWeak Allow signatures that contain known weaknesses, such as an + insecure resource envelope. + @constant kSecAssessmentIgnoreWhitelist Do not search the weak signature whitelist. Flags common to multiple calls are assigned from high-bit down. Flags for particular calls are assigned low-bit up, and are documented with that call. @@ -97,6 +102,8 @@ enum { kSecAssessmentFlagIgnoreCache = 1 << 28, // do not search cache kSecAssessmentFlagNoCache = 1 << 27, // do not populate cache kSecAssessmentFlagEnforce = 1 << 26, // force on (disable bypass switches) + kSecAssessmentFlagAllowWeak = 1 << 25, // allow weak signatures + kSecAssessmentFlagIgnoreWhitelist = 1 << 24, // do not search weak signature whitelist }; @@ -130,6 +137,8 @@ extern CFStringRef kSecAssessmentAssessmentOriginator; // CFStringRef: describin extern CFStringRef kSecAssessmentAssessmentAuthority; // CFDictionaryRef: authority used to arrive at result extern CFStringRef kSecAssessmentAssessmentSource; // CFStringRef: primary source of authority extern CFStringRef kSecAssessmentAssessmentFromCache; // present if result is from cache +extern CFStringRef kSecAssessmentAssessmentWeakSignature; // present if result attributable to signature weakness +extern CFStringRef kSecAssessmentAssessmentCodeSigningError; // error code returned by code signing API extern CFStringRef kSecAssessmentAssessmentAuthorityRow; // (internal) extern CFStringRef kSecAssessmentAssessmentAuthorityOverride; // (internal) extern CFStringRef kSecAssessmentAssessmentAuthorityOriginalVerdict; // (internal) diff --git a/libsecurity_codesigning/lib/SecCodeSigner.cpp b/libsecurity_codesigning/lib/SecCodeSigner.cpp index a705ba71..8fed4b1d 100644 --- a/libsecurity_codesigning/lib/SecCodeSigner.cpp +++ b/libsecurity_codesigning/lib/SecCodeSigner.cpp @@ -91,7 +91,9 @@ OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags, | kSecCSSignNestedCode | kSecCSSignOpaque | kSecCSSignV1 - | kSecCSSignNoV1); + | kSecCSSignNoV1 + | kSecCSSignBundleRoot + | kSecCSSignStrictPreflight); SecPointer signer = new SecCodeSigner(flags); signer->parameters(parameters); CodeSigning::Required(signerRef) = signer->handle(); diff --git a/libsecurity_codesigning/lib/SecCodeSigner.h b/libsecurity_codesigning/lib/SecCodeSigner.h index d5c15962..f34f273b 100644 --- a/libsecurity_codesigning/lib/SecCodeSigner.h +++ b/libsecurity_codesigning/lib/SecCodeSigner.h @@ -190,6 +190,8 @@ enum { kSecCSSignOpaque = 1 << 3, // treat all files as resources (no nest scan, no flexibility) kSecCSSignV1 = 1 << 4, // sign ONLY in V1 form kSecCSSignNoV1 = 1 << 5, // do not include V1 form + kSecCSSignBundleRoot = 1 << 6, // include files in bundle root + kSecCSSignStrictPreflight = 1 << 7, // fail signing operation if signature would fail strict validation }; diff --git a/libsecurity_codesigning/lib/SecStaticCode.cpp b/libsecurity_codesigning/lib/SecStaticCode.cpp index f0a67ccc..ee14ed9c 100644 --- a/libsecurity_codesigning/lib/SecStaticCode.cpp +++ b/libsecurity_codesigning/lib/SecStaticCode.cpp @@ -112,7 +112,8 @@ OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCodeRef, Se | kSecCSDoNotValidateResources | kSecCSConsiderExpiration | kSecCSEnforceRevocationChecks - | kSecCSCheckNestedCode); + | kSecCSCheckNestedCode + | kSecCSStrictValidate); SecPointer code = SecStaticCode::requiredStatic(staticCodeRef); const SecRequirement *req = SecRequirement::optional(requirementRef); @@ -141,7 +142,7 @@ OSStatus SecCodeCopyPath(SecStaticCodeRef staticCodeRef, SecCSFlags flags, CFURL checkFlags(flags); SecPointer staticCode = SecStaticCode::requiredStatic(staticCodeRef); - CodeSigning::Required(path) = staticCode->canonicalPath(); + CodeSigning::Required(path) = staticCode->copyCanonicalPath(); END_CSAPI } @@ -233,3 +234,15 @@ OSStatus SecStaticCodeSetCallback(SecStaticCodeRef codeRef, SecCSFlags flags, Se END_CSAPI } + + +OSStatus SecStaticCodeSetValidationConditions(SecStaticCodeRef codeRef, CFDictionaryRef conditions) +{ + BEGIN_CSAPI + + checkFlags(0); + SecStaticCode *code = SecStaticCode::requiredStatic(codeRef); + code->setValidationModifiers(conditions); + + END_CSAPI +} diff --git a/libsecurity_codesigning/lib/SecStaticCode.h b/libsecurity_codesigning/lib/SecStaticCode.h index ba083b41..8e5f8c20 100644 --- a/libsecurity_codesigning/lib/SecStaticCode.h +++ b/libsecurity_codesigning/lib/SecStaticCode.h @@ -125,6 +125,10 @@ OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flag @constant kSecCSCheckNestedCode For code in bundle form, locate and recursively check embedded code. Only code in standard locations is considered. + @constant kSecCSStrictValidate + For code in bundle form, perform additional checks to verify that the bundle + is not structured in a way that would allow tampering, and reject any resource + envelope that introduces weaknesses into the signature. @param requirement On optional code requirement specifying additional conditions the staticCode object must satisfy to be considered valid. If NULL, no additional @@ -142,6 +146,7 @@ enum { kSecCSDoNotValidateResources = 1 << 2, kSecCSBasicValidateOnly = kSecCSDoNotValidateExecutable | kSecCSDoNotValidateResources, kSecCSCheckNestedCode = 1 << 3, + kSecCSStrictValidate = 1 << 4, }; OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCode, SecCSFlags flags, diff --git a/libsecurity_codesigning/lib/SecStaticCodePriv.h b/libsecurity_codesigning/lib/SecStaticCodePriv.h index e40f0dfa..ccd9d2b5 100644 --- a/libsecurity_codesigning/lib/SecStaticCodePriv.h +++ b/libsecurity_codesigning/lib/SecStaticCodePriv.h @@ -58,6 +58,18 @@ extern "C" { */ OSStatus SecStaticCodeSetCallback(SecStaticCodeRef code, SecCSFlags flag, SecCodeCallback *olds, SecCodeCallback callback); + +/* + @function SecStaticCodeSetValidationConditions + Set various parameters that modify the evaluation of a signature. + This is an internal affordance used by Gatekeeper to implement checkfix evaluation. + It is not meant to be a generally useful mechanism. + + @param code A Code or StaticCode object whose validation should be modified. + @param conditions A dictionary containing one or more validation conditions. Must not be NULL. + */ +OSStatus SecStaticCodeSetValidationConditions(SecStaticCodeRef code, CFDictionaryRef conditions); + #ifdef __cplusplus } diff --git a/libsecurity_codesigning/lib/StaticCode.cpp b/libsecurity_codesigning/lib/StaticCode.cpp index 2f6b48d2..446647ad 100644 --- a/libsecurity_codesigning/lib/StaticCode.cpp +++ b/libsecurity_codesigning/lib/StaticCode.cpp @@ -35,6 +35,7 @@ #include "detachedrep.h" #include "csdatabase.h" #include "csutilities.h" +#include "dirscanner.h" #include #include #include @@ -48,6 +49,8 @@ #include #include #include +#include +#include namespace Security { @@ -117,7 +120,7 @@ bool SecStaticCode::equal(SecCFObject &secOther) if (mine || his) return mine && his && CFEqual(mine, his); else - return CFEqual(this->canonicalPath(), other->canonicalPath()); + return CFEqual(CFRef(this->copyCanonicalPath()), CFRef(other->copyCanonicalPath())); } CFHashCode SecStaticCode::hash() @@ -125,7 +128,7 @@ CFHashCode SecStaticCode::hash() if (CFDataRef h = this->cdHash()) return CFHash(h); else - return CFHash(this->canonicalPath()); + return CFHash(CFRef(this->copyCanonicalPath())); } @@ -139,6 +142,30 @@ CFTypeRef SecStaticCode::reportEvent(CFStringRef stage, CFDictionaryRef info) else return NULL; } + + +// +// Set validation conditions for fine-tuning legacy tolerance +// +static void addError(CFTypeRef cfError, void* context) +{ + if (CFGetTypeID(cfError) == CFNumberGetTypeID()) { + int64_t error; + CFNumberGetValue(CFNumberRef(cfError), kCFNumberSInt64Type, (void*)&error); + MacOSErrorSet* errors = (MacOSErrorSet*)context; + errors->insert(OSStatus(error)); + } +} + +void SecStaticCode::setValidationModifiers(CFDictionaryRef conditions) +{ + if (conditions) { + CFDictionary source(conditions, errSecCSDbCorrupt); + mAllowOmissions = source.get("omissions"); + if (CFArrayRef errors = source.get("errors")) + CFArrayApplyFunction(errors, CFRangeMake(0, CFArrayGetCount(errors)), addError, &this->mTolerateErrors); + } +} // @@ -691,7 +718,7 @@ void SecStaticCode::validateResources(SecCSFlags flags) try { // sanity first CFDictionaryRef sealedResources = resourceDictionary(); - if (this->resourceBase()) // disk has resources + if (this->resourceBase()) // disk has resources if (sealedResources) /* go to work below */; else @@ -722,11 +749,17 @@ void SecStaticCode::validateResources(SecCSFlags flags) } if (!rules || !files) MacOSError::throwMe(errSecCSResourcesInvalid); + // check for weak resource rules + bool strict = flags & kSecCSStrictValidate; + if (strict && (version == 1 || hasWeakResourceRules(rules, mAllowOmissions))) + if (mTolerateErrors.find(errSecCSWeakResourceRules) == mTolerateErrors.end()) + MacOSError::throwMe(errSecCSWeakResourceRules); __block CFRef resourceMap = makeCFMutableDictionary(files); - ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType); + string base = cfString(this->resourceBase()); + ResourceBuilder resources(base, base, rules, codeDirectory()->hashType, strict, mTolerateErrors); diskRep()->adjustResources(resources); resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, ResourceBuilder::Rule *rule) { - validateResource(files, relpath, *mResourcesValidContext, flags, version); + validateResource(files, relpath, ent->fts_info == FTS_SL, *mResourcesValidContext, flags, version); CFDictionaryRemoveValue(resourceMap, CFTempString(relpath)); }); @@ -776,6 +809,42 @@ void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void * } +static bool isOmitRule(CFTypeRef value) +{ + if (CFGetTypeID(value) == CFBooleanGetTypeID()) + return value == kCFBooleanFalse; + CFDictionary rule(value, errSecCSResourceRulesInvalid); + return rule.get("omit") == kCFBooleanTrue; +} + +bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, CFArrayRef allowedOmissions) +{ + // compute allowed omissions + CFRef defaultOmissions = this->diskRep()->allowedResourceOmissions(); + assert(defaultOmissions); + CFRef allowed = CFArrayCreateMutableCopy(NULL, 0, defaultOmissions); + if (allowedOmissions) + CFArrayAppendArray(allowed, allowedOmissions, CFRangeMake(0, CFArrayGetCount(allowedOmissions))); + CFRange range = CFRangeMake(0, CFArrayGetCount(allowed)); + + // check all resource rules for weakness + __block bool coversAll = false; + __block bool forbiddenOmission = false; + CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid); + rules.apply(^(CFStringRef key, CFTypeRef value) { + string pattern = cfString(key, errSecCSResourceRulesInvalid); + if (pattern == "^.*" && value == kCFBooleanTrue) { + coversAll = true; + return; + } + if (isOmitRule(value)) + forbiddenOmission |= !CFArrayContainsValue(allowed, range, key); + }); + + return !coversAll || forbiddenOmission; +} + + // // Load, validate, cache, and return CFDictionary forms of sealed resources. // @@ -909,7 +978,7 @@ CFDataRef SecStaticCode::resource(string path) } -void SecStaticCode::validateResource(CFDictionaryRef files, string path, ValidationContext &ctx, SecCSFlags flags, uint32_t version) +void SecStaticCode::validateResource(CFDictionaryRef files, string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version) { if (!resourceBase()) // no resources in DiskRep MacOSError::throwMe(errSecCSResourcesNotFound); @@ -917,7 +986,12 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, Validat if (CFTypeRef file = CFDictionaryGetValue(files, CFTempString(path))) { ResourceSeal seal = file; if (seal.nested()) { - validateNestedCode(fullpath, seal, flags); + if (isSymlink) + return ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // changed type + string suffix = ".framework"; + bool isFramework = (path.length() > suffix.length()) + && (path.compare(path.length()-suffix.length(), suffix.length(), suffix) == 0); + validateNestedCode(fullpath, seal, flags, isFramework); } else if (seal.link()) { char target[PATH_MAX]; ssize_t len = ::readlink(cfString(fullpath).c_str(), target, sizeof(target)-1); @@ -953,7 +1027,7 @@ void SecStaticCode::validateResource(CFDictionaryRef files, string path, Validat ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase())); } -void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags) +void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags, bool isFramework) { CFRef req; if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref())) @@ -966,6 +1040,16 @@ void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecPointer code = new SecStaticCode(DiskRep::bestGuess(cfString(path))); code->setMonitor(this->monitor()); code->staticValidate(flags, SecRequirement::required(req)); + + if (isFramework && (flags & kSecCSStrictValidate)) + try { + validateOtherVersions(path, flags, req, code); + } catch (const CSError &err) { + MacOSError::throwMe(errSecCSBadFrameworkVersion); + } catch (const MacOSError &err) { + MacOSError::throwMe(errSecCSBadFrameworkVersion); + } + } catch (CSError &err) { if (err.error == errSecCSReqFailed) { mResourcesValidContext->reportProblem(errSecCSBadNestedCode, kSecCFErrorResourceAltered, path); @@ -982,6 +1066,52 @@ void SecStaticCode::validateNestedCode(CFURLRef path, const ResourceSeal &seal, } } +void SecStaticCode::validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRequirementRef req, SecStaticCode *code) +{ + // Find out what current points to and do not revalidate + std::string mainPath = cfStringRelease(code->diskRep()->copyCanonicalPath()); + + char main_path[PATH_MAX]; + bool foundTarget = false; + + /* If it failed to get the target of the symlink, do not fail. It is a performance loss, + not a security hole */ + if (realpath(mainPath.c_str(), main_path) != NULL) + foundTarget = true; + + std::ostringstream versionsPath; + versionsPath << cfString(path) << "/Versions/"; + + DirScanner scanner(versionsPath.str()); + + if (scanner.initialized()) { + struct dirent *entry = NULL; + while ((entry = scanner.getNext()) != NULL) { + std::ostringstream fullPath; + + if (entry->d_type != DT_DIR || + strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0 || + strcmp(entry->d_name, "Current") == 0) + continue; + + fullPath << versionsPath.str() << entry->d_name; + + char real_full_path[PATH_MAX]; + if (realpath(fullPath.str().c_str(), real_full_path) == NULL) + UnixError::check(-1); + + // Do case insensitive comparions because realpath() was called for both paths + if (foundTarget && strcmp(main_path, real_full_path) == 0) + continue; + + SecPointer frameworkVersion = new SecStaticCode(DiskRep::bestGuess(real_full_path)); + frameworkVersion->setMonitor(this->monitor()); + frameworkVersion->staticValidate(flags, SecRequirement::required(req)); + } + } +} + // // Test a CodeDirectory flag. @@ -1318,10 +1448,17 @@ void SecStaticCode::staticValidate(SecCSFlags flags, const SecRequirement *req) subcode->staticValidateCore(flags, req); }); + // allow monitor intervention in source validation phase + reportEvent(CFSTR("prepared"), NULL); + // resources: once for all architectures if (!(flags & kSecCSDoNotValidateResources)) this->validateResources(flags); + // perform strict validation if desired + if (flags & kSecCSStrictValidate) + mRep->strictValidate(mTolerateErrors); + // allow monitor intervention if (CFRef veto = reportEvent(CFSTR("validated"), NULL)) { if (CFGetTypeID(veto) == CFNumberGetTypeID()) @@ -1375,6 +1512,9 @@ void SecStaticCode::handleOtherArchitectures(void (^handle)(SecStaticCode* other size_t activeOffset = fat->archOffset(); for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) { ctx.offset = fat->archOffset(*arch); + if (ctx.offset > SIZE_MAX) + MacOSError::throwMe(errSecCSInternalError); + ctx.size = fat->lengthOfSlice((size_t)ctx.offset); if (ctx.offset != activeOffset) { // inactive architecture; check it SecPointer subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx)); subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature diff --git a/libsecurity_codesigning/lib/StaticCode.h b/libsecurity_codesigning/lib/StaticCode.h index b7c5b72a..29816541 100644 --- a/libsecurity_codesigning/lib/StaticCode.h +++ b/libsecurity_codesigning/lib/StaticCode.h @@ -117,7 +117,7 @@ public: DiskRep *diskRep() { return mRep; } bool isDetached() const { return mRep->base() != mRep; } std::string mainExecutablePath() { return mRep->mainExecutablePath(); } - CFURLRef canonicalPath() const { return mRep->canonicalPath(); } + CFURLRef copyCanonicalPath() const { return mRep->copyCanonicalPath(); } std::string identifier() { return codeDirectory()->identifier(); } const char *teamID() { return codeDirectory()->teamID(); } std::string format() const { return mRep->format(); } @@ -131,7 +131,7 @@ public: CFURLRef resourceBase(); CFDataRef resource(std::string path); CFDataRef resource(std::string path, ValidationContext &ctx); - void validateResource(CFDictionaryRef files, std::string path, ValidationContext &ctx, SecCSFlags flags, uint32_t version); + void validateResource(CFDictionaryRef files, std::string path, bool isSymlink, ValidationContext &ctx, SecCSFlags flags, uint32_t version); bool flag(uint32_t tested); @@ -139,6 +139,8 @@ public: void setMonitor(SecCodeCallback monitor) { mMonitor = monitor; } CFTypeRef reportEvent(CFStringRef stage, CFDictionaryRef info); + void setValidationModifiers(CFDictionaryRef modifiers); + void resetValidity(); // clear validation caches (if something may have changed) bool validated() const { return mValidated; } @@ -152,7 +154,7 @@ public: void validateNonResourceComponents(); void validateResources(SecCSFlags flags); void validateExecutable(); - void validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags); + void validateNestedCode(CFURLRef path, const ResourceSeal &seal, SecCSFlags flags, bool isFramework); const Requirements *internalRequirements(); const Requirement *internalRequirement(SecRequirementType type); @@ -182,13 +184,21 @@ protected: CFTypeRef verificationPolicy(SecCSFlags flags); static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context); + bool hasWeakResourceRules(CFDictionaryRef rulesDict, CFArrayRef allowedOmissions); void handleOtherArchitectures(void (^handle)(SecStaticCode* other)); +private: + void validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRequirementRef req, SecStaticCode *code); + private: RefPointer mRep; // on-disk representation CFRef mDetachedSig; // currently applied explicit detached signature + // private validation modifiers (only used by Gatekeeper checkfixes) + MacOSErrorSet mTolerateErrors; // soft error conditions to ignore + CFRef mAllowOmissions; // additionally allowed resource omissions + // master validation state bool mValidated; // core validation was attempted OSStatus mValidationResult; // outcome of core validation diff --git a/libsecurity_codesigning/lib/bundlediskrep.cpp b/libsecurity_codesigning/lib/bundlediskrep.cpp index 41423faf..636e213a 100644 --- a/libsecurity_codesigning/lib/bundlediskrep.cpp +++ b/libsecurity_codesigning/lib/bundlediskrep.cpp @@ -22,6 +22,7 @@ */ #include "bundlediskrep.h" #include "filediskrep.h" +#include "dirscanner.h" #include #include #include @@ -68,22 +69,36 @@ BundleDiskRep::~BundleDiskRep() void BundleDiskRep::setup(const Context *ctx) { mInstallerPackage = false; // default - - // deal with versioned bundles (aka Frameworks) - string version = resourcesRootPath() - + "/Versions/" + + // capture the path of the main executable before descending into a specific version + CFRef mainExecBefore = CFBundleCopyExecutableURL(mBundle); + + // validate the bundle root; fish around for the desired framework version + string root = cfStringRelease(copyCanonicalPath()); + string contents = root + "/Contents"; + string version = root + "/Versions/" + ((ctx && ctx->version) ? ctx->version : "Current") + "/."; - if (::access(version.c_str(), F_OK) == 0) { // versioned bundle + if (::access(contents.c_str(), F_OK) == 0) { // not shallow + DirValidator val; + val.require("^Contents$", DirValidator::directory); // duh + val.allow("^(\\.LSOverride|\\.DS_Store|Icon\r|\\.SoftwareDepot\\.tracking)$", DirValidator::file | DirValidator::noexec); + try { + val.validate(root, errSecCSUnsealedAppRoot); + } catch (const MacOSError &err) { + recordStrictError(err.error); + } + } else if (::access(version.c_str(), F_OK) == 0) { // versioned bundle if (CFBundleRef versionBundle = CFBundleCreate(NULL, CFTempURL(version))) mBundle.take(versionBundle); // replace top bundle ref else MacOSError::throwMe(errSecCSStaticCodeNotFound); + validateFrameworkRoot(root); } else { if (ctx && ctx->version) // explicitly specified MacOSError::throwMe(errSecCSStaticCodeNotFound); } - + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle); assert(infoDict); // CFBundle will always make one up for us CFTypeRef mainHTML = CFDictionaryGetValue(infoDict, CFSTR("MainHTML")); @@ -92,8 +107,28 @@ void BundleDiskRep::setup(const Context *ctx) // conventional executable bundle: CFBundle identifies an executable for us if (CFRef mainExec = CFBundleCopyExecutableURL(mBundle)) // if CFBundle claims an executable... if (mainHTML == NULL) { // ... and it's not a widget + + // Note that this check is skipped if there is a specific framework version checked. + // That's because you know what you are doing if you are looking at a specific version. + // This check is designed to stop someone who did a verification on an app root, from mistakenly + // verifying a framework + if (mainExecBefore && (!ctx || !ctx->version)) { + char main_exec_before[PATH_MAX]; + char main_exec[PATH_MAX]; + // The realpath call is important because alot of Framework bundles have a symlink + // to their "Current" version binary in the main bundle + if (realpath(cfString(mainExecBefore).c_str(), main_exec_before) == NULL || + realpath(cfString(mainExec).c_str(), main_exec) == NULL) + MacOSError::throwMe(errSecCSInternalError); + + if (strcmp(main_exec_before, main_exec) != 0) + recordStrictError(errSecCSAmbiguousBundleFormat); + } + mMainExecutableURL = mainExec; mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx); + if (!mExecRep->fd().isPlainFile(this->mainExecutablePath())) + recordStrictError(errSecCSRegularFile); mFormat = "bundle with " + mExecRep->format(); return; } @@ -107,6 +142,8 @@ void BundleDiskRep::setup(const Context *ctx) if (!mMainExecutableURL) MacOSError::throwMe(errSecCSBadBundleFormat); mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); + if (!mExecRep->fd().isPlainFile(this->mainExecutablePath())) + recordStrictError(errSecCSRegularFile); mFormat = "widget bundle"; return; } @@ -116,6 +153,8 @@ void BundleDiskRep::setup(const Context *ctx) // focus on the Info.plist (which we know exists) as the nominal "main executable" file mMainExecutableURL = infoURL; mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); + if (!mExecRep->fd().isPlainFile(this->mainExecutablePath())) + recordStrictError(errSecCSRegularFile); if (packageVersion) { mInstallerPackage = true; mFormat = "installer package bundle"; @@ -130,6 +169,8 @@ void BundleDiskRep::setup(const Context *ctx) if (!distFile.empty()) { mMainExecutableURL = makeCFURL(distFile); mExecRep = new FileDiskRep(this->mainExecutablePath().c_str()); + if (!mExecRep->fd().isPlainFile(this->mainExecutablePath())) + recordStrictError(errSecCSRegularFile); mInstallerPackage = true; mFormat = "installer package bundle"; return; @@ -206,7 +247,7 @@ void BundleDiskRep::createMeta() string meta = metaPath(BUNDLEDISKREP_DIRECTORY); if (!mMetaExists) { if (::mkdir(meta.c_str(), 0755) == 0) { - copyfile(cfString(canonicalPath(), true).c_str(), meta.c_str(), NULL, COPYFILE_SECURITY); + copyfile(cfStringRelease(copyCanonicalPath()).c_str(), meta.c_str(), NULL, COPYFILE_SECURITY); mMetaPath = meta; mMetaExists = true; } else if (errno != EEXIST) @@ -214,6 +255,31 @@ void BundleDiskRep::createMeta() } } +// +// Load's a CFURL and makes sure that it is a regular file and not a symlink (or fifo, etc.) +// +CFDataRef BundleDiskRep::loadRegularFile(CFURLRef url) +{ + assert(url); + + CFDataRef data = NULL; + + std::string path(cfString(url)); + + AutoFileDesc fd(path); + + if (!fd.isPlainFile(path)) + recordStrictError(errSecCSRegularFile); + + data = cfLoadFile(fd, fd.fileSize()); + + if (!data) { + secdebug(__PRETTY_FUNCTION__, "failed to load %s", cfString(url).c_str()); + MacOSError::throwMe(errSecCSInternalError); + } + + return data; +} // // Load and return a component, by slot number. @@ -228,7 +294,7 @@ CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot) // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there case cdInfoSlot: if (CFRef info = _CFBundleCopyInfoPlistURL(mBundle)) - return cfLoadFile(info); + return loadRegularFile(info); else return NULL; // by default, we take components from the executable image or files @@ -258,7 +324,7 @@ CFDataRef BundleDiskRep::identification() // // Various aspects of our DiskRep personality. // -CFURLRef BundleDiskRep::canonicalPath() +CFURLRef BundleDiskRep::copyCanonicalPath() { if (CFURLRef url = CFBundleCopyBundleURL(mBundle)) return url; @@ -361,10 +427,10 @@ string BundleDiskRep::recommendedIdentifier(const SigningContext &) return cfString(identifier); // fall back to using the canonical path - return canonicalIdentifier(cfString(this->canonicalPath())); + return canonicalIdentifier(cfStringRelease(this->copyCanonicalPath())); } -CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx) +string BundleDiskRep::resourcesRelativePath() { // figure out the resource directory base. Clean up some gunk inserted by CFBundle in frameworks string rbase = this->resourcesRootPath(); @@ -385,6 +451,13 @@ CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx) else resources = resources.substr(rbase.length() + 1) + "/"; // differential path segment + return resources; +} + +CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx) +{ + string resources = this->resourcesRelativePath(); + // installer package rules if (mInstallerPackage) return cfmake("{rules={" @@ -447,6 +520,20 @@ CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx) ); } + +CFArrayRef BundleDiskRep::allowedResourceOmissions() +{ + return cfmake("[" + "'^(.*/)?\\.DS_Store$'" + "'^Info\\.plist$'" + "'^PkgInfo$'" + "%s" + "]", + (string("^") + this->resourcesRelativePath() + ".*\\.lproj/locversion.plist$").c_str() + ); +} + + const Requirements *BundleDiskRep::defaultRequirements(const Architecture *arch, const SigningContext &ctx) { return mExecRep->defaultRequirements(arch, ctx); @@ -458,6 +545,61 @@ size_t BundleDiskRep::pageSize(const SigningContext &ctx) } +// +// Strict validation. +// Takes an array of CFNumbers of errors to tolerate. +// +void BundleDiskRep::strictValidate(const ToleratedErrors& tolerated) +{ + std::vector fatalErrors; + set_difference(mStrictErrors.begin(), mStrictErrors.end(), tolerated.begin(), tolerated.end(), back_inserter(fatalErrors)); + if (!fatalErrors.empty()) + MacOSError::throwMe(fatalErrors[0]); + mExecRep->strictValidate(tolerated); +} + +void BundleDiskRep::recordStrictError(OSStatus error) +{ + mStrictErrors.insert(error); +} + + +// +// Check framework root for unsafe symlinks and unsealed content. +// +void BundleDiskRep::validateFrameworkRoot(string root) +{ + // build regex element that matches either the "Current" symlink, or the name of the current version + string current = "Current"; + char currentVersion[PATH_MAX]; + ssize_t len = ::readlink((root + "/Versions/Current").c_str(), currentVersion, sizeof(currentVersion)-1); + if (len > 0) { + currentVersion[len] = '\0'; + current = string("(Current|") + ResourceBuilder::escapeRE(currentVersion) + ")"; + } + + DirValidator val; + val.require("^Versions$", DirValidator::directory | DirValidator::descend); // descend into Versions directory + val.require("^Versions/[^/]+$", DirValidator::directory); // require at least one version + val.require("^Versions/Current$", DirValidator::symlink, // require Current symlink... + "^(\\./)?(\\.\\.[^/]+|\\.?[^\\./][^/]*)$"); // ...must point to a version + val.allow("^(Versions/)?\\.DS_Store$", DirValidator::file | DirValidator::noexec); // allow .DS_Store files + val.allow("^[^/]+$", DirValidator::symlink, ^ string (const string &name, const string &target) { + // top-level symlinks must point to namesake in current version + return string("^(\\./)?Versions/") + current + "/" + ResourceBuilder::escapeRE(name) + "$"; + }); + // module.map must be regular non-executable file, or symlink to module.map in current version + val.allow("^module\\.map$", DirValidator::file | DirValidator::noexec | DirValidator::symlink, + string("^(\\./)?Versions/") + current + "/module\\.map$"); + + try { + val.validate(root, errSecCSUnsealedFrameworkRoot); + } catch (const MacOSError &err) { + recordStrictError(err.error); + } +} + + // // Writers // diff --git a/libsecurity_codesigning/lib/bundlediskrep.h b/libsecurity_codesigning/lib/bundlediskrep.h index 6dfb12fb..a697237a 100644 --- a/libsecurity_codesigning/lib/bundlediskrep.h +++ b/libsecurity_codesigning/lib/bundlediskrep.h @@ -56,8 +56,9 @@ public: CFDataRef component(CodeDirectory::SpecialSlot slot); CFDataRef identification(); std::string mainExecutablePath(); - CFURLRef canonicalPath(); + CFURLRef copyCanonicalPath(); std::string resourcesRootPath(); + std::string resourcesRelativePath(); void adjustResources(ResourceBuilder &builder); Universal *mainExecutableImage(); size_t signingBase(); @@ -72,6 +73,9 @@ public: const Requirements *defaultRequirements(const Architecture *arch, const SigningContext &ctx); size_t pageSize(const SigningContext &ctx); + void strictValidate(const ToleratedErrors& tolerated); + CFArrayRef allowedResourceOmissions(); + CFBundleRef bundle() const { return mBundle; } public: @@ -87,6 +91,9 @@ protected: private: void setup(const Context *ctx); // shared init void checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot); + CFDataRef loadRegularFile(CFURLRef url); + void recordStrictError(OSStatus error); + void validateFrameworkRoot(std::string root); private: CFRef mBundle; @@ -96,6 +103,7 @@ private: bool mInstallerPackage; // is an installer (not executable) bundle string mFormat; // format description string RefPointer mExecRep; // DiskRep for main executable file + std::set mStrictErrors; // strict validation errors encountered }; diff --git a/libsecurity_codesigning/lib/dirscanner.cpp b/libsecurity_codesigning/lib/dirscanner.cpp new file mode 100644 index 00000000..12756ab3 --- /dev/null +++ b/libsecurity_codesigning/lib/dirscanner.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include "dirscanner.h" + +namespace Security { +namespace CodeSigning { + + +DirScanner::DirScanner(const char *path) + : init(false) +{ + this->path = std::string(path); + this->initialize(); +} + +DirScanner::DirScanner(string path) + : init(false) +{ + this->path = path; + this->initialize(); +} + +DirScanner::~DirScanner() +{ + if (this->dp != NULL) + (void) closedir(this->dp); +} + +void DirScanner::initialize() +{ + if (this->dp == NULL) { + errno = 0; + if ((this->dp = opendir(this->path.c_str())) == NULL) { + if (errno == ENOENT) { + init = false; + } else { + UnixError::check(-1); + } + } else + init = true; + } else + MacOSError::throwMe(errSecInternalError); +} + +struct dirent * DirScanner::getNext() +{ + return readdir(this->dp); +} + +bool DirScanner::initialized() +{ + return this->init; +} + + +DirValidator::~DirValidator() +{ + for (Rules::iterator it = mRules.begin(); it != mRules.end(); ++it) + delete *it; +} + +void DirValidator::validate(const string &root, OSStatus error) +{ + std::set reqMatched; + FTS fts(root); + while (FTSENT *ent = fts_read(fts)) { + const char *relpath = ent->fts_path + root.size() + 1; // skip prefix + "/" + bool executable = ent->fts_statp->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH); + Rule *rule = NULL; + switch (ent->fts_info) { + case FTS_F: + secdebug("dirval", "file %s", ent->fts_path); + rule = match(relpath, file, executable); + break; + case FTS_SL: { + secdebug("dirval", "symlink %s", ent->fts_path); + char target[PATH_MAX]; + ssize_t len = ::readlink(ent->fts_accpath, target, sizeof(target)-1); + if (len < 0) + UnixError::throwMe(); + target[len] = '\0'; + rule = match(relpath, symlink, executable, target); + break; + } + case FTS_D: + secdebug("dirval", "entering %s", ent->fts_path); + if (ent->fts_level == FTS_ROOTLEVEL) + continue; // skip root directory + rule = match(relpath, directory, executable); + if (!rule || !(rule->flags & descend)) + fts_set(fts, ent, FTS_SKIP); // do not descend + break; + case FTS_DP: + secdebug("dirval", "leaving %s", ent->fts_path); + continue; + default: + secdebug("dirval", "type %d (errno %d): %s", ent->fts_info, ent->fts_errno, ent->fts_path); + MacOSError::throwMe(error); // not a file, symlink, or directory + } + if (!rule) + MacOSError::throwMe(error); // no match + else if (rule->flags & required) + reqMatched.insert(rule); + } + if (reqMatched.size() != mRequireCount) { + secdebug("dirval", "matched %d of %d required rules", reqMatched.size(), mRequireCount); + MacOSError::throwMe(error); // not all required rules were matched + } +} + +DirValidator::Rule * DirValidator::match(const char *path, uint32_t flags, bool executable, const char *target) +{ + for (Rules::iterator it = mRules.begin(); it != mRules.end(); ++it) { + Rule *rule = *it; + if ((rule->flags & flags) + && !(executable && (rule->flags & noexec)) + && rule->match(path) + && (!target || rule->matchTarget(path, target))) + return rule; + } + return NULL; +} + +DirValidator::FTS::FTS(const string &path, int options) +{ + const char * paths[2] = { path.c_str(), NULL }; + mFTS = fts_open((char * const *)paths, options, NULL); + if (!mFTS) + UnixError::throwMe(); +} + +DirValidator::FTS::~FTS() +{ + fts_close(mFTS); +} + +DirValidator::Rule::Rule(const string &pattern, uint32_t flags, TargetPatternBuilder targetBlock) + : ResourceBuilder::Rule(pattern, 0, flags), mTargetBlock(NULL) +{ + if (targetBlock) + mTargetBlock = Block_copy(targetBlock); +} + +DirValidator::Rule::~Rule() +{ + if (mTargetBlock) + Block_release(mTargetBlock); +} + +bool DirValidator::Rule::matchTarget(const char *path, const char *target) const +{ + if (!mTargetBlock) + MacOSError::throwMe(errSecCSInternalError); + string pattern = mTargetBlock(path, target); + if (pattern.empty()) + return true; // always match empty pattern + secdebug("dirval", "%s: match target %s against %s", path, target, pattern.c_str()); + regex_t re; + if (::regcomp(&re, pattern.c_str(), REG_EXTENDED | REG_NOSUB)) + MacOSError::throwMe(errSecCSInternalError); + switch (::regexec(&re, target, 0, NULL, 0)) { + case 0: + return true; + case REG_NOMATCH: + return false; + default: + MacOSError::throwMe(errSecCSInternalError); + } +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/libsecurity_codesigning/lib/dirscanner.h b/libsecurity_codesigning/lib/dirscanner.h new file mode 100644 index 00000000..ddd5e56f --- /dev/null +++ b/libsecurity_codesigning/lib/dirscanner.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _H_DIRSCANNER +#define _H_DIRSCANNER + +#include "resources.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +class DirScanner { +public: + DirScanner(const char *path); + DirScanner(string path); + ~DirScanner(); + + struct dirent *getNext(); // gets the next item out of this DirScanner + bool initialized(); // returns false if the constructor failed to initialize the dirent + +private: + string path; + DIR *dp = NULL; + void initialize(); + bool init; +}; + + +class DirValidator { +public: + DirValidator() : mRequireCount(0) { } + ~DirValidator(); + + enum { + file = 0x01, + directory = 0x02, + symlink = 0x04, + noexec = 0x08, + required = 0x10, + descend = 0x20, + }; + + typedef std::string (^TargetPatternBuilder)(const std::string &name, const std::string &target); + +private: + class Rule : public ResourceBuilder::Rule { + public: + Rule(const std::string &pattern, uint32_t flags, TargetPatternBuilder targetBlock); + ~Rule(); + + bool matchTarget(const char *path, const char *target) const; + + private: + TargetPatternBuilder mTargetBlock; + }; + void addRule(Rule *rule) { mRules.push_back(rule); } + + class FTS { + public: + FTS(const std::string &path, int options = FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR); + ~FTS(); + + operator ::FTS* () const { return mFTS; } + + private: + ::FTS *mFTS; + }; + +public: + void allow(const std::string &namePattern, uint32_t flags, TargetPatternBuilder targetBlock = NULL) + { addRule(new Rule(namePattern, flags, targetBlock)); } + void require(const std::string &namePattern, uint32_t flags, TargetPatternBuilder targetBlock = NULL) + { addRule(new Rule(namePattern, flags | required, targetBlock)); mRequireCount++; } + + void allow(const std::string &namePattern, uint32_t flags, std::string targetPattern) + { allow(namePattern, flags, ^ string (const std::string &name, const std::string &target) { return targetPattern; }); } + void require(const std::string &namePattern, uint32_t flags, std::string targetPattern) + { require(namePattern, flags, ^ string (const std::string &name, const std::string &target) { return targetPattern; }); } + + void validate(const std::string &root, OSStatus error); + +private: + Rule * match(const char *relpath, uint32_t flags, bool executable, const char *target = NULL); + +private: + typedef std::vector Rules; + Rules mRules; + int mRequireCount; +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif // !_H_DIRSCANNER diff --git a/libsecurity_codesigning/lib/diskrep.cpp b/libsecurity_codesigning/lib/diskrep.cpp index 395402c6..69598084 100644 --- a/libsecurity_codesigning/lib/diskrep.cpp +++ b/libsecurity_codesigning/lib/diskrep.cpp @@ -221,6 +221,17 @@ size_t DiskRep::pageSize(const SigningContext &) } +void DiskRep::strictValidate(const ToleratedErrors&) +{ + // do nothing +} + +CFArrayRef DiskRep::allowedResourceOmissions() +{ + return NULL; +} + + // // Given some string (usually a pathname), derive a suggested signing identifier // in a canonical way (so there's some consistency). diff --git a/libsecurity_codesigning/lib/diskrep.h b/libsecurity_codesigning/lib/diskrep.h index e5de06be..c63a3d2b 100644 --- a/libsecurity_codesigning/lib/diskrep.h +++ b/libsecurity_codesigning/lib/diskrep.h @@ -53,6 +53,8 @@ class DiskRep : public RefCount { public: class SigningContext; + typedef std::set ToleratedErrors; + public: DiskRep(); virtual ~DiskRep(); @@ -60,7 +62,7 @@ public: virtual CFDataRef component(CodeDirectory::SpecialSlot slot) = 0; // fetch component virtual CFDataRef identification() = 0; // binary lookup identifier virtual std::string mainExecutablePath() = 0; // path to main executable - virtual CFURLRef canonicalPath() = 0; // path to whole code + virtual CFURLRef copyCanonicalPath() = 0; // path to whole code virtual std::string resourcesRootPath(); // resource directory if any [none] virtual void adjustResources(ResourceBuilder &builder); // adjust resource rule set [no change] virtual Universal *mainExecutableImage(); // Mach-O image if Mach-O based [null] @@ -77,7 +79,10 @@ public: virtual const Requirements *defaultRequirements(const Architecture *arch, const SigningContext &ctx); // default internal requirements [none] virtual size_t pageSize(const SigningContext &ctx); // default main executable page size [infinite, i.e. no paging] - + + virtual void strictValidate(const ToleratedErrors& tolerated); // perform strict validation + virtual CFArrayRef allowedResourceOmissions(); // allowed (default) resource omission rules + bool mainExecutableIsMachO() { return mainExecutableImage() != NULL; } // shorthands @@ -91,12 +96,13 @@ public: public: // optional information that might be used to create a suitable DiskRep. All optional struct Context { - Context() : arch(Architecture::none), version(NULL), offset(0), fileOnly(false), inMemory(NULL) { } + Context() : arch(Architecture::none), version(NULL), offset(0), fileOnly(false), inMemory(NULL), size(0) { } Architecture arch; // explicit architecture (choose amongst universal variants) const char *version; // bundle version (string) off_t offset; // explicit file offset bool fileOnly; // only consider single-file representations (no bundles etc.) const void *inMemory; // consider using in-memory copy at this address + size_t size; // size of this mach-o slice }; static DiskRep *bestGuess(const char *path, const Context *ctx = NULL); // canonical heuristic, any path @@ -195,7 +201,7 @@ public: // the rest of the virtual behavior devolves on the original DiskRep CFDataRef identification() { return mOriginal->identification(); } std::string mainExecutablePath() { return mOriginal->mainExecutablePath(); } - CFURLRef canonicalPath() { return mOriginal->canonicalPath(); } + CFURLRef copyCanonicalPath() { return mOriginal->copyCanonicalPath(); } std::string resourcesRootPath() { return mOriginal->resourcesRootPath(); } void adjustResources(ResourceBuilder &builder) { return mOriginal->adjustResources(builder); } Universal *mainExecutableImage() { return mOriginal->mainExecutableImage(); } diff --git a/libsecurity_codesigning/lib/kerneldiskrep.cpp b/libsecurity_codesigning/lib/kerneldiskrep.cpp index f39cc511..b196c071 100644 --- a/libsecurity_codesigning/lib/kerneldiskrep.cpp +++ b/libsecurity_codesigning/lib/kerneldiskrep.cpp @@ -54,7 +54,7 @@ CFDataRef KernelDiskRep::identification() } -CFURLRef KernelDiskRep::canonicalPath() +CFURLRef KernelDiskRep::copyCanonicalPath() { return makeCFURL("/mach_kernel"); } diff --git a/libsecurity_codesigning/lib/kerneldiskrep.h b/libsecurity_codesigning/lib/kerneldiskrep.h index 997462f6..0b1085e1 100644 --- a/libsecurity_codesigning/lib/kerneldiskrep.h +++ b/libsecurity_codesigning/lib/kerneldiskrep.h @@ -49,7 +49,7 @@ public: CFDataRef component(CodeDirectory::SpecialSlot slot); CFDataRef identification(); std::string mainExecutablePath(); - CFURLRef canonicalPath(); + CFURLRef copyCanonicalPath(); size_t signingLimit(); std::string format(); UnixPlusPlus::FileDesc &fd(); diff --git a/libsecurity_codesigning/lib/machorep.cpp b/libsecurity_codesigning/lib/machorep.cpp index 2130171d..7d9f5d02 100644 --- a/libsecurity_codesigning/lib/machorep.cpp +++ b/libsecurity_codesigning/lib/machorep.cpp @@ -47,14 +47,15 @@ MachORep::MachORep(const char *path, const Context *ctx) { if (ctx) if (ctx->offset) - mExecutable = new Universal(fd(), (size_t)ctx->offset); + mExecutable = new Universal(fd(), (size_t)ctx->offset, ctx->size); else if (ctx->arch) { auto_ptr full(new Universal(fd())); - mExecutable = new Universal(fd(), full->archOffset(ctx->arch)); + mExecutable = new Universal(fd(), full->archOffset(ctx->arch), full->archLength(ctx->arch)); } else mExecutable = new Universal(fd()); else mExecutable = new Universal(fd()); + assert(mExecutable); CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx); } @@ -248,12 +249,13 @@ string MachORep::format() void MachORep::flush() { size_t offset = mExecutable->offset(); + size_t length = mExecutable->length(); delete mExecutable; mExecutable = NULL; ::free(mSigningData); mSigningData = NULL; SingleDiskRep::flush(); - mExecutable = new Universal(fd(), offset); + mExecutable = new Universal(fd(), offset, length); } @@ -359,6 +361,16 @@ size_t MachORep::pageSize(const SigningContext &) } +// +// Strict validation +// +void MachORep::strictValidate(const ToleratedErrors& tolerated) +{ + if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end()) + MacOSError::throwMe(errSecCSBadMainExecutable); +} + + // // FileDiskRep::Writers // diff --git a/libsecurity_codesigning/lib/machorep.h b/libsecurity_codesigning/lib/machorep.h index 24861395..b7f248ab 100644 --- a/libsecurity_codesigning/lib/machorep.h +++ b/libsecurity_codesigning/lib/machorep.h @@ -59,6 +59,8 @@ public: std::string recommendedIdentifier(const SigningContext &ctx); const Requirements *defaultRequirements(const Architecture *arch, const SigningContext &ctx); size_t pageSize(const SigningContext &ctx); + + void strictValidate(const ToleratedErrors& tolerated); void flush(); // flush cache diff --git a/libsecurity_codesigning/lib/opaquewhitelist.cpp b/libsecurity_codesigning/lib/opaquewhitelist.cpp new file mode 100644 index 00000000..ae5b6e2a --- /dev/null +++ b/libsecurity_codesigning/lib/opaquewhitelist.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include "opaquewhitelist.h" +#include "csutilities.h" +#include "StaticCode.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Security { +namespace CodeSigning { + +using namespace SQLite; + + +static void traceHash(MessageTrace &trace, const char *key, CFDataRef hash); +static void attachOpaque(SecStaticCodeRef code); + + +// +// Open the database +// +OpaqueWhitelist::OpaqueWhitelist(const char *path, int flags) + : SQLite::Database(path ? path : opaqueDatabase, flags) +{ + SQLite::Statement createConditions(*this, + "CREATE TABLE IF NOT EXISTS conditions (" + " label text," + " weight real not null unique," + " source text," + " identifier text," + " version text," + " conditions text not null);" + ); + createConditions.execute(); + +} + +OpaqueWhitelist::~OpaqueWhitelist() +{ /* virtual */ } + + +// +// Check if a code object is whitelisted +// +bool OpaqueWhitelist::contains(SecStaticCodeRef codeRef, OSStatus reason, bool trace) +{ + // make our own copy of the code object, so we can poke at it without disturbing the original + SecPointer code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep()); + + CFCopyRef current = code->cdHash(); // current cdhash + CFDataRef opaque = NULL; // holds computed opaque cdhash + bool match = false; // holds final result + + if (!current) + return false; // unsigned + + // collect auxiliary information for trace + CFRef info; + std::string team = ""; + CFStringRef cfVersion = NULL, cfShortVersion = NULL, cfExecutable = NULL; + if (errSecSuccess == SecCodeCopySigningInformation(code->handle(false), kSecCSSigningInformation, &info.aref())) { + if (CFStringRef cfTeam = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier))) + team = cfString(cfTeam); + if (CFDictionaryRef infoPlist = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList))) { + if (CFTypeRef version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey)) + if (CFGetTypeID(version) == CFStringGetTypeID()) + cfVersion = CFStringRef(version); + if (CFTypeRef shortVersion = CFDictionaryGetValue(infoPlist, _kCFBundleShortVersionStringKey)) + if (CFGetTypeID(shortVersion) == CFStringGetTypeID()) + cfShortVersion = CFStringRef(shortVersion); + if (CFTypeRef executable = CFDictionaryGetValue(infoPlist, kCFBundleExecutableKey)) + if (CFGetTypeID(executable) == CFStringGetTypeID()) + cfExecutable = CFStringRef(executable); + } + } + + // compute and attach opaque signature + attachOpaque(code->handle(false)); + opaque = code->cdHash(); + + // lookup current cdhash in whitelist + SQLite::Statement lookup(*this, "SELECT opaque FROM whitelist WHERE current=:current"); + lookup.bind(":current") = current.get(); + while (lookup.nextRow()) { + CFRef expected = lookup[0].data(); + if (CFEqual(opaque, expected)) { + match = true; // actual opaque cdhash matches expected + break; + } + } + + if (trace) { + // send a trace indicating the result + MessageTrace trace("com.apple.security.assessment.whitelist2", code->identifier().c_str()); + traceHash(trace, "signature2", current); + traceHash(trace, "signature3", opaque); + trace.add("result", match ? "pass" : "fail"); + trace.add("reason", "%d", reason); + if (!team.empty()) + trace.add("teamid", "%s", team.c_str()); + if (cfVersion) + trace.add("version", "%s", cfString(cfVersion).c_str()); + if (cfShortVersion) + trace.add("version2", "%s", cfString(cfShortVersion).c_str()); + if (cfExecutable) + trace.add("execname", "%s", cfString(cfExecutable).c_str()); + trace.send(""); + } + + return match; +} + + +// +// Obtain special validation conditions for a static code, based on database configuration. +// +CFDictionaryRef OpaqueWhitelist::validationConditionsFor(SecStaticCodeRef code) +{ + // figure out which team key to use + std::string team = "UNKNOWN"; + CFStringRef cfId = NULL; + CFStringRef cfVersion = NULL; + CFRef info; // holds lifetimes for the above + if (errSecSuccess == SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())) { + if (CFStringRef cfTeam = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier))) + team = cfString(cfTeam); + cfId = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier)); + if (CFDictionaryRef infoPlist = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList))) + if (CFTypeRef version = CFDictionaryGetValue(infoPlist, _kCFBundleShortVersionStringKey)) + if (CFGetTypeID(version) == CFStringGetTypeID()) + cfVersion = CFStringRef(version); + } + if (cfId == NULL) // unsigned; punt + return NULL; + + // find the highest weight matching condition. We perform no merging and the heaviest rule wins + SQLite::Statement matches(*this, + "SELECT conditions FROM conditions" + " WHERE (source = :source or source IS NULL)" + " AND (identifier = :identifier or identifier is NULL)" + " AND ((:version IS NULL AND version IS NULL) OR (version = :version OR version IS NULL))" + " ORDER BY weight DESC" + " LIMIT 1" + ); + matches.bind(":source") = team; + matches.bind(":identifier") = cfString(cfId); + if (cfVersion) + matches.bind(":version") = cfString(cfVersion); + if (matches.nextRow()) { + CFTemp conditions((const char*)matches[0]); + return conditions.yield(); + } + // no matches + return NULL; +} + + +// +// Convert a SHA1 hash to hex and add to a trace +// +static void traceHash(MessageTrace &trace, const char *key, CFDataRef hash) +{ + if (CFDataGetLength(hash) != sizeof(SHA1::Digest)) { + trace.add(key, "(unknown format)"); + } else { + const UInt8 *bytes = CFDataGetBytePtr(hash); + char s[2 * SHA1::digestLength + 1]; + for (unsigned n = 0; n < SHA1::digestLength; n++) + sprintf(&s[2*n], "%2.2x", bytes[n]); + trace.add(key, s); + } +} + + +// +// Add a code object to the whitelist +// +void OpaqueWhitelist::add(SecStaticCodeRef codeRef) +{ + // make our own copy of the code object + SecPointer code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep()); + + CFCopyRef current = code->cdHash(); + attachOpaque(code->handle(false)); // compute and attach an opaque signature + CFDataRef opaque = code->cdHash(); + + SQLite::Statement insert(*this, "INSERT OR REPLACE INTO whitelist (current,opaque) VALUES (:current, :opaque)"); + insert.bind(":current") = current.get(); + insert.bind(":opaque") = opaque; + insert.execute(); +} + + +// +// Generate and attach an ad-hoc opaque signature +// +static void attachOpaque(SecStaticCodeRef code) +{ + CFTemp rules("{" // same resource rules as used for collection + "rules={" + "'^.*' = #T" + "'^Info\\.plist$' = {omit=#T,weight=10}" + "},rules2={" + "'^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/' = {nested=#T, weight=0}" + "'^.*' = #T" + "'^Info\\.plist$' = {omit=#T,weight=10}" + "'^[^/]+$' = {top=#T, weight=0}" + "}" + "}"); + + CFRef signature = CFDataCreateMutable(NULL, 0); + CFTemp arguments("{%O=%O, %O=#N, %O=%O}", + kSecCodeSignerDetached, signature.get(), + kSecCodeSignerIdentity, /* kCFNull, */ + kSecCodeSignerResourceRules, rules.get()); + CFRef signer; + SecCSFlags flags = kSecCSSignOpaque | kSecCSSignNoV1 | kSecCSSignBundleRoot; + MacOSError::check(SecCodeSignerCreate(arguments, flags, &signer.aref())); + MacOSError::check(SecCodeSignerAddSignature(signer, code, kSecCSDefaultFlags)); + MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags)); +} + + +} // end namespace CodeSigning +} // end namespace Security diff --git a/libsecurity_codesigning/lib/opaquewhitelist.h b/libsecurity_codesigning/lib/opaquewhitelist.h new file mode 100644 index 00000000..732cce3c --- /dev/null +++ b/libsecurity_codesigning/lib/opaquewhitelist.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _H_OPAQUEWHITELIST +#define _H_OPAQUEWHITELIST + +#include "SecAssessment.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +namespace SQLite = SQLite3; + + +static const char opaqueDatabase[] = "/var/db/gkopaque.bundle/Contents/Resources/gkopaque.db"; + + +class OpaqueWhitelist : public SQLite::Database { +public: + OpaqueWhitelist(const char *path = NULL, int flags = SQLITE_OPEN_READWRITE); + virtual ~OpaqueWhitelist(); + +public: + void add(SecStaticCodeRef code); + bool contains(SecStaticCodeRef code, OSStatus reason, bool trace); + + CFDictionaryRef validationConditionsFor(SecStaticCodeRef code); +}; + + +} // end namespace CodeSigning +} // end namespace Security + +#endif //_H_OPAQUEWHITELIST diff --git a/libsecurity_codesigning/lib/piddiskrep.cpp b/libsecurity_codesigning/lib/piddiskrep.cpp index aafba02f..37398eb0 100644 --- a/libsecurity_codesigning/lib/piddiskrep.cpp +++ b/libsecurity_codesigning/lib/piddiskrep.cpp @@ -130,7 +130,7 @@ CFDataRef PidDiskRep::identification() } -CFURLRef PidDiskRep::canonicalPath() +CFURLRef PidDiskRep::copyCanonicalPath() { return mBundleURL.retain(); } diff --git a/libsecurity_codesigning/lib/piddiskrep.h b/libsecurity_codesigning/lib/piddiskrep.h index b7f42159..b804a20d 100644 --- a/libsecurity_codesigning/lib/piddiskrep.h +++ b/libsecurity_codesigning/lib/piddiskrep.h @@ -46,7 +46,7 @@ public: CFDataRef component(CodeDirectory::SpecialSlot slot); CFDataRef identification(); std::string mainExecutablePath(); - CFURLRef canonicalPath(); + CFURLRef copyCanonicalPath(); size_t signingLimit(); std::string format(); UnixPlusPlus::FileDesc &fd(); diff --git a/libsecurity_codesigning/lib/policyengine.cpp b/libsecurity_codesigning/lib/policyengine.cpp index e3b6fe8e..eb253d43 100644 --- a/libsecurity_codesigning/lib/policyengine.cpp +++ b/libsecurity_codesigning/lib/policyengine.cpp @@ -78,42 +78,6 @@ PolicyEngine::PolicyEngine() PolicyEngine::~PolicyEngine() { } -#define GKBIS_XPC_SERVICE_NAME "com.apple.gkbisd" -#define GKBIS_REQUEST_KEY_PATH "path" -#define GKBIS_REQUEST_KEY_DEFER "defer" -#define GKBIS_REQUEST_KEY_QUARANTINED "quarantined" - -static void -gkbis_invoke_collector(const char *path) -{ - dispatch_queue_t queue = dispatch_queue_create("gkbis_invoke_collector", NULL); - dispatch_group_t group = dispatch_group_create(); - /* Set up a connection to gkbisd. */ - xpc_connection_t connection = xpc_connection_create_mach_service(GKBIS_XPC_SERVICE_NAME, - queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); - xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { - }); - xpc_connection_resume(connection); - - /* Construct and send the request. */ - xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); - xpc_dictionary_set_string(message, GKBIS_REQUEST_KEY_PATH, path); - xpc_dictionary_set_bool(message, GKBIS_REQUEST_KEY_QUARANTINED, true); - xpc_dictionary_set_bool(message, GKBIS_REQUEST_KEY_DEFER, true); - xpc_connection_send_message(connection, message); - xpc_release(message); - /* Cancel the connection after the request has been sent. */ - dispatch_group_enter(group); - xpc_connection_send_barrier(connection, ^{ - xpc_connection_cancel(connection); - xpc_release(connection); - dispatch_group_leave(group); - }); - /* Wait until the connection is canceled. */ - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - dispatch_release(queue); - dispatch_release(group); -} // // Top-level evaluation driver @@ -125,7 +89,6 @@ void PolicyEngine::evaluate(CFURLRef path, AuthorityType type, SecAssessmentFlag switch (type) { case kAuthorityExecute: - gkbis_invoke_collector(cfString(path).c_str()); evaluateCode(path, kAuthorityExecute, flags, context, result, true); break; case kAuthorityInstall: @@ -177,11 +140,13 @@ void PolicyEngine::evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, Author CFRef requirement; MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &requirement.aref())); - switch (OSStatus rc = SecStaticCodeCheckValidity(code, kSecCSDefaultFlags, requirement)) { + switch (OSStatus rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, requirement)) { case errSecSuccess: break; // rule match; process below case errSecCSReqFailed: continue; // rule does not apply + case errSecCSVetoed: + return; // nested code has failed to pass default: MacOSError::throwMe(rc); // general error; pass to caller } @@ -257,6 +222,13 @@ void PolicyEngine::evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, Author cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false); addAuthority(flags, result, latentLabel.c_str(), latentID); } + + +void PolicyEngine::adjustValidation(SecStaticCodeRef code) +{ + CFRef conditions = mOpaqueWhitelist.validationConditionsFor(code); + SecStaticCodeSetValidationConditions(code, conditions); +} bool PolicyEngine::temporarySigning(SecStaticCodeRef code, AuthorityType type, CFURLRef path, SecAssessmentFlags matchFlags) @@ -334,10 +306,11 @@ bool PolicyEngine::temporarySigning(SecStaticCodeRef code, AuthorityType type, C // // Executable code. -// Read from disk, evaluate properly, cache as indicated. The whole thing, so far. +// Read from disk, evaluate properly, cache as indicated. // void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result, bool handleUnsigned) { + // not really a Gatekeeper function... but reject all "hard quarantined" files because they were made from sandboxed sources without download privilege FileQuarantine qtn(cfString(path).c_str()); if (qtn.flag(QTN_FLAG_HARD)) MacOSError::throwMe(errSecCSFileHardQuarantined); @@ -345,7 +318,10 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment CFCopyRef code; MacOSError::check(SecStaticCodeCreateWithPath(path, kSecCSDefaultFlags, &code.aref())); - SecCSFlags validationFlags = kSecCSEnforceRevocationChecks; + SecCSFlags validationFlags = kSecCSEnforceRevocationChecks | kSecCSCheckAllArchitectures; + if (!(flags & kSecAssessmentFlagAllowWeak)) + validationFlags |= kSecCSStrictValidate; + adjustValidation(code); // first, perform a shallow check OSStatus rc = SecStaticCodeCheckValidity(code, validationFlags, NULL); @@ -369,12 +345,18 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment } } - MacOSError::check(SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, ^CFTypeRef (SecStaticCodeRef item, CFStringRef stage, CFDictionaryRef info) { - SecStaticCodeSetCallback(item, kSecCSDefaultFlags, NULL, NULL); // clear callback to avoid unwanted recursion - evaluateCodeItem(item, path, type, flags, item != code, result); - if (CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict)) - if (CFEqual(verdict, kCFBooleanFalse)) - return makeCFNumber(OSStatus(errSecCSVetoed)); // (signal nested-code policy failure, picked up below) + MacOSError::check(SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, ^CFTypeRef (SecStaticCodeRef item, CFStringRef cfStage, CFDictionaryRef info) { + string stage = cfString(cfStage); + if (stage == "prepared") { + if (!CFEqual(item, code)) // genuine nested (not top) code + adjustValidation(item); + } else if (stage == "validated") { + SecStaticCodeSetCallback(item, kSecCSDefaultFlags, NULL, NULL); // clear callback to avoid unwanted recursion + evaluateCodeItem(item, path, type, flags, item != code, result); + if (CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict)) + if (CFEqual(verdict, kCFBooleanFalse)) + return makeCFNumber(OSStatus(errSecCSVetoed)); // (signal nested-code policy failure, picked up below) + } return NULL; })); switch (rc = SecStaticCodeCheckValidity(code, validationFlags | kSecCSCheckNestedCode, NULL)) { @@ -386,6 +368,42 @@ void PolicyEngine::evaluateCode(CFURLRef path, AuthorityType type, SecAssessment return; case errSecCSVetoed: // nested code rejected by rule book; result was filled out there return; + case errSecCSWeakResourceRules: + case errSecCSResourceNotSupported: + case errSecCSAmbiguousBundleFormat: + case errSecCSSignatureNotVerifiable: + case errSecCSRegularFile: + case errSecCSBadMainExecutable: + case errSecCSBadFrameworkVersion: + case errSecCSUnsealedAppRoot: + case errSecCSUnsealedFrameworkRoot: + { + // consult the whitelist + bool allow = false; + const char *label; + // we've bypassed evaluateCodeItem before we failed validation. Explicitly apply it now + SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, NULL); + evaluateCodeItem(code, path, type, flags | kSecAssessmentFlagNoCache, false, result); + if (CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict)) { + // verdict rendered from a nested component - signature not acceptable to Gatekeeper + if (CFEqual(verdict, kCFBooleanFalse)) // nested code rejected by rule book; result was filled out there + return; + if (CFEqual(verdict, kCFBooleanTrue) && !(flags & kSecAssessmentFlagIgnoreWhitelist)) { + bool trace = CFDictionaryContainsKey(context, kSecAssessmentContextQuarantineFlags); + if (mOpaqueWhitelist.contains(code, rc, trace)) + allow = true; + } + } + if (allow) { + label = "allowed cdhash"; + } else { + CFDictionaryReplaceValue(result, kSecAssessmentAssessmentVerdict, kCFBooleanFalse); + label = "obsolete resource envelope"; + } + cfadd(result, "{%O=%d}", kSecAssessmentAssessmentCodeSigningError, rc); + addAuthority(flags, result, label, 0, NULL, true); + return; + } default: MacOSError::throwMe(rc); } @@ -602,7 +620,7 @@ void PolicyEngine::evaluateDocOpen(CFURLRef path, SecAssessmentFlags flags, CFDi // // Result-creation helpers // -void PolicyEngine::addAuthority(SecAssessmentFlags flags, CFMutableDictionaryRef parent, const char *label, SQLite::int64 row, CFTypeRef cacheInfo) +void PolicyEngine::addAuthority(SecAssessmentFlags flags, CFMutableDictionaryRef parent, const char *label, SQLite::int64 row, CFTypeRef cacheInfo, bool weak) { CFRef auth = makeCFMutableDictionary(); if (label && label[0]) @@ -613,7 +631,12 @@ void PolicyEngine::addAuthority(SecAssessmentFlags flags, CFMutableDictionaryRef CFDictionaryAddValue(auth, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride); if (cacheInfo) CFDictionaryAddValue(auth, kSecAssessmentAssessmentFromCache, cacheInfo); - CFDictionaryAddValue(parent, kSecAssessmentAssessmentAuthority, auth); + if (weak) { + CFDictionaryAddValue(auth, kSecAssessmentAssessmentWeakSignature, kCFBooleanTrue); + CFDictionaryReplaceValue(parent, kSecAssessmentAssessmentAuthority, auth); + } else { + CFDictionaryAddValue(parent, kSecAssessmentAssessmentAuthority, auth); + } } void PolicyEngine::addToAuthority(CFMutableDictionaryRef parent, CFStringRef key, CFTypeRef value) @@ -1035,6 +1058,11 @@ void PolicyEngine::normalizeTarget(CFRef &target, AuthorityType type, CFDictionaryAddValue(dict, kSecAssessmentUpdateKeyRemarks, CFTempString(cfString(path))); context.take(dict); } + CFStringRef edit = CFStringRef(context.get(kSecAssessmentContextKeyUpdate)); + if (type == kAuthorityExecute && CFEqual(edit, kSecAssessmentUpdateOperationAdd)) { + // implicitly whitelist the code + mOpaqueWhitelist.add(code); + } } } diff --git a/libsecurity_codesigning/lib/policyengine.h b/libsecurity_codesigning/lib/policyengine.h index af06f660..8f4d7a17 100644 --- a/libsecurity_codesigning/lib/policyengine.h +++ b/libsecurity_codesigning/lib/policyengine.h @@ -24,6 +24,7 @@ #define _H_POLICYENGINE #include "SecAssessment.h" +#include "opaquewhitelist.h" #include "policydb.h" #include #include @@ -63,7 +64,7 @@ public: void recordFailure(CFDictionaryRef info); public: - static void addAuthority(SecAssessmentFlags flags, CFMutableDictionaryRef parent, const char *label, SQLite::int64 row = 0, CFTypeRef cacheInfo = NULL); + static void addAuthority(SecAssessmentFlags flags, CFMutableDictionaryRef parent, const char *label, SQLite::int64 row = 0, CFTypeRef cacheInfo = NULL, bool weak = false); static void addToAuthority(CFMutableDictionaryRef parent, CFStringRef key, CFTypeRef value); private: @@ -72,6 +73,7 @@ private: void evaluateDocOpen(CFURLRef path, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result); void evaluateCodeItem(SecStaticCodeRef code, CFURLRef path, AuthorityType type, SecAssessmentFlags flags, bool nested, CFMutableDictionaryRef result); + void adjustValidation(SecStaticCodeRef code); bool temporarySigning(SecStaticCodeRef code, AuthorityType type, CFURLRef path, SecAssessmentFlags matchFlags); void normalizeTarget(CFRef &target, AuthorityType type, CFDictionary &context, std::string *signUnsigned); @@ -83,6 +85,9 @@ private: void setOrigin(CFArrayRef chain, CFMutableDictionaryRef result); void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, SQLite::int64 authority); + +private: + OpaqueWhitelist mOpaqueWhitelist; }; diff --git a/libsecurity_codesigning/lib/resources.cpp b/libsecurity_codesigning/lib/resources.cpp index 0ac68ecb..1a4d1e2a 100644 --- a/libsecurity_codesigning/lib/resources.cpp +++ b/libsecurity_codesigning/lib/resources.cpp @@ -47,15 +47,30 @@ namespace Security { namespace CodeSigning { +static string removeTrailingSlash(string path) +{ + if (path.substr(path.length()-2, 2) == "/.") + return path.substr(0, path.length()-2); + else if (path.substr(path.length()-1, 1) == "/") + return path.substr(0, path.length()-1); + else + return path; +} + // // Construction and maintainance // -ResourceBuilder::ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType) - : mRoot(root), mHashType(hashType) +ResourceBuilder::ResourceBuilder(const std::string &root, const std::string &relBase, + CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType, bool strict, const MacOSErrorSet& toleratedErrors) + : mHashType(hashType), + mCheckUnreadable(strict && toleratedErrors.find(errSecCSSignatureNotVerifiable) == toleratedErrors.end()), + mCheckUnknownType(strict && toleratedErrors.find(errSecCSResourceNotSupported) == toleratedErrors.end()) { - assert(!mRoot.empty()); - if (mRoot.substr(mRoot.length()-2, 2) == "/.") // produced by versioned bundle implicit "Current" case - mRoot = mRoot.substr(0, mRoot.length()-2); // ... so take it off for this + assert(!root.empty()); + mRoot = removeTrailingSlash(root); + mRelBase = removeTrailingSlash(relBase); + if (mRoot != mRelBase && mRelBase != mRoot + "/Contents") + MacOSError::throwMe(errSecCSInternalError); const char * paths[2] = { mRoot.c_str(), NULL }; mFTS = fts_open((char * const *)paths, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR, NULL); if (!mFTS) @@ -125,6 +140,14 @@ void ResourceBuilder::scan(Scanner next) while (FTSENT *ent = fts_read(mFTS)) { const char *relpath = ent->fts_path + mRoot.size() + 1; // skip prefix + "/" + std::string rp; + if (mRelBase != mRoot) { + assert(mRelBase == mRoot + "/Contents"); + rp = "../" + string(relpath); + if (rp.substr(0, 12) == "../Contents/") + rp = rp.substr(12); + relpath = rp.c_str(); + } switch (ent->fts_info) { case FTS_F: secdebug("rdirenum", "file %s", ent->fts_path); @@ -178,9 +201,16 @@ void ResourceBuilder::scan(Scanner next) case FTS_DP: secdebug("rdirenum", "leaving %s", ent->fts_path); break; + case FTS_DNR: + secdebug("rdirenum", "cannot read directory %s", ent->fts_path); + if (mCheckUnreadable) + MacOSError::throwMe(errSecCSSignatureNotVerifiable); + break; default: secdebug("rdirenum", "type %d (errno %d): %s", ent->fts_info, ent->fts_errno, ent->fts_path); + if (mCheckUnknownType) + MacOSError::throwMe(errSecCSResourceNotSupported); break; } } diff --git a/libsecurity_codesigning/lib/resources.h b/libsecurity_codesigning/lib/resources.h index 749939da..3bdd8e8b 100644 --- a/libsecurity_codesigning/lib/resources.h +++ b/libsecurity_codesigning/lib/resources.h @@ -49,7 +49,8 @@ namespace CodeSigning { class ResourceBuilder { NOCOPY(ResourceBuilder) public: - ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType); + ResourceBuilder(const std::string &root, const std::string &relBase, + CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType, bool strict, const MacOSErrorSet& toleratedErrors); ~ResourceBuilder(); enum { @@ -93,12 +94,14 @@ protected: void addRule(CFTypeRef key, CFTypeRef value); private: - std::string mRoot; + std::string mRoot, mRelBase; FTS *mFTS; CFCopyRef mRawRules; typedef std::vector Rules; Rules mRules; CodeDirectory::HashAlgorithm mHashType; + bool mCheckUnreadable; + bool mCheckUnknownType; }; diff --git a/libsecurity_codesigning/lib/security_codesigning.exp b/libsecurity_codesigning/lib/security_codesigning.exp index 1bb8bfdf..b4e83480 100644 --- a/libsecurity_codesigning/lib/security_codesigning.exp +++ b/libsecurity_codesigning/lib/security_codesigning.exp @@ -162,6 +162,8 @@ _kSecAssessmentAssessmentAuthorityOverride _kSecAssessmentAssessmentOriginalVerdict _kSecAssessmentAssessmentSource _kSecAssessmentAssessmentVerdict +_kSecAssessmentAssessmentWeakSignature +_kSecAssessmentAssessmentCodeSigningError # gatekeeper logging diff --git a/libsecurity_codesigning/lib/signer.cpp b/libsecurity_codesigning/lib/signer.cpp index 9b676c33..c11ce0c7 100644 --- a/libsecurity_codesigning/lib/signer.cpp +++ b/libsecurity_codesigning/lib/signer.cpp @@ -117,6 +117,10 @@ void SecCodeSigner::Signer::remove(SecCSFlags flags) // void SecCodeSigner::Signer::prepare(SecCSFlags flags) { + // make sure the rep passes strict validation + if (strict) + rep->strictValidate(MacOSErrorSet()); + // get the Info.plist out of the rep for some creative defaulting CFRef infoDict; if (CFRef infoData = rep->component(cdInfoSlot)) @@ -232,9 +236,15 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) // finally, ask the DiskRep for its default if (!resourceRules) resourceRules.take(rep->defaultResourceRules(state)); - + + // resource root can optionally be the canonical bundle path, + // but sealed resource paths are always relative to rpath + string root = rpath; + if (state.signingFlags() & kSecCSSignBundleRoot) + root = cfStringRelease(rep->copyCanonicalPath()); + // build the resource directory - buildResources(rpath, resourceRules); + buildResources(root, rpath, resourceRules); } // screen and set the signing time @@ -261,7 +271,7 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags) // Collect the resource seal for a program. // This includes both sealed resources and information about nested code. // -void SecCodeSigner::Signer::buildResources(std::string root, CFDictionaryRef rulesDict) +void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict) { typedef ResourceBuilder::Rule Rule; @@ -284,7 +294,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, CFDictionaryRef rul } // build the modern (V2) resource seal __block CFRef files = makeCFMutableDictionary(); - ResourceBuilder resourceBuilder(root, rules2, digestAlgorithm()); + ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet()); ResourceBuilder &resources = resourceBuilder; // (into block) rep->adjustResources(resources); resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) { @@ -319,7 +329,7 @@ void SecCodeSigner::Signer::buildResources(std::string root, CFDictionaryRef rul if (!(state.signingFlags() & kSecCSSignNoV1)) { // build the legacy (V1) resource seal __block CFRef files = makeCFMutableDictionary(); - ResourceBuilder resourceBuilder(root, rules, digestAlgorithm()); + ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet()); ResourceBuilder &resources = resourceBuilder; rep->adjustResources(resources); // DiskRep-specific adjustments resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const char *relpath, Rule *rule) { diff --git a/libsecurity_codesigning/lib/signer.h b/libsecurity_codesigning/lib/signer.h index 495cc00c..d27abcb2 100644 --- a/libsecurity_codesigning/lib/signer.h +++ b/libsecurity_codesigning/lib/signer.h @@ -45,7 +45,8 @@ namespace CodeSigning { // class SecCodeSigner::Signer { public: - Signer(SecCodeSigner &s, SecStaticCode *c) : state(s), code(c), requirements(NULL) { } + Signer(SecCodeSigner &s, SecStaticCode *c) : state(s), code(c), requirements(NULL) + { strict = state.signingFlags() & kSecCSSignStrictPreflight; } ~Signer() { ::free((Requirements *)requirements); } void sign(SecCSFlags flags); @@ -56,7 +57,7 @@ public: CodeDirectory::HashAlgorithm digestAlgorithm() const { return state.mDigestAlgorithm; } - std::string path() const { return cfString(rep->canonicalPath()); } + std::string path() const { return cfString(rep->copyCanonicalPath()); } SecIdentityRef signingIdentity() const { return state.mSigner; } std::string signingIdentifier() const { return identifier; } @@ -74,7 +75,7 @@ protected: std::string uniqueName() const; // derive unique string from rep protected: - void buildResources(std::string root, CFDictionaryRef rules); + void buildResources(std::string root, std::string relBase, CFDictionaryRef rules); CFMutableDictionaryRef signNested(FTSENT *ent, const char *relpath); CFDataRef hashFile(const char *path); @@ -89,6 +90,7 @@ private: const Requirements *requirements; // internal requirements ready-to-use size_t pagesize; // size of main executable pages CFAbsoluteTime signingTime; // signing time for CMS signature (0 => none) + bool strict; // strict validation }; diff --git a/libsecurity_codesigning/lib/singlediskrep.cpp b/libsecurity_codesigning/lib/singlediskrep.cpp index 154ed2e9..c3ba5207 100644 --- a/libsecurity_codesigning/lib/singlediskrep.cpp +++ b/libsecurity_codesigning/lib/singlediskrep.cpp @@ -61,7 +61,7 @@ CFDataRef SingleDiskRep::identification() // // Both the canonical and main executable path of a SingleDiskRep is, well, its path. // -CFURLRef SingleDiskRep::canonicalPath() +CFURLRef SingleDiskRep::copyCanonicalPath() { return makeCFURL(mPath); } diff --git a/libsecurity_codesigning/lib/singlediskrep.h b/libsecurity_codesigning/lib/singlediskrep.h index 2ff73e10..c8e94c60 100644 --- a/libsecurity_codesigning/lib/singlediskrep.h +++ b/libsecurity_codesigning/lib/singlediskrep.h @@ -48,7 +48,7 @@ public: CFDataRef identification(); // partial file hash std::string mainExecutablePath(); // base path - CFURLRef canonicalPath(); // base path + CFURLRef copyCanonicalPath(); // base path size_t signingLimit(); // size of file UnixPlusPlus::FileDesc &fd(); // readable fd for this file void flush(); // close cached fd diff --git a/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/project.pbxproj b/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/project.pbxproj index 468ff302..fa010c9a 100644 --- a/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/project.pbxproj +++ b/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/project.pbxproj @@ -71,6 +71,11 @@ 18B9658B1472FC5B005A4D2E /* reqparser.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383340A237F47005C63A2 /* reqparser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18B9658C1472FC9E005A4D2E /* codedirectory.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383170A237F47005C63A2 /* codedirectory.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18B965951472FE30005A4D2E /* cdbuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = C2D383150A237F47005C63A2 /* cdbuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A078F651996C1C2003D68DA /* dirscanner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A078F631996C1C2003D68DA /* dirscanner.cpp */; }; + 7A078F661996C1C2003D68DA /* dirscanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A078F641996C1C2003D68DA /* dirscanner.h */; }; + 7A9DA65C1948D1BA004635E6 /* opaquewhitelist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */; }; + 7A9DA65D1948D1BA004635E6 /* opaquewhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */; }; + 7ACF261219958B6F00849B25 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C2CC30A00B8519CC005FA59D /* CoreFoundation.framework */; }; BEC3A75C16F78D21003E5634 /* SecTaskPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = BEC3A75B16F78D21003E5634 /* SecTaskPriv.h */; }; C200424D15D425D9004AE0A1 /* libsecurity_codesigning.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C200424915D425B7004AE0A1 /* libsecurity_codesigning.a */; }; C200424E15D425D9004AE0A1 /* libsecurity_utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C200424A15D425B7004AE0A1 /* libsecurity_utilities.a */; }; @@ -365,6 +370,10 @@ 184461A1146E9AD100B12992 /* lib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = lib.xcconfig; sourceTree = ""; }; 184461A2146E9AD100B12992 /* release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = release.xcconfig; sourceTree = ""; }; 4CA1FEBE052A3C8100F22E42 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libsecurity_codesigning.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A078F631996C1C2003D68DA /* dirscanner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dirscanner.cpp; sourceTree = ""; }; + 7A078F641996C1C2003D68DA /* dirscanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirscanner.h; sourceTree = ""; }; + 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaquewhitelist.cpp; sourceTree = ""; }; + 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opaquewhitelist.h; sourceTree = ""; }; BEC3A75B16F78D21003E5634 /* SecTaskPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecTaskPriv.h; sourceTree = ""; }; C200424915D425B7004AE0A1 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_codesigning.a; path = ../../../usr/local/lib/libsecurity_codesigning.a; sourceTree = ""; }; C200424A15D425B7004AE0A1 /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_utilities.a; path = ../../../usr/local/lib/libsecurity_utilities.a; sourceTree = ""; }; @@ -372,7 +381,7 @@ C2093AA70BB0948000EB8599 /* reqreader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqreader.h; sourceTree = ""; }; C209696015BF52040093035F /* gkunpack */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gkunpack; sourceTree = BUILT_PRODUCTS_DIR; }; C209696315BF52040093035F /* gkunpack.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = gkunpack.cpp; sourceTree = ""; }; - C209697215BF57EB0093035F /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_utilities.a; sourceTree = ""; }; + C209697215BF57EB0093035F /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecurity_utilities.a; sourceTree = ""; }; C2110704158BF5C8001D7F76 /* gkmerge */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = gkmerge; sourceTree = ""; }; C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqdumper.cpp; sourceTree = ""; }; C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqdumper.h; sourceTree = ""; }; @@ -622,6 +631,7 @@ files = ( C200424D15D425D9004AE0A1 /* libsecurity_codesigning.a in Frameworks */, C200424E15D425D9004AE0A1 /* libsecurity_utilities.a in Frameworks */, + 7ACF261219958B6F00849B25 /* CoreFoundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -738,6 +748,8 @@ children = ( C273606D1433F09000A9A5FF /* SecAssessment.h */, C273606C1433F09000A9A5FF /* SecAssessment.cpp */, + 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */, + 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */, C24EABAA1421432800C16AA9 /* policydb.h */, C24EABAC1421433700C16AA9 /* policydb.cpp */, C273601D1432A60B00A9A5FF /* policyengine.h */, @@ -948,6 +960,8 @@ C2353410145F1B110073F964 /* xar++.cpp */, C2F4439914C626D4000A01E6 /* quarantine++.h */, C2F4439814C626D4000A01E6 /* quarantine++.cpp */, + 7A078F641996C1C2003D68DA /* dirscanner.h */, + 7A078F631996C1C2003D68DA /* dirscanner.cpp */, ); name = "Local Utilities"; sourceTree = ""; @@ -1147,6 +1161,7 @@ 18B965861472FBF6005A4D2E /* reqdumper.h in Headers */, 18B965871472FC5B005A4D2E /* requirement.h in Headers */, 18B965881472FC5B005A4D2E /* reqmaker.h in Headers */, + 7A9DA65D1948D1BA004635E6 /* opaquewhitelist.h in Headers */, 18B965891472FC5B005A4D2E /* reqreader.h in Headers */, 18B9658A1472FC5B005A4D2E /* reqinterp.h in Headers */, 18B9658B1472FC5B005A4D2E /* reqparser.h in Headers */, @@ -1175,6 +1190,7 @@ C2A436160F2133B2007A41A6 /* slcrep.h in Headers */, C24EABAB1421432800C16AA9 /* policydb.h in Headers */, C2F4439B14C626D4000A01E6 /* quarantine++.h in Headers */, + 7A078F661996C1C2003D68DA /* dirscanner.h in Headers */, C26763D814FD9EBE00A46EDF /* drmaker.h in Headers */, EBDAF050166D65FA0042CDCE /* piddiskrep.h in Headers */, ); @@ -1499,6 +1515,7 @@ C2D3834F0A237F47005C63A2 /* SecRequirement.cpp in Sources */, C2D383510A237F47005C63A2 /* diskrep.cpp in Sources */, C2D383550A237F47005C63A2 /* filediskrep.cpp in Sources */, + 7A078F651996C1C2003D68DA /* dirscanner.cpp in Sources */, C2D383570A237F47005C63A2 /* Code.cpp in Sources */, C2D383590A237F47005C63A2 /* kerneldiskrep.cpp in Sources */, C2D3835B0A237F47005C63A2 /* StaticCode.cpp in Sources */, @@ -1528,6 +1545,7 @@ C2A436150F2133B2007A41A6 /* slcrep.cpp in Sources */, FEB30C9310DAC89D00557BA2 /* SecTask.c in Sources */, C24EABAD1421433700C16AA9 /* policydb.cpp in Sources */, + 7A9DA65C1948D1BA004635E6 /* opaquewhitelist.cpp in Sources */, C273606E1433F09000A9A5FF /* SecAssessment.cpp in Sources */, C27360D51436866D00A9A5FF /* xpcengine.cpp in Sources */, C2DC2DCA145F594000AD2A3A /* xar++.cpp in Sources */, diff --git a/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..47700900 --- /dev/null +++ b/libsecurity_codesigning/libsecurity_codesigning.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,54 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + C209695F15BF52040093035F + + primary + + + C26AC0EB143BCF01001C98CE + + primary + + + C26AC7090DAEB3A7005BFB40 + + primary + + + C2BC1F250B580D3A003EC9DC + + primary + + + C2BC1F2E0B580D4B003EC9DC + + primary + + + C2D383B80A23A8C4005C63A2 + + primary + + + C2E2873F0B5D8F8F009336A0 + + primary + + + EBB9FF6E1682E51300FF9774 + + primary + + + + + diff --git a/libsecurity_comcryption/libsecurity_comcryption.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_comcryption/libsecurity_comcryption.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..2adfe6f3 --- /dev/null +++ b/libsecurity_comcryption/libsecurity_comcryption.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 7264321D00A8AD0A7F000001 + + primary + + + + + diff --git a/libsecurity_cryptkit/libsecurity_cryptkit.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cryptkit/libsecurity_cryptkit.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..43c0bd93 --- /dev/null +++ b/libsecurity_cryptkit/libsecurity_cryptkit.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + 0535DCBD074A944D00805B04 + + primary + + + 0536B294074BC91A00F9F1AD + + primary + + + 7264322800A8AD0A7F000001 + + primary + + + + + diff --git a/libsecurity_cssm/libsecurity_cssm.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_cssm/libsecurity_cssm.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_cssm/libsecurity_cssm.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_filedb/libsecurity_filedb.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_filedb/libsecurity_filedb.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_filedb/libsecurity_filedb.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_keychain/lib/SecTrust.cpp b/libsecurity_keychain/lib/SecTrust.cpp index 297b76d7..efdb501f 100644 --- a/libsecurity_keychain/lib/SecTrust.cpp +++ b/libsecurity_keychain/lib/SecTrust.cpp @@ -402,10 +402,22 @@ SecKeyRef SecTrustCopyPublicKey(SecTrustRef trust) OSStatus __secapiresult = errSecSuccess; try { Trust *trustObj = Trust::required(trust); - if (trustObj->result() == kSecTrustResultInvalid) - MacOSError::throwMe(errSecTrustNotAvailable); - if (trustObj->evidence() == nil) + if (trustObj->result() == kSecTrustResultInvalid) { + // Trust hasn't been evaluated; attempt to retrieve public key from leaf. + SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, 0); + __secapiresult = SecCertificateCopyPublicKey(cert, &pubKey); + if (pubKey) { + return pubKey; + } + // Otherwise, we must evaluate first. + trustObj->evaluate(); + if (trustObj->result() == kSecTrustResultInvalid) { + MacOSError::throwMe(errSecTrustNotAvailable); + } + } + if (trustObj->evidence() == nil) { trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain)); + } evidenceChain = trustObj->evidence(); } catch (const MacOSError &err) { __secapiresult=err.osStatus(); } @@ -470,12 +482,27 @@ SecCertificateRef SecTrustGetCertificateAtIndex(SecTrustRef trust, CFIndex ix) try { Trust *trustObj = Trust::required(trust); if (trustObj->result() == kSecTrustResultInvalid) { + // If caller is asking for the leaf, we can return it without + // having to evaluate the entire chain. Note that we don't retain + // the cert as it's owned by the trust and this is a 'Get' API. + if (ix == 0) { + CFArrayRef certs = trustObj->certificates(); + if (certs && (CFArrayGetCount(certs) > 0)) { + certificate = (SecCertificateRef) CFArrayGetValueAtIndex(certs, 0); + if (certificate) { + return certificate; + } + } + } + // Otherwise, we must evaluate first. trustObj->evaluate(); - if (trustObj->result() == kSecTrustResultInvalid) + if (trustObj->result() == kSecTrustResultInvalid) { MacOSError::throwMe(errSecTrustNotAvailable); + } } - if (trustObj->evidence() == nil) + if (trustObj->evidence() == nil) { trustObj->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain)); + } evidenceChain = trustObj->evidence(); } catch (const MacOSError &err) { __secapiresult=err.osStatus(); } diff --git a/libsecurity_keychain/lib/Trust.cpp b/libsecurity_keychain/lib/Trust.cpp index e22acfba..49f23236 100644 --- a/libsecurity_keychain/lib/Trust.cpp +++ b/libsecurity_keychain/lib/Trust.cpp @@ -327,6 +327,7 @@ void Trust::evaluate(bool disableEV) CFMutableArrayRef allPolicies = NULL; uint32 numRevocationAdded = 0; bool requirePerCert = (actionDataP->ActionFlags & CSSM_TP_ACTION_REQUIRE_REV_PER_CERT); + bool avoidRevChecks = (policySpecified(mPolicies, CSSMOID_APPLE_TP_EAP)); // If a new unified revocation policy was explicitly specified, // convert into old-style individual OCSP and CRL policies. @@ -350,13 +351,13 @@ void Trust::evaluate(bool disableEV) allPolicies = NULL; // use only mPolicies isEVCandidate = false; } - else if (isEVCandidate || requirePerCert) { + else if ((isEVCandidate && !avoidRevChecks) || requirePerCert) { // force revocation checking for this evaluation secdebug("evTrust", "Trust::evaluate() forcing OCSP/CRL revocation check"); allPolicies = forceRevocationPolicies(numRevocationAdded, context.allocator, requirePerCert); } - else if(!(revocationPolicySpecified(mPolicies))) { + else if(!(revocationPolicySpecified(mPolicies)) && !avoidRevChecks) { // none specified in mPolicies; try preferences allPolicies = addPreferenceRevocationPolicies(numRevocationAdded, context.allocator); diff --git a/libsecurity_keychain/lib/Trust.h b/libsecurity_keychain/lib/Trust.h index 344f81f4..77f8aac1 100644 --- a/libsecurity_keychain/lib/Trust.h +++ b/libsecurity_keychain/lib/Trust.h @@ -102,6 +102,7 @@ public: CFArrayRef evidence() const { return mEvidenceReturned; } CFArrayRef policies() const { return mPolicies; } CFArrayRef anchors() const { return mAnchors; } + CFArrayRef certificates() const { return mCerts; } CFDateRef time() const { return mVerifyTime; } AnchorPolicy anchorPolicy() const { return mAnchorPolicy; } NetworkPolicy networkPolicy() const { return mNetworkPolicy; } diff --git a/libsecurity_keychain/libDER/libDER.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_keychain/libDER/libDER.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..6ace9d59 --- /dev/null +++ b/libsecurity_keychain/libDER/libDER.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,39 @@ + + + + + SuppressBuildableAutocreation + + 053BA30F091C00B100A7007A + + primary + + + 053BA313091C00BF00A7007A + + primary + + + 053BA444091FE58C00A7007A + + primary + + + 053BA46A091FE63E00A7007A + + primary + + + 058F16530925135E009FA1C5 + + primary + + + 4C96C8CD113F4132005483E8 + + primary + + + + + diff --git a/libsecurity_keychain/libsecurity_keychain.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_keychain/libsecurity_keychain.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..2c7d8fe6 --- /dev/null +++ b/libsecurity_keychain/libsecurity_keychain.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,29 @@ + + + + + SuppressBuildableAutocreation + + 0CBD500016C3242200713B6C + + primary + + + 4C5719C712FB5E9E00B31F85 + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + 52200F8714F2B87F00F7F6E7 + + primary + + + + + diff --git a/libsecurity_manifest/libsecurity_manifest.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_manifest/libsecurity_manifest.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..ab80c26f --- /dev/null +++ b/libsecurity_manifest/libsecurity_manifest.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + D6C8AFAD05DD2430003DB724 + + primary + + + + + diff --git a/libsecurity_mds/libsecurity_mds.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_mds/libsecurity_mds.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_mds/libsecurity_mds.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_ocspd/libsecurity_ocspd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_ocspd/libsecurity_ocspd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..fb66b79c --- /dev/null +++ b/libsecurity_ocspd/libsecurity_ocspd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 051BDB75069B372600F9D07E + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_pkcs12/libsecurity_pkcs12.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_pkcs12/libsecurity_pkcs12.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..ee70f1d0 --- /dev/null +++ b/libsecurity_pkcs12/libsecurity_pkcs12.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 0592AC8B0415523C00003D05 + + primary + + + + + diff --git a/libsecurity_sd_cspdl/libsecurity_sd_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_sd_cspdl/libsecurity_sd_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..988e9b5a --- /dev/null +++ b/libsecurity_sd_cspdl/libsecurity_sd_cspdl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_smime/libsecurity_smime.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_smime/libsecurity_smime.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..c1314930 --- /dev/null +++ b/libsecurity_smime/libsecurity_smime.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SuppressBuildableAutocreation + + 0540498B0A37699A0035F195 + + primary + + + 4C2741ED03E9FBF700A80181 + + primary + + + AC62F5EF18B4356A00704BBD + + primary + + + + + diff --git a/libsecurity_ssl/lib/sslCrypto.c b/libsecurity_ssl/lib/sslCrypto.c index f3a9216b..b6ad13ef 100644 --- a/libsecurity_ssl/lib/sslCrypto.c +++ b/libsecurity_ssl/lib/sslCrypto.c @@ -875,16 +875,6 @@ extern OSStatus sslCopyPeerPubKey( check(pubKey); check(ctx->peerSecTrust); -#if !TARGET_OS_IPHONE - /* This is not required on iOS, but still required on osx */ - if (!ctx->enableCertVerify) { - OSStatus status; - SecTrustResultType result; - verify_noerr_action(status = SecTrustEvaluate(ctx->peerSecTrust, &result), - return status); - } -#endif - SecKeyRef key = SecTrustCopyPublicKey(ctx->peerSecTrust); if (!key) { sslErrorLog("sslCopyPeerPubKey: %s, ctx->peerSecTrust=%p\n", diff --git a/libsecurity_ssl/libsecurity_ssl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_ssl/libsecurity_ssl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..48b06b64 --- /dev/null +++ b/libsecurity_ssl/libsecurity_ssl.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,39 @@ + + + + + SuppressBuildableAutocreation + + 0C1C92EF15C8AC81007D377B + + primary + + + 0C6C633A15D1BD4800BC68CD + + primary + + + 0CCA415815C89E8B002AEC4C + + primary + + + 0CCA42C815C8A387002AEC4C + + primary + + + 0CCA42D615C8A395002AEC4C + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + + + diff --git a/libsecurity_transform/libsecurity_transform.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_transform/libsecurity_transform.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..dee7a39c --- /dev/null +++ b/libsecurity_transform/libsecurity_transform.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,29 @@ + + + + + SuppressBuildableAutocreation + + 4C010B86121AE8DF0094CB72 + + primary + + + 4C738256112DF65200EA003B + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + 4CBCBEB51130A2D700CC18E9 + + primary + + + + + diff --git a/libsecurity_utilities/lib/cfmunge.cpp b/libsecurity_utilities/lib/cfmunge.cpp index 13d5b599..478835fe 100644 --- a/libsecurity_utilities/lib/cfmunge.cpp +++ b/libsecurity_utilities/lib/cfmunge.cpp @@ -127,10 +127,9 @@ CFTypeRef CFMake::make() return makespecial(); case ']': case '}': - assert(false); // unexpected return NULL; // error default: - if (isdigit(*format)) + if (isdigit(*format) || *format == '-') return makenumber(); else if (isalpha(*format)) return makestring(); diff --git a/libsecurity_utilities/lib/cfutilities.cpp b/libsecurity_utilities/lib/cfutilities.cpp index 942f5ec8..5bc5378d 100644 --- a/libsecurity_utilities/lib/cfutilities.cpp +++ b/libsecurity_utilities/lib/cfutilities.cpp @@ -249,6 +249,28 @@ CFDataRef cfLoadFile(CFURLRef url) } } +CFDataRef cfLoadFile(int fd, size_t bytes) +{ + uint8_t *buffer = (uint8_t *) malloc(bytes); + + if (buffer == NULL) + return NULL; + + if (read(fd, buffer, bytes) != bytes) { + free(buffer); + return NULL; + } + + CFDataRef result = CFDataCreateWithBytesNoCopy(kCFAllocatorMalloc, buffer, bytes, kCFAllocatorMalloc); + + // If CFDataCreateWithBytesNoCopy fails, the buffer is not free()-ed + if (result == NULL) { + free(buffer); + return NULL; + } + + return result; +} // // CFArray creators diff --git a/libsecurity_utilities/lib/cfutilities.h b/libsecurity_utilities/lib/cfutilities.h index dd1a7e8b..4481f170 100644 --- a/libsecurity_utilities/lib/cfutilities.h +++ b/libsecurity_utilities/lib/cfutilities.h @@ -499,12 +499,23 @@ private: void (T::*func)(CFTypeRef key, CFTypeRef value); static void apply(CFTypeRef key, CFTypeRef value, void *context) { Applier *me = (Applier *)context; return ((me->object)->*(me->func))(key, value); } - }; + }; + + template + struct BlockApplier { + void (^action)(Key key, Value value); + static void apply(CFTypeRef key, CFTypeRef value, void* context) + { BlockApplier *me = (BlockApplier *)context; return me->action(Key(key), Value(value)); } + }; public: template void apply(T *object, void (T::*func)(CFTypeRef key, CFTypeRef value)) { Applier app; app.object = object; app.func = func; return apply(app.apply, &app); } + + template + void apply(void (^action)(Key key, Value value)) + { BlockApplier app; app.action = action; return apply(app.apply, &app); } private: OSStatus mDefaultError; @@ -515,6 +526,7 @@ private: // CFURLAccess wrappers for specific purposes // CFDataRef cfLoadFile(CFURLRef url); +CFDataRef cfLoadFile(int fd, size_t bytes); inline CFDataRef cfLoadFile(CFStringRef path) { return cfLoadFile(CFTempURL(path)); } inline CFDataRef cfLoadFile(const std::string &path) { return cfLoadFile(CFTempURL(path)); } inline CFDataRef cfLoadFile(const char *path) { return cfLoadFile(CFTempURL(path)); } diff --git a/libsecurity_utilities/lib/errors.h b/libsecurity_utilities/lib/errors.h index dfc686d5..9208379a 100644 --- a/libsecurity_utilities/lib/errors.h +++ b/libsecurity_utilities/lib/errors.h @@ -32,6 +32,7 @@ #include #include #include +#include #undef check @@ -94,6 +95,8 @@ public: static void check(OSStatus status) { if (status != errSecSuccess) throwMe(status); } static void throwMe(int err) __attribute__((noreturn)); }; + +typedef std::set MacOSErrorSet; // diff --git a/libsecurity_utilities/lib/macho++.cpp b/libsecurity_utilities/lib/macho++.cpp index e6f39de3..ff26e594 100644 --- a/libsecurity_utilities/lib/macho++.cpp +++ b/libsecurity_utilities/lib/macho++.cpp @@ -25,12 +25,20 @@ // macho++ - Mach-O object file helpers // #include "macho++.h" +#include #include #include #include +#include +#include +#include namespace Security { +/* Maximum number of archs a fat binary can have */ +static const int MAX_ARCH_COUNT = 100; +/* Maximum power of 2 that a mach-o can be aligned by */ +static const int MAX_ALIGN = 30; // // Architecture values @@ -132,6 +140,7 @@ void MachOBase::initHeader(const mach_header *header) m64 = true; break; default: + secdebug("macho", "%p: unrecognized header magic (%x)", this, mHeader->magic); UnixError::throwMe(ENOEXEC); } } @@ -164,8 +173,10 @@ size_t MachOBase::commandSize() const // (not relative to some intermediate container). // MachO::MachO(FileDesc fd, size_t offset, size_t length) - : FileDesc(fd), mOffset(offset), mLength(length ? length : (fd.fileSize() - offset)) + : FileDesc(fd), mOffset(offset), mLength(length), mSuspicious(false) { + if (mOffset == 0) + mLength = fd.fileSize(); size_t size = fd.read(&mHeaderBuffer, sizeof(mHeaderBuffer), mOffset); if (size != sizeof(mHeaderBuffer)) UnixError::throwMe(ENOEXEC); @@ -177,6 +188,45 @@ MachO::MachO(FileDesc fd, size_t offset, size_t length) if (fd.read(mCommandBuffer, cmdSize, this->headerSize() + mOffset) != cmdSize) UnixError::throwMe(ENOEXEC); this->initCommands(mCommandBuffer); + /* If we do not know the length, we cannot do a verification of the mach-o structure */ + if (mLength != 0) + this->validateStructure(); +} + +void MachO::validateStructure() +{ + bool isValid = false; + + /* There should be either an LC_SEGMENT, an LC_SEGMENT_64, or an LC_SYMTAB + load_command and that + size must be equal to the end of the arch */ + for (const struct load_command *cmd = loadCommands(); cmd != NULL; cmd = nextCommand(cmd)) { + uint32_t cmd_type = flip(cmd->cmd); + struct segment_command *seg = NULL; + struct segment_command_64 *seg64 = NULL; + struct symtab_command *symtab = NULL; + + if (cmd_type == LC_SEGMENT) { + seg = (struct segment_command *)cmd; + if (strcmp(seg->segname, SEG_LINKEDIT) == 0) { + isValid = flip(seg->fileoff) + flip(seg->filesize) == this->length(); + break; + } + } else if (cmd_type == LC_SEGMENT_64) { + seg64 = (struct segment_command_64 *)cmd; + if (strcmp(seg64->segname, SEG_LINKEDIT) == 0) { + isValid = flip(seg64->fileoff) + flip(seg64->filesize) == this->length(); + break; + } + /* PPC binaries have a SYMTAB section */ + } else if (cmd_type == LC_SYMTAB) { + symtab = (struct symtab_command *)cmd; + isValid = flip(symtab->stroff) + flip(symtab->strsize) == this->length(); + break; + } + } + + if (!isValid) + mSuspicious = true; } MachO::~MachO() @@ -386,13 +436,12 @@ CFDataRef MachO::dataAt(size_t offset, size_t size) return buffer; } - // // Fat (aka universal) file wrappers. // The offset is relative to the start of the containing file. // -Universal::Universal(FileDesc fd, size_t offset /* = 0 */) - : FileDesc(fd), mBase(offset) +Universal::Universal(FileDesc fd, size_t offset /* = 0 */, size_t length /* = 0 */) + : FileDesc(fd), mBase(offset), mLength(length), mSuspicious(false) { union { fat_header header; // if this is a fat file @@ -434,6 +483,67 @@ Universal::Universal(FileDesc fd, size_t offset /* = 0 */) } secdebug("macho", "%p is a fat file with %d architectures", this, mArchCount); + + /* A Mach-O universal file has padding of no more than "page size" + * between the header and slices. This padding must be zeroed out or the file + is not valid */ + std::list sortedList; + for (unsigned i = 0; i < mArchCount; i++) + sortedList.push_back(mArchList + i); + + sortedList.sort(^ bool (const struct fat_arch *arch1, const struct fat_arch *arch2) { return arch1->offset < arch2->offset; }); + + const size_t universalHeaderEnd = mBase + sizeof(header) + (sizeof(fat_arch) * mArchCount); + size_t prevHeaderEnd = universalHeaderEnd; + size_t prevArchSize = 0, prevArchStart = 0; + + for (auto iterator = sortedList.begin(); iterator != sortedList.end(); ++iterator) { + auto ret = mSizes.insert(std::pair((*iterator)->offset, (*iterator)->size)); + if (ret.second == false) { + ::free(mArchList); + MacOSError::throwMe(errSecInternalError); // Something is wrong if the same size was encountered twice + } + + size_t gapSize = (*iterator)->offset - prevHeaderEnd; + + /* The size of the padding after the universal cannot be calculated to a fixed size */ + if (prevHeaderEnd != universalHeaderEnd) { + if (((*iterator)->align > MAX_ALIGN) || gapSize >= (1 << (*iterator)->align)) { + mSuspicious = true; + break; + } + } + + // validate gap bytes in tasty page-sized chunks + CssmAutoPtr gapBytes(Allocator::standard().malloc(PAGE_SIZE)); + size_t off = 0; + while (off < gapSize) { + size_t want = min(gapSize - off, (size_t)PAGE_SIZE); + size_t got = fd.read(gapBytes, want, prevHeaderEnd + off); + off += got; + for (size_t x = 0; x < got; x++) { + if (gapBytes[x] != 0) { + mSuspicious = true; + break; + } + } + if (mSuspicious) + break; + } + if (off != gapSize) + mSuspicious = true; + if (mSuspicious) + break; + + prevHeaderEnd = (*iterator)->offset + (*iterator)->size; + prevArchSize = (*iterator)->size; + prevArchStart = (*iterator)->offset; + } + + /* If there is anything extra at the end of the file, reject this */ + if (!mSuspicious && (prevArchStart + prevArchSize != fd.fileSize())) + mSuspicious = true; + break; } case MH_MAGIC: @@ -460,6 +570,13 @@ Universal::~Universal() ::free(mArchList); } +const size_t Universal::lengthOfSlice(size_t offset) const +{ + auto ret = mSizes.find(offset); + if (ret == mSizes.end()) + MacOSError::throwMe(errSecInternalError); + return ret->second; +} // // Get the "local" architecture from the fat file @@ -470,7 +587,7 @@ MachO *Universal::architecture() const if (isUniversal()) return findImage(bestNativeArch()); else - return new MachO(*this, mBase); + return new MachO(*this, mBase, mLength); } size_t Universal::archOffset() const @@ -506,6 +623,15 @@ size_t Universal::archOffset(const Architecture &arch) const UnixError::throwMe(ENOEXEC); } +size_t Universal::archLength(const Architecture &arch) const +{ + if (isUniversal()) + return mBase + findArch(arch)->size; + else if (mThinArch.matches(arch)) + return this->fileSize(); + else + UnixError::throwMe(ENOEXEC); +} // // Get the architecture at a specified offset from the fat file. @@ -573,11 +699,10 @@ Architecture Universal::bestNativeArch() const return mThinArch; } - // // List all architectures from the fat file's list. // -void Universal::architectures(Architectures &archs) +void Universal::architectures(Architectures &archs) const { if (isUniversal()) { for (unsigned n = 0; n < mArchCount; n++) @@ -588,7 +713,6 @@ void Universal::architectures(Architectures &archs) } } - // // Quickly guess the Mach-O type of a file. // Returns type zero if the file isn't Mach-O or Universal. @@ -627,5 +751,22 @@ uint32_t Universal::typeOf(FileDesc fd) return 0; } +// +// Strict validation +// +bool Universal::isSuspicious() const +{ + if (mSuspicious) + return true; + Universal::Architectures archList; + architectures(archList); + for (Universal::Architectures::const_iterator it = archList.begin(); it != archList.end(); ++it) { + auto_ptr macho(architecture(*it)); + if (macho->isSuspicious()) + return true; + } + return false; +} + } // Security diff --git a/libsecurity_utilities/lib/macho++.h b/libsecurity_utilities/lib/macho++.h index 54fb4abb..4cfc6ba1 100644 --- a/libsecurity_utilities/lib/macho++.h +++ b/libsecurity_utilities/lib/macho++.h @@ -34,6 +34,7 @@ #include #include #include +#include namespace Security { @@ -143,6 +144,9 @@ public: void seek(size_t offset); // relative to start of image CFDataRef dataAt(size_t offset, size_t size); + void validateStructure(); // is the structure of the mach-o sane + + bool isSuspicious() const { return mSuspicious; } private: size_t mOffset; // starting file offset @@ -150,6 +154,8 @@ private: mach_header mHeaderBuffer; // read-in Mach-O header load_command *mCommandBuffer; // read-in (malloc'ed) Mach-O load commands + + bool mSuspicious; // strict validation failed }; @@ -182,7 +188,7 @@ public: // class Universal : public UnixPlusPlus::FileDesc { public: - Universal(FileDesc fd, size_t offset = 0); + Universal(FileDesc fd, size_t offset = 0, size_t length = 0); ~Universal(); // return a genuine MachO object for the given architecture @@ -193,16 +199,21 @@ public: // return (just) the starting offset of an architecture size_t archOffset() const; // native size_t archOffset(const Architecture &arch) const; // given + size_t archLength(const Architecture &arch) const; // given bool narrowed() const { return mBase != 0; } // part of a fat file // return a set of architectures contained typedef std::set Architectures; - void architectures(Architectures &archs); + void architectures(Architectures &archs) const; bool isUniversal() const { return mArchList != NULL; } Architecture bestNativeArch() const; + const size_t lengthOfSlice(size_t offset) const; size_t offset() const { return mBase; } + size_t length() const { return mLength; } + + bool isSuspicious() const; public: static uint32_t typeOf(FileDesc fd); @@ -216,6 +227,10 @@ private: unsigned mArchCount; // number of architectures (if fat) Architecture mThinArch; // single architecture (if thin) size_t mBase; // overriding offset in file (all types) + size_t mLength; // length of the architecture if thin file + typedef std::map OffsetsToLength; + OffsetsToLength mSizes; // the length for the slice at a given offset + bool mSuspicious; // strict validation failed }; diff --git a/libsecurity_utilities/lib/unix++.cpp b/libsecurity_utilities/lib/unix++.cpp index 9e9643ac..57bc9c9d 100644 --- a/libsecurity_utilities/lib/unix++.cpp +++ b/libsecurity_utilities/lib/unix++.cpp @@ -342,6 +342,16 @@ std::string FileDesc::getAttr(const std::string &name, int options /* = 0 */) return string(); } +bool FileDesc::isPlainFile(const std::string &path) +{ + UnixStat st1, st2; + this->fstat(st1); + if (::lstat(path.c_str(), &st2)) + UnixError::throwMe(); + + return (st1.st_ino == st2.st_ino && S_ISREG(st2.st_mode)); +} + // // Stat support diff --git a/libsecurity_utilities/lib/unix++.h b/libsecurity_utilities/lib/unix++.h index 03895d08..f9dbe92f 100644 --- a/libsecurity_utilities/lib/unix++.h +++ b/libsecurity_utilities/lib/unix++.h @@ -231,6 +231,9 @@ public: // stdio interactions FILE *fdopen(const char *mode = NULL); // fdopen(3) + // Is this a regular file? (not a symlink, fifo, etc.) + bool isPlainFile(const std::string &path); + private: int mFd; // UNIX file descriptor diff --git a/libsecurity_utilities/libsecurity_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurity_utilities/libsecurity_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..a78d8bcc --- /dev/null +++ b/libsecurity_utilities/libsecurity_utilities.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SuppressBuildableAutocreation + + 4CA2A5390523D32800978A7B + + primary + + + C2C9C69D0CECBE8400B3FE07 + + primary + + + + + diff --git a/libsecurityd/libsecurityd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/libsecurityd/libsecurityd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..c9daec41 --- /dev/null +++ b/libsecurityd/libsecurityd.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,34 @@ + + + + + SuppressBuildableAutocreation + + 4C09A2920557240300FED7A3 + + primary + + + 4C31C2D9055341AA006D00BD + + primary + + + 4CA1FEBD052A3C8100F22E42 + + primary + + + 4CA1FEC8052A44A100F22E42 + + primary + + + C2A788530B7AA65B00CFF85C + + primary + + + + + diff --git a/sec/sec.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/sec/sec.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..c028ee75 --- /dev/null +++ b/sec/sec.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,89 @@ + + + + + SuppressBuildableAutocreation + + 0C0BDB55175687EC00BC1A7E + + primary + + + 18270F5414CF651900B05E7F + + primary + + + 186CDD0E14CA116C00AF9171 + + primary + + + 18D4043414CE0CF300A2BE4E + + primary + + + 18D4056114CE53C200A2BE4E + + primary + + + 4A5CCA4E15ACEFA500702357 + + primary + + + 4A824AFB158FF07000F932C0 + + primary + + + 4CC92AC215A3BC6B00C6D578 + + primary + + + 5284029F164445760035F320 + + primary + + + BEF963FE18B4171200813FA3 + + primary + + + E702E73514E1F3EA00CDE635 + + primary + + + E702E75714E1F48800CDE635 + + primary + + + E71049F1169E023B00DB0045 + + primary + + + E7104A12169E216E00DB0045 + + primary + + + E76079971951FD2800F69731 + + primary + + + E7FEFB82169E363300E18152 + + primary + + + + + diff --git a/sec/securityd/Regressions/secd-55-account-circle.c b/sec/securityd/Regressions/secd-55-account-circle.c index fc4f8348..271a8f15 100644 --- a/sec/securityd/Regressions/secd-55-account-circle.c +++ b/sec/securityd/Regressions/secd-55-account-circle.c @@ -213,7 +213,7 @@ static void tests(void) // Both in circle. - // Emulation of Innsbruck11A368 +Roots: Device A was removed when Device B joined. + // Emulation of // We want Alice to leave circle while an Applicant on a full concordance signed circle with old-Alice as an Alum and Bob a peer. // ZZZ diff --git a/sec/securityd/SecItemServer.c b/sec/securityd/SecItemServer.c index 29485ea8..c6dec55c 100644 --- a/sec/securityd/SecItemServer.c +++ b/sec/securityd/SecItemServer.c @@ -611,7 +611,7 @@ struct sql_stages { the script in the main table. {pre,main,post, reencode} */ static struct sql_stages s3dl_upgrade_script[] = { - { -1, 0, 1, false },/* 0->current: Create version 6 (Innsbruck) database. */ + { -1, 0, 1, false },/* 0->current: Create version 6 database. */ {}, /* 1->current: Upgrade to version 6 from version 1 (LittleBear) -- Unsupported. */ {}, /* 2->current: Upgrade to version 6 from version 2 (BigBearBeta) -- Unsupported */ {}, /* 3->current: Upgrade to version 6 from version 3 (Apex) -- Unsupported */ -- 2.47.2