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 "csutilities.h"
35 namespace CodeSigning
{
38 static CFStringRef appleIntermediateCN
= CFSTR("Apple Code Signing Certification Authority");
39 static CFStringRef appleIntermediateO
= CFSTR("Apple Inc.");
43 // Construct an interpreter given a Requirement and an evaluation context.
45 Requirement::Interpreter::Interpreter(const Requirement
*req
, const Context
*ctx
)
46 : Reader(req
), mContext(ctx
)
52 // Main interpreter function.
54 // ExprOp code is in Polish Notation (operator followed by operands),
55 // and this engine uses opportunistic evaluation.
57 bool Requirement::Interpreter::evaluate()
59 ExprOp op
= ExprOp(get
<uint32_t>());
60 CODESIGN_EVAL_REQINT_OP(op
, this->pc() - sizeof(uint32_t));
61 switch (op
& ~opFlagMask
) {
67 return getString() == mContext
->directory
->identifier();
70 case opAppleGenericAnchor
:
71 return appleAnchored();
74 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
75 return verifyAnchor(cert
, getSHA1());
77 case opInfoKeyValue
: // [legacy; use opInfoKeyField]
79 string key
= getString();
80 return infoKeyValue(key
, Match(CFTempString(getString()), matchEqual
));
83 return evaluate() & evaluate();
85 return evaluate() | evaluate();
89 hash(mContext
->directory
, mContext
->directory
->length());
90 return hash
.verify(getHash());
96 string key
= getString();
98 return infoKeyValue(key
, match
);
100 case opEntitlementField
:
102 string key
= getString();
104 return entitlementValue(key
, match
);
108 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
109 string key
= getString();
111 return certFieldValue(key
, match
, cert
);
115 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
116 string key
= getString();
118 return certFieldGeneric(key
, match
, cert
);
121 return trustedCert(get
<int32_t>());
123 return trustedCerts();
125 // opcode not recognized - handle generically if possible, fail otherwise
126 if (op
& (opGenericFalse
| opGenericSkip
)) {
127 // unknown opcode, but it has a size field and can be safely bypassed
128 skip(get
<uint32_t>());
129 if (op
& opGenericFalse
) {
130 CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op
);
133 CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op
);
137 // unrecognized opcode and no way to interpret it
138 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op
);
139 MacOSError::throwMe(errSecCSUnimplemented
);
145 // Evaluate an Info.plist key condition
147 bool Requirement::Interpreter::infoKeyValue(const string
&key
, const Match
&match
)
149 if (mContext
->info
) // we have an Info.plist
150 if (CFTypeRef value
= CFDictionaryGetValue(mContext
->info
, CFTempString(key
)))
157 // Evaluate an entitlement condition
159 bool Requirement::Interpreter::entitlementValue(const string
&key
, const Match
&match
)
161 if (mContext
->entitlements
) // we have an Info.plist
162 if (CFTypeRef value
= CFDictionaryGetValue(mContext
->entitlements
, CFTempString(key
)))
168 bool Requirement::Interpreter::certFieldValue(const string
&key
, const Match
&match
, SecCertificateRef cert
)
170 // no cert, no chance
174 // a table of recognized keys for the "certificate[foo]" syntax
175 static const struct CertField
{
179 { "subject.C", &CSSMOID_CountryName
},
180 { "subject.CN", &CSSMOID_CommonName
},
181 { "subject.D", &CSSMOID_Description
},
182 { "subject.L", &CSSMOID_LocalityName
},
183 { "subject.O", &CSSMOID_OrganizationName
},
184 { "subject.OU", &CSSMOID_OrganizationalUnitName
},
185 { "subject.ST", &CSSMOID_StateProvinceName
},
186 { "subject.STREET", &CSSMOID_StreetAddress
},
190 // DN-component single-value match
191 for (const CertField
*cf
= certFields
; cf
->name
; cf
++)
192 if (cf
->name
== key
) {
193 CFRef
<CFStringRef
> value
;
194 if (OSStatus rc
= SecCertificateCopySubjectComponent(cert
, cf
->oid
, &value
.aref())) {
195 secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert
, key
.c_str(), rc
);
201 // email multi-valued match (any of...)
202 if (key
== "email") {
203 CFRef
<CFArrayRef
> value
;
204 if (OSStatus rc
= SecCertificateCopyEmailAddresses(cert
, &value
.aref())) {
205 secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert
, rc
);
211 // unrecognized key. Fail but do not abort to promote backward compatibility down the road
212 secdebug("csinterp", "cert field notation \"%s\" not understood", key
.c_str());
217 bool Requirement::Interpreter::certFieldGeneric(const string
&key
, const Match
&match
, SecCertificateRef cert
)
219 // the key is actually a (binary) OID value
220 CssmOid
oid((char *)key
.data(), key
.length());
221 return certFieldGeneric(oid
, match
, cert
);
224 bool Requirement::Interpreter::certFieldGeneric(const CssmOid
&oid
, const Match
&match
, SecCertificateRef cert
)
226 return cert
&& certificateHasField(cert
, oid
) && match(kCFBooleanTrue
);
231 // Check the Apple-signed condition
233 bool Requirement::Interpreter::appleAnchored()
235 if (SecCertificateRef cert
= mContext
->cert(anchorCert
))
236 if (verifyAnchor(cert
, appleAnchorHash())
237 #if defined(TEST_APPLE_ANCHOR)
238 || verifyAnchor(cert
, testAppleAnchorHash())
245 bool Requirement::Interpreter::appleSigned()
248 if (SecCertificateRef intermed
= mContext
->cert(-2)) // first intermediate
249 // first intermediate common name match (exact)
250 if (certFieldValue("subject.CN", Match(appleIntermediateCN
, matchEqual
), intermed
)
251 && certFieldValue("subject.O", Match(appleIntermediateO
, matchEqual
), intermed
))
258 // Verify an anchor requirement against the context
260 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert
, const unsigned char *digest
)
262 // get certificate bytes
265 MacOSError::check(SecCertificateGetData(cert
, &certData
));
268 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
270 hasher(certData
.Data
, certData
.Length
);
271 return hasher
.verify(digest
);
278 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
280 bool Requirement::Interpreter::trustedCerts()
282 int anchor
= mContext
->certCount() - 1;
283 for (int slot
= 0; slot
<= anchor
; slot
++)
284 if (SecCertificateRef cert
= mContext
->cert(slot
))
285 switch (trustSetting(cert
, slot
== anchor
)) {
286 case kSecTrustSettingsResultTrustRoot
:
287 case kSecTrustSettingsResultTrustAsRoot
:
289 case kSecTrustSettingsResultDeny
:
291 case kSecTrustSettingsResultUnspecified
:
302 bool Requirement::Interpreter::trustedCert(int slot
)
304 if (SecCertificateRef cert
= mContext
->cert(slot
)) {
305 int anchorSlot
= mContext
->certCount() - 1;
306 switch (trustSetting(cert
, slot
== anchorCert
|| slot
== anchorSlot
)) {
307 case kSecTrustSettingsResultTrustRoot
:
308 case kSecTrustSettingsResultTrustAsRoot
:
310 case kSecTrustSettingsResultDeny
:
311 case kSecTrustSettingsResultUnspecified
:
323 // Explicitly check one certificate against the Trust Settings database and report
324 // the findings. This is a helper for the various Trust Settings evaluators.
326 SecTrustSettingsResult
Requirement::Interpreter::trustSetting(SecCertificateRef cert
, bool isAnchor
)
328 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
331 hashOfCertificate(cert
, digest
);
332 string Certhex
= CssmData(digest
, sizeof(digest
)).toHex();
333 for (string::iterator it
= Certhex
.begin(); it
!= Certhex
.end(); ++it
)
337 // call Trust Settings and see what it finds
338 SecTrustSettingsDomain domain
;
339 SecTrustSettingsResult result
;
340 CSSM_RETURN
*errors
= NULL
;
341 uint32 errorCount
= 0;
342 bool foundMatch
, foundAny
;
343 switch (OSStatus rc
= SecTrustSettingsEvaluateCert(
344 CFTempString(Certhex
), // settings index
345 &CSSMOID_APPLE_TP_CODE_SIGNING
, // standard code signing policy
346 NULL
, 0, // policy string (unused)
347 kSecTrustSettingsKeyUseAny
, // no restriction on key usage @@@
348 isAnchor
, // consult system default anchor set
350 &domain
, // domain of found setting
351 &errors
, &errorCount
, // error set and maximum count
352 &result
, // the actual setting
353 &foundMatch
, &foundAny
// optimization hints (not used)
360 return kSecTrustSettingsResultUnspecified
;
363 MacOSError::throwMe(rc
);
369 // Create a Match object from the interpreter stream
371 Requirement::Interpreter::Match::Match(Interpreter
&interp
)
373 switch (mOp
= interp
.get
<MatchOperation
>()) {
378 case matchBeginsWith
:
381 case matchGreaterThan
:
383 case matchGreaterEqual
:
384 mValue
= makeCFString(interp
.getString());
387 // Assume this (unknown) match type has a single data argument.
388 // This gives us a chance to keep the instruction stream aligned.
389 interp
.getString(); // discard
396 // Execute a match against a candidate value
398 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate
) const
400 // null candidates always fail
404 // interpret an array as matching alternatives (any one succeeds)
405 if (CFGetTypeID(candidate
) == CFArrayGetTypeID()) {
406 CFArrayRef array
= CFArrayRef(candidate
);
407 CFIndex count
= CFArrayGetCount(array
);
408 for (CFIndex n
= 0; n
< count
; n
++)
409 if ((*this)(CFArrayGetValueAtIndex(array
, n
))) // yes, it's recursive
414 case matchExists
: // anything but NULL and boolean false "exists"
415 return !CFEqual(candidate
, kCFBooleanFalse
);
416 case matchEqual
: // equality works for all CF types
417 return CFEqual(candidate
, mValue
);
419 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
420 CFStringRef value
= CFStringRef(candidate
);
421 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(0, CFStringGetLength(value
)), 0, NULL
))
425 case matchBeginsWith
:
426 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
427 CFStringRef value
= CFStringRef(candidate
);
428 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(0, CFStringGetLength(mValue
)), 0, NULL
))
433 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
434 CFStringRef value
= CFStringRef(candidate
);
435 CFIndex matchLength
= CFStringGetLength(mValue
);
436 CFIndex start
= CFStringGetLength(value
) - matchLength
;
438 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(start
, matchLength
), 0, NULL
))
443 return inequality(candidate
, kCFCompareNumerically
, kCFCompareLessThan
, true);
444 case matchGreaterThan
:
445 return inequality(candidate
, kCFCompareNumerically
, kCFCompareGreaterThan
, true);
447 return inequality(candidate
, kCFCompareNumerically
, kCFCompareGreaterThan
, false);
448 case matchGreaterEqual
:
449 return inequality(candidate
, kCFCompareNumerically
, kCFCompareLessThan
, false);
451 // unrecognized match types can never match
457 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate
, CFStringCompareFlags flags
,
458 CFComparisonResult outcome
, bool negate
) const
460 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
461 CFStringRef value
= CFStringRef(candidate
);
462 if ((CFStringCompare(value
, mValue
, flags
) == outcome
) == negate
)