]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/reqinterp.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / reqinterp.cpp
diff --git a/libsecurity_codesigning/lib/reqinterp.cpp b/libsecurity_codesigning/lib/reqinterp.cpp
deleted file mode 100644 (file)
index 077eb90..0000000
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
- * 
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-//
-// 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 {
-
-
-//
-// 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;
-
-
-//
-// Magic certificate features
-//
-static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
-static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
-
-
-//
-// Main interpreter function.
-//
-// ExprOp code is in Polish Notation (operator followed by operands),
-// and this engine uses opportunistic evaluation.
-//
-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 mContext->directory && getString() == mContext->directory->identifier();
-       case opAppleAnchor:
-               return appleSigned();
-       case opAppleGenericAnchor:
-               return appleAnchored();
-       case opAnchorHash:
-               {
-                       SecCertificateRef cert = mContext->cert(get<int32_t>());
-                       return verifyAnchor(cert, getSHA1());
-               }
-       case opInfoKeyValue:    // [legacy; use opInfoKeyField]
-               {
-                       string key = getString();
-                       return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
-               }
-       case opAnd:
-               return evaluate() & 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:
-               {
-                       string key = getString();
-                       Match match(*this);
-                       return infoKeyValue(key, match);
-               }
-       case opEntitlementField:
-               {
-                       string key = getString();
-                       Match match(*this);
-                       return entitlementValue(key, match);
-               }
-       case opCertField:
-               {
-                       SecCertificateRef cert = mContext->cert(get<int32_t>());
-                       string key = getString();
-                       Match match(*this);
-                       return certFieldValue(key, match, cert);
-               }
-       case opCertGeneric:
-               {
-                       SecCertificateRef cert = mContext->cert(get<int32_t>());
-                       string key = getString();
-                       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) {
-                               CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op);
-                               return false;
-                       } else {
-                               CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
-                               return evaluate();
-                       }
-               }
-               // unrecognized opcode and no way to interpret it
-               secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
-               MacOSError::throwMe(errSecCSUnimplemented);
-       }
-}
-
-
-//
-// Evaluate an Info.plist key condition
-//
-bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
-{
-       if (mContext->info)             // we have an Info.plist
-               if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
-                       return match(value);
-       return false;
-}
-
-
-//
-// Evaluate an entitlement condition
-//
-bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
-{
-       if (mContext->entitlements)             // we have an Info.plist
-               if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
-                       return match(value);
-       return false;
-}
-
-
-bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
-{
-       // no cert, no chance
-       if (cert == NULL)
-               return false;
-
-       // a table of recognized keys for the "certificate[foo]" syntax
-       static const struct CertField {
-               const char *name;
-               const CSSM_OID *oid;
-       } certFields[] = {
-               { "subject.C", &CSSMOID_CountryName },
-               { "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 }
-       };
-       
-       // DN-component single-value match
-       for (const CertField *cf = certFields; cf->name; cf++)
-               if (cf->name == key) {
-                       CFRef<CFStringRef> 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);
-                               return false;
-                       }
-                       return match(value);
-               }
-
-       // email multi-valued match (any of...)
-       if (key == "email") {
-               CFRef<CFArrayRef> value;
-               if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) {
-                       secdebug("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());
-       return false;
-}
-
-       
-bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
-{
-       // the key is actually a (binary) OID value
-       CssmOid oid((char *)key.data(), key.length());
-       return certFieldGeneric(oid, match, cert);
-}
-
-bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
-{
-       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
-//
-bool Requirement::Interpreter::appleAnchored()
-{
-       if (SecCertificateRef cert = mContext->cert(anchorCert))
-               if (isAppleCA(cert)
-#if defined(TEST_APPLE_ANCHOR)
-                       || verifyAnchor(cert, testAppleAnchorHash())
-#endif
-               )
-               return true;
-       return false;
-}
-
-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;
-       return false;
-}
-
-
-//
-// Verify an anchor requirement against the context
-//
-bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
-{
-       // get certificate bytes
-       if (cert) {
-               CSSM_DATA certData;
-               MacOSError::check(SecCertificateGetData(cert, &certData));
-               
-               // verify hash
-               //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
-               SHA1 hasher;
-               hasher(certData.Data, certData.Length);
-               return hasher.verify(digest);
-       }
-       return false;
-}
-
-
-//
-// Check one or all certificate(s) in the cert chain against the Trust Settings database.
-//
-bool Requirement::Interpreter::trustedCerts()
-{
-       int anchor = mContext->certCount() - 1;
-       for (int slot = 0; slot <= anchor; slot++)
-               if (SecCertificateRef cert = mContext->cert(slot))
-                       switch (trustSetting(cert, slot == anchor)) {
-                       case kSecTrustSettingsResultTrustRoot:
-                       case kSecTrustSettingsResultTrustAsRoot:
-                               return true;
-                       case kSecTrustSettingsResultDeny:
-                               return false;
-                       case kSecTrustSettingsResultUnspecified:
-                               break;
-                       default:
-                               assert(false);
-                               return false;
-                       }
-               else
-                       return false;
-       return false;
-}
-
-bool Requirement::Interpreter::trustedCert(int slot)
-{
-       if (SecCertificateRef cert = mContext->cert(slot)) {
-               int anchorSlot = mContext->certCount() - 1;
-               switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) {
-               case kSecTrustSettingsResultTrustRoot:
-               case kSecTrustSettingsResultTrustAsRoot:
-                       return true;
-               case kSecTrustSettingsResultDeny:
-               case kSecTrustSettingsResultUnspecified:
-                       return false;
-               default:
-                       assert(false);
-                       return false;
-               }
-       } else
-               return false;
-}
-
-
-//
-// Explicitly check one certificate against the Trust Settings database and report
-// the findings. This is a helper for the various Trust Settings evaluators.
-//
-SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
-{
-       // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
-       assert(cert);
-       SHA1::Digest digest;
-       hashOfCertificate(cert, digest);
-       string Certhex = CssmData(digest, sizeof(digest)).toHex();
-       for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
-               if (islower(*it))
-                       *it = toupper(*it);
-       
-       // call Trust Settings and see what it finds
-       SecTrustSettingsDomain domain;
-       SecTrustSettingsResult result;
-       CSSM_RETURN *errors = NULL;
-       uint32 errorCount = 0;
-       bool foundMatch, foundAny;
-       switch (OSStatus rc = SecTrustSettingsEvaluateCert(
-               CFTempString(Certhex),                                  // settings index
-               &CSSMOID_APPLE_TP_CODE_SIGNING,                 // standard code signing policy
-               NULL, 0,                                                                // policy string (unused)
-               kSecTrustSettingsKeyUseAny,                             // no restriction on key usage @@@
-               isAnchor,                                                               // consult system default anchor set
-
-               &domain,                                                                // domain of found setting
-               &errors, &errorCount,                                   // error set and maximum count
-               &result,                                                                // the actual setting
-               &foundMatch, &foundAny                                  // optimization hints (not used)
-               )) {
-       case errSecSuccess:
-               ::free(errors);
-               if (foundMatch)
-                       return result;
-               else
-                       return kSecTrustSettingsResultUnspecified;
-       default:
-               ::free(errors);
-               MacOSError::throwMe(rc);
-       }
-}
-
-
-//
-// Create a Match object from the interpreter stream
-//
-Requirement::Interpreter::Match::Match(Interpreter &interp)
-{
-       switch (mOp = interp.get<MatchOperation>()) {
-       case matchExists:
-               break;
-       case matchEqual:
-       case matchContains:
-       case matchBeginsWith:
-       case matchEndsWith:
-       case matchLessThan:
-       case matchGreaterThan:
-       case matchLessEqual:
-       case matchGreaterEqual:
-               mValue.take(makeCFString(interp.getString()));
-               break;
-       default:
-               // Assume this (unknown) match type has a single data argument.
-               // This gives us a chance to keep the instruction stream aligned.
-               interp.getString();                     // discard
-               break;
-       }
-}
-
-
-//
-// Execute a match against a candidate value
-//
-bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
-{
-       // null candidates always fail
-       if (!candidate)
-               return false;
-
-       // interpret an array as matching alternatives (any one succeeds)
-       if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
-               CFArrayRef array = CFArrayRef(candidate);
-               CFIndex count = CFArrayGetCount(array);
-               for (CFIndex n = 0; n < count; n++)
-                       if ((*this)(CFArrayGetValueAtIndex(array, n)))  // yes, it's recursive
-                               return true;
-       }
-
-       switch (mOp) {
-       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()) {
-                       CFStringRef value = CFStringRef(candidate);
-                       if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
-                               return true;
-               }
-               return false;
-       case matchBeginsWith:
-               if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
-                       CFStringRef value = CFStringRef(candidate);
-                       if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL))
-                               return true;
-               }
-               return false;
-       case matchEndsWith:
-               if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
-                       CFStringRef value = CFStringRef(candidate);
-                       CFIndex matchLength = CFStringGetLength(mValue);
-                       CFIndex start = CFStringGetLength(value) - matchLength;
-                       if (start >= 0)
-                               if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL))
-                                       return true;
-               }
-               return false;
-       case matchLessThan:
-               return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
-       case matchGreaterThan:
-               return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
-       case matchLessEqual:
-               return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
-       case matchGreaterEqual:
-               return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
-       default:
-               // unrecognized match types can never match
-               return false;
-       }
-}
-
-
-bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
-       CFComparisonResult outcome, bool negate) const
-{
-       if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
-               CFStringRef value = CFStringRef(candidate);
-               if ((CFStringCompare(value, mValue, flags) == outcome) == negate)
-                       return true;
-       }
-       return false;
-}
-
-
-//
-// 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