From: Apple Date: Tue, 23 Sep 2014 21:24:36 +0000 (+0000) Subject: Security-55471.14.18.tar.gz X-Git-Tag: os-x-1095^0 X-Git-Url: https://git.saurik.com/apple/security.git/commitdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef?ds=sidebyside Security-55471.14.18.tar.gz --- 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 00000000..a5e3d880 Binary files /dev/null and b/Security.xcodeproj/project.xcworkspace/xcuserdata/jkauth.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Security.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist b/Security.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..fc11631f --- /dev/null +++ b/Security.xcodeproj/xcuserdata/jkauth.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,104 @@ + + + + + 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 */