2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 // reqinterp - Requirement language (exprOp) interpreter
27 #include "reqinterp.h"
28 #include "codesigning_dtrace.h"
29 #include <Security/SecTrustSettingsPriv.h>
30 #include <Security/SecCertificatePriv.h>
31 #include <security_utilities/memutils.h>
32 #include <security_utilities/logging.h>
33 #include "csutilities.h"
36 namespace CodeSigning
{
40 // Fragment fetching, caching, and evaluation.
42 // Several language elements allow "calling" of separate requirement programs
43 // stored on disk as (binary) requirement blobs. The Fragments class takes care
44 // of finding, loading, caching, and evaluating them.
46 // This is a singleton for (process global) caching. It works fine as multiple instances,
47 // at a loss of caching effectiveness.
53 bool named(const std::string
&name
, const Requirement::Context
&ctx
)
54 { return evalNamed("subreq", name
, ctx
); }
55 bool namedAnchor(const std::string
&name
, const Requirement::Context
&ctx
)
56 { return evalNamed("anchorreq", name
, ctx
); }
59 bool evalNamed(const char *type
, const std::string
&name
, const Requirement::Context
&ctx
);
60 CFDataRef
fragment(const char *type
, const std::string
&name
);
62 typedef std::map
<std::string
, CFRef
<CFDataRef
> > FragMap
;
65 CFBundleRef mMyBundle
; // Security.framework bundle
66 Mutex mLock
; // lock for all of the below...
67 FragMap mFragments
; // cached fragments
70 static ModuleNexus
<Fragments
> fragments
;
74 // Magic certificate features
76 static CFStringRef appleIntermediateCN
= CFSTR("Apple Code Signing Certification Authority");
77 static CFStringRef appleIntermediateO
= CFSTR("Apple Inc.");
81 // Main interpreter function.
83 // ExprOp code is in Polish Notation (operator followed by operands),
84 // and this engine uses opportunistic evaluation.
86 bool Requirement::Interpreter::evaluate()
88 ExprOp op
= ExprOp(get
<uint32_t>());
89 CODESIGN_EVAL_REQINT_OP(op
, this->pc() - sizeof(uint32_t));
90 switch (op
& ~opFlagMask
) {
96 return getString() == mContext
->directory
->identifier();
99 case opAppleGenericAnchor
:
100 return appleAnchored();
103 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
104 return verifyAnchor(cert
, getSHA1());
106 case opInfoKeyValue
: // [legacy; use opInfoKeyField]
108 string key
= getString();
109 return infoKeyValue(key
, Match(CFTempString(getString()), matchEqual
));
112 return evaluate() & evaluate();
114 return evaluate() | evaluate();
118 hash(mContext
->directory
, mContext
->directory
->length());
119 return hash
.verify(getHash());
125 string key
= getString();
127 return infoKeyValue(key
, match
);
129 case opEntitlementField
:
131 string key
= getString();
133 return entitlementValue(key
, match
);
137 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
138 string key
= getString();
140 return certFieldValue(key
, match
, cert
);
144 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
145 string key
= getString();
147 return certFieldGeneric(key
, match
, cert
);
151 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
152 string key
= getString();
154 return certFieldPolicy(key
, match
, cert
);
157 return trustedCert(get
<int32_t>());
159 return trustedCerts();
161 return fragments().namedAnchor(getString(), *mContext
);
163 return fragments().named(getString(), *mContext
);
165 // opcode not recognized - handle generically if possible, fail otherwise
166 if (op
& (opGenericFalse
| opGenericSkip
)) {
167 // unknown opcode, but it has a size field and can be safely bypassed
168 skip(get
<uint32_t>());
169 if (op
& opGenericFalse
) {
170 CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op
);
173 CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op
);
177 // unrecognized opcode and no way to interpret it
178 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op
);
179 MacOSError::throwMe(errSecCSUnimplemented
);
185 // Evaluate an Info.plist key condition
187 bool Requirement::Interpreter::infoKeyValue(const string
&key
, const Match
&match
)
189 if (mContext
->info
) // we have an Info.plist
190 if (CFTypeRef value
= CFDictionaryGetValue(mContext
->info
, CFTempString(key
)))
197 // Evaluate an entitlement condition
199 bool Requirement::Interpreter::entitlementValue(const string
&key
, const Match
&match
)
201 if (mContext
->entitlements
) // we have an Info.plist
202 if (CFTypeRef value
= CFDictionaryGetValue(mContext
->entitlements
, CFTempString(key
)))
208 bool Requirement::Interpreter::certFieldValue(const string
&key
, const Match
&match
, SecCertificateRef cert
)
210 // no cert, no chance
214 // a table of recognized keys for the "certificate[foo]" syntax
215 static const struct CertField
{
219 { "subject.C", &CSSMOID_CountryName
},
220 { "subject.CN", &CSSMOID_CommonName
},
221 { "subject.D", &CSSMOID_Description
},
222 { "subject.L", &CSSMOID_LocalityName
},
223 { "subject.O", &CSSMOID_OrganizationName
},
224 { "subject.OU", &CSSMOID_OrganizationalUnitName
},
225 { "subject.ST", &CSSMOID_StateProvinceName
},
226 { "subject.STREET", &CSSMOID_StreetAddress
},
230 // DN-component single-value match
231 for (const CertField
*cf
= certFields
; cf
->name
; cf
++)
232 if (cf
->name
== key
) {
233 CFRef
<CFStringRef
> value
;
234 if (OSStatus rc
= SecCertificateCopySubjectComponent(cert
, cf
->oid
, &value
.aref())) {
235 secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert
, key
.c_str(), rc
);
241 // email multi-valued match (any of...)
242 if (key
== "email") {
243 CFRef
<CFArrayRef
> value
;
244 if (OSStatus rc
= SecCertificateCopyEmailAddresses(cert
, &value
.aref())) {
245 secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert
, rc
);
251 // unrecognized key. Fail but do not abort to promote backward compatibility down the road
252 secdebug("csinterp", "cert field notation \"%s\" not understood", key
.c_str());
257 bool Requirement::Interpreter::certFieldGeneric(const string
&key
, const Match
&match
, SecCertificateRef cert
)
259 // the key is actually a (binary) OID value
260 CssmOid
oid((char *)key
.data(), key
.length());
261 return certFieldGeneric(oid
, match
, cert
);
264 bool Requirement::Interpreter::certFieldGeneric(const CssmOid
&oid
, const Match
&match
, SecCertificateRef cert
)
266 return cert
&& certificateHasField(cert
, oid
) && match(kCFBooleanTrue
);
269 bool Requirement::Interpreter::certFieldPolicy(const string
&key
, const Match
&match
, SecCertificateRef cert
)
271 // the key is actually a (binary) OID value
272 CssmOid
oid((char *)key
.data(), key
.length());
273 return certFieldPolicy(oid
, match
, cert
);
276 bool Requirement::Interpreter::certFieldPolicy(const CssmOid
&oid
, const Match
&match
, SecCertificateRef cert
)
278 return cert
&& certificateHasPolicy(cert
, oid
) && match(kCFBooleanTrue
);
283 // Check the Apple-signed condition
285 bool Requirement::Interpreter::appleAnchored()
287 if (SecCertificateRef cert
= mContext
->cert(anchorCert
))
288 if (verifyAnchor(cert
, appleAnchorHash())
289 #if defined(TEST_APPLE_ANCHOR)
290 || verifyAnchor(cert
, testAppleAnchorHash())
297 bool Requirement::Interpreter::appleSigned()
300 if (SecCertificateRef intermed
= mContext
->cert(-2)) // first intermediate
301 // first intermediate common name match (exact)
302 if (certFieldValue("subject.CN", Match(appleIntermediateCN
, matchEqual
), intermed
)
303 && certFieldValue("subject.O", Match(appleIntermediateO
, matchEqual
), intermed
))
310 // Verify an anchor requirement against the context
312 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert
, const unsigned char *digest
)
314 // get certificate bytes
317 MacOSError::check(SecCertificateGetData(cert
, &certData
));
320 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
322 hasher(certData
.Data
, certData
.Length
);
323 return hasher
.verify(digest
);
330 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
332 bool Requirement::Interpreter::trustedCerts()
334 int anchor
= mContext
->certCount() - 1;
335 for (int slot
= 0; slot
<= anchor
; slot
++)
336 if (SecCertificateRef cert
= mContext
->cert(slot
))
337 switch (trustSetting(cert
, slot
== anchor
)) {
338 case kSecTrustSettingsResultTrustRoot
:
339 case kSecTrustSettingsResultTrustAsRoot
:
341 case kSecTrustSettingsResultDeny
:
343 case kSecTrustSettingsResultUnspecified
:
354 bool Requirement::Interpreter::trustedCert(int slot
)
356 if (SecCertificateRef cert
= mContext
->cert(slot
)) {
357 int anchorSlot
= mContext
->certCount() - 1;
358 switch (trustSetting(cert
, slot
== anchorCert
|| slot
== anchorSlot
)) {
359 case kSecTrustSettingsResultTrustRoot
:
360 case kSecTrustSettingsResultTrustAsRoot
:
362 case kSecTrustSettingsResultDeny
:
363 case kSecTrustSettingsResultUnspecified
:
375 // Explicitly check one certificate against the Trust Settings database and report
376 // the findings. This is a helper for the various Trust Settings evaluators.
378 SecTrustSettingsResult
Requirement::Interpreter::trustSetting(SecCertificateRef cert
, bool isAnchor
)
380 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
383 hashOfCertificate(cert
, digest
);
384 string Certhex
= CssmData(digest
, sizeof(digest
)).toHex();
385 for (string::iterator it
= Certhex
.begin(); it
!= Certhex
.end(); ++it
)
389 // call Trust Settings and see what it finds
390 SecTrustSettingsDomain domain
;
391 SecTrustSettingsResult result
;
392 CSSM_RETURN
*errors
= NULL
;
393 uint32 errorCount
= 0;
394 bool foundMatch
, foundAny
;
395 switch (OSStatus rc
= SecTrustSettingsEvaluateCert(
396 CFTempString(Certhex
), // settings index
397 &CSSMOID_APPLE_TP_CODE_SIGNING
, // standard code signing policy
398 NULL
, 0, // policy string (unused)
399 kSecTrustSettingsKeyUseAny
, // no restriction on key usage @@@
400 isAnchor
, // consult system default anchor set
402 &domain
, // domain of found setting
403 &errors
, &errorCount
, // error set and maximum count
404 &result
, // the actual setting
405 &foundMatch
, &foundAny
// optimization hints (not used)
412 return kSecTrustSettingsResultUnspecified
;
415 MacOSError::throwMe(rc
);
421 // Create a Match object from the interpreter stream
423 Requirement::Interpreter::Match::Match(Interpreter
&interp
)
425 switch (mOp
= interp
.get
<MatchOperation
>()) {
430 case matchBeginsWith
:
433 case matchGreaterThan
:
435 case matchGreaterEqual
:
436 mValue
.take(makeCFString(interp
.getString()));
439 // Assume this (unknown) match type has a single data argument.
440 // This gives us a chance to keep the instruction stream aligned.
441 interp
.getString(); // discard
448 // Execute a match against a candidate value
450 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate
) const
452 // null candidates always fail
456 // interpret an array as matching alternatives (any one succeeds)
457 if (CFGetTypeID(candidate
) == CFArrayGetTypeID()) {
458 CFArrayRef array
= CFArrayRef(candidate
);
459 CFIndex count
= CFArrayGetCount(array
);
460 for (CFIndex n
= 0; n
< count
; n
++)
461 if ((*this)(CFArrayGetValueAtIndex(array
, n
))) // yes, it's recursive
466 case matchExists
: // anything but NULL and boolean false "exists"
467 return !CFEqual(candidate
, kCFBooleanFalse
);
468 case matchEqual
: // equality works for all CF types
469 return CFEqual(candidate
, mValue
);
471 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
472 CFStringRef value
= CFStringRef(candidate
);
473 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(0, CFStringGetLength(value
)), 0, NULL
))
477 case matchBeginsWith
:
478 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
479 CFStringRef value
= CFStringRef(candidate
);
480 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(0, CFStringGetLength(mValue
)), 0, NULL
))
485 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
486 CFStringRef value
= CFStringRef(candidate
);
487 CFIndex matchLength
= CFStringGetLength(mValue
);
488 CFIndex start
= CFStringGetLength(value
) - matchLength
;
490 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(start
, matchLength
), 0, NULL
))
495 return inequality(candidate
, kCFCompareNumerically
, kCFCompareLessThan
, true);
496 case matchGreaterThan
:
497 return inequality(candidate
, kCFCompareNumerically
, kCFCompareGreaterThan
, true);
499 return inequality(candidate
, kCFCompareNumerically
, kCFCompareGreaterThan
, false);
500 case matchGreaterEqual
:
501 return inequality(candidate
, kCFCompareNumerically
, kCFCompareLessThan
, false);
503 // unrecognized match types can never match
509 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate
, CFStringCompareFlags flags
,
510 CFComparisonResult outcome
, bool negate
) const
512 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
513 CFStringRef value
= CFStringRef(candidate
);
514 if ((CFStringCompare(value
, mValue
, flags
) == outcome
) == negate
)
522 // External fragments
524 Fragments::Fragments()
526 mMyBundle
= CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
530 bool Fragments::evalNamed(const char *type
, const std::string
&name
, const Requirement::Context
&ctx
)
532 if (CFDataRef fragData
= fragment(type
, name
)) {
533 const Requirement
*req
= (const Requirement
*)CFDataGetBytePtr(fragData
); // was prevalidated as Requirement
534 return req
->validates(ctx
);
540 CFDataRef
Fragments::fragment(const char *type
, const std::string
&name
)
542 string key
= name
+ "!!" + type
; // compound key
543 StLock
<Mutex
> _(mLock
); // lock for cache access
544 FragMap::const_iterator it
= mFragments
.find(key
);
545 if (it
== mFragments
.end()) {
546 CFRef
<CFDataRef
> fragData
; // will always be set (NULL on any errors)
547 if (CFRef
<CFURLRef
> fragURL
= CFBundleCopyResourceURL(mMyBundle
, CFTempString(name
), CFSTR("csreq"), CFTempString(type
)))
548 if (CFRef
<CFDataRef
> data
= cfLoadFile(fragURL
)) { // got data
549 const Requirement
*req
= (const Requirement
*)CFDataGetBytePtr(data
);
550 if (req
->validateBlob(CFDataGetLength(data
))) // looks like a Requirement...
551 fragData
= data
; // ... so accept it
553 Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL
).c_str());
555 if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED())
556 CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type
, name
.c_str(), fragData
? CFDataGetBytePtr(fragData
) : NULL
);
557 mFragments
[key
] = fragData
; // cache it, success or failure
560 CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type
, name
.c_str());