]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/reqinterp.cpp
b661b178ba9910af053f99be03cb020354236f10
[apple/libsecurity_codesigning.git] / lib / reqinterp.cpp
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // reqinterp - Requirement language (exprOp) interpreter
26 //
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"
33
34 namespace Security {
35 namespace CodeSigning {
36
37
38 static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
39 static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
40
41
42 //
43 // Construct an interpreter given a Requirement and an evaluation context.
44 //
45 Requirement::Interpreter::Interpreter(const Requirement *req, const Context *ctx)
46 : Reader(req), mContext(ctx)
47 {
48 }
49
50
51 //
52 // Main interpreter function.
53 //
54 // ExprOp code is in Polish Notation (operator followed by operands),
55 // and this engine uses opportunistic evaluation.
56 //
57 bool Requirement::Interpreter::evaluate()
58 {
59 ExprOp op = ExprOp(get<uint32_t>());
60 switch (op & ~opFlagMask) {
61 case opFalse:
62 return false;
63 case opTrue:
64 return true;
65 case opIdent:
66 return getString() == mContext->directory->identifier();
67 case opAppleAnchor:
68 return appleSigned();
69 case opAnchorHash:
70 {
71 SecCertificateRef cert = mContext->cert(get<int32_t>());
72 return verifyAnchor(cert, getSHA1());
73 }
74 case opInfoKeyValue: // [legacy; use opInfoKeyField]
75 {
76 string key = getString();
77 return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
78 }
79 case opAnd:
80 return evaluate() && evaluate();
81 case opOr:
82 return evaluate() || evaluate();
83 case opCDHash:
84 {
85 SHA1 hash;
86 hash(mContext->directory, mContext->directory->length());
87 return hash.verify(getHash());
88 }
89 case opNot:
90 return !evaluate();
91 case opInfoKeyField:
92 {
93 string key = getString();
94 Match match(*this);
95 return infoKeyValue(key, match);
96 }
97 case opCertField:
98 {
99 SecCertificateRef cert = mContext->cert(get<int32_t>());
100 string key = getString();
101 Match match(*this);
102 return certFieldValue(key, match, cert);
103 }
104 case opTrustedCert:
105 return trustedCert(get<int32_t>());
106 case opTrustedCerts:
107 return trustedCerts();
108 default:
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);
115 return false;
116 } else {
117 secdebug("csinterp", "opcode 0x%x ignored; continuing", op);
118 return evaluate();
119 }
120 }
121 // unrecognized opcode and no way to interpret it
122 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
123 MacOSError::throwMe(errSecCSUnimplemented);
124 }
125 }
126
127
128 //
129 // Evaluate an Info.plist key condition
130 //
131 bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
132 {
133 if (mContext->info) // we have an Info.plist
134 if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
135 return match(value);
136 return false;
137 }
138
139
140 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
141 {
142 // no cert, no chance
143 if (cert == NULL)
144 return false;
145
146 // a table of recognized keys for the "certificate[foo]" syntax
147 static const struct CertField {
148 const char *name;
149 const CSSM_OID *oid;
150 } certFields[] = {
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 },
159 { NULL, NULL }
160 };
161
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);
167 return false;
168 }
169 return match(value);
170 }
171
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);
178 return false;
179 }
180 return match(value);
181 }
182
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());
185 return false;
186 }
187
188
189 //
190 // Check the Apple-signed condition
191 //
192 bool Requirement::Interpreter::appleSigned()
193 {
194 if (SecCertificateRef cert = mContext->cert(anchorCert))
195 if (verifyAnchor(cert, appleAnchorHash())
196 #if defined(TEST_APPLE_ANCHOR)
197 || verifyAnchor(cert, testAppleAnchorHash())
198 #endif
199 )
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))
204 return true;
205 return false;
206 }
207
208
209 //
210 // Verify an anchor requirement against the context
211 //
212 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
213 {
214 // get certificate bytes
215 if (cert) {
216 CSSM_DATA certData;
217 MacOSError::check(SecCertificateGetData(cert, &certData));
218
219 // verify hash
220 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
221 SHA1 hasher;
222 hasher(certData.Data, certData.Length);
223 return hasher.verify(digest);
224 }
225 return false;
226 }
227
228
229 //
230 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
231 //
232 bool Requirement::Interpreter::trustedCerts()
233 {
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:
240 return true;
241 case kSecTrustSettingsResultDeny:
242 return false;
243 case kSecTrustSettingsResultUnspecified:
244 break;
245 default:
246 assert(false);
247 return false;
248 }
249 else
250 return false;
251 return false;
252 }
253
254 bool Requirement::Interpreter::trustedCert(int slot)
255 {
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:
261 return true;
262 case kSecTrustSettingsResultDeny:
263 case kSecTrustSettingsResultUnspecified:
264 return false;
265 default:
266 assert(false);
267 return false;
268 }
269 } else
270 return false;
271 }
272
273
274 //
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.
277 //
278 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
279 {
280 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
281 assert(cert);
282 SHA1::Digest digest;
283 hashOfCertificate(cert, digest);
284 string Certhex = CssmData(digest, sizeof(digest)).toHex();
285 for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
286 if (islower(*it))
287 *it = toupper(*it);
288
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
301
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)
306 )) {
307 case noErr:
308 ::free(errors);
309 if (foundMatch)
310 return result;
311 else
312 return kSecTrustSettingsResultUnspecified;
313 default:
314 ::free(errors);
315 MacOSError::throwMe(rc);
316 }
317 }
318
319
320 //
321 // Create a Match object from the interpreter stream
322 //
323 Requirement::Interpreter::Match::Match(Interpreter &interp)
324 {
325 switch (mOp = interp.get<MatchOperation>()) {
326 case matchExists:
327 break;
328 case matchEqual:
329 case matchContains:
330 mValue = makeCFString(interp.getString());
331 break;
332 default:
333 assert(false);
334 break;
335 }
336 }
337
338
339 //
340 // Execute a match against a candidate value
341 //
342 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
343 {
344 // null candidates always fail
345 if (!candidate)
346 return false;
347
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
354 return true;
355 }
356
357 switch (mOp) {
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);
362 case matchContains:
363 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
364 CFStringRef value = CFStringRef(candidate);
365 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
366 return true;
367 }
368 return false;
369 default:
370 assert(false);
371 return false;
372 }
373 }
374
375
376 } // CodeSigning
377 } // Security