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 <Security/SecTrustSettingsPriv.h>
29 #include <Security/SecCertificatePriv.h>
30 #include <security_utilities/memutils.h>
31 #include <security_cdsa_utilities/cssmdata.h> // for hex encoding
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 switch (op
& ~opFlagMask
) {
66 return getString() == mContext
->directory
->identifier();
71 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
72 return verifyAnchor(cert
, getSHA1());
74 case opInfoKeyValue
: // [legacy; use opInfoKeyField]
76 string key
= getString();
77 return infoKeyValue(key
, Match(CFTempString(getString()), matchEqual
));
80 return evaluate() && evaluate();
82 return evaluate() || evaluate();
86 hash(mContext
->directory
, mContext
->directory
->length());
87 return hash
.verify(getHash());
93 string key
= getString();
95 return infoKeyValue(key
, match
);
99 SecCertificateRef cert
= mContext
->cert(get
<int32_t>());
100 string key
= getString();
102 return certFieldValue(key
, match
, cert
);
105 return trustedCert(get
<int32_t>());
107 return trustedCerts();
109 // opcode not recognized - handle generically if possible, fail otherwise
110 if (op
& (opGenericFalse
| opGenericSkip
)) {
111 // unknown opcode, but it has a size field and can be safely bypassed
112 skip(get
<uint32_t>());
113 if (op
& opGenericFalse
) {
114 secdebug("csinterp", "opcode 0x%x interpreted as false", op
);
117 secdebug("csinterp", "opcode 0x%x ignored; continuing", op
);
121 // unrecognized opcode and no way to interpret it
122 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op
);
123 MacOSError::throwMe(errSecCSUnimplemented
);
129 // Evaluate an Info.plist key condition
131 bool Requirement::Interpreter::infoKeyValue(const string
&key
, const Match
&match
)
133 if (mContext
->info
) // we have an Info.plist
134 if (CFTypeRef value
= CFDictionaryGetValue(mContext
->info
, CFTempString(key
)))
140 bool Requirement::Interpreter::certFieldValue(const string
&key
, const Match
&match
, SecCertificateRef cert
)
142 // no cert, no chance
146 // a table of recognized keys for the "certificate[foo]" syntax
147 static const struct CertField
{
151 { "subject.C", &CSSMOID_CountryName
},
152 { "subject.CN", &CSSMOID_CommonName
},
153 { "subject.D", &CSSMOID_Description
},
154 { "subject.L", &CSSMOID_LocalityName
},
155 { "subject.O", &CSSMOID_OrganizationName
},
156 { "subject.OU", &CSSMOID_OrganizationalUnitName
},
157 { "subject.ST", &CSSMOID_StateProvinceName
},
158 { "subject.STREET", &CSSMOID_StreetAddress
},
162 // email multi-valued match (any of...)
163 if (key
== "email") {
164 CFRef
<CFArrayRef
> value
;
165 if (IFDEBUG(OSStatus rc
=) SecCertificateCopyEmailAddresses(cert
, &value
.aref())) {
166 secdebug("csinterp", "cert %p lookup for email failed rc=%ld", cert
, rc
);
172 // DN-component single-value match
173 for (const CertField
*cf
= certFields
; cf
->name
; cf
++)
174 if (cf
->name
== key
) {
175 CFRef
<CFStringRef
> value
;
176 if (IFDEBUG(OSStatus rc
=) SecCertificateCopySubjectComponent(cert
, cf
->oid
, &value
.aref())) {
177 secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%ld", cert
, key
.c_str(), rc
);
183 // unrecognized key. Fail but do not abort to promote backward compatibility down the road
184 secdebug("csinterp", "cert field notation \"%s\" not understood", key
.c_str());
190 // Check the Apple-signed condition
192 bool Requirement::Interpreter::appleSigned()
194 if (SecCertificateRef cert
= mContext
->cert(anchorCert
))
195 if (verifyAnchor(cert
, appleAnchorHash())
196 #if defined(TEST_APPLE_ANCHOR)
197 || verifyAnchor(cert
, testAppleAnchorHash())
200 if (SecCertificateRef intermed
= mContext
->cert(-2)) // first intermediate
201 // first intermediate common name match (exact)
202 if (certFieldValue("subject.CN", Match(appleIntermediateCN
, matchEqual
), intermed
)
203 && certFieldValue("subject.O", Match(appleIntermediateO
, matchEqual
), intermed
))
210 // Verify an anchor requirement against the context
212 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert
, const unsigned char *digest
)
214 // get certificate bytes
217 MacOSError::check(SecCertificateGetData(cert
, &certData
));
220 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
222 hasher(certData
.Data
, certData
.Length
);
223 return hasher
.verify(digest
);
230 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
232 bool Requirement::Interpreter::trustedCerts()
234 int anchor
= mContext
->certCount() - 1;
235 for (int slot
= 0; slot
<= anchor
; slot
++)
236 if (SecCertificateRef cert
= mContext
->cert(slot
))
237 switch (trustSetting(cert
, slot
== anchor
)) {
238 case kSecTrustSettingsResultTrustRoot
:
239 case kSecTrustSettingsResultTrustAsRoot
:
241 case kSecTrustSettingsResultDeny
:
243 case kSecTrustSettingsResultUnspecified
:
254 bool Requirement::Interpreter::trustedCert(int slot
)
256 if (SecCertificateRef cert
= mContext
->cert(slot
)) {
257 int anchorSlot
= mContext
->certCount() - 1;
258 switch (trustSetting(cert
, slot
== anchorCert
|| slot
== anchorSlot
)) {
259 case kSecTrustSettingsResultTrustRoot
:
260 case kSecTrustSettingsResultTrustAsRoot
:
262 case kSecTrustSettingsResultDeny
:
263 case kSecTrustSettingsResultUnspecified
:
275 // Explicitly check one certificate against the Trust Settings database and report
276 // the findings. This is a helper for the various Trust Settings evaluators.
278 SecTrustSettingsResult
Requirement::Interpreter::trustSetting(SecCertificateRef cert
, bool isAnchor
)
280 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
283 hashOfCertificate(cert
, digest
);
284 string Certhex
= CssmData(digest
, sizeof(digest
)).toHex();
285 for (string::iterator it
= Certhex
.begin(); it
!= Certhex
.end(); ++it
)
289 // call Trust Settings and see what it finds
290 SecTrustSettingsDomain domain
;
291 SecTrustSettingsResult result
;
292 CSSM_RETURN
*errors
= NULL
;
293 uint32 errorCount
= 0;
294 bool foundMatch
, foundAny
;
295 switch (OSStatus rc
= SecTrustSettingsEvaluateCert(
296 CFTempString(Certhex
), // settings index
297 &CSSMOID_APPLE_TP_CODE_SIGNING
, // standard code signing policy
298 NULL
, 0, // policy string (unused)
299 kSecTrustSettingsKeyUseAny
, // no restriction on key usage @@@
300 isAnchor
, // consult system default anchor set
302 &domain
, // domain of found setting
303 &errors
, &errorCount
, // error set and maximum count
304 &result
, // the actual setting
305 &foundMatch
, &foundAny
// optimization hints (not used)
312 return kSecTrustSettingsResultUnspecified
;
315 MacOSError::throwMe(rc
);
321 // Create a Match object from the interpreter stream
323 Requirement::Interpreter::Match::Match(Interpreter
&interp
)
325 switch (mOp
= interp
.get
<MatchOperation
>()) {
330 mValue
= makeCFString(interp
.getString());
340 // Execute a match against a candidate value
342 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate
) const
344 // null candidates always fail
348 // interpret an array as matching alternatives (any one succeeds)
349 if (CFGetTypeID(candidate
) == CFArrayGetTypeID()) {
350 CFArrayRef array
= CFArrayRef(candidate
);
351 CFIndex count
= CFArrayGetCount(array
);
352 for (CFIndex n
= 0; n
< count
; n
++)
353 if ((*this)(CFArrayGetValueAtIndex(array
, n
))) // yes, it's recursive
358 case matchExists
: // anything but NULL and boolean false "exists"
359 return !CFEqual(candidate
, kCFBooleanFalse
);
360 case matchEqual
: // equality works for all CF types
361 return CFEqual(candidate
, mValue
);
363 if (CFGetTypeID(candidate
) == CFStringGetTypeID()) {
364 CFStringRef value
= CFStringRef(candidate
);
365 if (CFStringFindWithOptions(value
, mValue
, CFRangeMake(0, CFStringGetLength(value
)), 0, NULL
))