]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/reqinterp.cpp
f8b39d3b9f2b558f22ff4fc3b13427a26a5e1816
[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 <security_utilities/logging.h>
33 #include "csutilities.h"
34
35 namespace Security {
36 namespace CodeSigning {
37
38
39 //
40 // Fragment fetching, caching, and evaluation.
41 //
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.
45 //
46 // This is a singleton for (process global) caching. It works fine as multiple instances,
47 // at a loss of caching effectiveness.
48 //
49 class Fragments {
50 public:
51 Fragments();
52
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); }
57
58 private:
59 bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx);
60 CFDataRef fragment(const char *type, const std::string &name);
61
62 typedef std::map<std::string, CFRef<CFDataRef> > FragMap;
63
64 private:
65 CFBundleRef mMyBundle; // Security.framework bundle
66 Mutex mLock; // lock for all of the below...
67 FragMap mFragments; // cached fragments
68 };
69
70 static ModuleNexus<Fragments> fragments;
71
72
73 //
74 // Magic certificate features
75 //
76 static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
77 static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
78
79
80 //
81 // Main interpreter function.
82 //
83 // ExprOp code is in Polish Notation (operator followed by operands),
84 // and this engine uses opportunistic evaluation.
85 //
86 bool Requirement::Interpreter::evaluate()
87 {
88 ExprOp op = ExprOp(get<uint32_t>());
89 CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t));
90 switch (op & ~opFlagMask) {
91 case opFalse:
92 return false;
93 case opTrue:
94 return true;
95 case opIdent:
96 return getString() == mContext->directory->identifier();
97 case opAppleAnchor:
98 return appleSigned();
99 case opAppleGenericAnchor:
100 return appleAnchored();
101 case opAnchorHash:
102 {
103 SecCertificateRef cert = mContext->cert(get<int32_t>());
104 return verifyAnchor(cert, getSHA1());
105 }
106 case opInfoKeyValue: // [legacy; use opInfoKeyField]
107 {
108 string key = getString();
109 return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
110 }
111 case opAnd:
112 return evaluate() & evaluate();
113 case opOr:
114 return evaluate() | evaluate();
115 case opCDHash:
116 {
117 SHA1 hash;
118 hash(mContext->directory, mContext->directory->length());
119 return hash.verify(getHash());
120 }
121 case opNot:
122 return !evaluate();
123 case opInfoKeyField:
124 {
125 string key = getString();
126 Match match(*this);
127 return infoKeyValue(key, match);
128 }
129 case opEntitlementField:
130 {
131 string key = getString();
132 Match match(*this);
133 return entitlementValue(key, match);
134 }
135 case opCertField:
136 {
137 SecCertificateRef cert = mContext->cert(get<int32_t>());
138 string key = getString();
139 Match match(*this);
140 return certFieldValue(key, match, cert);
141 }
142 case opCertGeneric:
143 {
144 SecCertificateRef cert = mContext->cert(get<int32_t>());
145 string key = getString();
146 Match match(*this);
147 return certFieldGeneric(key, match, cert);
148 }
149 case opCertPolicy:
150 {
151 SecCertificateRef cert = mContext->cert(get<int32_t>());
152 string key = getString();
153 Match match(*this);
154 return certFieldPolicy(key, match, cert);
155 }
156 case opTrustedCert:
157 return trustedCert(get<int32_t>());
158 case opTrustedCerts:
159 return trustedCerts();
160 case opNamedAnchor:
161 return fragments().namedAnchor(getString(), *mContext);
162 case opNamedCode:
163 return fragments().named(getString(), *mContext);
164 default:
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);
171 return false;
172 } else {
173 CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
174 return evaluate();
175 }
176 }
177 // unrecognized opcode and no way to interpret it
178 secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op);
179 MacOSError::throwMe(errSecCSUnimplemented);
180 }
181 }
182
183
184 //
185 // Evaluate an Info.plist key condition
186 //
187 bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
188 {
189 if (mContext->info) // we have an Info.plist
190 if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
191 return match(value);
192 return false;
193 }
194
195
196 //
197 // Evaluate an entitlement condition
198 //
199 bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
200 {
201 if (mContext->entitlements) // we have an Info.plist
202 if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
203 return match(value);
204 return false;
205 }
206
207
208 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
209 {
210 // no cert, no chance
211 if (cert == NULL)
212 return false;
213
214 // a table of recognized keys for the "certificate[foo]" syntax
215 static const struct CertField {
216 const char *name;
217 const CSSM_OID *oid;
218 } certFields[] = {
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 },
227 { NULL, NULL }
228 };
229
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);
236 return false;
237 }
238 return match(value);
239 }
240
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);
246 return false;
247 }
248 return match(value);
249 }
250
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());
253 return false;
254 }
255
256
257 bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
258 {
259 // the key is actually a (binary) OID value
260 CssmOid oid((char *)key.data(), key.length());
261 return certFieldGeneric(oid, match, cert);
262 }
263
264 bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
265 {
266 return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue);
267 }
268
269 bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
270 {
271 // the key is actually a (binary) OID value
272 CssmOid oid((char *)key.data(), key.length());
273 return certFieldPolicy(oid, match, cert);
274 }
275
276 bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
277 {
278 return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue);
279 }
280
281
282 //
283 // Check the Apple-signed condition
284 //
285 bool Requirement::Interpreter::appleAnchored()
286 {
287 if (SecCertificateRef cert = mContext->cert(anchorCert))
288 if (verifyAnchor(cert, appleAnchorHash())
289 #if defined(TEST_APPLE_ANCHOR)
290 || verifyAnchor(cert, testAppleAnchorHash())
291 #endif
292 )
293 return true;
294 return false;
295 }
296
297 bool Requirement::Interpreter::appleSigned()
298 {
299 if (appleAnchored())
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))
304 return true;
305 return false;
306 }
307
308
309 //
310 // Verify an anchor requirement against the context
311 //
312 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
313 {
314 // get certificate bytes
315 if (cert) {
316 CSSM_DATA certData;
317 MacOSError::check(SecCertificateGetData(cert, &certData));
318
319 // verify hash
320 //@@@ should get SHA1(cert(-1).data) precalculated during chain verification
321 SHA1 hasher;
322 hasher(certData.Data, certData.Length);
323 return hasher.verify(digest);
324 }
325 return false;
326 }
327
328
329 //
330 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
331 //
332 bool Requirement::Interpreter::trustedCerts()
333 {
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:
340 return true;
341 case kSecTrustSettingsResultDeny:
342 return false;
343 case kSecTrustSettingsResultUnspecified:
344 break;
345 default:
346 assert(false);
347 return false;
348 }
349 else
350 return false;
351 return false;
352 }
353
354 bool Requirement::Interpreter::trustedCert(int slot)
355 {
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:
361 return true;
362 case kSecTrustSettingsResultDeny:
363 case kSecTrustSettingsResultUnspecified:
364 return false;
365 default:
366 assert(false);
367 return false;
368 }
369 } else
370 return false;
371 }
372
373
374 //
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.
377 //
378 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
379 {
380 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
381 assert(cert);
382 SHA1::Digest digest;
383 hashOfCertificate(cert, digest);
384 string Certhex = CssmData(digest, sizeof(digest)).toHex();
385 for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
386 if (islower(*it))
387 *it = toupper(*it);
388
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
401
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)
406 )) {
407 case noErr:
408 ::free(errors);
409 if (foundMatch)
410 return result;
411 else
412 return kSecTrustSettingsResultUnspecified;
413 default:
414 ::free(errors);
415 MacOSError::throwMe(rc);
416 }
417 }
418
419
420 //
421 // Create a Match object from the interpreter stream
422 //
423 Requirement::Interpreter::Match::Match(Interpreter &interp)
424 {
425 switch (mOp = interp.get<MatchOperation>()) {
426 case matchExists:
427 break;
428 case matchEqual:
429 case matchContains:
430 case matchBeginsWith:
431 case matchEndsWith:
432 case matchLessThan:
433 case matchGreaterThan:
434 case matchLessEqual:
435 case matchGreaterEqual:
436 mValue.take(makeCFString(interp.getString()));
437 break;
438 default:
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
442 break;
443 }
444 }
445
446
447 //
448 // Execute a match against a candidate value
449 //
450 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
451 {
452 // null candidates always fail
453 if (!candidate)
454 return false;
455
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
462 return true;
463 }
464
465 switch (mOp) {
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);
470 case matchContains:
471 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
472 CFStringRef value = CFStringRef(candidate);
473 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
474 return true;
475 }
476 return false;
477 case matchBeginsWith:
478 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
479 CFStringRef value = CFStringRef(candidate);
480 if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL))
481 return true;
482 }
483 return false;
484 case matchEndsWith:
485 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
486 CFStringRef value = CFStringRef(candidate);
487 CFIndex matchLength = CFStringGetLength(mValue);
488 CFIndex start = CFStringGetLength(value) - matchLength;
489 if (start >= 0)
490 if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL))
491 return true;
492 }
493 return false;
494 case matchLessThan:
495 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
496 case matchGreaterThan:
497 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
498 case matchLessEqual:
499 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
500 case matchGreaterEqual:
501 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
502 default:
503 // unrecognized match types can never match
504 return false;
505 }
506 }
507
508
509 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
510 CFComparisonResult outcome, bool negate) const
511 {
512 if (CFGetTypeID(candidate) == CFStringGetTypeID()) {
513 CFStringRef value = CFStringRef(candidate);
514 if ((CFStringCompare(value, mValue, flags) == outcome) == negate)
515 return true;
516 }
517 return false;
518 }
519
520
521 //
522 // External fragments
523 //
524 Fragments::Fragments()
525 {
526 mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
527 }
528
529
530 bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx)
531 {
532 if (CFDataRef fragData = fragment(type, name)) {
533 const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData); // was prevalidated as Requirement
534 return req->validates(ctx);
535 }
536 return false;
537 }
538
539
540 CFDataRef Fragments::fragment(const char *type, const std::string &name)
541 {
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
552 else
553 Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str());
554 }
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
558 return fragData;
559 }
560 CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str());
561 return it->second;
562 }
563
564
565 } // CodeSigning
566 } // Security