<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2013 Apple, Inc. All rights reserved.</string>
</dict>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSMinimumSystemVersion</key>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
);
dependencies = (
721680B3179B4C6C00406BB4 /* PBXTargetDependency */,
+ 37A7CEDA197DBA8700926CE8 /* PBXTargetDependency */,
722CF218175D602F00BCE0A5 /* PBXTargetDependency */,
521470291697842500DF0DB3 /* PBXTargetDependency */,
18F235FF15CA100300060520 /* PBXTargetDependency */,
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 */; };
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 */;
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;
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 = "<absolute>"; };
18F2360015CAF41100060520 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_codesigning.a; path = /usr/local/lib/libsecurity_codesigning.a; sourceTree = "<absolute>"; };
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 = "<group>"; };
+ 37A7CEDB197DCDD700926CE8 /* validation.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = validation.sh; sourceTree = "<group>"; };
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 = "<group>"; };
4C2505B616D2DF9F002CE025 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Icon.icns; sourceTree = "<group>"; };
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 37A7CEA8197DB8FA00926CE8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
4C96F7BE16D6DF8300D3B39D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
4C96F7C316D6DF8400D3B39D /* Keychain Circle Notification */,
72756C00175D485D00F52070 /* cloud_keychain_diagnose */,
721680A7179B40F600406BB4 /* iCloudStats */,
+ 37A7CEAC197DB8FA00926CE8 /* codesign_tests */,
1807384D146D0D4E00F05C24 /* Frameworks */,
1807384C146D0D4E00F05C24 /* Products */,
);
4C96F7C116D6DF8300D3B39D /* Keychain Circle Notification.app */,
72756BFE175D485D00F52070 /* cloud_keychain_diagnose */,
721680A5179B40F600406BB4 /* iCloudStats */,
+ 37A7CEAB197DB8FA00926CE8 /* codesign_tests */,
);
name = Products;
sourceTree = "<group>";
name = "Supporting Files";
sourceTree = "<group>";
};
+ 4C0F6FAF1985879300178101 /* sectask */ = {
+ isa = PBXGroup;
+ children = (
+ 4C0F6F861985877800178101 /* SecEntitlements.h */,
+ );
+ name = sectask;
+ path = ../sectask;
+ sourceTree = "<group>";
+ };
+ 37A7CEAC197DB8FA00926CE8 /* codesign_tests */ = {
+ isa = PBXGroup;
+ children = (
+ 37A7CEAD197DB8FA00926CE8 /* FatDynamicValidation.c */,
+ 37A7CEDB197DCDD700926CE8 /* validation.sh */,
+ );
+ path = codesign_tests;
+ sourceTree = "<group>";
+ };
4C1288F615FFECF2008CE3E3 /* utilities */ = {
isa = PBXGroup;
children = (
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" */;
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0420;
+ TargetAttributes = {
+ 37A7CEAA197DB8FA00926CE8 = {
+ CreatedOnToolsVersion = 6.0;
+ };
+ };
};
buildConfigurationList = 18073844146D0D4E00F05C24 /* Build configuration list for PBXProject "Security" */;
compatibilityVersion = "Xcode 3.2";
4CE4729E16D833FD009070D1 /* Security_temporary_UI */,
72756BFD175D485D00F52070 /* cloud_keychain_diagnose */,
721680A4179B40F600406BB4 /* iCloudStats */,
+ 37A7CEAA197DB8FA00926CE8 /* codesign_tests */,
);
};
/* End PBXProject section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 37A7CEA7197DB8FA00926CE8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 37A7CEAE197DB8FA00926CE8 /* FatDynamicValidation.c in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
4C96F7BD16D6DF8300D3B39D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
target = 18FE67E91471A3AA00A2CBE3 /* copyHeaders */;
targetProxy = 18FE688E1471A4C900A2CBE3 /* PBXContainerItemProxy */;
};
+ 37A7CEDA197DBA8700926CE8 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 37A7CEAA197DB8FA00926CE8 /* codesign_tests */;
+ targetProxy = 37A7CED9197DBA8700926CE8 /* PBXContainerItemProxy */;
+ };
4AD6F6F41651CC2500DB4CE6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = libSecOtrOSX;
};
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 = {
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 = (
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0C6C630A15D193C800BC68CD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0C6C642915D5ADB500BC68CD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0CC3350716C1ED8000399E53</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>1807384A146D0D4E00F05C24</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18270ED514CF282600B05E7F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>182BB567146F4DCA000BF1F3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>182BB598146FE295000BF1F3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>186F778814E59FB200434E1F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>186F778C14E59FDA00434E1F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18F234EA15C9F9A600060520</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18FE67E91471A3AA00A2CBE3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>37A7CEAA197DB8FA00926CE8</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C96F7C016D6DF8300D3B39D</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CB23B45169F5873003A0131</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CC7A7B216CC2A84003E10C1</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CE4729E16D833FD009070D1</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>5214700516977CB800DF0DB3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>721680A4179B40F600406BB4</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>72756BFD175D485D00F52070</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2012 Apple. All rights reserved.</string>
<key>XPCService</key>
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+
+int main(int argc, const char * argv[]) {
+
+ printf("Going to sleep for 60 seconds\n");
+
+ sleep(60);
+
+ return 0;
+}
--- /dev/null
+#!/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 $?
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
</dict>
</plist>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>55471.14.8</string>
+ <string>55471.14.18</string>
<key>CFBundleShortVersionString</key>
<string>3.0</string>
</dict>
_kSecAssessmentAssessmentOriginator
_kSecAssessmentAssessmentSource
_kSecAssessmentAssessmentVerdict
+_kSecAssessmentAssessmentWeakSignature
+_kSecAssessmentAssessmentCodeSigningError
_kSecAssessmentRuleKeyID
_kSecAssessmentRuleKeyPriority
_kSecAssessmentRuleKeyAllow
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0539107D0A37721E00B9E848</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C207F276053B21E600FF85CB</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>795CA7FE0D38013D00BAE6A2</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2C38A530535EDE600D7421F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA2A5390523D32800978A7B</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CF9C5B80535E557009B9B8D</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>1C6C40211121FC0C00031CDE</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>1CD90B6611011176008DD07F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
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 */
};
//
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<CFURLRef> path;
+ CFCopyRef<CFDictionaryRef> context;
AuthorityType type;
CFRef<CFDictionaryRef> result;
CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install");
CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen");
+CFStringRef kSecAssessmentContextQuarantineFlags = CFSTR("context:qtnflags");
+
//
// Read-only in-process access to the policy database
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");
// 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) {
__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)
}
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")))
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)
extern CFStringRef kSecAssessmentOperationTypeInstall; // .. install software
extern CFStringRef kSecAssessmentOperationTypeOpenDocument; // .. LaunchServices-level document open
+extern CFStringRef kSecAssessmentContextQuarantineFlags;
+
/*!
Operational flags for SecAssessment calls
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.
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
};
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)
| kSecCSSignNestedCode
| kSecCSSignOpaque
| kSecCSSignV1
- | kSecCSSignNoV1);
+ | kSecCSSignNoV1
+ | kSecCSSignBundleRoot
+ | kSecCSSignStrictPreflight);
SecPointer<SecCodeSigner> signer = new SecCodeSigner(flags);
signer->parameters(parameters);
CodeSigning::Required(signerRef) = signer->handle();
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
};
| kSecCSDoNotValidateResources
| kSecCSConsiderExpiration
| kSecCSEnforceRevocationChecks
- | kSecCSCheckNestedCode);
+ | kSecCSCheckNestedCode
+ | kSecCSStrictValidate);
SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(staticCodeRef);
const SecRequirement *req = SecRequirement::optional(requirementRef);
checkFlags(flags);
SecPointer<SecStaticCode> staticCode = SecStaticCode::requiredStatic(staticCodeRef);
- CodeSigning::Required(path) = staticCode->canonicalPath();
+ CodeSigning::Required(path) = staticCode->copyCanonicalPath();
END_CSAPI
}
END_CSAPI
}
+
+
+OSStatus SecStaticCodeSetValidationConditions(SecStaticCodeRef codeRef, CFDictionaryRef conditions)
+{
+ BEGIN_CSAPI
+
+ checkFlags(0);
+ SecStaticCode *code = SecStaticCode::requiredStatic(codeRef);
+ code->setValidationModifiers(conditions);
+
+ END_CSAPI
+}
@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
kSecCSDoNotValidateResources = 1 << 2,
kSecCSBasicValidateOnly = kSecCSDoNotValidateExecutable | kSecCSDoNotValidateResources,
kSecCSCheckNestedCode = 1 << 3,
+ kSecCSStrictValidate = 1 << 4,
};
OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCode, SecCSFlags flags,
*/
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
}
#include "detachedrep.h"
#include "csdatabase.h"
#include "csutilities.h"
+#include "dirscanner.h"
#include <CoreFoundation/CFURLAccess.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTrustPriv.h>
#include <security_utilities/cfmunge.h>
#include <Security/CMSDecoder.h>
#include <security_utilities/logging.h>
+#include <dirent.h>
+#include <sstream>
namespace Security {
if (mine || his)
return mine && his && CFEqual(mine, his);
else
- return CFEqual(this->canonicalPath(), other->canonicalPath());
+ return CFEqual(CFRef<CFURLRef>(this->copyCanonicalPath()), CFRef<CFURLRef>(other->copyCanonicalPath()));
}
CFHashCode SecStaticCode::hash()
if (CFDataRef h = this->cdHash())
return CFHash(h);
else
- return CFHash(this->canonicalPath());
+ return CFHash(CFRef<CFURLRef>(this->copyCanonicalPath()));
}
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<CFArrayRef>("omissions");
+ if (CFArrayRef errors = source.get<CFArrayRef>("errors"))
+ CFArrayApplyFunction(errors, CFRangeMake(0, CFArrayGetCount(errors)), addError, &this->mTolerateErrors);
+ }
+}
//
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
}
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<CFMutableDictionaryRef> 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));
});
}
+static bool isOmitRule(CFTypeRef value)
+{
+ if (CFGetTypeID(value) == CFBooleanGetTypeID())
+ return value == kCFBooleanFalse;
+ CFDictionary rule(value, errSecCSResourceRulesInvalid);
+ return rule.get<CFBooleanRef>("omit") == kCFBooleanTrue;
+}
+
+bool SecStaticCode::hasWeakResourceRules(CFDictionaryRef rulesDict, CFArrayRef allowedOmissions)
+{
+ // compute allowed omissions
+ CFRef<CFArrayRef> defaultOmissions = this->diskRep()->allowedResourceOmissions();
+ assert(defaultOmissions);
+ CFRef<CFMutableArrayRef> 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.
//
}
-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);
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);
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<SecRequirementRef> req;
if (SecRequirementCreateWithString(seal.requirement(), kSecCSDefaultFlags, &req.aref()))
SecPointer<SecStaticCode> 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);
}
}
+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<SecStaticCode> frameworkVersion = new SecStaticCode(DiskRep::bestGuess(real_full_path));
+ frameworkVersion->setMonitor(this->monitor());
+ frameworkVersion->staticValidate(flags, SecRequirement::required(req));
+ }
+ }
+}
+
//
// Test a CodeDirectory flag.
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<CFTypeRef> veto = reportEvent(CFSTR("validated"), NULL)) {
if (CFGetTypeID(veto) == CFNumberGetTypeID())
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<SecStaticCode> subcode = new SecStaticCode(DiskRep::bestGuess(this->mainExecutablePath(), &ctx));
subcode->detachedSignature(this->mDetachedSig); // carry over explicit (but not implicit) detached signature
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(); }
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);
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; }
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);
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<DiskRep> mRep; // on-disk representation
CFRef<CFDataRef> mDetachedSig; // currently applied explicit detached signature
+ // private validation modifiers (only used by Gatekeeper checkfixes)
+ MacOSErrorSet mTolerateErrors; // soft error conditions to ignore
+ CFRef<CFArrayRef> mAllowOmissions; // additionally allowed resource omissions
+
// master validation state
bool mValidated; // core validation was attempted
OSStatus mValidationResult; // outcome of core validation
*/
#include "bundlediskrep.h"
#include "filediskrep.h"
+#include "dirscanner.h"
#include <CoreFoundation/CFURLAccess.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <security_utilities/cfmunge.h>
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<CFURLRef> 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"));
// conventional executable bundle: CFBundle identifies an executable for us
if (CFRef<CFURLRef> 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;
}
if (!mMainExecutableURL)
MacOSError::throwMe(errSecCSBadBundleFormat);
mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
+ if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
+ recordStrictError(errSecCSRegularFile);
mFormat = "widget bundle";
return;
}
// 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";
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;
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)
}
}
+//
+// 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.
// the Info.plist comes from the magic CFBundle-indicated place and ONLY from there
case cdInfoSlot:
if (CFRef<CFURLRef> info = _CFBundleCopyInfoPlistURL(mBundle))
- return cfLoadFile(info);
+ return loadRegularFile(info);
else
return NULL;
// by default, we take components from the executable image or files
//
// Various aspects of our DiskRep personality.
//
-CFURLRef BundleDiskRep::canonicalPath()
+CFURLRef BundleDiskRep::copyCanonicalPath()
{
if (CFURLRef url = CFBundleCopyBundleURL(mBundle))
return url;
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();
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<CFDictionaryRef>("{rules={"
);
}
+
+CFArrayRef BundleDiskRep::allowedResourceOmissions()
+{
+ return cfmake<CFArrayRef>("["
+ "'^(.*/)?\\.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);
}
+//
+// Strict validation.
+// Takes an array of CFNumbers of errors to tolerate.
+//
+void BundleDiskRep::strictValidate(const ToleratedErrors& tolerated)
+{
+ std::vector<OSStatus> 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
//
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();
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:
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<CFBundleRef> mBundle;
bool mInstallerPackage; // is an installer (not executable) bundle
string mFormat; // format description string
RefPointer<DiskRep> mExecRep; // DiskRep for main executable file
+ std::set<OSStatus> mStrictErrors; // strict validation errors encountered
};
--- /dev/null
+/*
+ * 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 <dirent.h>
+#include <unistd.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/debugging.h>
+#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<Rule *> 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
--- /dev/null
+/*
+ * 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 <dirent.h>
+#include <fts.h>
+#include <security_utilities/cfutilities.h>
+
+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<Rule *> Rules;
+ Rules mRules;
+ int mRequireCount;
+};
+
+
+} // end namespace CodeSigning
+} // end namespace Security
+
+#endif // !_H_DIRSCANNER
}
+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).
public:
class SigningContext;
+ typedef std::set<OSStatus> ToleratedErrors;
+
public:
DiskRep();
virtual ~DiskRep();
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]
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
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
// 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(); }
}
-CFURLRef KernelDiskRep::canonicalPath()
+CFURLRef KernelDiskRep::copyCanonicalPath()
{
return makeCFURL("/mach_kernel");
}
CFDataRef component(CodeDirectory::SpecialSlot slot);
CFDataRef identification();
std::string mainExecutablePath();
- CFURLRef canonicalPath();
+ CFURLRef copyCanonicalPath();
size_t signingLimit();
std::string format();
UnixPlusPlus::FileDesc &fd();
{
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<Universal> 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);
}
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);
}
}
+//
+// Strict validation
+//
+void MachORep::strictValidate(const ToleratedErrors& tolerated)
+{
+ if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end())
+ MacOSError::throwMe(errSecCSBadMainExecutable);
+}
+
+
//
// FileDiskRep::Writers
//
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
--- /dev/null
+/*
+ * 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 <CoreFoundation/CoreFoundation.h>
+#include <Security/SecCodePriv.h>
+#include <Security/SecCodeSigner.h>
+#include <Security/SecStaticCode.h>
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/cfmunge.h>
+#include <CoreFoundation/CFBundlePriv.h>
+
+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<SecStaticCode> code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep());
+
+ CFCopyRef<CFDataRef> 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<CFDictionaryRef> 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<CFDataRef> 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<CFDictionaryRef> 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<CFDictionaryRef> 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<SecStaticCode> code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep());
+
+ CFCopyRef<CFDataRef> 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<CFDictionaryRef> 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<CFDataRef> signature = CFDataCreateMutable(NULL, 0);
+ CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N, %O=%O}",
+ kSecCodeSignerDetached, signature.get(),
+ kSecCodeSignerIdentity, /* kCFNull, */
+ kSecCodeSignerResourceRules, rules.get());
+ CFRef<SecCodeSignerRef> 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
--- /dev/null
+/*
+ * 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 <Security/CodeSigning.h>
+#include <security_utilities/sqlite++.h>
+
+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
}
-CFURLRef PidDiskRep::canonicalPath()
+CFURLRef PidDiskRep::copyCanonicalPath()
{
return mBundleURL.retain();
}
CFDataRef component(CodeDirectory::SpecialSlot slot);
CFDataRef identification();
std::string mainExecutablePath();
- CFURLRef canonicalPath();
+ CFURLRef copyCanonicalPath();
size_t signingLimit();
std::string format();
UnixPlusPlus::FileDesc &fd();
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
switch (type) {
case kAuthorityExecute:
- gkbis_invoke_collector(cfString(path).c_str());
evaluateCode(path, kAuthorityExecute, flags, context, result, true);
break;
case kAuthorityInstall:
CFRef<SecRequirementRef> 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
}
cfadd(result, "{%O=%B}", kSecAssessmentAssessmentVerdict, false);
addAuthority(flags, result, latentLabel.c_str(), latentID);
}
+
+
+void PolicyEngine::adjustValidation(SecStaticCodeRef code)
+{
+ CFRef<CFDictionaryRef> conditions = mOpaqueWhitelist.validationConditionsFor(code);
+ SecStaticCodeSetValidationConditions(code, conditions);
+}
bool PolicyEngine::temporarySigning(SecStaticCodeRef code, AuthorityType type, CFURLRef path, SecAssessmentFlags matchFlags)
//
// 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);
CFCopyRef<SecStaticCodeRef> 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);
}
}
- 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)) {
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);
}
//
// 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<CFMutableDictionaryRef> auth = makeCFMutableDictionary();
if (label && label[0])
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)
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);
+ }
}
}
#define _H_POLICYENGINE
#include "SecAssessment.h"
+#include "opaquewhitelist.h"
#include "policydb.h"
#include <security_utilities/globalizer.h>
#include <security_utilities/cfutilities.h>
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:
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<CFTypeRef> &target, AuthorityType type, CFDictionary &context, std::string *signUnsigned);
void setOrigin(CFArrayRef chain, CFMutableDictionaryRef result);
void recordOutcome(SecStaticCodeRef code, bool allow, AuthorityType type, double expires, SQLite::int64 authority);
+
+private:
+ OpaqueWhitelist mOpaqueWhitelist;
};
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)
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);
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;
}
}
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 {
void addRule(CFTypeRef key, CFTypeRef value);
private:
- std::string mRoot;
+ std::string mRoot, mRelBase;
FTS *mFTS;
CFCopyRef<CFDictionaryRef> mRawRules;
typedef std::vector<Rule *> Rules;
Rules mRules;
CodeDirectory::HashAlgorithm mHashType;
+ bool mCheckUnreadable;
+ bool mCheckUnknownType;
};
_kSecAssessmentAssessmentOriginalVerdict
_kSecAssessmentAssessmentSource
_kSecAssessmentAssessmentVerdict
+_kSecAssessmentAssessmentWeakSignature
+_kSecAssessmentAssessmentCodeSigningError
# gatekeeper logging
//
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<CFDictionaryRef> infoDict;
if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
// 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
// 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;
}
// build the modern (V2) resource seal
__block CFRef<CFMutableDictionaryRef> 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) {
if (!(state.signingFlags() & kSecCSSignNoV1)) {
// build the legacy (V1) resource seal
__block CFRef<CFMutableDictionaryRef> 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) {
//
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);
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; }
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);
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
};
//
// Both the canonical and main executable path of a SingleDiskRep is, well, its path.
//
-CFURLRef SingleDiskRep::canonicalPath()
+CFURLRef SingleDiskRep::copyCanonicalPath()
{
return makeCFURL(mPath);
}
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
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 */; };
184461A1146E9AD100B12992 /* lib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = lib.xcconfig; sourceTree = "<group>"; };
184461A2146E9AD100B12992 /* release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = release.xcconfig; sourceTree = "<group>"; };
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 = "<group>"; };
+ 7A078F641996C1C2003D68DA /* dirscanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dirscanner.h; sourceTree = "<group>"; };
+ 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaquewhitelist.cpp; sourceTree = "<group>"; };
+ 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opaquewhitelist.h; sourceTree = "<group>"; };
BEC3A75B16F78D21003E5634 /* SecTaskPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecTaskPriv.h; sourceTree = "<group>"; };
C200424915D425B7004AE0A1 /* libsecurity_codesigning.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_codesigning.a; path = ../../../usr/local/lib/libsecurity_codesigning.a; sourceTree = "<group>"; };
C200424A15D425B7004AE0A1 /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_utilities.a; path = ../../../usr/local/lib/libsecurity_utilities.a; sourceTree = "<group>"; };
C2093AA70BB0948000EB8599 /* reqreader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqreader.h; sourceTree = "<group>"; };
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 = "<group>"; };
- C209697215BF57EB0093035F /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecurity_utilities.a; sourceTree = "<group>"; };
+ C209697215BF57EB0093035F /* libsecurity_utilities.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecurity_utilities.a; sourceTree = "<group>"; };
C2110704158BF5C8001D7F76 /* gkmerge */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = gkmerge; sourceTree = "<group>"; };
C21CFC5D0A250D1C006CD5B1 /* reqdumper.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = reqdumper.cpp; sourceTree = "<group>"; };
C21CFC5E0A250D1C006CD5B1 /* reqdumper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reqdumper.h; sourceTree = "<group>"; };
files = (
C200424D15D425D9004AE0A1 /* libsecurity_codesigning.a in Frameworks */,
C200424E15D425D9004AE0A1 /* libsecurity_utilities.a in Frameworks */,
+ 7ACF261219958B6F00849B25 /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
children = (
C273606D1433F09000A9A5FF /* SecAssessment.h */,
C273606C1433F09000A9A5FF /* SecAssessment.cpp */,
+ 7A9DA65B1948D1BA004635E6 /* opaquewhitelist.h */,
+ 7A9DA65A1948D1BA004635E6 /* opaquewhitelist.cpp */,
C24EABAA1421432800C16AA9 /* policydb.h */,
C24EABAC1421433700C16AA9 /* policydb.cpp */,
C273601D1432A60B00A9A5FF /* policyengine.h */,
C2353410145F1B110073F964 /* xar++.cpp */,
C2F4439914C626D4000A01E6 /* quarantine++.h */,
C2F4439814C626D4000A01E6 /* quarantine++.cpp */,
+ 7A078F641996C1C2003D68DA /* dirscanner.h */,
+ 7A078F631996C1C2003D68DA /* dirscanner.cpp */,
);
name = "Local Utilities";
sourceTree = "<group>";
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 */,
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 */,
);
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 */,
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 */,
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C209695F15BF52040093035F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C26AC0EB143BCF01001C98CE</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C26AC7090DAEB3A7005BFB40</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2BC1F250B580D3A003EC9DC</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2BC1F2E0B580D4B003EC9DC</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2D383B80A23A8C4005C63A2</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2E2873F0B5D8F8F009336A0</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>EBB9FF6E1682E51300FF9774</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>7264321D00A8AD0A7F000001</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0535DCBD074A944D00805B04</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0536B294074BC91A00F9F1AD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>7264322800A8AD0A7F000001</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
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(); }
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(); }
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.
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);
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; }
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>053BA30F091C00B100A7007A</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>053BA313091C00BF00A7007A</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>053BA444091FE58C00A7007A</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>053BA46A091FE63E00A7007A</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>058F16530925135E009FA1C5</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C96C8CD113F4132005483E8</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0CBD500016C3242200713B6C</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C5719C712FB5E9E00B31F85</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>52200F8714F2B87F00F7F6E7</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>D6C8AFAD05DD2430003DB724</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>051BDB75069B372600F9D07E</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0592AC8B0415523C00003D05</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0540498B0A37699A0035F195</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C2741ED03E9FBF700A80181</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>AC62F5EF18B4356A00704BBD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
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",
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0C1C92EF15C8AC81007D377B</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0C6C633A15D1BD4800BC68CD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0CCA415815C89E8B002AEC4C</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0CCA42C815C8A387002AEC4C</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>0CCA42D615C8A395002AEC4C</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4C010B86121AE8DF0094CB72</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C738256112DF65200EA003B</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CBCBEB51130A2D700CC18E9</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
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();
}
}
+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
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 <class Key, class Value>
+ 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 <class T>
void apply(T *object, void (T::*func)(CFTypeRef key, CFTypeRef value))
{ Applier<T> app; app.object = object; app.func = func; return apply(app.apply, &app); }
+
+ template <class Key = CFTypeRef, class Value = CFTypeRef>
+ void apply(void (^action)(Key key, Value value))
+ { BlockApplier<Key, Value> app; app.action = action; return apply(app.apply, &app); }
private:
OSStatus mDefaultError;
// 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)); }
#include <exception>
#include <errno.h>
#include <Security/SecBase.h>
+#include <set>
#undef check
static void check(OSStatus status) { if (status != errSecSuccess) throwMe(status); }
static void throwMe(int err) __attribute__((noreturn));
};
+
+typedef std::set<OSStatus> MacOSErrorSet;
//
// macho++ - Mach-O object file helpers
//
#include "macho++.h"
+#include <security_utilities/alloc.h>
#include <security_utilities/memutils.h>
#include <security_utilities/endian.h>
#include <mach-o/dyld.h>
+#include <list>
+#include <algorithm>
+#include <iterator>
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
m64 = true;
break;
default:
+ secdebug("macho", "%p: unrecognized header magic (%x)", this, mHeader->magic);
UnixError::throwMe(ENOEXEC);
}
}
// (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);
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()
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
}
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<struct fat_arch *> 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<size_t, size_t>((*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<uint8_t> gapBytes(Allocator::standard().malloc<uint8_t>(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:
::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
if (isUniversal())
return findImage(bestNativeArch());
else
- return new MachO(*this, mBase);
+ return new MachO(*this, mBase, mLength);
}
size_t Universal::archOffset() 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.
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++)
}
}
-
//
// Quickly guess the Mach-O type of a file.
// Returns type zero if the file isn't Mach-O or Universal.
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> macho(architecture(*it));
+ if (macho->isSuspicious())
+ return true;
+ }
+ return false;
+}
+
} // Security
#include <security_utilities/endian.h>
#include <security_utilities/unix++.h>
#include <security_utilities/cfutilities.h>
+#include <map>
namespace Security {
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
mach_header mHeaderBuffer; // read-in Mach-O header
load_command *mCommandBuffer; // read-in (malloc'ed) Mach-O load commands
+
+ bool mSuspicious; // strict validation failed
};
//
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
// 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<Architecture> 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);
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<size_t, size_t> OffsetsToLength;
+ OffsetsToLength mSizes; // the length for the slice at a given offset
+ bool mSuspicious; // strict validation failed
};
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
// 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
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4CA2A5390523D32800978A7B</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2C9C69D0CECBE8400B3FE07</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>4C09A2920557240300FED7A3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4C31C2D9055341AA006D00BD</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEBD052A3C8100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CA1FEC8052A44A100F22E42</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>C2A788530B7AA65B00CFF85C</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>SuppressBuildableAutocreation</key>
+ <dict>
+ <key>0C0BDB55175687EC00BC1A7E</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18270F5414CF651900B05E7F</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>186CDD0E14CA116C00AF9171</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18D4043414CE0CF300A2BE4E</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>18D4056114CE53C200A2BE4E</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4A5CCA4E15ACEFA500702357</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4A824AFB158FF07000F932C0</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>4CC92AC215A3BC6B00C6D578</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>5284029F164445760035F320</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>BEF963FE18B4171200813FA3</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E702E73514E1F3EA00CDE635</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E702E75714E1F48800CDE635</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E71049F1169E023B00DB0045</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E7104A12169E216E00DB0045</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E76079971951FD2800F69731</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ <key>E7FEFB82169E363300E18152</key>
+ <dict>
+ <key>primary</key>
+ <true/>
+ </dict>
+ </dict>
+</dict>
+</plist>
// Both in circle.
- // Emulation of <rdar://problem/13919554> Innsbruck11A368 +Roots: Device A was removed when Device B joined.
+ // Emulation of <rdar://problem/13919554>
// 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
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 */