X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..7e6b461318c8a779d91381531435a68ee4e8b6ed:/OSX/libsecurity_codesigning/lib/reqinterp.cpp?ds=inline diff --git a/OSX/libsecurity_codesigning/lib/reqinterp.cpp b/OSX/libsecurity_codesigning/lib/reqinterp.cpp index effd233e..4ff17212 100644 --- a/OSX/libsecurity_codesigning/lib/reqinterp.cpp +++ b/OSX/libsecurity_codesigning/lib/reqinterp.cpp @@ -24,13 +24,19 @@ // // reqinterp - Requirement language (exprOp) interpreter // + #include "reqinterp.h" #include "codesigning_dtrace.h" #include #include #include #include +#include +#include +#include #include "csutilities.h" +#include "notarization.h" +#include "legacydevid.h" namespace Security { namespace CodeSigning { @@ -146,6 +152,7 @@ bool Requirement::Interpreter::eval(int depth) Match match(*this); return certFieldValue(key, match, cert); } +#if TARGET_OS_OSX case opCertGeneric: { SecCertificateRef cert = mContext->cert(get()); @@ -153,6 +160,13 @@ bool Requirement::Interpreter::eval(int depth) Match match(*this); return certFieldGeneric(key, match, cert); } + case opCertFieldDate: + { + SecCertificateRef cert = mContext->cert(get()); + string key = getString(); + Match match(*this); + return certFieldDate(key, match, cert); + } case opCertPolicy: { SecCertificateRef cert = mContext->cert(get()); @@ -160,6 +174,7 @@ bool Requirement::Interpreter::eval(int depth) Match match(*this); return certFieldPolicy(key, match, cert); } +#endif case opTrustedCert: return trustedCert(get()); case opTrustedCerts: @@ -173,6 +188,14 @@ bool Requirement::Interpreter::eval(int depth) int32_t targetPlatform = get(); return mContext->directory && mContext->directory->platform == targetPlatform; } + case opNotarized: + { + return isNotarized(mContext); + } + case opLegacyDevID: + { + return meetsDeveloperIDLegacyAllowedPolicy(mContext); + } default: // opcode not recognized - handle generically if possible, fail otherwise if (op & (opGenericFalse | opGenericSkip)) { @@ -187,7 +210,7 @@ bool Requirement::Interpreter::eval(int depth) } } // unrecognized opcode and no way to interpret it - secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op); + secinfo("csinterp", "opcode 0x%x cannot be handled; aborting", op); MacOSError::throwMe(errSecCSUnimplemented); } } @@ -201,7 +224,7 @@ bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &matc if (mContext->info) // we have an Info.plist if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key))) return match(value); - return false; + return match(kCFNull); } @@ -213,12 +236,14 @@ bool Requirement::Interpreter::entitlementValue(const string &key, const Match & if (mContext->entitlements) // we have an Info.plist if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key))) return match(value); - return false; + return match(kCFNull); } bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert) { +// XXX: Not supported on embedded yet due to lack of supporting API +#if TARGET_OS_OSX // no cert, no chance if (cert == NULL) return false; @@ -244,13 +269,14 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma { "subject.UID", &CSSMOID_UserID }, { NULL, NULL } }; - + // DN-component single-value match for (const CertField *cf = certFields; cf->name; cf++) if (cf->name == key) { CFRef value; - if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { - secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc); + OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref()); + if (rc) { + secinfo("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc); return false; } return match(value); @@ -259,19 +285,21 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma // email multi-valued match (any of...) if (key == "email") { CFRef value; - if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) { - secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc); + OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref()); + if (rc) { + secinfo("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc); return false; } return match(value); } // unrecognized key. Fail but do not abort to promote backward compatibility down the road - secdebug("csinterp", "cert field notation \"%s\" not understood", key.c_str()); + secinfo("csinterp", "cert field notation \"%s\" not understood", key.c_str()); +#endif return false; } - +#if TARGET_OS_OSX bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert) { // the key is actually a (binary) OID value @@ -281,7 +309,26 @@ bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match & bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert) { - return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue); + return cert && match(certificateHasField(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull); +} + +bool Requirement::Interpreter::certFieldDate(const string &key, const Match &match, SecCertificateRef cert) +{ + // the key is actually a (binary) OID value + CssmOid oid((char *)key.data(), key.length()); + return certFieldDate(oid, match, cert); +} + +bool Requirement::Interpreter::certFieldDate(const CssmOid &oid, const Match &match, SecCertificateRef cert) +{ + CFTypeRef value = cert != NULL ? certificateCopyFieldDate(cert, oid) : NULL; + bool matching = match(value != NULL ? value : kCFNull); + + if (value) { + CFRelease(value); + } + + return matching; } bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert) @@ -293,9 +340,9 @@ bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &m bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert) { - return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue); + return cert && match(certificateHasPolicy(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull); } - +#endif // // Check the Apple-signed condition @@ -303,23 +350,97 @@ bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match & bool Requirement::Interpreter::appleAnchored() { if (SecCertificateRef cert = mContext->cert(anchorCert)) - if (isAppleCA(cert) -#if defined(TEST_APPLE_ANCHOR) - || verifyAnchor(cert, testAppleAnchorHash()) -#endif - ) + if (isAppleCA(cert)) return true; return false; } +static CFStringRef kAMFINVRAMTrustedKeys = CFSTR("AMFITrustedKeys"); + +CFArrayRef Requirement::Interpreter::getAdditionalTrustedAnchors() +{ + __block CFRef keys = makeCFMutableArray(0); + + try { + io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options"); + if (entry == IO_OBJECT_NULL) + return NULL; + + CFRef configData = (CFDataRef)IORegistryEntryCreateCFProperty(entry, kAMFINVRAMTrustedKeys, kCFAllocatorDefault, 0); + IOObjectRelease(entry); + if (!configData) + return NULL; + + CFRef configDict = CFDictionaryRef(IOCFUnserializeWithSize((const char *)CFDataGetBytePtr(configData), + (size_t)CFDataGetLength(configData), + kCFAllocatorDefault, 0, NULL)); + if (!configDict) + return NULL; + + CFArrayRef trustedKeys = CFArrayRef(CFDictionaryGetValue(configDict, CFSTR("trustedKeys"))); + if (!trustedKeys && CFGetTypeID(trustedKeys) != CFArrayGetTypeID()) + return NULL; + + cfArrayApplyBlock(trustedKeys, ^(const void *value) { + CFDictionaryRef key = CFDictionaryRef(value); + if (!key && CFGetTypeID(key) != CFDictionaryGetTypeID()) + return; + + CFDataRef hash = CFDataRef(CFDictionaryGetValue(key, CFSTR("certDigest"))); + if (!hash && CFGetTypeID(hash) != CFDataGetTypeID()) + return; + CFArrayAppendValue(keys, hash); + }); + + } catch (...) { + } + + if (CFArrayGetCount(keys) == 0) + return NULL; + + return keys.yield(); +} + +bool Requirement::Interpreter::appleLocalAnchored() +{ + static CFArrayRef additionalTrustedCertificates = NULL; + + if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) + return false; + + if (mContext->forcePlatform) { + return true; + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + additionalTrustedCertificates = getAdditionalTrustedAnchors(); + }); + + if (additionalTrustedCertificates == NULL) + return false; + + CFRef hash = SecCertificateCopySHA256Digest(mContext->cert(leafCert)); + if (!hash) + return false; + + if (CFArrayContainsValue(additionalTrustedCertificates, CFRangeMake(0, CFArrayGetCount(additionalTrustedCertificates)), hash)) + return true; + + return false; +} + bool Requirement::Interpreter::appleSigned() { - if (appleAnchored()) + if (appleAnchored()) { if (SecCertificateRef intermed = mContext->cert(-2)) // first intermediate // first intermediate common name match (exact) if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed) && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed)) return true; + } else if (appleLocalAnchored()) { + return true; + } return false; } @@ -331,12 +452,16 @@ bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsign { // get certificate bytes if (cert) { + SHA1 hasher; +#if TARGET_OS_OSX CSSM_DATA certData; MacOSError::check(SecCertificateGetData(cert, &certData)); // verify hash - SHA1 hasher; hasher(certData.Data, certData.Length); +#else + hasher(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert)); +#endif return hasher.verify(digest); } return false; @@ -394,6 +519,8 @@ bool Requirement::Interpreter::trustedCert(int slot) // SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor) { + // XXX: Not supported on embedded yet due to lack of supporting API +#if TARGET_OS_OSX // the SPI input is the uppercase hex form of the SHA-1 of the certificate... assert(cert); SHA1::Digest digest; @@ -431,6 +558,9 @@ SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef ::free(errors); MacOSError::throwMe(rc); } +#else + return kSecTrustSettingsResultUnspecified; +#endif } @@ -440,6 +570,7 @@ SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef Requirement::Interpreter::Match::Match(Interpreter &interp) { switch (mOp = interp.get()) { + case matchAbsent: case matchExists: break; case matchEqual: @@ -452,6 +583,14 @@ Requirement::Interpreter::Match::Match(Interpreter &interp) case matchGreaterEqual: mValue.take(makeCFString(interp.getString())); break; + case matchOn: + case matchBefore: + case matchAfter: + case matchOnOrBefore: + case matchOnOrAfter: { + mValue.take(CFDateCreate(NULL, interp.getAbsoluteTime())); + break; + } default: // Assume this (unknown) match type has a single data argument. // This gives us a chance to keep the instruction stream aligned. @@ -470,6 +609,10 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const if (!candidate) return false; + if (candidate == kCFNull) { + return mOp == matchAbsent; // only 'absent' matches + } + // interpret an array as matching alternatives (any one succeeds) if (CFGetTypeID(candidate) == CFArrayGetTypeID()) { CFArrayRef array = CFArrayRef(candidate); @@ -480,31 +623,33 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const } switch (mOp) { + case matchAbsent: + return false; // it exists, so it cannot be absent case matchExists: // anything but NULL and boolean false "exists" return !CFEqual(candidate, kCFBooleanFalse); case matchEqual: // equality works for all CF types return CFEqual(candidate, mValue); case matchContains: - if (CFGetTypeID(candidate) == CFStringGetTypeID()) { + if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) { CFStringRef value = CFStringRef(candidate); - if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL)) + if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(value)), 0, NULL)) return true; } return false; case matchBeginsWith: - if (CFGetTypeID(candidate) == CFStringGetTypeID()) { + if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) { CFStringRef value = CFStringRef(candidate); - if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL)) + if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(cfStringValue())), 0, NULL)) return true; } return false; case matchEndsWith: - if (CFGetTypeID(candidate) == CFStringGetTypeID()) { + if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) { CFStringRef value = CFStringRef(candidate); - CFIndex matchLength = CFStringGetLength(mValue); + CFIndex matchLength = CFStringGetLength(cfStringValue()); CFIndex start = CFStringGetLength(value) - matchLength; if (start >= 0) - if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL)) + if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(start, matchLength), 0, NULL)) return true; } return false; @@ -516,6 +661,26 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false); case matchGreaterEqual: return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false); + case matchOn: + case matchBefore: + case matchAfter: + case matchOnOrBefore: + case matchOnOrAfter: { + if (!isDateValue() || CFGetTypeID(candidate) != CFDateGetTypeID()) { + return false; + } + + CFComparisonResult res = CFDateCompare((CFDateRef)candidate, cfDateValue(), NULL); + + switch (mOp) { + case matchOn: return res == 0; + case matchBefore: return res < 0; + case matchAfter: return res > 0; + case matchOnOrBefore: return res <= 0; + case matchOnOrAfter: return res >= 0; + default: abort(); + } + } default: // unrecognized match types can never match return false; @@ -526,9 +691,9 @@ bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags, CFComparisonResult outcome, bool negate) const { - if (CFGetTypeID(candidate) == CFStringGetTypeID()) { + if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) { CFStringRef value = CFStringRef(candidate); - if ((CFStringCompare(value, mValue, flags) == outcome) == negate) + if ((CFStringCompare(value, cfStringValue(), flags) == outcome) == negate) return true; } return false;