]> git.saurik.com Git - apple/libsecurity_codesigning.git/blobdiff - lib/reqinterp.cpp
libsecurity_codesigning-55037.6.tar.gz
[apple/libsecurity_codesigning.git] / lib / reqinterp.cpp
index 089154bad8a5f855e719f854563c38e4fb457657..5e1567d58961fdcda7646f4a6094f907e198a644 100644 (file)
 // reqinterp - Requirement language (exprOp) interpreter
 //
 #include "reqinterp.h"
+#include "codesigning_dtrace.h"
 #include <Security/SecTrustSettingsPriv.h>
 #include <Security/SecCertificatePriv.h>
 #include <security_utilities/memutils.h>
+#include <security_utilities/logging.h>
 #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<std::string, CFRef<CFDataRef> > FragMap;
+       
+private:
+       CFBundleRef mMyBundle;                  // Security.framework bundle
+       Mutex mLock;                                    // lock for all of the below...
+       FragMap mFragments;                             // cached fragments
+};
+
+static ModuleNexus<Fragments> 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<uint32_t>());
+       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<int32_t>());
+                       string key = getString();
+                       Match match(*this);
+                       return certFieldPolicy(key, match, cert);
+               }
        case opTrustedCert:
                return trustedCert(get<int32_t>());
        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<uint32_t>());
                        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<CFStringRef> 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<CFArrayRef> 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<Mutex> _(mLock);                         // lock for cache access
+       FragMap::const_iterator it = mFragments.find(key);
+       if (it == mFragments.end()) {
+               CFRef<CFDataRef> fragData;              // will always be set (NULL on any errors)
+               if (CFRef<CFURLRef> fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type)))
+                       if (CFRef<CFDataRef> 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