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