X-Git-Url: https://git.saurik.com/apple/libsecurity_codesigning.git/blobdiff_plain/516ae4771041b50b4461674fd622f46f053aa4bc..935e692843d9c528f9a4c5eee98e00961ca5f4a4:/lib/reqinterp.cpp diff --git a/lib/reqinterp.cpp b/lib/reqinterp.cpp index 089154b..5e1567d 100644 --- a/lib/reqinterp.cpp +++ b/lib/reqinterp.cpp @@ -25,26 +25,56 @@ // reqinterp - Requirement language (exprOp) interpreter // #include "reqinterp.h" +#include "codesigning_dtrace.h" #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."); // @@ -56,13 +86,14 @@ Requirement::Interpreter::Interpreter(const Requirement *req, const Context *ctx bool Requirement::Interpreter::evaluate() { ExprOp op = ExprOp(get()); + CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t)); switch (op & ~opFlagMask) { case opFalse: return false; case opTrue: return true; case opIdent: - return getString() == mContext->directory->identifier(); + return mContext->directory && getString() == mContext->directory->identifier(); case opAppleAnchor: return appleSigned(); case opAppleGenericAnchor: @@ -82,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: @@ -115,20 +147,31 @@ 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)) { // unknown opcode, but it has a size field and can be safely bypassed skip(get()); if (op & opGenericFalse) { - secdebug("csinterp", "opcode 0x%x interpreted as false", op); + CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op); return false; } else { - secdebug("csinterp", "opcode 0x%x ignored; continuing", op); + CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op); return evaluate(); } } @@ -178,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 } }; @@ -189,8 +238,8 @@ bool Requirement::Interpreter::certFieldValue(const string &key, const Match &ma for (const CertField *cf = certFields; cf->name; cf++) if (cf->name == key) { CFRef value; - if (IFDEBUG(OSStatus rc =) SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { - secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert, key.c_str(), rc); + if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { + secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), rc); return false; } return match(value); @@ -200,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); @@ -211,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 @@ -224,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 @@ -243,11 +304,11 @@ bool Requirement::Interpreter::appleAnchored() 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) + 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; + return true; return false; } @@ -379,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. @@ -464,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