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