]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/reqinterp.cpp
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / reqinterp.cpp
1 /*
2 * Copyright (c) 2006,2011-2014 Apple 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
28 #include "reqinterp.h"
29 #include "codesigning_dtrace.h"
30 #include <Security/SecTrustSettingsPriv.h>
31 #include <Security/SecCertificatePriv.h>
32 #include <security_utilities/memutils.h>
33 #include <security_utilities/logging.h>
34 #include <sys/csr.h>
35 #include <IOKit/IOKitLib.h>
36 #include <IOKit/IOCFUnserialize.h>
37 #include "csutilities.h"
38 #include "notarization.h"
39
40 namespace Security {
41 namespace CodeSigning {
42
43
44 //
45 // Fragment fetching, caching, and evaluation.
46 //
47 // Several language elements allow "calling" of separate requirement programs
48 // stored on disk as (binary) requirement blobs. The Fragments class takes care
49 // of finding, loading, caching, and evaluating them.
50 //
51 // This is a singleton for (process global) caching. It works fine as multiple instances,
52 // at a loss of caching effectiveness.
53 //
54 class Fragments {
55 public:
56 Fragments();
57
58 bool named(const std::string &name, const Requirement::Context &ctx)
59 { return evalNamed("subreq", name, ctx); }
60 bool namedAnchor(const std::string &name, const Requirement::Context &ctx)
61 { return evalNamed("anchorreq", name, ctx); }
62
63 private:
64 bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx);
65 CFDataRef fragment(const char *type, const std::string &name);
66
67 typedef std::map<std::string, CFRef<CFDataRef> > FragMap;
68
69 private:
70 CFBundleRef mMyBundle; // Security.framework bundle
71 Mutex mLock; // lock for all of the below...
72 FragMap mFragments; // cached fragments
73 };
74
75 static ModuleNexus<Fragments> fragments;
76
77
78 //
79 // Magic certificate features
80 //
81 static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority");
82 static CFStringRef appleIntermediateO = CFSTR("Apple Inc.");
83
84
85 //
86 // Main interpreter function.
87 //
88 // ExprOp code is in Polish Notation (operator followed by operands),
89 // and this engine uses opportunistic evaluation.
90 //
91 bool Requirement::Interpreter::evaluate()
92 { return eval(stackLimit); }
93
94 bool Requirement::Interpreter::eval(int depth)
95 {
96 if (--depth <= 0) // nested too deeply - protect the stack
97 MacOSError::throwMe(errSecCSReqInvalid);
98
99 ExprOp op = ExprOp(get<uint32_t>());
100 CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t));
101 switch (op & ~opFlagMask) {
102 case opFalse:
103 return false;
104 case opTrue:
105 return true;
106 case opIdent:
107 return mContext->directory && getString() == mContext->directory->identifier();
108 case opAppleAnchor:
109 return appleSigned();
110 case opAppleGenericAnchor:
111 return appleAnchored();
112 case opAnchorHash:
113 {
114 SecCertificateRef cert = mContext->cert(get<int32_t>());
115 return verifyAnchor(cert, getSHA1());
116 }
117 case opInfoKeyValue: // [legacy; use opInfoKeyField]
118 {
119 string key = getString();
120 return infoKeyValue(key, Match(CFTempString(getString()), matchEqual));
121 }
122 case opAnd:
123 return eval(depth) & eval(depth);
124 case opOr:
125 return eval(depth) | eval(depth);
126 case opCDHash:
127 if (mContext->directory) {
128 CFRef<CFDataRef> cdhash = mContext->directory->cdhash();
129 CFRef<CFDataRef> required = getHash();
130 return CFEqual(cdhash, required);
131 } else
132 return false;
133 case opNot:
134 return !eval(depth);
135 case opInfoKeyField:
136 {
137 string key = getString();
138 Match match(*this);
139 return infoKeyValue(key, match);
140 }
141 case opEntitlementField:
142 {
143 string key = getString();
144 Match match(*this);
145 return entitlementValue(key, match);
146 }
147 case opCertField:
148 {
149 SecCertificateRef cert = mContext->cert(get<int32_t>());
150 string key = getString();
151 Match match(*this);
152 return certFieldValue(key, match, cert);
153 }
154 #if TARGET_OS_OSX
155 case opCertGeneric:
156 {
157 SecCertificateRef cert = mContext->cert(get<int32_t>());
158 string key = getString();
159 Match match(*this);
160 return certFieldGeneric(key, match, cert);
161 }
162 case opCertFieldDate:
163 {
164 SecCertificateRef cert = mContext->cert(get<int32_t>());
165 string key = getString();
166 Match match(*this);
167 return certFieldDate(key, match, cert);
168 }
169 case opCertPolicy:
170 {
171 SecCertificateRef cert = mContext->cert(get<int32_t>());
172 string key = getString();
173 Match match(*this);
174 return certFieldPolicy(key, match, cert);
175 }
176 #endif
177 case opTrustedCert:
178 return trustedCert(get<int32_t>());
179 case opTrustedCerts:
180 return trustedCerts();
181 case opNamedAnchor:
182 return fragments().namedAnchor(getString(), *mContext);
183 case opNamedCode:
184 return fragments().named(getString(), *mContext);
185 case opPlatform:
186 {
187 int32_t targetPlatform = get<int32_t>();
188 return mContext->directory && mContext->directory->platform == targetPlatform;
189 }
190 case opNotarized:
191 {
192 return isNotarized(mContext);
193 }
194 default:
195 // opcode not recognized - handle generically if possible, fail otherwise
196 if (op & (opGenericFalse | opGenericSkip)) {
197 // unknown opcode, but it has a size field and can be safely bypassed
198 skip(get<uint32_t>());
199 if (op & opGenericFalse) {
200 CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op);
201 return false;
202 } else {
203 CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op);
204 return eval(depth);
205 }
206 }
207 // unrecognized opcode and no way to interpret it
208 secinfo("csinterp", "opcode 0x%x cannot be handled; aborting", op);
209 MacOSError::throwMe(errSecCSUnimplemented);
210 }
211 }
212
213
214 //
215 // Evaluate an Info.plist key condition
216 //
217 bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match)
218 {
219 if (mContext->info) // we have an Info.plist
220 if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key)))
221 return match(value);
222 return match(kCFNull);
223 }
224
225
226 //
227 // Evaluate an entitlement condition
228 //
229 bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match)
230 {
231 if (mContext->entitlements) // we have an Info.plist
232 if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key)))
233 return match(value);
234 return match(kCFNull);
235 }
236
237
238 bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert)
239 {
240 // XXX: Not supported on embedded yet due to lack of supporting API
241 #if TARGET_OS_OSX
242 // no cert, no chance
243 if (cert == NULL)
244 return false;
245
246 // a table of recognized keys for the "certificate[foo]" syntax
247 static const struct CertField {
248 const char *name;
249 const CSSM_OID *oid;
250 } certFields[] = {
251 { "subject.C", &CSSMOID_CountryName },
252 { "subject.CN", &CSSMOID_CommonName },
253 { "subject.D", &CSSMOID_Description },
254 { "subject.L", &CSSMOID_LocalityName },
255 // { "subject.C-L", &CSSMOID_CollectiveLocalityName }, // missing from Security.framework headers
256 { "subject.O", &CSSMOID_OrganizationName },
257 { "subject.C-O", &CSSMOID_CollectiveOrganizationName },
258 { "subject.OU", &CSSMOID_OrganizationalUnitName },
259 { "subject.C-OU", &CSSMOID_CollectiveOrganizationalUnitName },
260 { "subject.ST", &CSSMOID_StateProvinceName },
261 { "subject.C-ST", &CSSMOID_CollectiveStateProvinceName },
262 { "subject.STREET", &CSSMOID_StreetAddress },
263 { "subject.C-STREET", &CSSMOID_CollectiveStreetAddress },
264 { "subject.UID", &CSSMOID_UserID },
265 { NULL, NULL }
266 };
267
268 // DN-component single-value match
269 for (const CertField *cf = certFields; cf->name; cf++)
270 if (cf->name == key) {
271 CFRef<CFStringRef> value;
272 OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref());
273 if (rc) {
274 secinfo("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc);
275 return false;
276 }
277 return match(value);
278 }
279
280 // email multi-valued match (any of...)
281 if (key == "email") {
282 CFRef<CFArrayRef> value;
283 OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref());
284 if (rc) {
285 secinfo("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc);
286 return false;
287 }
288 return match(value);
289 }
290
291 // unrecognized key. Fail but do not abort to promote backward compatibility down the road
292 secinfo("csinterp", "cert field notation \"%s\" not understood", key.c_str());
293 #endif
294 return false;
295 }
296
297 #if TARGET_OS_OSX
298 bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert)
299 {
300 // the key is actually a (binary) OID value
301 CssmOid oid((char *)key.data(), key.length());
302 return certFieldGeneric(oid, match, cert);
303 }
304
305 bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert)
306 {
307 return cert && match(certificateHasField(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
308 }
309
310 bool Requirement::Interpreter::certFieldDate(const string &key, const Match &match, SecCertificateRef cert)
311 {
312 // the key is actually a (binary) OID value
313 CssmOid oid((char *)key.data(), key.length());
314 return certFieldDate(oid, match, cert);
315 }
316
317 bool Requirement::Interpreter::certFieldDate(const CssmOid &oid, const Match &match, SecCertificateRef cert)
318 {
319 CFTypeRef value = cert != NULL ? certificateCopyFieldDate(cert, oid) : NULL;
320 bool matching = match(value != NULL ? value : kCFNull);
321
322 if (value) {
323 CFRelease(value);
324 }
325
326 return matching;
327 }
328
329 bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert)
330 {
331 // the key is actually a (binary) OID value
332 CssmOid oid((char *)key.data(), key.length());
333 return certFieldPolicy(oid, match, cert);
334 }
335
336 bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert)
337 {
338 return cert && match(certificateHasPolicy(cert, oid) ? (CFTypeRef)kCFBooleanTrue : (CFTypeRef)kCFNull);
339 }
340 #endif
341
342 //
343 // Check the Apple-signed condition
344 //
345 bool Requirement::Interpreter::appleAnchored()
346 {
347 if (SecCertificateRef cert = mContext->cert(anchorCert))
348 if (isAppleCA(cert))
349 return true;
350 return false;
351 }
352
353 static CFStringRef kAMFINVRAMTrustedKeys = CFSTR("AMFITrustedKeys");
354
355 CFArrayRef Requirement::Interpreter::getAdditionalTrustedAnchors()
356 {
357 __block CFRef<CFMutableArrayRef> keys = makeCFMutableArray(0);
358
359 try {
360 io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
361 if (entry == IO_OBJECT_NULL)
362 return NULL;
363
364 CFRef<CFDataRef> configData = (CFDataRef)IORegistryEntryCreateCFProperty(entry, kAMFINVRAMTrustedKeys, kCFAllocatorDefault, 0);
365 IOObjectRelease(entry);
366 if (!configData)
367 return NULL;
368
369 CFRef<CFDictionaryRef> configDict = CFDictionaryRef(IOCFUnserializeWithSize((const char *)CFDataGetBytePtr(configData),
370 (size_t)CFDataGetLength(configData),
371 kCFAllocatorDefault, 0, NULL));
372 if (!configDict)
373 return NULL;
374
375 CFArrayRef trustedKeys = CFArrayRef(CFDictionaryGetValue(configDict, CFSTR("trustedKeys")));
376 if (!trustedKeys && CFGetTypeID(trustedKeys) != CFArrayGetTypeID())
377 return NULL;
378
379 cfArrayApplyBlock(trustedKeys, ^(const void *value) {
380 CFDictionaryRef key = CFDictionaryRef(value);
381 if (!key && CFGetTypeID(key) != CFDictionaryGetTypeID())
382 return;
383
384 CFDataRef hash = CFDataRef(CFDictionaryGetValue(key, CFSTR("certDigest")));
385 if (!hash && CFGetTypeID(hash) != CFDataGetTypeID())
386 return;
387 CFArrayAppendValue(keys, hash);
388 });
389
390 } catch (...) {
391 }
392
393 if (CFArrayGetCount(keys) == 0)
394 return NULL;
395
396 return keys.yield();
397 }
398
399 bool Requirement::Interpreter::appleLocalAnchored()
400 {
401 static CFArrayRef additionalTrustedCertificates = NULL;
402
403 if (csr_check(CSR_ALLOW_APPLE_INTERNAL))
404 return false;
405
406 if (mContext->forcePlatform) {
407 return true;
408 }
409
410 static dispatch_once_t onceToken;
411 dispatch_once(&onceToken, ^{
412 additionalTrustedCertificates = getAdditionalTrustedAnchors();
413 });
414
415 if (additionalTrustedCertificates == NULL)
416 return false;
417
418 CFRef<CFDataRef> hash = SecCertificateCopySHA256Digest(mContext->cert(leafCert));
419 if (!hash)
420 return false;
421
422 if (CFArrayContainsValue(additionalTrustedCertificates, CFRangeMake(0, CFArrayGetCount(additionalTrustedCertificates)), hash))
423 return true;
424
425 return false;
426 }
427
428 bool Requirement::Interpreter::appleSigned()
429 {
430 if (appleAnchored()) {
431 if (SecCertificateRef intermed = mContext->cert(-2)) // first intermediate
432 // first intermediate common name match (exact)
433 if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed)
434 && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed))
435 return true;
436 } else if (appleLocalAnchored()) {
437 return true;
438 }
439 return false;
440 }
441
442
443 //
444 // Verify an anchor requirement against the context
445 //
446 bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest)
447 {
448 // get certificate bytes
449 if (cert) {
450 SHA1 hasher;
451 #if TARGET_OS_OSX
452 CSSM_DATA certData;
453 MacOSError::check(SecCertificateGetData(cert, &certData));
454
455 // verify hash
456 hasher(certData.Data, certData.Length);
457 #else
458 hasher(SecCertificateGetBytePtr(cert), SecCertificateGetLength(cert));
459 #endif
460 return hasher.verify(digest);
461 }
462 return false;
463 }
464
465
466 //
467 // Check one or all certificate(s) in the cert chain against the Trust Settings database.
468 //
469 bool Requirement::Interpreter::trustedCerts()
470 {
471 int anchor = mContext->certCount() - 1;
472 for (int slot = 0; slot <= anchor; slot++)
473 if (SecCertificateRef cert = mContext->cert(slot))
474 switch (trustSetting(cert, slot == anchor)) {
475 case kSecTrustSettingsResultTrustRoot:
476 case kSecTrustSettingsResultTrustAsRoot:
477 return true;
478 case kSecTrustSettingsResultDeny:
479 return false;
480 case kSecTrustSettingsResultUnspecified:
481 break;
482 default:
483 assert(false);
484 return false;
485 }
486 else
487 return false;
488 return false;
489 }
490
491 bool Requirement::Interpreter::trustedCert(int slot)
492 {
493 if (SecCertificateRef cert = mContext->cert(slot)) {
494 int anchorSlot = mContext->certCount() - 1;
495 switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) {
496 case kSecTrustSettingsResultTrustRoot:
497 case kSecTrustSettingsResultTrustAsRoot:
498 return true;
499 case kSecTrustSettingsResultDeny:
500 case kSecTrustSettingsResultUnspecified:
501 return false;
502 default:
503 assert(false);
504 return false;
505 }
506 } else
507 return false;
508 }
509
510
511 //
512 // Explicitly check one certificate against the Trust Settings database and report
513 // the findings. This is a helper for the various Trust Settings evaluators.
514 //
515 SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor)
516 {
517 // XXX: Not supported on embedded yet due to lack of supporting API
518 #if TARGET_OS_OSX
519 // the SPI input is the uppercase hex form of the SHA-1 of the certificate...
520 assert(cert);
521 SHA1::Digest digest;
522 hashOfCertificate(cert, digest);
523 string Certhex = CssmData(digest, sizeof(digest)).toHex();
524 for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it)
525 if (islower(*it))
526 *it = toupper(*it);
527
528 // call Trust Settings and see what it finds
529 SecTrustSettingsDomain domain;
530 SecTrustSettingsResult result;
531 CSSM_RETURN *errors = NULL;
532 uint32 errorCount = 0;
533 bool foundMatch, foundAny;
534 switch (OSStatus rc = SecTrustSettingsEvaluateCert(
535 CFTempString(Certhex), // settings index
536 &CSSMOID_APPLE_TP_CODE_SIGNING, // standard code signing policy
537 NULL, 0, // policy string (unused)
538 kSecTrustSettingsKeyUseAny, // no restriction on key usage @@@
539 isAnchor, // consult system default anchor set
540
541 &domain, // domain of found setting
542 &errors, &errorCount, // error set and maximum count
543 &result, // the actual setting
544 &foundMatch, &foundAny // optimization hints (not used)
545 )) {
546 case errSecSuccess:
547 ::free(errors);
548 if (foundMatch)
549 return result;
550 else
551 return kSecTrustSettingsResultUnspecified;
552 default:
553 ::free(errors);
554 MacOSError::throwMe(rc);
555 }
556 #else
557 return kSecTrustSettingsResultUnspecified;
558 #endif
559 }
560
561
562 //
563 // Create a Match object from the interpreter stream
564 //
565 Requirement::Interpreter::Match::Match(Interpreter &interp)
566 {
567 switch (mOp = interp.get<MatchOperation>()) {
568 case matchAbsent:
569 case matchExists:
570 break;
571 case matchEqual:
572 case matchContains:
573 case matchBeginsWith:
574 case matchEndsWith:
575 case matchLessThan:
576 case matchGreaterThan:
577 case matchLessEqual:
578 case matchGreaterEqual:
579 mValue.take(makeCFString(interp.getString()));
580 break;
581 case matchOn:
582 case matchBefore:
583 case matchAfter:
584 case matchOnOrBefore:
585 case matchOnOrAfter: {
586 mValue.take(CFDateCreate(NULL, interp.getAbsoluteTime()));
587 break;
588 }
589 default:
590 // Assume this (unknown) match type has a single data argument.
591 // This gives us a chance to keep the instruction stream aligned.
592 interp.getString(); // discard
593 break;
594 }
595 }
596
597
598 //
599 // Execute a match against a candidate value
600 //
601 bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const
602 {
603 // null candidates always fail
604 if (!candidate)
605 return false;
606
607 if (candidate == kCFNull) {
608 return mOp == matchAbsent; // only 'absent' matches
609 }
610
611 // interpret an array as matching alternatives (any one succeeds)
612 if (CFGetTypeID(candidate) == CFArrayGetTypeID()) {
613 CFArrayRef array = CFArrayRef(candidate);
614 CFIndex count = CFArrayGetCount(array);
615 for (CFIndex n = 0; n < count; n++)
616 if ((*this)(CFArrayGetValueAtIndex(array, n))) // yes, it's recursive
617 return true;
618 }
619
620 switch (mOp) {
621 case matchAbsent:
622 return false; // it exists, so it cannot be absent
623 case matchExists: // anything but NULL and boolean false "exists"
624 return !CFEqual(candidate, kCFBooleanFalse);
625 case matchEqual: // equality works for all CF types
626 return CFEqual(candidate, mValue);
627 case matchContains:
628 if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
629 CFStringRef value = CFStringRef(candidate);
630 if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(value)), 0, NULL))
631 return true;
632 }
633 return false;
634 case matchBeginsWith:
635 if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
636 CFStringRef value = CFStringRef(candidate);
637 if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(0, CFStringGetLength(cfStringValue())), 0, NULL))
638 return true;
639 }
640 return false;
641 case matchEndsWith:
642 if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
643 CFStringRef value = CFStringRef(candidate);
644 CFIndex matchLength = CFStringGetLength(cfStringValue());
645 CFIndex start = CFStringGetLength(value) - matchLength;
646 if (start >= 0)
647 if (CFStringFindWithOptions(value, cfStringValue(), CFRangeMake(start, matchLength), 0, NULL))
648 return true;
649 }
650 return false;
651 case matchLessThan:
652 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true);
653 case matchGreaterThan:
654 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true);
655 case matchLessEqual:
656 return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false);
657 case matchGreaterEqual:
658 return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false);
659 case matchOn:
660 case matchBefore:
661 case matchAfter:
662 case matchOnOrBefore:
663 case matchOnOrAfter: {
664 if (!isDateValue() || CFGetTypeID(candidate) != CFDateGetTypeID()) {
665 return false;
666 }
667
668 CFComparisonResult res = CFDateCompare((CFDateRef)candidate, cfDateValue(), NULL);
669
670 switch (mOp) {
671 case matchOn: return res == 0;
672 case matchBefore: return res < 0;
673 case matchAfter: return res > 0;
674 case matchOnOrBefore: return res <= 0;
675 case matchOnOrAfter: return res >= 0;
676 default: abort();
677 }
678 }
679 default:
680 // unrecognized match types can never match
681 return false;
682 }
683 }
684
685
686 bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags,
687 CFComparisonResult outcome, bool negate) const
688 {
689 if (isStringValue() && CFGetTypeID(candidate) == CFStringGetTypeID()) {
690 CFStringRef value = CFStringRef(candidate);
691 if ((CFStringCompare(value, cfStringValue(), flags) == outcome) == negate)
692 return true;
693 }
694 return false;
695 }
696
697
698 //
699 // External fragments
700 //
701 Fragments::Fragments()
702 {
703 mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
704 }
705
706
707 bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx)
708 {
709 if (CFDataRef fragData = fragment(type, name)) {
710 const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData); // was prevalidated as Requirement
711 return req->validates(ctx);
712 }
713 return false;
714 }
715
716
717 CFDataRef Fragments::fragment(const char *type, const std::string &name)
718 {
719 string key = name + "!!" + type; // compound key
720 StLock<Mutex> _(mLock); // lock for cache access
721 FragMap::const_iterator it = mFragments.find(key);
722 if (it == mFragments.end()) {
723 CFRef<CFDataRef> fragData; // will always be set (NULL on any errors)
724 if (CFRef<CFURLRef> fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type)))
725 if (CFRef<CFDataRef> data = cfLoadFile(fragURL)) { // got data
726 const Requirement *req = (const Requirement *)CFDataGetBytePtr(data);
727 if (req->validateBlob(CFDataGetLength(data))) // looks like a Requirement...
728 fragData = data; // ... so accept it
729 else
730 Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str());
731 }
732 if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED())
733 CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type, name.c_str(), fragData ? CFDataGetBytePtr(fragData) : NULL);
734 mFragments[key] = fragData; // cache it, success or failure
735 return fragData;
736 }
737 CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str());
738 return it->second;
739 }
740
741
742 } // CodeSigning
743 } // Security