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