]> git.saurik.com Git - apple/security.git/commitdiff
Security-58286.70.7.tar.gz macos-10136 v58286.70.7
authorApple <opensource@apple.com>
Thu, 30 Aug 2018 17:29:35 +0000 (17:29 +0000)
committerApple <opensource@apple.com>
Thu, 30 Aug 2018 17:29:35 +0000 (17:29 +0000)
35 files changed:
OSX/authd/engine.c
OSX/libsecurity_codesigning/lib/CSCommon.h
OSX/libsecurity_codesigning/lib/CSCommonPriv.h
OSX/libsecurity_codesigning/lib/CodeSigner.cpp
OSX/libsecurity_codesigning/lib/CodeSigner.h
OSX/libsecurity_codesigning/lib/SecCode.cpp
OSX/libsecurity_codesigning/lib/SecCode.h
OSX/libsecurity_codesigning/lib/SecCodePriv.h
OSX/libsecurity_codesigning/lib/SecCodeSigner.cpp
OSX/libsecurity_codesigning/lib/SecCodeSigner.h
OSX/libsecurity_codesigning/lib/SecStaticCode.cpp
OSX/libsecurity_codesigning/lib/SecStaticCode.h
OSX/libsecurity_codesigning/lib/StaticCode.cpp
OSX/libsecurity_codesigning/lib/StaticCode.h
OSX/libsecurity_codesigning/lib/bundlediskrep.cpp
OSX/libsecurity_codesigning/lib/cdbuilder.cpp
OSX/libsecurity_codesigning/lib/cdbuilder.h
OSX/libsecurity_codesigning/lib/codedirectory.cpp
OSX/libsecurity_codesigning/lib/codedirectory.h
OSX/libsecurity_codesigning/lib/machorep.cpp
OSX/libsecurity_codesigning/lib/piddiskrep.cpp
OSX/libsecurity_codesigning/lib/sigblob.cpp
OSX/libsecurity_codesigning/lib/sigblob.h
OSX/libsecurity_codesigning/lib/signer.cpp
OSX/libsecurity_codesigning/lib/signer.h
OSX/libsecurity_codesigning/lib/signerutils.cpp
OSX/libsecurity_codesigning/lib/signerutils.h
OSX/libsecurity_utilities/lib/macho++.cpp
OSX/libsecurity_utilities/lib/macho++.h
OSX/sec/Security/Regressions/secitem/si-28-sectrustsettings.m
OSX/sec/Security/SecExports.exp-in
OSX/sec/securityd/SecPolicyServer.c
Security.exp-in
securityd/src/csproxy.cpp
securityd/src/csproxy.h

index 2a3de5c08d6a81bf10a81317a611d923b68a4717..848a0b8fe484a78250fc61e159967873406d908e 100644 (file)
@@ -496,7 +496,7 @@ _evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms)
                                        // otherwise we need to check la_result
                                        if (auth_items_exist(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) || auth_items_exist(engine->context, kAuthorizationEnvironmentPassword)) {
                                                // do not try to get credentials as it has been already passed by sheet
-                                               os_log(AUTHD_LOG, "engine: ingoring builtin sheet authenticate");
+                                               os_log(AUTHD_LOG, "engine: ignoring builtin sheet authenticate");
                                        } else {
                                                // sheet itself did the authenticate the user
                                                os_log(AUTHD_LOG, "engine: running builtin sheet authenticate");
@@ -1376,6 +1376,14 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
         auth_items_copy(engine->hints, environment);
     }
 
+    // First restore all context values from the AuthorizationRef
+    auth_items_t decrypted_items = auth_items_create();
+    require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "engine: enable to create items"));
+    auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth));
+    auth_items_decrypt(decrypted_items, auth_token_get_encryption_key(engine->auth));
+    auth_items_copy(engine->context, decrypted_items);
+    CFReleaseSafe(decrypted_items);
+    
        if (engine->flags & kAuthorizationFlagSheet) {
                CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password");
                if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
@@ -1390,7 +1398,11 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
                if (!enforced_entitlement()) {
                        save_password = true;
                }
-               const char *user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
+        
+        // Try to use/update fresh context values from the environment
+        require_action(environment, done, os_log_debug(AUTHD_LOG, "engine: Missing environment for sheet authorization"); status = errAuthorizationDenied);
+            
+        const char *user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
                require_action(user, done, os_log_debug(AUTHD_LOG, "engine: Missing username"); status = errAuthorizationDenied);
 
                auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
@@ -1425,13 +1437,6 @@ OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t en
                _extract_password_from_la(engine);
                engine->preauthorizing = true;
        }
-
-       auth_items_t decrypted_items = auth_items_create();
-       require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "engine: enable to create items"));
-       auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth));
-       auth_items_decrypt(decrypted_items, auth_token_get_encryption_key(engine->auth));
-       auth_items_copy(engine->context, decrypted_items);
-       CFReleaseSafe(decrypted_items);
     
     engine->dismissed = false;
     auth_rights_clear(engine->grantedRights);
index 3ede88d9e6b8838016c69ee81e204f5fe50c6af6..22129bcb342bc567a324e5b2fb81528b80f7af8d 100644 (file)
@@ -123,6 +123,8 @@ CF_ENUM(OSStatus) {
     errSecCSBadTeamIdentifier =         -66997, /* a Team Identifier is wrong or inappropriate */
     errSecCSSignatureUntrusted =        -66996, /* signature is valid but signer is not trusted */
        errSecMultipleExecSegments =            -66995, /* the image contains multiple executable segments */
+       errSecCSInvalidEntitlements =           -66994, /* invalid entitlement plist */
+       errSecCSInvalidRuntimeVersion =     -66993, /* an invalid runtime version was explicitly set */
 };
 
 /*
@@ -245,6 +247,9 @@ typedef CF_OPTIONS(uint32_t, SecCSFlags) {
        immediately     if it becomes invalid.
        @constant kSecCodeSignatureForceExpiration
        Forces the kSecCSConsiderExpiration flag on all validations of the code.
+       @constant kSecCodeSignatureRuntime
+       Instructs the kernel to apply runtime hardening policies as required by the
+       hardened runtime version
  */
 typedef CF_OPTIONS(uint32_t, SecCodeSignatureFlags) {
        kSecCodeSignatureHost = 0x0001,                 /* may host guest code */
@@ -255,6 +260,7 @@ typedef CF_OPTIONS(uint32_t, SecCodeSignatureFlags) {
        kSecCodeSignatureRestrict = 0x0800, /* restrict dyld loading */
        kSecCodeSignatureEnforcement = 0x1000, /* enforce code signing */
        kSecCodeSignatureLibraryValidation = 0x2000, /* library validation required */
+       kSecCodeSignatureRuntime = 0x10000, /* apply runtime hardening policies */
 };
 
 /*!
index d527dd37f8d2308603c271f18c12c2fc66cf6d16..0bb8202197180807dd4bc09374848d5edea99ccd 100644 (file)
@@ -71,6 +71,7 @@ extern const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[];
        data that is usually written to separate files. This is the format of
        detached signatures if the program is capable of having multiple architectures.
        @constant kSecCodeMagicEntitlement Magic number for a standard entitlement blob.
+       @constant kSecCodeMagicEntitlementDER Magic number for a DER entitlement blob.
        @constant kSecCodeMagicByte The first byte (in NBO) shared by all these magic
        numbers. This is not a valid ASCII character; test for this to distinguish
        between text and binary data if you expect a code signing-related binary blob.
@@ -83,7 +84,8 @@ enum {
        kSecCodeMagicEmbeddedSignature = 0xfade0cc0, /* single-architecture embedded signature */
        kSecCodeMagicDetachedSignature = 0xfade0cc1, /* detached multi-architecture signature */
        kSecCodeMagicEntitlement = 0xfade7171,          /* entitlement blob */
-       
+       kSecCodeMagicEntitlementDER = 0xfade7172,       /* entitlement DER blob */
+
        kSecCodeMagicByte = 0xfa                                        /* shared first byte */
 };
 
index ee8045f98601d8c74fd5c74112ff80c2b76f20af..b6b274e2fe7b10d87495aae50946a911874cd699 100644 (file)
@@ -34,6 +34,7 @@
 #include <Security/SecCertificate.h>
 #include <Security/SecCertificatePriv.h>
 #include <vector>
+#include <errno.h>
 
 namespace Security {
 
@@ -58,6 +59,53 @@ public:
                else
                        return false;
        }
+
+       uint32_t parseRuntimeVersion(std::string& runtime)
+       {
+               uint32_t version = 0;
+               char* cursor = const_cast<char*>(runtime.c_str());
+               char* end = cursor + runtime.length();
+               char* nxt = NULL;
+               long component = 0;
+               int component_shift = 16;
+
+               // x should convert to 0x00XX0000
+               // x.y should convert to 0x00XXYY00
+               // x.y.z should covert to 0x00XXYYZZ
+               // 0, 0.0, and 0.0.0 are rejected
+               // anything else should be rejected
+               while (cursor < end) {
+                       nxt = NULL;
+                       errno = 0;
+                       component = strtol(cursor, &nxt, 10);
+                       if (cursor == nxt ||
+                               (errno != 0) ||
+                               (component < 0 || component > UINT8_MAX)) {
+                               secdebug("signer", "Runtime version: %s is invalid", runtime.c_str());
+                               MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
+                       }
+                       version |= (component & 0xff) << component_shift;
+                       component_shift -= 8;
+
+                       if (*nxt == '\0') {
+                               break;
+                       }
+
+                       if (*nxt != '.' || component_shift < 0 || (nxt + 1) == end) {
+                               // Catch a trailing "."
+                               secdebug("signer", "Runtime version: %s is invalid", runtime.c_str());
+                               MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
+                       }
+                       cursor = nxt + 1;
+               }
+
+               if (version == 0) {
+                       secdebug("signer","Runtime version: %s is a version of zero", runtime.c_str());
+                       MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
+               }
+
+               return version;
+       }
 };
 
 
@@ -65,7 +113,7 @@ public:
 // Construct a SecCodeSigner
 //
 SecCodeSigner::SecCodeSigner(SecCSFlags flags)
-       : mOpFlags(flags), mLimitedAsync(NULL)
+       : mOpFlags(flags), mLimitedAsync(NULL), mRuntimeVersionOverride(0)
 {
 }
 
@@ -204,7 +252,7 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
        if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
                state.mCMSSize = cfNumber<size_t>(cmsSize);
        else
-               state.mCMSSize = 9000;  // likely big enough
+               state.mCMSSize = 18000; // big enough for now, not forever.
 
        // metadata preservation options
        if (CFNumberRef preserve = get<CFNumberRef>(kSecCodeSignerPreserveMetadata)) {
@@ -280,6 +328,14 @@ SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
        state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication);
        state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer);
        state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates);
+
+       if (CFStringRef runtimeVersionOverride = get<CFStringRef>(kSecCodeSignerRuntimeVersion)) {
+               std::string runtime = cfString(runtimeVersionOverride);
+               if (runtime.empty()) {
+                       MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
+               }
+               state.mRuntimeVersionOverride = parseRuntimeVersion(runtime);
+       }
 }
 
 
index e9e41d288ca3acfd31e20902553d19f700122438..7cb3b6d30d179a1a5ce77c74fadcb2be2c1b2494 100644 (file)
@@ -93,6 +93,7 @@ public:
     bool mWantTimeStamp;          // use a Timestamp server
     bool mNoTimeStampCerts;       // don't request certificates with timestamping request
        LimitedAsync *mLimitedAsync;    // limited async workers for verification
+       uint32_t mRuntimeVersionOverride;       // runtime Version Override
 
 };
 
index 3d918e4beb261e79e86256d4435f15448a69722c..47d1cc162cc69a774ee684f8494760b12febe36f 100644 (file)
@@ -214,7 +214,8 @@ OSStatus SecCodeCheckValidityWithErrors(SecCodeRef codeRef, SecCSFlags flags,
                  kSecCSConsiderExpiration
                | kSecCSStrictValidate
                | kSecCSRestrictSidebandData
-               | kSecCSEnforceRevocationChecks);
+               | kSecCSEnforceRevocationChecks
+       );
        SecPointer<SecCode> code = SecCode::required(codeRef);
        code->checkValidity(flags);
        if (const SecRequirement *req = SecRequirement::optional(requirementRef))
@@ -257,6 +258,7 @@ const CFStringRef kSecCodeInfoTimestamp =           CFSTR("signing-timestamp");
 const CFStringRef kSecCodeInfoTrust =                  CFSTR("trust");
 const CFStringRef kSecCodeInfoUnique =                 CFSTR("unique");
 const CFStringRef kSecCodeInfoCdHashes =        CFSTR("cdhashes");
+const CFStringRef kSecCodeInfoRuntimeVersion =         CFSTR("runtime-version");
 
 
 const CFStringRef kSecCodeInfoCodeDirectory =  CFSTR("CodeDirectory");
@@ -265,10 +267,10 @@ const CFStringRef kSecCodeInfoDiskRepInfo =     CFSTR("DiskRepInfo");
 const CFStringRef kSecCodeInfoResourceDirectory = CFSTR("ResourceDirectory");
 
 /* DiskInfoRepInfo types */
-const CFStringRef kSecCodeInfoDiskRepOSPlatform =          CFSTR("OSPlatform");
-const CFStringRef kSecCodeInfoDiskRepOSVersionMin =        CFSTR("OSVersionMin");
-const CFStringRef kSecCodeInfoDiskRepOSSDKVersion =        CFSTR("SDKVersion");
-const CFStringRef kSecCodeInfoDiskRepNoLibraryValidation = CFSTR("NoLibraryValidation");
+const CFStringRef kSecCodeInfoDiskRepVersionPlatform =         CFSTR("VersionPlatform");
+const CFStringRef kSecCodeInfoDiskRepVersionMin =              CFSTR("VersionMin");
+const CFStringRef kSecCodeInfoDiskRepVersionSDK =              CFSTR("VersionSDK");
+const CFStringRef kSecCodeInfoDiskRepNoLibraryValidation =     CFSTR("NoLibraryValidation");
 
 
 OSStatus SecCodeCopySigningInformation(SecStaticCodeRef codeRef, SecCSFlags flags,
index 20ba29f411633317c30e3b9cbfd1576c1cf83779..b1a6afbc1b5f498cae1dfc7826fb846a7e27871b 100644 (file)
@@ -455,6 +455,7 @@ extern const CFStringRef kSecCodeInfoTimestamp;             /* Signing */
 extern const CFStringRef kSecCodeInfoTrust;                    /* Signing */
 extern const CFStringRef kSecCodeInfoUnique;           /* generic */
 extern const CFStringRef kSecCodeInfoCdHashes;         /* generic */
+extern const CFStringRef kSecCodeInfoRuntimeVersion; /*generic */
 
 OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags,
        CFDictionaryRef * __nonnull CF_RETURNS_RETAINED information);
index 7faa363426ac15c36a8fcf2da0da66a92892569d..b610af860ebc41d9849c26eb117aafff1c5d85da 100644 (file)
@@ -45,9 +45,9 @@ extern const CFStringRef kSecCodeInfoCodeOffset;                      /* Internal */
 extern const CFStringRef kSecCodeInfoDiskRepInfo;           /* Internal */
 extern const CFStringRef kSecCodeInfoResourceDirectory;                /* Internal */
 
-extern const CFStringRef kSecCodeInfoDiskRepOSPlatform;          /* Number */
-extern const CFStringRef kSecCodeInfoDiskRepOSVersionMin;        /* Number */
-extern const CFStringRef kSecCodeInfoDiskRepOSSDKVersion;        /* Number */
+extern const CFStringRef kSecCodeInfoDiskRepVersionPlatform;     /* Number */
+extern const CFStringRef kSecCodeInfoDiskRepVersionMin;          /* Number */
+extern const CFStringRef kSecCodeInfoDiskRepVersionSDK;          /* Number */
 extern const CFStringRef kSecCodeInfoDiskRepNoLibraryValidation; /* String */
 
 /*!
index 7d83ce7a39959d5d8c90953cf91fa0c5231be2d7..f043523fd9d4eab0138bdd8cb13407399b3d64f0 100644 (file)
@@ -59,6 +59,7 @@ const CFStringRef kSecCodeSignerTimestampOmitCertificates =   CFSTR("timestamp-omi
 const CFStringRef kSecCodeSignerPreserveMetadata = CFSTR("preserve-metadata");
 const CFStringRef kSecCodeSignerTeamIdentifier =       CFSTR("teamidentifier");
 const CFStringRef kSecCodeSignerPlatformIdentifier = CFSTR("platform-identifier");
+const CFStringRef kSecCodeSignerRuntimeVersion = CFSTR("runtime-version");
 
 
 
@@ -89,7 +90,9 @@ OSStatus SecCodeSignerCreate(CFDictionaryRef parameters, SecCSFlags flags,
                | kSecCSSignV1
                | kSecCSSignNoV1
                | kSecCSSignBundleRoot
-               | kSecCSSignStrictPreflight);
+               | kSecCSSignStrictPreflight
+        | kSecCSSignGeneratePEH
+               | kSecCSSignGenerateEntitlementDER);
        SecPointer<SecCodeSigner> signer = new SecCodeSigner(flags);
        signer->parameters(parameters);
        CodeSigning::Required(signerRef) = signer->handle();
index 7b3704b2ff4388adecb42bf6585291b0960d0033..88067c14b8b8a3167702037068a297ee92d5168c 100644 (file)
@@ -135,6 +135,12 @@ CFTypeID SecCodeSignerGetTypeID(void);
                on the verifying system.
                The default is to embed enough certificates to ensure proper verification of Apple-generated
                timestamp signatures.
+       @constant kSecCodeSignerRuntimeVersion A CFString indicating the version of runtime hardening policies
+               that the process should be opted into. The string should be of the form "x", "x.x", or "x.x.x" where
+               x is a number between 0 and 255. This parameter is optional. If the signer specifies
+               kSecCodeSignatureRuntime but does not provide this parameter, the runtime version will be the SDK
+               version built into the Mach-O.
+
  */
 extern const CFStringRef kSecCodeSignerApplicationData;
 extern const CFStringRef kSecCodeSignerDetached;
@@ -157,6 +163,7 @@ extern const CFStringRef kSecCodeSignerTimestampOmitCertificates;
 extern const CFStringRef kSecCodeSignerPreserveMetadata;
 extern const CFStringRef kSecCodeSignerTeamIdentifier;
 extern const CFStringRef kSecCodeSignerPlatformIdentifier;
+extern const CFStringRef kSecCodeSignerRuntimeVersion;
 
 enum {
     kSecCodeSignerPreserveIdentifier = 1 << 0,         // preserve signing identifier
@@ -165,7 +172,9 @@ enum {
     kSecCodeSignerPreserveResourceRules = 1 << 3,      // preserve resource rules (and thus resources)
     kSecCodeSignerPreserveFlags = 1 << 4,                      // preserve signing flags
        kSecCodeSignerPreserveTeamIdentifier = 1 << 5,  // preserve team identifier flags
-       kSecCodeSignerPreserveDigestAlgorithm = 1 << 6,  // preserve digest algorithms used
+       kSecCodeSignerPreserveDigestAlgorithm = 1 << 6, // preserve digest algorithms used
+       kSecCodeSignerPreservePEH = 1 << 7,                             // preserve pre-encryption hashes
+       kSecCodeSignerPreserveRuntime = 1 << 8,        // preserve the runtime version
 };
 
 
@@ -194,6 +203,8 @@ enum {
        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
+       kSecCSSignGeneratePEH = 1 << 8,         // generate pre-encryption hashes
+    kSecCSSignGenerateEntitlementDER = 1 << 9, // generate entitlement DER
 };
 
 
index 3d96abd051ef60ad52fe09dfcc0155fa8b0ae954..7a93ee246e74945fd02a737416a5308b61ca182a 100644 (file)
@@ -122,6 +122,7 @@ OSStatus SecStaticCodeCheckValidityWithErrors(SecStaticCodeRef staticCodeRef, Se
                | kSecCSRestrictSymlinks
                | kSecCSRestrictToAppLike
         | kSecCSUseSoftwareSigningCert
+           | kSecCSValidatePEH
        );
 
        if (errors)
index 5fdb98b76e006b88cdcfa2de9794ecebbe2296e4..1183adca446585ca883fc7a3ce1ab7aec0c5219f 100644 (file)
@@ -179,6 +179,7 @@ CF_ENUM(uint32_t) {
        kSecCSRestrictToAppLike = 1 << 8,
        kSecCSRestrictSidebandData = 1 << 9,
     kSecCSUseSoftwareSigningCert = 1 << 10,
+       kSecCSValidatePEH = 1 << 11,
 };
 
 OSStatus SecStaticCodeCheckValidity(SecStaticCodeRef staticCode, SecCSFlags flags,
index ff621d537a71b80e2d21ce347b2186535a0b897b..059a8c00e5db4b6a0ab10f9a65f9d30d4f668ef6 100644 (file)
@@ -35,6 +35,7 @@
 #include "sigblob.h"
 #include "resources.h"
 #include "detachedrep.h"
+#include "signerutils.h"
 #if TARGET_OS_OSX
 #include "csdatabase.h"
 #endif
@@ -343,6 +344,7 @@ void SecStaticCode::resetValidity()
                mResourcesValidContext = NULL;
        }
        mDir = NULL;
+       mCodeDirectories.clear();
        mSignature = NULL;
        for (unsigned n = 0; n < cdSlotCount; n++)
                mCache[n] = NULL;
@@ -384,7 +386,7 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
                                        return NULL;
 
                                if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
-                                               CFDataGetLength(data), -slot))
+                                               CFDataGetLength(data), -slot, false))
                                        MacOSError::throwMe(errorForSlot(slot)); // ... then bail
                        }
                        cache = data;   // it's okay, cache it
@@ -399,6 +401,41 @@ CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fai
 }
 
 
+//
+// Get the CodeDirectories.
+// Throws (if check==true) or returns NULL (check==false) if there are none.
+// Always throws if the CodeDirectories exist but are invalid.
+// NEVER validates against the signature.
+//
+const SecStaticCode::CodeDirectoryMap *
+SecStaticCode::codeDirectories(bool check /* = true */) const
+{
+       if (mCodeDirectories.empty()) {
+               try {
+                       loadCodeDirectories(mCodeDirectories);
+               } catch (...) {
+                       if (check)
+                               throw;
+                       // We wanted a NON-checked peek and failed to safely decode the existing CodeDirectories.
+                       // Pretend this is unsigned, but make sure we didn't somehow cache an invalid CodeDirectory.
+                       if (!mCodeDirectories.empty()) {
+                               assert(false);
+                               Syslog::warning("code signing internal problem: mCodeDirectories set despite exception exit");
+                               MacOSError::throwMe(errSecCSInternalError);
+                       }
+               }
+       } else {
+               return &mCodeDirectories;
+       }
+       if (!mCodeDirectories.empty()) {
+               return &mCodeDirectories;
+       }
+       if (check) {
+               MacOSError::throwMe(errSecCSUnsigned);
+       }
+       return NULL;
+}
+
 //
 // Get the CodeDirectory.
 // Throws (if check==true) or returns NULL (check==false) if there is none.
@@ -410,11 +447,10 @@ const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */) const
        if (!mDir) {
                // pick our favorite CodeDirectory from the choices we've got
                try {
-                       CodeDirectoryMap candidates;
-                       if (loadCodeDirectories(candidates)) {
+                       CodeDirectoryMap const *candidates = codeDirectories(check);
+                       if (candidates != NULL) {
                                CodeDirectory::HashAlgorithm type = CodeDirectory::bestHashOf(mHashAlgorithms);
-                               mDir = candidates[type];                                                                // and the winner is...
-                               candidates.swap(mCodeDirectories);
+                               mDir = candidates->at(type);    // and the winner is...
                        }
                } catch (...) {
                        if (check)
@@ -689,14 +725,17 @@ bool SecStaticCode::verifySignature()
                MacOSError::throwMe(errSecCSSignatureFailed);
        }
 
-       // retrieve auxiliary data bag and verify against current state
-       CFRef<CFDataRef> hashBag;
-       switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgility(cms, 0, &hashBag.aref())) {
+       // retrieve auxiliary v1 data bag and verify against current state
+       CFRef<CFDataRef> hashAgilityV1;
+       switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgility(cms, 0, &hashAgilityV1.aref())) {
        case noErr:
-               if (hashBag) {
-                       CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashBag);
+               if (hashAgilityV1) {
+                       CFRef<CFDictionaryRef> hashDict = makeCFDictionaryFrom(hashAgilityV1);
                        CFArrayRef cdList = CFArrayRef(CFDictionaryGetValue(hashDict, CFSTR("cdhashes")));
                        CFArrayRef myCdList = this->cdHashes();
+
+                       /* Note that this is not very "agile": There's no way to calculate the exact
+                        * list for comparison if it contains hash algorithms we don't know yet... */
                        if (cdList == NULL || !CFEqual(cdList, myCdList))
                                MacOSError::throwMe(errSecCSSignatureFailed);
                }
@@ -707,6 +746,62 @@ bool SecStaticCode::verifySignature()
                MacOSError::throwMe(rc);
        }
 
+       // retrieve auxiliary v2 data bag and verify against current state
+       CFRef<CFDictionaryRef> hashAgilityV2;
+       switch (OSStatus rc = CMSDecoderCopySignerAppleCodesigningHashAgilityV2(cms, 0, &hashAgilityV2.aref())) {
+               case noErr:
+                       if (hashAgilityV2) {
+                               /* Require number of code directoris and entries in the hash agility
+                                * dict to be the same size (no stripping out code directories).
+                                */
+                               if (CFDictionaryGetCount(hashAgilityV2) != mCodeDirectories.size()) {
+                                       MacOSError::throwMe(errSecCSSignatureFailed);
+                               }
+
+                               /* Require every cdhash of every code directory whose hash
+                                * algorithm we know to be in the agility dictionary.
+                                *
+                                * We check untruncated cdhashes here because we can.
+                                */
+                               bool foundOurs = false;
+                               for (auto& entry : mCodeDirectories) {
+                                       SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(entry.first);
+
+                                       if (tag == SEC_OID_UNKNOWN) {
+                                               // Unknown hash algorithm, ignore.
+                                               continue;
+                                       }
+
+                                       CFRef<CFNumberRef> key = makeCFNumber(int(tag));
+                                       CFRef<CFDataRef> entryCdhash;
+                                       entryCdhash = (CFDataRef)CFDictionaryGetValue(hashAgilityV2, (void*)key.get());
+
+                                       CodeDirectory const *cd = (CodeDirectory const*)CFDataGetBytePtr(entry.second);
+                                       CFRef<CFDataRef> ourCdhash = cd->cdhash(false); // Untruncated cdhash!
+                                       if (!CFEqual(entryCdhash, ourCdhash)) {
+                                               MacOSError::throwMe(errSecCSSignatureFailed);
+                                       }
+
+                                       if (entry.first == this->hashAlgorithm()) {
+                                               foundOurs = true;
+                                       }
+                               }
+
+                               /* Require the cdhash of our chosen code directory to be in the dictionary.
+                                * In theory, the dictionary could be full of unsupported cdhashes, but we
+                                * really want ours, which is bound to be supported, to be covered.
+                                */
+                               if (!foundOurs) {
+                                       MacOSError::throwMe(errSecCSSignatureFailed);
+                               }
+                       }
+                       break;
+               case -1:        /* CMS used to return this for "no attribute found", so tolerate it. Now returning noErr/NULL */
+                       break;
+               default:
+                       MacOSError::throwMe(rc);
+       }
+
        // internal signing time (as specified by the signer; optional)
        mSigningTime = 0;       // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
        switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
@@ -974,7 +1069,7 @@ void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus
                if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
                                MacOSError::throwMe(fail);      // ... and is missing
        } else {
-               if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
+               if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot, false))
                        MacOSError::throwMe(fail);
        }
 }
@@ -1009,7 +1104,8 @@ void SecStaticCode::validateExecutable()
                                __block bool good = true;
                                CodeDirectory::multipleHashFileData(fd, thisPage, hashAlgorithms(), ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
                                        const CodeDirectory* cd = (const CodeDirectory*)CFDataGetBytePtr(mCodeDirectories[type]);
-                                       if (!hasher->verify((*cd)[slot]))
+                                       if (!hasher->verify(cd->getSlot(slot,
+                                                                                                       mValidationFlags & kSecCSValidatePEH)))
                                                good = false;
                                });
                                if (!good) {
@@ -1292,7 +1388,7 @@ CFDataRef SecStaticCode::copyComponent(CodeDirectory::SpecialSlot slot, CFDataRe
        const CodeDirectory* cd = this->codeDirectory();
        if (CFCopyRef<CFDataRef> component = this->component(slot)) {
                if (hash) {
-                       const void *slotHash = (*cd)[slot];
+                       const void *slotHash = cd->getSlot(slot, false);
                        if (cd->hashSize != CFDataGetLength(hash) || 0 != memcmp(slotHash, CFDataGetBytePtr(hash), cd->hashSize)) {
                                Syslog::notice("copyComponent hash mismatch slot %d length %d", slot, int(CFDataGetLength(hash)));
                                return NULL;    // mismatch
@@ -1358,12 +1454,12 @@ bool SecStaticCode::checkfix30814861(string path, bool addition) {
        CFRef<CFDictionaryRef> inf = diskRepInformation();
        try {
                CFDictionary info(diskRepInformation(), errSecCSNotSupported);
-               uint32_t platformCmd =
-                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepOSPlatform, errSecCSNotSupported), 0);
+               uint32_t platform =
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionPlatform, errSecCSNotSupported), 0);
                uint32_t sdkVersion =
-                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepOSSDKVersion, errSecCSNotSupported), 0);
+                       cfNumber(info.get<CFNumberRef>(kSecCodeInfoDiskRepVersionSDK, errSecCSNotSupported), 0);
 
-               if (platformCmd != LC_VERSION_MIN_IPHONEOS || sdkVersion >= 0x00090000) {
+               if (platform != PLATFORM_IOS || sdkVersion >= 0x00090000) {
                        return false;
                }
        } catch (const MacOSError &error) {
@@ -1834,6 +1930,9 @@ CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
        CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithms, digests);
        if (cd->platform)
                CFDictionaryAddValue(dict, kSecCodeInfoPlatformIdentifier, CFTempNumber(cd->platform));
+       if (cd->runtimeVersion()) {
+               CFDictionaryAddValue(dict, kSecCodeInfoRuntimeVersion, CFTempNumber(cd->runtimeVersion()));
+       }
 
        //
        // Deliver any Info.plist only if it looks intact
index 46dfbd2e05eff3574598c0ae6c563e30720843a5..71ead90f33b1cadd5122f3190c78d242222bf552 100644 (file)
@@ -117,7 +117,10 @@ public:
        void detachedSignature(CFDataRef sig);          // attach an explicitly given detached signature
        void checkForSystemSignature();                         // check for and attach system-supplied detached signature
 
+       typedef std::map<CodeDirectory::HashAlgorithm, CFCopyRef<CFDataRef> > CodeDirectoryMap;
+
        const CodeDirectory *codeDirectory(bool check = true) const;
+       const CodeDirectoryMap *codeDirectories(bool check = true) const;
        CodeDirectory::HashAlgorithm hashAlgorithm() const { return codeDirectory()->hashType; }
        CodeDirectory::HashAlgorithms hashAlgorithms() const { return mHashAlgorithms; }
        CFDataRef cdHash();
@@ -200,12 +203,13 @@ public:
     bool trustedSigningCertChain() { return mTrustedSigningCertChain; }
 #endif
 
+       void handleOtherArchitectures(void (^handle)(SecStaticCode* other));
+
 public:
        void staticValidate(SecCSFlags flags, const SecRequirement *req);
        void staticValidateCore(SecCSFlags flags, const SecRequirement *req);
        
 protected:
-       typedef std::map<CodeDirectory::HashAlgorithm, CFCopyRef<CFDataRef> > CodeDirectoryMap;
        bool loadCodeDirectories(CodeDirectoryMap& cdMap) const;
        
 protected:
@@ -220,8 +224,6 @@ protected:
        static void checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context);
        bool hasWeakResourceRules(CFDictionaryRef rulesDict, uint32_t version, CFArrayRef allowedOmissions);
 
-       void handleOtherArchitectures(void (^handle)(SecStaticCode* other));
-
 private:
        void validateOtherVersions(CFURLRef path, SecCSFlags flags, SecRequirementRef req, SecStaticCode *code);
        bool checkfix30814861(string path, bool addition);
index 8ec8852c640e3c4da426033e22435e8a95530710..8b6a67b90e187d48f50fa374701e055c15f5543d 100644 (file)
@@ -471,6 +471,7 @@ CFArrayRef BundleDiskRep::modifiedFiles()
        checkModifiedFile(files, cdResourceDirSlot);
        checkModifiedFile(files, cdTopDirectorySlot);
        checkModifiedFile(files, cdEntitlementSlot);
+       checkModifiedFile(files, cdEntitlementDERSlot);
        checkModifiedFile(files, cdRepSpecificSlot);
        for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot)
                checkModifiedFile(files, slot);
index babfe29c1603f114eee3a3fa5765f36601a360ac..e3c4240ddb68e1e0d96c686ce9e8e048f9a0818f 100644 (file)
@@ -50,6 +50,8 @@ CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm)
          mExecSegOffset(0),
          mExecSegLimit(0),
          mExecSegFlags(0),
+         mGeneratePreEncryptHashes(false),
+         mRuntimeVersion(0),
          mDir(NULL)
 {
        mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength();
@@ -121,6 +123,8 @@ CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count)
 size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
 {
        size_t cdSize = sizeof(CodeDirectory);
+       if (version < supportsPreEncrypt)
+               cdSize -= sizeof(mDir->runtime) + sizeof(mDir->preEncryptOffset);
        if (version < supportsExecSegment)
                cdSize -= sizeof(mDir->execSegBase) + sizeof(mDir->execSegLimit) + sizeof(mDir->execSegFlags);
        if (version < supportsCodeLimit64)
@@ -157,6 +161,11 @@ size_t CodeDirectory::Builder::size(const uint32_t version)
        if (mTeamID.size())
                offset += mTeamID.size() + 1;   // size of teamID (with null byte)
        offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector
+
+       if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
+               offset += mCodeSlots * mDigestLength;
+       }
+
        if (offset <= offset0)
                UnixError::throwMe(ENOEXEC);
 
@@ -187,8 +196,10 @@ CodeDirectory *CodeDirectory::Builder::build()
        size_t teamIDLength = mTeamID.size() + 1;
        
        // Determine the version
-       if (mExecSegLimit > 0) {
+       if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty() || mRuntimeVersion) {
                version = currentVersion;
+       } else if (mExecSegLimit > 0) {
+               version = supportsExecSegment;
        } else if (mExecLength > UINT32_MAX) {
                version = supportsCodeLimit64;
        } else if (mTeamID.size()) {
@@ -231,6 +242,7 @@ CodeDirectory *CodeDirectory::Builder::build()
        mDir->execSegBase = mExecSegOffset;
        mDir->execSegLimit = mExecSegLimit;
        mDir->execSegFlags = mExecSegFlags;
+       mDir->runtime = mRuntimeVersion;
 
        // locate and fill flex fields
        size_t offset = fixedSize(mDir->version);
@@ -250,18 +262,27 @@ CodeDirectory *CodeDirectory::Builder::build()
                memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength);
                offset += teamIDLength;
        }
+
        // (add new flexibly-allocated fields here)
 
+       /* Pre-encrypt hashes come before normal hashes, so that the kernel can free
+        * the normal, potentially post-encrypt hashes away easily. */
+       if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
+               mDir->preEncryptOffset = (uint32_t)offset;
+               offset += mCodeSlots * mDigestLength;
+       }
+
        mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength);
        offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
+
        assert(offset == total);        // matches allocated size
 
        (void)offset;
        
        // fill special slots
-       memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots);
+       memset(mDir->getSlotMutable((int)-mSpecialSlots, false), 0, mDigestLength * mSpecialSlots);
        for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
-               memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength);
+               memcpy(mDir->getSlotMutable((int)-slot, false), specialSlot((SpecialSlot)slot), mDigestLength);
        
        // fill code slots
        mExec.seek(mExecOffset);
@@ -271,10 +292,23 @@ CodeDirectory *CodeDirectory::Builder::build()
                if (mPageSize)
                        thisPage = min(thisPage, mPageSize);
                MakeHash<Builder> hasher(this);
-               generateHash(hasher, mExec, (*mDir)[slot], thisPage);
+               generateHash(hasher, mExec, mDir->getSlotMutable(slot, false), thisPage);
+               if (mGeneratePreEncryptHashes && mPreservedPreEncryptHashMap.empty()) {
+                       memcpy(mDir->getSlotMutable(slot, true), mDir->getSlot(slot, false),
+                                  mDir->hashSize);
+               }
                remaining -= thisPage;
        }
        assert(remaining == 0);
+
+       PreEncryptHashMap::iterator preEncrypt =
+               mPreservedPreEncryptHashMap.find(mHashType);
+       if (preEncrypt != mPreservedPreEncryptHashMap.end()) {
+               memcpy(mDir->getSlotMutable(0, true),
+                          CFDataGetBytePtr(preEncrypt->second),
+                          mCodeSlots * mDigestLength);
+               mPreservedPreEncryptHashMap.erase(preEncrypt->first); // Releases the CFData memory.
+       }
        
        // all done. Pass ownership to caller
        return mDir;
index 761dcf631dbbf0e0752ebeb249bdeac0186a60e2..7872fd58a2c1afb2ea885109ecd8815aaf2053bd 100644 (file)
@@ -64,6 +64,18 @@ public:
                mExecSegOffset = base; mExecSegLimit = limit; mExecSegFlags = flags; }
        void addExecSegFlags(uint64_t flags) { mExecSegFlags |= flags; }
 
+       typedef std::map<CodeDirectory::HashAlgorithm, CFCopyRef<CFDataRef> >
+               PreEncryptHashMap;
+
+       void generatePreEncryptHashes(bool pre) { mGeneratePreEncryptHashes = pre; }
+       void preservePreEncryptHashMap(PreEncryptHashMap preEncryptHashMap) {
+               mPreservedPreEncryptHashMap = preEncryptHashMap;
+       }
+
+       void runTimeVersion(uint32_t runtime) {
+               mRuntimeVersion = runtime;
+       }
+
        size_t size(const uint32_t version);            // calculate size
        CodeDirectory *build();                                         // build CodeDirectory and return it
     size_t fixedSize(const uint32_t version);  // calculate fixed size of the CodeDirectory
@@ -102,6 +114,11 @@ private:
        uint64_t mExecSegLimit;                                         // limit of executable segment
        uint64_t mExecSegFlags;                                         // executable segment flags
 
+       bool mGeneratePreEncryptHashes;                         // whether to also generate new pre-encrypt hashes
+       PreEncryptHashMap mPreservedPreEncryptHashMap; // existing pre-encrypt hashes to be set
+
+       uint32_t mRuntimeVersion;                                       // Hardened Runtime Version
+
        CodeDirectory *mDir;                                            // what we're building
 };
 
index 1fc4fdddb6460cee6935dbce43e6a17a6f1c795c..33ae72ca4edfb1e1d0b77ce369690ab4581442bf 100644 (file)
@@ -79,6 +79,8 @@ const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
                return kSecCS_TOPDIRECTORYFILE;
        case cdEntitlementSlot:
                return kSecCS_ENTITLEMENTFILE;
+       case cdEntitlementDERSlot:
+               return kSecCS_ENTITLEMENTDERFILE;
        case cdRepSpecificSlot:
                return kSecCS_REPSPECIFICFILE;
        default:
@@ -105,6 +107,7 @@ unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
        case cdSignatureSlot:
                return cdComponentPerArchitecture; // raw
        case cdEntitlementSlot:
+       case cdEntitlementDERSlot:
                return cdComponentIsBlob; // global
        case cdIdentificationSlot:
                return cdComponentPerArchitecture; // raw
@@ -157,7 +160,9 @@ void CodeDirectory::checkIntegrity() const
        if (version > currentVersion)
                secinfo("codedir", "%p version 0x%x newer than current 0x%x",
                        this, uint32_t(version), currentVersion);
-       
+
+       bool hasPreEncryptHashes = version >= supportsPreEncrypt && preEncryptOffset != 0;
+
        // now check interior offsets for validity
        if (!stringAt(identOffset))
                MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
@@ -165,6 +170,8 @@ void CodeDirectory::checkIntegrity() const
                        MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
        if (!contains(hashOffset - int64_t(hashSize) * nSpecialSlots, hashSize * (int64_t(nSpecialSlots) + nCodeSlots)))
                MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range
+       if (hasPreEncryptHashes && !contains(preEncryptOffset, hashSize * (int64_t(nCodeSlots))))
+               MacOSError::throwMe(errSecCSSignatureFailed); // pre-encrypt array out of blob range
        if (const Scatter *scatter = this->scatterVector()) {
                // the optional scatter vector is terminated with an element having (count == 0)
                unsigned int pagesConsumed = 0;
@@ -175,7 +182,8 @@ void CodeDirectory::checkIntegrity() const
                                break;
                        pagesConsumed += scatter->count;
                }
-               if (!contains((*this)[pagesConsumed-1], hashSize))      // referenced too many main hash slots
+               if (!contains(getSlot(pagesConsumed-1, false), hashSize) ||
+                       (hasPreEncryptHashes && !contains(getSlot(pagesConsumed-1, true), hashSize)))   // referenced too many main hash slots
                        MacOSError::throwMe(errSecCSSignatureFailed);
        }
        
@@ -197,13 +205,13 @@ void CodeDirectory::checkIntegrity() const
 //
 // Validate a slot against data in memory.
 //
-bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
+bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot, bool preEncrypt) const
 {
        secinfo("codedir", "%p validating slot %d", this, int(slot));
        MakeHash<CodeDirectory> hasher(this);
        Hashing::Byte digest[hasher->digestLength()];
        generateHash(hasher, data, length, digest);
-       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
+       return memcmp(digest, getSlot(slot, preEncrypt), hasher->digestLength()) == 0;
 }
 
 
@@ -211,12 +219,12 @@ bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) con
 // Validate a slot against the contents of an open file. At most 'length' bytes
 // will be read from the file.
 //
-bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
+bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot, bool preEncrypt) const
 {
        MakeHash<CodeDirectory> hasher(this);
        Hashing::Byte digest[hasher->digestLength()];
        generateHash(hasher, fd, digest, length);
-       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
+       return memcmp(digest, getSlot(slot, preEncrypt), hasher->digestLength()) == 0;
 }
 
 
@@ -228,7 +236,7 @@ bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
 bool CodeDirectory::slotIsPresent(Slot slot) const
 {
        if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
-               const Hashing::Byte *digest = (*this)[slot];
+               const Hashing::Byte *digest = getSlot(slot, false);
                for (unsigned n = 0; n < hashSize; n++)
                        if (digest[n])
                                return true;    // non-zero digest => present
@@ -322,15 +330,17 @@ bool CodeDirectory::verifyMemoryContent(CFDataRef data, const Byte* digest) cons
        
 //
 // Generate the canonical cdhash - the internal hash of the CodeDirectory itself.
-// We currently truncate to 20 bytes because that's what the kernel can deal with.
+// With 'truncate' truncates to 20 bytes, because that's what's commonly used.
 //
-CFDataRef CodeDirectory::cdhash() const
+CFDataRef CodeDirectory::cdhash(bool truncate) const
 {
        MakeHash<CodeDirectory> hash(this);
        Hashing::Byte digest[hash->digestLength()];
        hash->update(this, this->length());
        hash->finish(digest);
-       return makeCFData(digest, min(hash->digestLength(), size_t(kSecCodeCDHashLength)));
+       return makeCFData(digest,
+                                         truncate ? min(hash->digestLength(), size_t(kSecCodeCDHashLength)) :
+                                         hash->digestLength());
 }
 
 
@@ -380,11 +390,11 @@ std::string CodeDirectory::hexHash(const unsigned char *hash) const
 std::string CodeDirectory::screeningCode() const
 {
        if (slotIsPresent(-cdInfoSlot))         // has Info.plist
-               return "I" + hexHash((*this)[-cdInfoSlot]); // use Info.plist hash
+               return "I" + hexHash(getSlot(-cdInfoSlot, false)); // use Info.plist hash
        if (slotIsPresent(-cdRepSpecificSlot))          // has Info.plist
-               return "R" + hexHash((*this)[-cdRepSpecificSlot]); // use Info.plist hash
+               return "R" + hexHash(getSlot(-cdRepSpecificSlot, false)); // use Info.plist hash
        if (pageSize == 0)                                      // good-enough proxy for "not a Mach-O file"
-               return "M" + hexHash((*this)[0]); // use hash of main executable
+               return "M" + hexHash(getSlot(0, false)); // use hash of main executable
        return "N";                                                     // no suitable screening code
 }
 
@@ -406,5 +416,6 @@ const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
        { "restrict",           kSecCodeSignatureRestrict,              true },
        { "enforcement",        kSecCodeSignatureEnforcement,           true },
        { "library-validation", kSecCodeSignatureLibraryValidation,             true },
+       { "runtime",    kSecCodeSignatureRuntime,               true },
        { NULL }
 };
index 5fdaa3557800463db463b6f258176590f21cacff..3da3be1503d67ee3d631f654d84e316dafa244e6 100644 (file)
@@ -65,7 +65,7 @@ namespace CodeSigning {
 #define kSecCS_ENTITLEMENTFILE         "CodeEntitlements"      // entitlement configuration
 #define kSecCS_REPSPECIFICFILE         "CodeRepSpecific"       // DiskRep-specific use slot
 #define kSecCS_TOPDIRECTORYFILE                "CodeTopDirectory"      // Top-level directory list
-
+#define kSecCS_ENTITLEMENTDERFILE      "CodeEntitlementDER" // DER entitlement representation
 
 //
 // Special hash slot values. In a CodeDirectory, these show up at negative slot
@@ -93,6 +93,7 @@ enum {
        cdTopDirectorySlot = 4,                         // Application specific slot
        cdEntitlementSlot = 5,                          // embedded entitlement configuration
        cdRepSpecificSlot = 6,                          // for use by disk rep
+       cdEntitlementDERSlot = 7,                       // DER representation of entitlements
        // (add further primary slot numbers here)
 
        cdSlotCount,                                            // total number of special slots (+1 for slot 0)
@@ -202,8 +203,11 @@ public:
        Endian<uint64_t> execSegLimit;  // limit of executable segment
        Endian<uint64_t> execSegFlags;  // exec segment flags
 
+       Endian<uint32_t> runtime;                       // Runtime version encoded as an unsigned int
+       Endian<uint32_t> preEncryptOffset;      // offset of pre-encrypt hash slots
+
        // works with the version field; see comments above
-       static const uint32_t currentVersion = 0x20400;         // "version 2.4"
+       static const uint32_t currentVersion = 0x20500;         // "version 2.5"
        static const uint32_t compatibilityLimit = 0x2F000;     // "version 3 with wiggle room"
        
        static const uint32_t earliestVersion = 0x20001;        // earliest supported version
@@ -211,6 +215,7 @@ public:
        static const uint32_t supportsTeamID = 0x20200;         // first version to support team ID option
        static const uint32_t supportsCodeLimit64 = 0x20300; // first version to support codeLimit64
        static const uint32_t supportsExecSegment = 0x20400; // first version to support exec base and limit
+       static const uint32_t supportsPreEncrypt = 0x20500; // first version to support pre-encrypt hashes and runtime version
 
        void checkIntegrity() const;    // throws if inconsistent or unsupported version
 
@@ -228,18 +233,28 @@ public:
        // main hash array access
        SpecialSlot maxSpecialSlot() const;
                
-       unsigned char *operator [] (Slot slot)
+       unsigned char *getSlotMutable (Slot slot, bool preEncrypt)
        {
                assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
-               return at<unsigned char>(hashOffset) + hashSize * slot;
+
+               if (preEncrypt) {
+                       if (version >= supportsPreEncrypt && preEncryptOffset != 0) {
+                               assert(slot >= 0);
+                               return at<unsigned char>(preEncryptOffset) + hashSize * slot;
+                       } else {
+                               return NULL;
+                       }
+               } else {
+                       return at<unsigned char>(hashOffset) + hashSize * slot;
+               }
        }
-       
-       const unsigned char *operator [] (Slot slot) const
+
+       const unsigned char *getSlot (Slot slot, bool preEncrypt) const
        {
-               assert(slot >= int(-nSpecialSlots) && slot < int(nCodeSlots));
-               return at<unsigned char>(hashOffset) + hashSize * slot;
+               CodeDirectory *cd = const_cast<CodeDirectory *>(this);
+               return const_cast<const unsigned char *>(cd->getSlotMutable(slot, preEncrypt));
        }
-       
+
        //
        // The main page hash array can be "scattered" across the code file
        // by specifying an array of Scatter elements, terminated with an
@@ -266,9 +281,13 @@ public:
        uint64_t execSegmentLimit() const { return (version >= supportsExecSegment) ? execSegLimit.get() : 0; }
        uint64_t execSegmentFlags() const { return (version >= supportsExecSegment) ? execSegFlags.get() : 0; }
 
+       const unsigned char *preEncryptHashes() const { return getSlot(0, true); }
+
+       uint32_t runtimeVersion() const {return (version >= supportsPreEncrypt) ? runtime.get() : 0; }
+
 public:
-       bool validateSlot(const void *data, size_t size, Slot slot) const;                      // validate memory buffer against page slot
-       bool validateSlot(UnixPlusPlus::FileDesc fd, size_t size, Slot slot) const;     // read and validate file
+       bool validateSlot(const void *data, size_t size, Slot slot, bool preEncrypted) const;                   // validate memory buffer against page slot
+       bool validateSlot(UnixPlusPlus::FileDesc fd, size_t size, Slot slot, bool preEncrypted) const;  // read and validate file
        bool slotIsPresent(Slot slot) const;
        
        class Builder;
@@ -276,7 +295,7 @@ public:
 public:
        static DynamicHash *hashFor(HashAlgorithm hashType);            // create a DynamicHash subclass for (hashType) digests
        DynamicHash *getHash() const { return hashFor(this->hashType); } // make one for me
-       CFDataRef cdhash() const;
+       CFDataRef cdhash(bool truncate = true) const;
        
        static void multipleHashFileData(UnixPlusPlus::FileDesc fd, size_t limit, HashAlgorithms types, void (^action)(HashAlgorithm type, DynamicHash* hasher));
     bool verifyMemoryContent(CFDataRef data, const Byte* digest) const;
index 0959b5595040c50434ef6881ede05a869b312bcc..6986c7251fa7028358a30783d9d9124ffbb6276f 100644 (file)
@@ -110,38 +110,31 @@ void MachORep::prepareForSigning(SigningContext &context)
 {
        if (context.digestAlgorithms().empty()) {
         auto_ptr<MachO> macho(mainExecutableImage()->architecture());
-
-               if (const version_min_command *version = macho->findMinVersion()) {
-                       uint32_t limit = 0;
-                       switch (macho->flip(version->cmd)) {
-                       case LC_VERSION_MIN_MACOSX:
+               
+               uint32_t limit = 0;
+               switch (macho->platform()) {
+                       case 0:
+                               // If we don't know the platform, we stay agile.
+                               return;
+                       case PLATFORM_MACOS:
+                               // 10.11.4 had first proper sha256 support.
                                limit = (10 << 16 | 11 << 8 | 4 << 0);
                                break;
-#if 0 /* need updated libMIS before we can do this switch */
-                       case LC_VERSION_MIN_IPHONEOS:
-                               limit = (9 << 16 | 3 << 8);
-                               break;
-                       case LC_VERSION_MIN_WATCHOS:
-                               limit = (2 << 16 | 2 << 8);
-                               break;
-                       case LC_VERSION_MIN_TVOS:
-                               limit = (9 << 16 | 2 << 8);
+                       case PLATFORM_TVOS:
+                       case PLATFORM_IOS:
+                               // iOS 11 and tvOS 11 had first proper sha256 support.
+                               limit = (11 << 16 | 0 << 8 | 0 << 0);
                                break;
+                       case PLATFORM_WATCHOS:
+                               // We stay agile on the watch right now.
+                               return;
                        default:
+                               // All other platforms are assumed to be new and support SHA256.
                                break;
-#else
-            case LC_VERSION_MIN_IPHONEOS:
-            case LC_VERSION_MIN_WATCHOS:
-            case LC_VERSION_MIN_TVOS:
-                return;
-            default:
-                break;
-#endif
-                       }
-                       if (macho->flip(version->version) >= limit) {
-                               // young enough not to need SHA-1 legacy support
-                               context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
-                       }
+               }
+               if (macho->minVersion() >= limit) {
+                       // young enough not to need SHA-1 legacy support
+                       context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
                }
        }
 }
@@ -162,29 +155,9 @@ size_t MachORep::signingLimit()
 }
 
 bool MachORep::needsExecSeg(const MachO& macho) {
-       if (const version_min_command *version = macho.findMinVersion()) {
-               uint32_t min = UINT32_MAX;
-
-               switch (macho.flip(version->cmd)) {
-                       case LC_VERSION_MIN_IPHONEOS:
-                       case LC_VERSION_MIN_TVOS:
-                               min = (11 << 16 | 0 << 8);
-                               break;
-                       case LC_VERSION_MIN_WATCHOS:
-                               min = (4 << 16 | 0 << 8);
-                               break;
-
-                       default:
-                               /* macOS currently does not get this. */
-                               return false;
-               }
-
-               if (macho.flip(version->version) >= min) {
-                       return true;
-               }
-       }
-
-       return false;
+       uint32_t platform = macho.platform();
+       // Everything embedded gets an exec segment.
+       return platform != 0 && platform != PLATFORM_MACOS;
 }
 
 size_t MachORep::execSegBase(const Architecture *arch)
@@ -398,15 +371,28 @@ CFDictionaryRef MachORep::diskRepInformation()
     auto_ptr<MachO> macho (mainExecutableImage()->architecture());
     CFRef<CFDictionaryRef> info;
 
-    if (const version_min_command *version = macho->findMinVersion()) {
-
+       uint32_t platform = 0;
+       uint32_t minVersion = 0;
+       uint32_t sdkVersion = 0;
+       
+    if (macho->version(&platform, &minVersion, &sdkVersion)) {
+
+               /* These keys replace the old kSecCodeInfoDiskRepOSPlatform, kSecCodeInfoDiskRepOSVersionMin
+                * and kSecCodeInfoDiskRepOSSDKVersion. The keys were renamed because we changed what value
+                * "platform" represents: For the old key, the actual load command (e.g. LC_VERSION_MIN_MACOSX)
+                * was returned; for the new key, we return one of the PLATFORM_* values used by LC_BUILD_VERSION.
+                *
+                * The keys are private and undocumented, and maintaining a translation table between the old and
+                * new domain would provide little value at high cost, but we do remove the old keys to make
+                * the change obvious.
+                */
+               
         info.take(cfmake<CFMutableDictionaryRef>("{%O = %d,%O = %d,%O = %d}",
-                                              kSecCodeInfoDiskRepOSPlatform, macho->flip(version->cmd),
-                                              kSecCodeInfoDiskRepOSVersionMin, macho->flip(version->version),
-                                              kSecCodeInfoDiskRepOSSDKVersion, macho->flip(version->sdk)));
+                                              kSecCodeInfoDiskRepVersionPlatform, platform,
+                                              kSecCodeInfoDiskRepVersionMin, minVersion,
+                                              kSecCodeInfoDiskRepVersionSDK, sdkVersion));
 
-        if (macho->flip(version->cmd) == LC_VERSION_MIN_MACOSX &&
-            macho->flip(version->sdk) < (10 << 16 | 9 << 8))
+        if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8))
         {
             info.take(cfmake<CFMutableDictionaryRef>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}",
                                                   info.get(),
index 176fcfb5c0d6825e1228dcb5c4bcc860f52e8cfe..e56d1f465170fda663bb5f16517e747c9b4352da 100644 (file)
@@ -39,7 +39,7 @@ PidDiskRep::setCredentials(const Security::CodeSigning::CodeDirectory *cd)
 {
        // save the Info.plist slot
        if (cd->slotIsPresent(cdInfoSlot)) {
-               mInfoPlistHash.take(makeCFData((*cd)[cdInfoSlot], cd->hashSize));
+               mInfoPlistHash.take(makeCFData(cd->getSlot(cdInfoSlot, false), cd->hashSize));
        }
 }
 
index 619986a139798510bbb08feda3198462bbd36ded..6e7cf0285319e2ef9398677b6979ba8fdd14d11e 100644 (file)
@@ -62,6 +62,21 @@ CFDictionaryRef EntitlementBlob::entitlements() const
                this->length() - sizeof(EntitlementBlob));
 }
 
+EntitlementDERBlob *EntitlementDERBlob::alloc(size_t length) {
+       size_t blobLength = length + sizeof(BlobCore);
+       if (blobLength < length) {
+               // overflow
+               return NULL;
+       }
+
+       EntitlementDERBlob *b = (EntitlementDERBlob *)malloc(blobLength);
+
+       if (b != NULL) {
+               b->BlobCore::initialize(kSecCodeMagicEntitlementDER, blobLength);
+       }
+
+       return b;
+}
 
 } // end namespace CodeSigning
 } // end namespace Security
index 50d8d21d1a7526c9699a2268e15da198d7b7131d..eed6b5c5c1325d718c45333fd9935eea28285c49 100644 (file)
@@ -74,6 +74,21 @@ public:
        CFDictionaryRef entitlements() const;
 };
 
+//
+// Similar, but in DER representation.
+//
+class EntitlementDERBlob : public Blob<EntitlementDERBlob, kSecCodeMagicEntitlementDER> {
+public:
+       static EntitlementDERBlob *alloc(size_t length);
+
+       uint8_t *der() { return data; }
+       const uint8_t *der() const { return data; }
+       size_t derLength() const { return BlobCore::length() - sizeof(BlobCore); }
+
+private:
+       uint8_t data[0];
+};
+
 
 } // end namespace CodeSigning
 } // end namespace Security
index fb9e75671f244bf934c4f1dfb89cdde7eeaf76bf..2a9aea29707de595c03148d282682eaa244ef361 100644 (file)
@@ -24,6 +24,7 @@
 //
 // signer - Signing operation supervisor and controller
 //
+#include "der_plist.h"
 #include "signer.h"
 #include "resources.h"
 #include "signerutils.h"
@@ -168,6 +169,8 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        entitlements = state.mEntitlementData;
        if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
                entitlements = code->component(cdEntitlementSlot);
+
+       generateEntitlementDER = signingFlags() & kSecCSSignGenerateEntitlementDER;
        
        // work out the CodeDirectory flags word
        bool haveCdFlags = false;
@@ -291,8 +294,88 @@ void SecCodeSigner::Signer::prepare(SecCSFlags flags)
        if (!rpath.empty()) {
                buildResources(rrpath, rpath, resourceRules);
        }
+
+       
+       
+       if (inherit & kSecCodeSignerPreservePEH) {
+               /* We need at least one architecture in all cases because we index our
+                * PreEncryptionMaps by architecture. However, only machOs have any
+                * architecture at all, for generic targets there will just be one
+                * PreEncryptionHashMap.
+                * So if the main executable is not a machO, we just choose the local
+                * (signer's) main architecture as dummy value for the first element in our pair. */
+               preEncryptMainArch = (code->diskRep()->mainExecutableIsMachO() ?
+                                                         code->diskRep()->mainExecutableImage()->bestNativeArch() :
+                                                          Architecture::local());
+
+               addPreEncryptHashes(preEncryptHashMaps[preEncryptMainArch], code);
+               
+               code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
+                       Universal *fat = subcode->diskRep()->mainExecutableImage();
+                       assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
+                       Architecture arch = fat->bestNativeArch();      // actually, only architecture for this slice.
+                       addPreEncryptHashes(preEncryptHashMaps[arch], subcode);
+               });
+       }
+
+       if (inherit & kSecCodeSignerPreserveRuntime) {
+               /* We need at least one architecture in all cases because we index our
+                * RuntimeVersionMaps by architecture. However, only machOs have any
+                * architecture at all, for generic targets there will just be one
+                * RuntimeVersionMap.
+                * So if the main executable is not a machO, we just choose the local
+                * (signer's) main architecture as dummy value for the first element in our pair. */
+               runtimeVersionMainArch = (code->diskRep()->mainExecutableIsMachO() ?
+                                                         code->diskRep()->mainExecutableImage()->bestNativeArch() :
+                                                         Architecture::local());
+
+               addRuntimeVersions(runtimeVersionMap[runtimeVersionMainArch], code);
+
+               code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
+                       Universal *fat = subcode->diskRep()->mainExecutableImage();
+                       assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
+                       Architecture arch = fat->bestNativeArch();      // actually, only architecture for this slice.
+                       addRuntimeVersions(runtimeVersionMap[arch], subcode);
+               });
+       }
 }
 
+void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code) {
+       SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
+       
+       if (cds != NULL) {
+               for(auto const& pair : *cds) {
+                       CodeDirectory::HashAlgorithm const alg = pair.first;
+                       CFDataRef const cddata = pair.second;
+                       
+                       CodeDirectory const * cd =
+                       reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
+                       if (cd->preEncryptHashes() != NULL) {
+                               CFRef<CFDataRef> preEncrypt = makeCFData(cd->preEncryptHashes(),
+                                                                                                                cd->nCodeSlots * cd->hashSize);
+                               map[alg] = preEncrypt;
+                       }
+               }
+       }
+}
+
+void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap &map, const SecStaticCode *code)
+{
+       SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
+
+       if (cds != NULL) {
+               for(auto const& pair : *cds) {
+                       CodeDirectory::HashAlgorithm const alg = pair.first;
+                       CFDataRef const cddata = pair.second;
+
+                       CodeDirectory const * cd =
+                       reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
+                       if (cd->runtimeVersion()) {
+                               map[alg] = cd->runtimeVersion();
+                       }
+               }
+       }
+}
 
 //
 // Collect the resource seal for a program.
@@ -462,15 +545,27 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
 
                bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
 
+               uint32_t runtimeVersion = 0;
+               if (cdFlags & kSecCodeSignatureRuntime) {
+                       runtimeVersion = state.mRuntimeVersionOverride ? state.mRuntimeVersionOverride : arch.source.get()->sdkVersion();
+               }
+
                arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
                if (editor->attribute(writerNoGlobal))  // can't store globally, add per-arch
                        populate(arch);
                for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
+                       uint32_t runtimeVersionToUse = runtimeVersion;
+                       if ((cdFlags & kSecCodeSignatureRuntime) && runtimeVersionMap.count(arch.architecture)) {
+                               if (runtimeVersionMap[arch.architecture].count(*type)) {
+                                       runtimeVersionToUse = runtimeVersionMap[arch.architecture][*type];
+                               }
+                       }
                        arch.eachDigest(^(CodeDirectory::Builder& builder) {
                                populate(builder, arch, arch.ireqs,
                                                 arch.source->offset(), arch.source->signingExtent(),
                                                 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
-                                                unsigned(digestAlgorithms().size()-1));
+                                                unsigned(digestAlgorithms().size()-1),
+                                                preEncryptHashMaps[arch.architecture], runtimeVersionToUse);
                        });
                }
        
@@ -502,10 +597,10 @@ void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context
                        CodeDirectory *cd = builder.build();
                        cdSet.add(cd);
                });
-               CFRef<CFArrayRef> hashes = cdSet.hashBag();
-               CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
-               CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
-               CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
+
+               CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
+               CFRef<CFArrayRef> hashList = cdSet.hashList();
+               CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
                
                // complete the SuperBlob
                cdSet.populate(&arch);
@@ -544,7 +639,9 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
                populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(),
                                 false,         // only machOs can currently be main binaries
                                 rep->execSegBase(NULL), rep->execSegLimit(NULL),
-                                unsigned(digestAlgorithms().size()-1));
+                                unsigned(digestAlgorithms().size()-1),
+                                preEncryptHashMaps[preEncryptMainArch], // Only one map, the default.
+                                (cdFlags & kSecCodeSignatureRuntime) ? state.mRuntimeVersionOverride : 0);
                
                CodeDirectory *cd = builder.build();
                if (!state.mDryRun)
@@ -561,10 +658,9 @@ void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
        if (!state.mDryRun)
                cdSet.populate(writer);
 
-       CFRef<CFArrayRef> hashes = cdSet.hashBag();
-       CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
-       CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
-       CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
+       CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
+       CFRef<CFArrayRef> hashList = cdSet.hashList();
+       CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
        writer->signature(signature);
        
        // commit to storage
@@ -591,7 +687,9 @@ void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
                                                                         InternalRequirements &ireqs, size_t offset, size_t length,
                                                                         bool mainBinary, size_t execSegBase, size_t execSegLimit,
-                                                                        unsigned alternateDigestCount)
+                                                                        unsigned alternateDigestCount,
+                                                                        PreEncryptHashMap const &preEncryptHashMap,
+                                                                        uint32_t runtimeVersion)
 {
        // fill the CodeDirectory
        builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
@@ -600,6 +698,9 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
        builder.teamID(teamID);
        builder.platform(state.mPlatform);
        builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
+       builder.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH);
+       builder.preservePreEncryptHashMap(preEncryptHashMap);
+       builder.runTimeVersion(runtimeVersion);
 
        if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
                builder.specialSlot(cdInfoSlot, data);
@@ -615,7 +716,17 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
                builder.specialSlot(cdEntitlementSlot, entitlements);
 
                if (mainBinary) {
-                       builder.addExecSegFlags(entitlementsToExecSegFlags(entitlements));
+                       CFRef<CFDataRef> entitlementDER;
+                       uint64_t execSegFlags = 0;
+                       cookEntitlements(entitlements, generateEntitlementDER,
+                                                        &execSegFlags, &entitlementDER.aref());
+
+                       if (generateEntitlementDER) {
+                               writer.component(cdEntitlementDERSlot, entitlementDER);
+                               builder.specialSlot(cdEntitlementDERSlot, entitlementDER);
+                       }
+
+                       builder.addExecSegFlags(execSegFlags);
                }
        }
        if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
@@ -648,7 +759,9 @@ void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::W
 //
 // Generate the CMS signature for a (finished) CodeDirectory.
 //
-CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag)
+CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd,
+                                                                                                  CFDictionaryRef hashDict,
+                                                                                                  CFArrayRef hashList)
 {
        assert(state.mSigner);
        CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
@@ -671,9 +784,21 @@ CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDa
                MacOSError::check(CMSEncoderSetSigningTime(cms, time));
        }
        
-       if (hashBag) {
+       if (hashDict != NULL) {
+               assert(hashList != NULL);
+
+               // V2 Hash Agility
+
+               MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgilityV2));
+               MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms, hashDict));
+
+               // V1 Hash Agility
+
+               CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashList);
+               CFRef<CFDataRef> hashAgilityV1Attribute = makeCFData(hashDict.get());
+
                MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
-               MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashBag));
+               MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashAgilityV1Attribute));
        }
        
        MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
@@ -787,37 +912,75 @@ bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFString
        return CFBooleanGetValue(entValue);
 }
 
-uint64_t SecCodeSigner::Signer::entitlementsToExecSegFlags(CFDataRef entitlements)
+void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements, bool generateDER,
+                                                                                        uint64_t *execSegFlags, CFDataRef *entitlementDER)
 {
        if (!entitlements) {
-               return 0;
+               return; // nothing to do.
        }
 
-       const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
-
-       if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
-               return 0;
-       }
+       EntitlementDERBlob *derBlob = NULL;
 
        try {
+               const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
+
+               if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
+                       MacOSError::throwMe(errSecCSInvalidEntitlements);
+               }
+
                CFRef<CFDictionaryRef> entDict = blob->entitlements();
 
-               uint64_t flags = 0;
+               if (generateDER) {
+                       CFRef<CFErrorRef> error = NULL;
+                       size_t const der_size = der_sizeof_plist(entDict, &error.aref());
 
-               flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
-               flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
+                       if (der_size == 0) {
+                               secerror("Getting DER size for entitlement plist failed: %@", error.get());
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+
+                       derBlob = EntitlementDERBlob::alloc(der_size);
+
+                       if (derBlob == NULL) {
+                               secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size);
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+                       uint8_t * const der_end = derBlob->der() + der_size;
+                       uint8_t * const der_start = der_encode_plist(entDict, &error.aref(), derBlob->der(), der_end);
 
-               return flags;
+                       if (der_start != derBlob->der()) {
+                               secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start - derBlob->der()));
+                               free(derBlob);
+                               MacOSError::throwMe(errSecCSInvalidEntitlements);
+                       }
+
+                       *entitlementDER = makeCFData(derBlob, derBlob->length());
+                       free(derBlob);
+                       derBlob = NULL;
+               }
+
+               if (execSegFlags != NULL) {
+                       uint64_t flags = 0;
+
+                       flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
+                       flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
+
+                       *execSegFlags = flags;
+               }
 
        } catch (const CommonError &err) {
-               // Not fatal.
+               free(derBlob);
+               // Not fatal if we're not asked to generate DER entitlements.
+
                secwarning("failed to parse entitlements: %s", err.what());
-               return 0;
+               if (generateDER) {
+                       throw;
+               }
        }
 }
 
index 65ea50a5ddd052e7407961dbc58021a6cbf1938f..f43494f04b77b14501cc8098990f6c8f82c25a57 100644 (file)
@@ -67,28 +67,49 @@ protected:
        void signMachO(Universal *fat, const Requirement::Context &context); // sign a Mach-O binary
        void signArchitectureAgnostic(const Requirement::Context &context); // sign anything else
 
+       // HashAlgorithm -> PreEncrypt hashes
+       typedef std::map<CodeDirectory::HashAlgorithm, CFCopyRef<CFDataRef> >
+               PreEncryptHashMap;
+       // Architecture -> PreEncryptHashMap
+       typedef std::map<Architecture, PreEncryptHashMap >
+               PreEncryptHashMaps;
+       // HashAlgorithm -> Hardened Runtime Version
+       typedef std::map<CodeDirectory::HashAlgorithm, uint32_t>
+               RuntimeVersionMap;
+       // Architecture -> RuntimeVersionMap
+       typedef std::map<Architecture, RuntimeVersionMap>
+               RuntimeVersionMaps;
+
        void populate(DiskRep::Writer &writer);         // global
        void populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
                                  InternalRequirements &ireqs,
                                  size_t offset, size_t length,
                                  bool mainBinary, size_t execSegBase, size_t execSegLimit,
-                                 unsigned alternateDigestCount);       // per-architecture
-       CFDataRef signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag);
+                                 unsigned alternateDigestCount,
+                                 const PreEncryptHashMap& preEncryptHashMap,
+                                 uint32_t runtimeVersion);     // per-architecture
+       CFDataRef signCodeDirectory(const CodeDirectory *cd,
+                                                               CFDictionaryRef hashDict, CFArrayRef hashList);
 
        uint32_t cdTextFlags(std::string text);         // convert text CodeDirectory flags
        std::string uniqueName() const;                         // derive unique string from rep
        
 protected:
+       
        std::string sdkPath(const std::string &path) const;
        bool isAdhoc() const;
        SecCSFlags signingFlags() const;
        
 private:
+       void addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code);
+       void addRuntimeVersions(RuntimeVersionMap &map, SecStaticCode const *code);
+       
        void considerTeamID(const PreSigningContext& context);
        std::vector<Endian<uint32_t> > topSlots(CodeDirectory::Builder &builder) const;
 
        static bool booleanEntitlement(CFDictionaryRef entDict, CFStringRef key);
-       static uint64_t entitlementsToExecSegFlags(CFDataRef entitlements);
+       static void cookEntitlements(CFDataRef entitlements, bool generateDER,
+                                                                uint64_t *execSegFlags, CFDataRef *entitlementsDER);
 
 protected:
        void buildResources(std::string root, std::string relBase, CFDictionaryRef rules);
@@ -104,12 +125,17 @@ private:
        std::string identifier;                 // signing identifier
        std::string teamID;             // team identifier
        CFRef<CFDataRef> entitlements;  // entitlements
+       Architecture preEncryptMainArch; // pre-encrypt main architecture
+       PreEncryptHashMaps preEncryptHashMaps;  // pre-encrypt hashes to keep
+       Architecture runtimeVersionMainArch; // runtime version main architecture
+       RuntimeVersionMaps runtimeVersionMap; // runtime versions to keep
        uint32_t cdFlags;                               // CodeDirectory flags
        const Requirements *requirements; // internal requirements ready-to-use
        size_t pagesize;                                // size of main executable pages
        CFAbsoluteTime signingTime;             // signing time for CMS signature (0 => now)
        bool emitSigningTime;                   // emit signing time as a signed CMS attribute
        bool strict;                                    // strict validation
+       bool generateEntitlementDER;    // generate entitlement DER
        
 private:
        Mutex resourceLock;
index 855d1949d8ffb99c0d3cfd63da19656ed0ece48a..d543433ac7cf3e903caf6376f0d4674f461aae8e 100644 (file)
 //
 // signerutils - utilities for signature generation
 //
+#include "csutilities.h"
+#include "drmaker.h"
+#include "resources.h"
 #include "signerutils.h"
 #include "signer.h"
-#include "SecCodeSigner.h"
+
+#include <Security/SecCmsBase.h>
 #include <Security/SecIdentity.h>
 #include <Security/CMSEncoder.h>
-#include "resources.h"
-#include "csutilities.h"
-#include "drmaker.h"
+
+#include "SecCodeSigner.h"
+
 #include <security_utilities/unix++.h>
 #include <security_utilities/logging.h>
 #include <security_utilities/unixchild.h>
+
 #include <vector>
 
 // for helper validation
@@ -416,17 +421,57 @@ const CodeDirectory* CodeDirectorySet::primary() const
        return mPrimary;
 }
 
-
-CFArrayRef CodeDirectorySet::hashBag() const
+CFArrayRef CodeDirectorySet::hashList() const
 {
        CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0);
        for (auto it = begin(); it != end(); ++it) {
-               CFRef<CFDataRef> cdhash = it->second->cdhash();
+               CFRef<CFDataRef> cdhash = it->second->cdhash(true);
                CFArrayAppendValue(hashList, cdhash);
        }
        return hashList.yield();
 }
 
+CFDictionaryRef CodeDirectorySet::hashDict() const
+{
+       CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary();
+
+       for (auto it = begin(); it != end(); ++it) {
+               SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first);
+
+               if (tag == SEC_OID_UNKNOWN) {
+                       MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
+               }
+
+               CFRef<CFNumberRef> hashType = makeCFNumber(int(tag));
+               CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash!
+               CFDictionarySetValue(hashDict, hashType, fullCdhash);
+       }
+
+       return hashDict.yield();
+}
+
+SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) {
+       SECOidTag tag;
+
+       switch (algorithm) {
+               case kSecCodeSignatureHashSHA1:
+                       tag = SEC_OID_SHA1;
+                       break;
+               case kSecCodeSignatureHashSHA256:
+               case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash
+                       tag = SEC_OID_SHA256;
+                       break;
+               case kSecCodeSignatureHashSHA384:
+                       tag = SEC_OID_SHA384;
+                       break;
+               default:
+                       tag = SEC_OID_UNKNOWN;
+       }
+
+       return tag;
+}
+
+
 
 } // end namespace CodeSigning
 } // end namespace Security
index 88cb2055fee373c2b6d4c2c58bf2937246870ff7..c4cb1238d5cec9c008270c9e7a8f234e634ea422 100644 (file)
@@ -30,6 +30,9 @@
 #include "CodeSigner.h"
 #include "sigblob.h"
 #include "cdbuilder.h"
+
+#include <Security/SecCmsBase.h>
+
 #include <security_utilities/utilities.h>
 #include <security_utilities/blob.h>
 #include <security_utilities/unix++.h>
@@ -214,8 +217,14 @@ public:
        void populate(DiskRep::Writer* writer) const;
        
        const CodeDirectory* primary() const;
-       CFArrayRef hashBag() const;
-       
+
+       // Note that the order of the hashList is relevant.
+       // (Which is also why there are separate methods, CFDictionary is not ordered.)
+       CFArrayRef hashList() const;
+       CFDictionaryRef hashDict() const;
+
+       static SECOidTag SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm);
+
 private:
        mutable const CodeDirectory* mPrimary;
 };
index c5599e5c4686dd60f430482ac9d960ca9f1129f4..32836ab0cbc0ee107171f9cc9235f0141343d22c 100644 (file)
@@ -452,6 +452,61 @@ const version_min_command *MachOBase::findMinVersion() const
        return NULL;
 }
 
+const build_version_command *MachOBase::findBuildVersion() const
+{
+    for (const load_command *command = loadCommands(); command; command = nextCommand(command)) {
+        if (flip(command->cmd) == LC_BUILD_VERSION) {
+            if(flip(command->cmdsize) < sizeof(build_version_command)) {
+                UnixError::throwMe(ENOEXEC);
+            }
+            
+            return reinterpret_cast<const build_version_command *>(command);
+        }
+    }
+    return NULL;
+}
+
+bool MachOBase::version(uint32_t *platform, uint32_t *minVersion, uint32_t *sdkVersion) const
+{
+    const build_version_command *bc = findBuildVersion();
+    
+    if (bc != NULL) {
+        if (platform != NULL) { *platform = flip(bc->platform); }
+        if (minVersion != NULL) { *minVersion = flip(bc->minos); }
+        if (sdkVersion != NULL) { *sdkVersion = flip(bc->sdk); }
+        return true;
+    }
+    
+    const version_min_command *vc = findMinVersion();
+    
+    if (vc != NULL) {
+        uint32_t pf;
+        switch (flip(vc->cmd)) {
+        case LC_VERSION_MIN_MACOSX:
+            pf = PLATFORM_MACOS;
+            break;
+        case LC_VERSION_MIN_IPHONEOS:
+            pf = PLATFORM_IOS;
+            break;
+        case LC_VERSION_MIN_WATCHOS:
+            pf = PLATFORM_WATCHOS;
+            break;
+        case LC_VERSION_MIN_TVOS:
+            pf = PLATFORM_TVOS;
+            break;
+        default:
+            // Old style load command, but we don't know what platform to map to.
+            pf = 0;
+        }
+                
+        if (platform != NULL) { *platform = pf; }
+        if (minVersion != NULL) { *minVersion = flip(vc->version); }
+        if (sdkVersion != NULL) { *sdkVersion = flip(vc->sdk); }
+        return true;
+    }
+    
+    return false;
+}
 
 //
 // Return the signing-limit length for this Mach-O binary image.
index 23ede19dec5b4337c489e83e95ed00e0e38e8bd4..a58ec3097b30174aa43190d8f17fbc2709edb643 100644 (file)
@@ -108,10 +108,14 @@ public:
 
        const linkedit_data_command *findCodeSignature() const;
        const linkedit_data_command *findLibraryDependencies() const;
-       const version_min_command *findMinVersion() const;
        
        size_t signingOffset() const;   // starting offset of CS section, or 0 if none
        size_t signingLength() const;   // length of CS section, or 0 if none
+    
+    bool version(uint32_t *platform, uint32_t *minVersion, uint32_t *sdkVersion) const;
+    uint32_t platform() const { uint32_t ret; return version(&ret, NULL, NULL) ? ret : 0; };
+    uint32_t minVersion() const  { uint32_t ret; return version(NULL, &ret, NULL) ? ret : 0; };
+    uint32_t sdkVersion() const  { uint32_t ret; return version(NULL, NULL, &ret) ? ret : 0; };
 
 protected:
        void initHeader(const mach_header *address);
@@ -120,6 +124,9 @@ protected:
        size_t headerSize() const;              // size of header
        size_t commandSize() const;             // size of commands area
 
+    const version_min_command *findMinVersion() const;
+    const build_version_command *findBuildVersion() const;
+
 private:
        const mach_header *mHeader;     // Mach-O header
        const load_command *mCommands; // load commands
index ee2af8d543a35a8c4a0aa50f3237c767b5410a04..522b3fd4288584c57333fa6436aaa47d40c9f778 100644 (file)
@@ -12,6 +12,7 @@
 #include <Security/SecTrust.h>
 #include <Security/SecTrustSettings.h>
 #include <Security/SecTrustSettingsPriv.h>
+#include <Security/SecTrustPriv.h>
 #include <utilities/SecCFRelease.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -532,15 +533,39 @@ static void test_multiple_constraints(void) {
 
 }
 
+#define kNumberPolicyNamePinnningConstraintsTests (1 + 1 + 5)
+static void test_policy_name_pinning_constraints(void) {
+    /* allow all but */
+    NSArray *allowAllBut = @[
+                             @{(__bridge NSString*)kSecTrustSettingsPolicy: (__bridge id)sslPolicy ,
+                               (__bridge NSString*)kSecTrustSettingsResult: @(kSecTrustSettingsResultUnspecified)},
+                             @{(__bridge NSString*)kSecTrustSettingsResult: @(kSecTrustSettingsResultTrustRoot) }
+                             ];
+    setTS(cert0, (__bridge CFArrayRef)allowAllBut);
+
+    SecTrustRef trust = NULL;
+    SecTrustResultType trust_result;
+    ok_status(SecTrustCreateWithCertificates(sslChain, sslPolicy, &trust), "create trust with ssl policy");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)verify_date), "set trust verify date");
+    ok_status(SecTrustSetPinningPolicyName(trust, CFSTR("not-a-db-policy-name")), "set policy name");
+    ok_status(SecTrustEvaluate(trust, &trust_result), "evaluate trust");
+    is(trust_result, kSecTrustResultRecoverableTrustFailure, "check trust result for sslServer policy with policy name");
+    CFReleaseSafe(trust);
+
+    removeTS(cert0);
+}
+
 int si_28_sectrustsettings(int argc, char *const *argv)
 {
-    plan_tests(kNumberNoConstraintsTests +
-               kNumberPolicyConstraintsTests +
-               kNumberPolicyStringConstraintsTests +
-               kNumberApplicationsConstraintsTests +
-               kNumberKeyUsageConstraintsTests +
-               kNumberAllowedErrorsTests +
-               kNumberMultipleConstraintsTests);
+    plan_tests(kNumberNoConstraintsTests
+               + kNumberPolicyConstraintsTests
+               + kNumberPolicyStringConstraintsTests
+               + kNumberApplicationsConstraintsTests
+               + kNumberKeyUsageConstraintsTests
+               + kNumberAllowedErrorsTests
+               + kNumberMultipleConstraintsTests
+               + kNumberPolicyNamePinnningConstraintsTests
+               );
 
 #if !TARGET_OS_IPHONE
     if (getuid() != 0) {
@@ -558,6 +583,7 @@ int si_28_sectrustsettings(int argc, char *const *argv)
         test_key_usage_constraints();
         test_allowed_errors();
         test_multiple_constraints();
+        test_policy_name_pinning_constraints();
         cleanup_globals();
     }
 
index d6bc9c3d322b8c6f2e8ff220a0a8a30e60fe306f..0cae30c67649c6200e01d9d488da33829198d511 100644 (file)
@@ -1723,6 +1723,7 @@ _kSecCodeInfoDigestAlgorithms
 _kSecCodeInfoEntitlements
 _kSecCodeInfoEntitlementsDict
 _kSecCodeInfoUnique
+_kSecCodeInfoRuntimeVersion
 
 _kSecCFErrorResourceAdded
 _kSecCFErrorResourceAltered
index fe2df07eccf166dc8a0b3d032e85ab90d486f126..ccdce83645eaf2e706e9f42f2575b9d5798209e3 100644 (file)
@@ -2612,6 +2612,13 @@ static bool SecPVCContainsPolicy(SecPVCRef pvc, CFStringRef searchOid, CFStringR
                 if (policyIX) { *policyIX = ix; }
                 return true;
             }
+            /* <rdar://40617139> sslServer trust settings need to apply to evals using policyName pinning
+             * but make sure we don't use this for SSL Client trust settings or policies. */
+            if (CFEqual(searchOid, policyOid) &&
+                CFEqual(searchName, kSecPolicyNameSSLServer) && !CFEqual(policyName, kSecPolicyNameSSLClient)) {
+                if (policyIX) { *policyIX = ix; }
+                return true;
+            }
         }
         /* Next best is just OID. */
         if (!searchName && searchOid && policyOid) {
index de4daeb6b25d61b01fec3e9580a130b79c4e4586..2eb7fe59cca13ee79249a67f704a4a977f618b75 100644 (file)
@@ -1537,6 +1537,7 @@ _kSecCodeSignerSigningTime
 _kSecCodeSignerRequireTimestamp
 _kSecCodeSignerTeamIdentifier
 _kSecCodeSignerPlatformIdentifier
+_kSecCodeSignerRuntimeVersion
 _kSecCodeSignerTimestampServer
 _kSecCodeSignerTimestampAuthentication
 _kSecCodeSignerTimestampOmitCertificates
@@ -1565,13 +1566,14 @@ _kSecCodeInfoTeamIdentifier
 _kSecCodeInfoTrust
 _kSecCodeInfoUnique
 _kSecCodeInfoCdHashes
+_kSecCodeInfoRuntimeVersion
 _kSecCodeInfoCodeDirectory
 _kSecCodeInfoCodeOffset
 _kSecCodeInfoDiskRepInfo
-_kSecCodeInfoDiskRepOSPlatform
-_kSecCodeInfoDiskRepOSVersionMin
-_kSecCodeInfoDiskRepOSSDKVersion
 _kSecCodeInfoDiskRepNoLibraryValidation
+_kSecCodeInfoDiskRepVersionPlatform
+_kSecCodeInfoDiskRepVersionMin
+_kSecCodeInfoDiskRepVersionSDK
 _kSecCodeInfoResourceDirectory
 _kSecGuestAttributeCanonical
 _kSecGuestAttributeDynamicCode
@@ -1702,9 +1704,9 @@ _kSecCodeInfoDigestAlgorithm
 _kSecCodeInfoDigestAlgorithms
 _kSecCodeInfoDiskRepInfo
 _kSecCodeInfoDiskRepNoLibraryValidation
-_kSecCodeInfoDiskRepOSPlatform
-_kSecCodeInfoDiskRepOSSDKVersion
-_kSecCodeInfoDiskRepOSVersionMin
+_kSecCodeInfoDiskRepVersionPlatform
+_kSecCodeInfoDiskRepVersionMin
+_kSecCodeInfoDiskRepVersionSDK
 _kSecCodeInfoFlags
 _kSecCodeInfoFormat
 _kSecCodeInfoImplicitDesignatedRequirement
index 8c3e866193f1f61dcf9c8ef2cd4d12c4dcd36505..7c8a27f5d34776c89a3d945a6fb40bb01c63a06b 100644 (file)
@@ -314,8 +314,17 @@ void CodeSigningHost::removeGuest(SecGuestRef hostRef, SecGuestRef guestRef)
 //
 // The internal Guest object
 //
+CodeSigningHost::Guest::Guest() : mAttrData(NULL), mAttrDataLength(0)
+{
+}
+
 CodeSigningHost::Guest::~Guest()
-{ }
+{
+    if (mAttrData != NULL) {
+        vm_size_t rounded_size = mach_vm_round_page(mAttrDataLength);
+        vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(mAttrData), rounded_size);
+    }
+}
 
 void CodeSigningHost::Guest::setAttributes(const CssmData &attrData)
 {
@@ -330,14 +339,28 @@ void CodeSigningHost::Guest::setAttributes(const CssmData &attrData)
        }
 }
 
-CFDataRef CodeSigningHost::Guest::attrData() const
-{
-       if (!mAttrData)
-               mAttrData = makeCFData(this->attributes.get());
-       return mAttrData;
+void CodeSigningHost::Guest::createAttrData() const {
+    if (!mAttrData) {
+        CFRef<CFDataRef> data = makeCFData(this->attributes.get());
+        
+        /* cshosting_server_identifyGuest() will point to the attrData in a MIG
+         * OOL buffer. To prevent leaking surrounding memory, the attr data gets its
+         * own (zeroed) pages. */
+        vm_address_t addr = 0;
+        vm_size_t rounded_size = mach_vm_round_page(CFDataGetLength(data.get()));
+        kern_return_t ret = vm_allocate(mach_task_self(), &addr, rounded_size, VM_FLAGS_ANYWHERE);
+        
+        if (ret == KERN_SUCCESS) {
+            mAttrData = reinterpret_cast<uint8_t *>(addr);
+            mAttrDataLength = CFDataGetLength(data.get());
+            memcpy(mAttrData, CFDataGetBytePtr(data.get()), mAttrDataLength);
+            // pages returned by vm_allocate are zeroed, no need to fill out padding.
+        } else {
+            secerror("csproxy attrData vm_allocate failed: %d", ret);
+        }
+    }
 }
 
-
 void CodeSigningHost::Guest::setHash(const CssmData &given, bool generate)
 {
        if (given.length())             // explicitly given
@@ -487,9 +510,8 @@ kern_return_t cshosting_server_identifyGuest(CSH_ARGS, SecGuestRef guestRef,
                *hashLength = 0;        // unavailable
 
        // visible attributes. This proxy returns all attributes set by the host
-       CFDataRef attrData = guest->attrData(); // (the guest will cache this until it dies)
-       *attributes = (void *)CFDataGetBytePtr(attrData);       // MIG botch (it doesn't need a writable pointer)
-       *attributesLength = int_cast<CFIndex, mach_msg_type_number_t>(CFDataGetLength(attrData));
+    *attributes = (void*)guest->attrData(); // MIG botch (it doesn't need a writable pointer)
+    *attributesLength = int_cast<CFIndex, mach_msg_type_number_t>(guest->attrDataLength());
        
        END_IPC
 }
index d22c28b5e6906ee3915482e4ebe629ea7435a991..37785f76170d799a209c23018d1b6e6bfe49f03e 100644 (file)
@@ -69,6 +69,7 @@ public:
        
        struct Guest : public RefCount, public HandleObject {
        public:
+        Guest();
                ~Guest();
                std::vector<SecGuestRef> guestPath; // guest chain to this guest
                uint32_t status;                        // dynamic status
@@ -80,7 +81,8 @@ public:
                operator bool() const { return attributes; }  // exists
                SecGuestRef guestRef() const { return int_cast<long, SecGuestRef>(handle()); }
                void setAttributes(const CssmData &attrData);
-               CFDataRef attrData() const;
+        uint8_t const *attrData() const  { createAttrData(); return mAttrData; };
+        CFIndex attrDataLength() const { createAttrData(); return mAttrDataLength; };
                void setHash(const CssmData &given, bool generate);
                
                bool isGuestOf(Guest *host, GuestCheck check) const;
@@ -89,7 +91,10 @@ public:
                IFDUMP(void dump() const);
        
        private:
-               mutable CFRef<CFDataRef> mAttrData; // XML form of attributes (must live until guest destruction)
+        void createAttrData() const;
+        
+               mutable uint8_t *mAttrData; // XML form of attributes (vm_allocate'd, must live until guest destruction)
+        mutable CFIndex mAttrDataLength;
        };
        
        void registerCodeSigning(mach_port_t hostingPort, SecCSFlags flags);