X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/866f8763175ff60e4fa455b92b5eb660a12fe6c7..refs/heads/master:/OSX/libsecurity_codesigning/lib/reqinterp.cpp?ds=sidebyside diff --git a/OSX/libsecurity_codesigning/lib/reqinterp.cpp b/OSX/libsecurity_codesigning/lib/reqinterp.cpp index b685802b..f3970650 100644 --- a/OSX/libsecurity_codesigning/lib/reqinterp.cpp +++ b/OSX/libsecurity_codesigning/lib/reqinterp.cpp @@ -24,6 +24,7 @@ // // reqinterp - Requirement language (exprOp) interpreter // + #include "reqinterp.h" #include "codesigning_dtrace.h" #include @@ -33,7 +34,19 @@ #include #include #include +#include #include "csutilities.h" +#include "notarization.h" +#include "legacydevid.h" + +#define WAITING_FOR_LIB_AMFI_INTERFACE 1 + +#if WAITING_FOR_LIB_AMFI_INTERFACE +#define __mac_syscall __sandbox_ms +#include + +#define AMFI_INTF_CD_HASH_LEN 20 +#endif namespace Security { namespace CodeSigning { @@ -157,6 +170,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()); @@ -178,6 +198,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)) { @@ -206,7 +234,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); } @@ -218,13 +246,12 @@ 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) @@ -233,32 +260,31 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma // a table of recognized keys for the "certificate[foo]" syntax static const struct CertField { const char *name; - const CSSM_OID *oid; + const DERItem *oid; } certFields[] = { - { "subject.C", &CSSMOID_CountryName }, - { "subject.CN", &CSSMOID_CommonName }, - { "subject.D", &CSSMOID_Description }, - { "subject.L", &CSSMOID_LocalityName }, + { "subject.C", &oidCountryName}, + { "subject.CN", &oidCommonName }, + { "subject.D", &oidDescription }, + { "subject.L", &oidLocalityName }, // { "subject.C-L", &CSSMOID_CollectiveLocalityName }, // missing from Security.framework headers - { "subject.O", &CSSMOID_OrganizationName }, - { "subject.C-O", &CSSMOID_CollectiveOrganizationName }, - { "subject.OU", &CSSMOID_OrganizationalUnitName }, - { "subject.C-OU", &CSSMOID_CollectiveOrganizationalUnitName }, - { "subject.ST", &CSSMOID_StateProvinceName }, - { "subject.C-ST", &CSSMOID_CollectiveStateProvinceName }, - { "subject.STREET", &CSSMOID_StreetAddress }, - { "subject.C-STREET", &CSSMOID_CollectiveStreetAddress }, - { "subject.UID", &CSSMOID_UserID }, + { "subject.O", &oidOrganizationName }, + { "subject.C-O", &oidCollectiveOrganizationName }, + { "subject.OU", &oidOrganizationalUnitName}, + { "subject.C-OU", &oidCollectiveOrganizationalUnitName}, + { "subject.ST", &oidStateOrProvinceName}, + { "subject.C-ST", &oidCollectiveStateOrProvinceName }, + { "subject.STREET", &oidStreetAddress }, + { "subject.C-STREET", &oidCollectiveStreetAddress }, + { "subject.UID", &oidUserId }, { NULL, NULL } }; // DN-component single-value match for (const CertField *cf = certFields; cf->name; cf++) if (cf->name == key) { - CFRef value; - 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); + CFRef value(SecCertificateCopySubjectAttributeValue(cert, (DERItem *)cf->oid)); + if (!value.get()) { + secinfo("csinterp", "cert %p lookup for DN.%s failed", cert, key.c_str()); return false; } return match(value); @@ -291,7 +317,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) @@ -303,7 +348,7 @@ 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 @@ -366,10 +411,15 @@ CFArrayRef Requirement::Interpreter::getAdditionalTrustedAnchors() bool Requirement::Interpreter::appleLocalAnchored() { - static CFArrayRef additionalTrustedCertificates = NULL; + static CFArrayRef additionalTrustedCertificates = NULL; - if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) + if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) { return false; + } + + if (mContext->forcePlatform) { + return true; + } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -389,18 +439,76 @@ bool Requirement::Interpreter::appleLocalAnchored() return false; } +#if WAITING_FOR_LIB_AMFI_INTERFACE +// These bits are here until we get get a new build alias for libamfi-interface. + +#define MAC_AMFI_POLICY_NAME "AMFI" + +#define AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE 95 + +typedef struct amfi_cdhash_in_trustcache_ { + uint8_t cdhash[20]; + uint64_t result; +} amfi_cdhash_in_trustcache_t; + +static int +__amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], uint64_t* trustcache_result) +{ + amfi_cdhash_in_trustcache_t args; + static_assert(AMFI_INTF_CD_HASH_LEN == sizeof(args.cdhash), "Error: cdhash length mismatch"); + int err; + memcpy(args.cdhash, cdhash, sizeof(args.cdhash)); + args.result = 0; + err = __mac_syscall(MAC_AMFI_POLICY_NAME, AMFI_SYSCALL_CDHASH_IN_TRUSTCACHE, &args); + if (err) { + err = errno; + } + *trustcache_result = args.result; + return err; +} + +static int +amfi_interface_cdhash_in_trustcache(const uint8_t cdhash[], size_t cdhash_len, uint64_t* trustcache_result) +{ + int err = EINVAL; + + if (cdhash == nullptr || cdhash_len != AMFI_INTF_CD_HASH_LEN || trustcache_result == nullptr) { + goto lb_end; + } + *trustcache_result = 0; + + err = __amfi_interface_cdhash_in_trustcache(cdhash, trustcache_result); + +lb_end: + return err; +} +#endif + +bool Requirement::Interpreter::inTrustCache() +{ + uint64_t result = 0; + CFRef cdhashRef = mContext->directory->cdhash(true); + const uint8_t *cdhash = CFDataGetBytePtr(cdhashRef); + size_t cdhash_len = CFDataGetLength(cdhashRef); + int err = amfi_interface_cdhash_in_trustcache(cdhash, cdhash_len, &result); + return (err == 0) && (result != 0); +} + bool Requirement::Interpreter::appleSigned() { - 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; + if (inTrustCache()) { + return true; + } + else 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; + return false; } @@ -412,15 +520,7 @@ 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 - hasher(certData.Data, certData.Length); -#else hasher(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert)); -#endif return hasher.verify(digest); } return false; @@ -529,6 +629,7 @@ SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef Requirement::Interpreter::Match::Match(Interpreter &interp) { switch (mOp = interp.get()) { + case matchAbsent: case matchExists: break; case matchEqual: @@ -541,6 +642,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. @@ -559,6 +668,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); @@ -569,31 +682,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; @@ -605,6 +720,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; @@ -615,9 +750,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;