X-Git-Url: https://git.saurik.com/apple/libsecurity_codesigning.git/blobdiff_plain/6aae018b5d43c30038cfa4003e5d4bcc81f134cf..935e692843d9c528f9a4c5eee98e00961ca5f4a4:/lib/reqinterp.cpp diff --git a/lib/reqinterp.cpp b/lib/reqinterp.cpp index 207efd8..5e1567d 100644 --- a/lib/reqinterp.cpp +++ b/lib/reqinterp.cpp @@ -29,23 +29,52 @@ #include #include #include +#include #include "csutilities.h" namespace Security { namespace CodeSigning { -static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority"); -static CFStringRef appleIntermediateO = CFSTR("Apple Inc."); +// +// Fragment fetching, caching, and evaluation. +// +// Several language elements allow "calling" of separate requirement programs +// stored on disk as (binary) requirement blobs. The Fragments class takes care +// of finding, loading, caching, and evaluating them. +// +// This is a singleton for (process global) caching. It works fine as multiple instances, +// at a loss of caching effectiveness. +// +class Fragments { +public: + Fragments(); + + bool named(const std::string &name, const Requirement::Context &ctx) + { return evalNamed("subreq", name, ctx); } + bool namedAnchor(const std::string &name, const Requirement::Context &ctx) + { return evalNamed("anchorreq", name, ctx); } + +private: + bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx); + CFDataRef fragment(const char *type, const std::string &name); + + typedef std::map > FragMap; + +private: + CFBundleRef mMyBundle; // Security.framework bundle + Mutex mLock; // lock for all of the below... + FragMap mFragments; // cached fragments +}; + +static ModuleNexus fragments; // -// Construct an interpreter given a Requirement and an evaluation context. +// Magic certificate features // -Requirement::Interpreter::Interpreter(const Requirement *req, const Context *ctx) - : Reader(req), mContext(ctx) -{ -} +static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority"); +static CFStringRef appleIntermediateO = CFSTR("Apple Inc."); // @@ -64,7 +93,7 @@ bool Requirement::Interpreter::evaluate() case opTrue: return true; case opIdent: - return getString() == mContext->directory->identifier(); + return mContext->directory && getString() == mContext->directory->identifier(); case opAppleAnchor: return appleSigned(); case opAppleGenericAnchor: @@ -84,11 +113,12 @@ bool Requirement::Interpreter::evaluate() case opOr: return evaluate() | evaluate(); case opCDHash: - { + if (mContext->directory) { SHA1 hash; hash(mContext->directory, mContext->directory->length()); return hash.verify(getHash()); - } + } else + return false; case opNot: return !evaluate(); case opInfoKeyField: @@ -117,10 +147,21 @@ bool Requirement::Interpreter::evaluate() Match match(*this); return certFieldGeneric(key, match, cert); } + case opCertPolicy: + { + SecCertificateRef cert = mContext->cert(get()); + string key = getString(); + Match match(*this); + return certFieldPolicy(key, match, cert); + } case opTrustedCert: return trustedCert(get()); case opTrustedCerts: return trustedCerts(); + case opNamedAnchor: + return fragments().namedAnchor(getString(), *mContext); + case opNamedCode: + return fragments().named(getString(), *mContext); default: // opcode not recognized - handle generically if possible, fail otherwise if (op & (opGenericFalse | opGenericSkip)) { @@ -180,10 +221,16 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma { "subject.CN", &CSSMOID_CommonName }, { "subject.D", &CSSMOID_Description }, { "subject.L", &CSSMOID_LocalityName }, +// { "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 }, { NULL, NULL } }; @@ -192,7 +239,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma 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=%ld", cert, key.c_str(), rc); + secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), rc); return false; } return match(value); @@ -202,7 +249,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma if (key == "email") { CFRef value; if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) { - secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert, rc); + secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, rc); return false; } return match(value); @@ -213,7 +260,7 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma return false; } - + bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert) { // the key is actually a (binary) OID value @@ -226,6 +273,18 @@ bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue); } +bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert) +{ + // the key is actually a (binary) OID value + CssmOid oid((char *)key.data(), key.length()); + return certFieldPolicy(oid, match, cert); +} + +bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert) +{ + return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue); +} + // // Check the Apple-signed condition @@ -381,7 +440,7 @@ Requirement::Interpreter::Match::Match(Interpreter &interp) case matchGreaterThan: case matchLessEqual: case matchGreaterEqual: - mValue = makeCFString(interp.getString()); + mValue.take(makeCFString(interp.getString())); break; default: // Assume this (unknown) match type has a single data argument. @@ -466,5 +525,49 @@ bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCo } +// +// External fragments +// +Fragments::Fragments() +{ + mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); +} + + +bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx) +{ + if (CFDataRef fragData = fragment(type, name)) { + const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData); // was prevalidated as Requirement + return req->validates(ctx); + } + return false; +} + + +CFDataRef Fragments::fragment(const char *type, const std::string &name) +{ + string key = name + "!!" + type; // compound key + StLock _(mLock); // lock for cache access + FragMap::const_iterator it = mFragments.find(key); + if (it == mFragments.end()) { + CFRef fragData; // will always be set (NULL on any errors) + if (CFRef fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type))) + if (CFRef data = cfLoadFile(fragURL)) { // got data + const Requirement *req = (const Requirement *)CFDataGetBytePtr(data); + if (req->validateBlob(CFDataGetLength(data))) // looks like a Requirement... + fragData = data; // ... so accept it + else + Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str()); + } + if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED()) + CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type, name.c_str(), fragData ? CFDataGetBytePtr(fragData) : NULL); + mFragments[key] = fragData; // cache it, success or failure + return fragData; + } + CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str()); + return it->second; +} + + } // CodeSigning } // Security