]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/reqinterp.cpp
207efd88fdc26ade29f88a3786188f5085f4f9af
[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 "codesigning_dtrace.h"
29 #include <Security/SecTrustSettingsPriv.h>
30 #include <Security/SecCertificatePriv.h>
31 #include <security_utilities/memutils.h>
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 CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t));
61 switch (op & ~opFlagMask) {
62 case opFalse:
63 return false;
64 case opTrue:
65 return true;
66 case opIdent:
67 return getString() == mContext->directory->identifier();
68 case opAppleAnchor:
69 return appleSigned();
70 case opAppleGenericAnchor:
71 return appleAnchored();
72 case opAnchorHash:
73 {
74 SecCertificateRef cert = mContext->cert(get<int32_t>());
75 return verifyAnchor(cert, getSHA1());
76 }
77 case opInfoKeyValue: // [legacy; use opInfoKeyField]
78 {
79 string key = getString();
80 return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
81 }
82 case opAnd:
83 return evaluate() & evaluate();
84 case opOr:
85 return evaluate() | evaluate();
86 case opCDHash:
87 {
88 SHA1 hash;
89 hash(mContext->directory, mContext->directory->length());
90 return hash.verify(getHash());
91 }
92 case opNot:
93 return !evaluate();
94 case opInfoKeyField:
95 {
96 string key = getString();
97 Match match(*this);
98 return infoKeyValue(key, match);
99 }
100 case opEntitlementField:
101 {
102 string key = getString();
103 Match match(*this);
104 return entitlementValue(key, match);
105 }
106 case opCertField:
107 {
108 SecCertificateRef cert = mContext->cert(get<int32_t>());
109 string key = getString();
110 Match match(*this);
111 return certFieldValue(key, match, cert);
112 }
113 case opCertGeneric:
114 {
115 SecCertificateRef cert = mContext->cert(get<int32_t>());
116 string key = getString();
117 Match match(*this);
118 return certFieldGeneric(key, match, cert);
119 }
120 case opTrustedCert:
121 return trustedCert(get<int32_t>());
122 case opTrustedCerts:
123 return trustedCerts();
124 default:
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);
131 return false;
132 } else {
133 CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
134 return evaluate();
135 }
136 }
137 // unrecognized opcode and no way to interpret it
138 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
139 MacOSError::throwMe(errSecCSUnimplemented);
140 }
141 }
142
143
144 //
145 // Evaluate an Info.plist key condition
146 //
147 bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
148 {
149 if (mContext->info) // we have an Info.plist
150 if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
151 return match(value);
152 return false;
153 }
154
155
156 //
157 // Evaluate an entitlement condition
158 //
159 bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
160 {
161 if (mContext->entitlements) // we have an Info.plist
162 if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
163 return match(value);
164 return false;
165 }
166
167
168 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
169 {
170 // no cert, no chance
171 if (cert == NULL)
172 return false;
173
174 // a table of recognized keys for the "certificate[foo]" syntax
175 static const struct CertField {
176 const char *name;
177 const CSSM_OID *oid;
178 } certFields[] = {
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 },
187 { NULL, NULL }
188 };
189
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);
196 return false;
197 }
198 return match(value);
199 }
200
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);
206 return false;
207 }
208 return match(value);
209 }
210
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());
213 return false;
214 }
215
216
217 bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
218 {
219 // the key is actually a (binary) OID value
220 CssmOid oid((char *)key.data(), key.length());
221 return certFieldGeneric(oid, match, cert);
222 }
223
224 bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
225 {
226 return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue);
227 }
228
229
230 //
231 // Check the Apple-signed condition
232 //
233 bool Requirement::Interpreter::appleAnchored()
234 {
235 if (SecCertificateRef cert = mContext->cert(anchorCert))
236 if (verifyAnchor(cert, appleAnchorHash())
237 #if defined(TEST_APPLE_ANCHOR)
238 || verifyAnchor(cert, testAppleAnchorHash())
239 #endif
240 )
241 return true;
242 return false;
243 }
244
245 bool Requirement::Interpreter::appleSigned()
246 {
247 if (appleAnchored())
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))
252 return true;
253 return false;
254 }
255
256
257 //
258 // Verify an anchor requirement against the context
259 //
260 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
261 {
262 // get certificate bytes
263 if (cert) {
264 CSSM_DATA certData;
265 MacOSError::check(SecCertificateGetData(cert, &certData));
266
267 // verify hash
268 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
269 SHA1 hasher;
270 hasher(certData.Data, certData.Length);
271 return hasher.verify(digest);
272 }
273 return false;
274 }
275
276
277 //
278 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
279 //
280 bool Requirement::Interpreter::trustedCerts()
281 {
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:
288 return true;
289 case kSecTrustSettingsResultDeny:
290 return false;
291 case kSecTrustSettingsResultUnspecified:
292 break;
293 default:
294 assert(false);
295 return false;
296 }
297 else
298 return false;
299 return false;
300 }
301
302 bool Requirement::Interpreter::trustedCert(int slot)
303 {
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:
309 return true;
310 case kSecTrustSettingsResultDeny:
311 case kSecTrustSettingsResultUnspecified:
312 return false;
313 default:
314 assert(false);
315 return false;
316 }
317 } else
318 return false;
319 }
320
321
322 //
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.
325 //
326 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
327 {
328 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
329 assert(cert);
330 SHA1::Digest digest;
331 hashOfCertificate(cert, digest);
332 string Certhex = CssmData(digest, sizeof(digest)).toHex();
333 for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
334 if (islower(*it))
335 *it = toupper(*it);
336
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
349
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)
354 )) {
355 case noErr:
356 ::free(errors);
357 if (foundMatch)
358 return result;
359 else
360 return kSecTrustSettingsResultUnspecified;
361 default:
362 ::free(errors);
363 MacOSError::throwMe(rc);
364 }
365 }
366
367
368 //
369 // Create a Match object from the interpreter stream
370 //
371 Requirement::Interpreter::Match::Match(Interpreter &interp)
372 {
373 switch (mOp = interp.get<MatchOperation>()) {
374 case matchExists:
375 break;
376 case matchEqual:
377 case matchContains:
378 case matchBeginsWith:
379 case matchEndsWith:
380 case matchLessThan:
381 case matchGreaterThan:
382 case matchLessEqual:
383 case matchGreaterEqual:
384 mValue = makeCFString(interp.getString());
385 break;
386 default:
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
390 break;
391 }
392 }
393
394
395 //
396 // Execute a match against a candidate value
397 //
398 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
399 {
400 // null candidates always fail
401 if (!candidate)
402 return false;
403
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
410 return true;
411 }
412
413 switch (mOp) {
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);
418 case matchContains:
419 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
420 CFStringRef value = CFStringRef(candidate);
421 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
422 return true;
423 }
424 return false;
425 case matchBeginsWith:
426 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
427 CFStringRef value = CFStringRef(candidate);
428 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL))
429 return true;
430 }
431 return false;
432 case matchEndsWith:
433 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
434 CFStringRef value = CFStringRef(candidate);
435 CFIndex matchLength = CFStringGetLength(mValue);
436 CFIndex start = CFStringGetLength(value) - matchLength;
437 if (start >= 0)
438 if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL))
439 return true;
440 }
441 return false;
442 case matchLessThan:
443 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
444 case matchGreaterThan:
445 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
446 case matchLessEqual:
447 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
448 case matchGreaterEqual:
449 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
450 default:
451 // unrecognized match types can never match
452 return false;
453 }
454 }
455
456
457 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
458 CFComparisonResult outcome, bool negate) const
459 {
460 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
461 CFStringRef value = CFStringRef(candidate);
462 if ((CFStringCompare(value, mValue, flags) == outcome) == negate)
463 return true;
464 }
465 return false;
466 }
467
468
469 } // CodeSigning
470 } // Security