]>
Commit | Line | Data |
---|---|---|
7d31e928 A |
1 | /* |
2 | * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | // | |
25 | // reqinterp - Requirement language (exprOp) interpreter | |
26 | // | |
27 | #include "reqinterp.h" | |
d1c1ab47 | 28 | #include "codesigning_dtrace.h" |
7d31e928 A |
29 | #include <Security/SecTrustSettingsPriv.h> |
30 | #include <Security/SecCertificatePriv.h> | |
31 | #include <security_utilities/memutils.h> | |
f60086fc | 32 | #include <security_utilities/logging.h> |
7d31e928 A |
33 | #include "csutilities.h" |
34 | ||
35 | namespace Security { | |
36 | namespace CodeSigning { | |
37 | ||
38 | ||
f60086fc A |
39 | // |
40 | // Fragment fetching, caching, and evaluation. | |
41 | // | |
42 | // Several language elements allow "calling" of separate requirement programs | |
43 | // stored on disk as (binary) requirement blobs. The Fragments class takes care | |
44 | // of finding, loading, caching, and evaluating them. | |
45 | // | |
46 | // This is a singleton for (process global) caching. It works fine as multiple instances, | |
47 | // at a loss of caching effectiveness. | |
48 | // | |
49 | class Fragments { | |
50 | public: | |
51 | Fragments(); | |
52 | ||
53 | bool named(const std::string &name, const Requirement::Context &ctx) | |
54 | { return evalNamed("subreq", name, ctx); } | |
55 | bool namedAnchor(const std::string &name, const Requirement::Context &ctx) | |
56 | { return evalNamed("anchorreq", name, ctx); } | |
57 | ||
58 | private: | |
59 | bool evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx); | |
60 | CFDataRef fragment(const char *type, const std::string &name); | |
61 | ||
62 | typedef std::map<std::string, CFRef<CFDataRef> > FragMap; | |
63 | ||
64 | private: | |
65 | CFBundleRef mMyBundle; // Security.framework bundle | |
66 | Mutex mLock; // lock for all of the below... | |
67 | FragMap mFragments; // cached fragments | |
68 | }; | |
69 | ||
70 | static ModuleNexus<Fragments> fragments; | |
f6842776 A |
71 | |
72 | ||
7d31e928 | 73 | // |
f60086fc | 74 | // Magic certificate features |
7d31e928 | 75 | // |
f60086fc A |
76 | static CFStringRef appleIntermediateCN = CFSTR("Apple Code Signing Certification Authority"); |
77 | static CFStringRef appleIntermediateO = CFSTR("Apple Inc."); | |
7d31e928 A |
78 | |
79 | ||
80 | // | |
81 | // Main interpreter function. | |
82 | // | |
83 | // ExprOp code is in Polish Notation (operator followed by operands), | |
84 | // and this engine uses opportunistic evaluation. | |
85 | // | |
86 | bool Requirement::Interpreter::evaluate() | |
87 | { | |
88 | ExprOp op = ExprOp(get<uint32_t>()); | |
d1c1ab47 | 89 | CODESIGN_EVAL_REQINT_OP(op, this->pc() - sizeof(uint32_t)); |
7d31e928 A |
90 | switch (op & ~opFlagMask) { |
91 | case opFalse: | |
92 | return false; | |
93 | case opTrue: | |
94 | return true; | |
95 | case opIdent: | |
f60086fc | 96 | return mContext->directory && getString() == mContext->directory->identifier(); |
7d31e928 | 97 | case opAppleAnchor: |
f6842776 | 98 | return appleSigned(); |
516ae477 A |
99 | case opAppleGenericAnchor: |
100 | return appleAnchored(); | |
7d31e928 A |
101 | case opAnchorHash: |
102 | { | |
103 | SecCertificateRef cert = mContext->cert(get<int32_t>()); | |
104 | return verifyAnchor(cert, getSHA1()); | |
105 | } | |
106 | case opInfoKeyValue: // [legacy; use opInfoKeyField] | |
107 | { | |
108 | string key = getString(); | |
109 | return infoKeyValue(key, Match(CFTempString(getString()), matchEqual)); | |
110 | } | |
111 | case opAnd: | |
516ae477 | 112 | return evaluate() & evaluate(); |
7d31e928 | 113 | case opOr: |
516ae477 | 114 | return evaluate() | evaluate(); |
7d31e928 | 115 | case opCDHash: |
f60086fc | 116 | if (mContext->directory) { |
7d31e928 A |
117 | SHA1 hash; |
118 | hash(mContext->directory, mContext->directory->length()); | |
119 | return hash.verify(getHash()); | |
f60086fc A |
120 | } else |
121 | return false; | |
7d31e928 A |
122 | case opNot: |
123 | return !evaluate(); | |
124 | case opInfoKeyField: | |
125 | { | |
126 | string key = getString(); | |
127 | Match match(*this); | |
128 | return infoKeyValue(key, match); | |
129 | } | |
516ae477 A |
130 | case opEntitlementField: |
131 | { | |
132 | string key = getString(); | |
133 | Match match(*this); | |
134 | return entitlementValue(key, match); | |
135 | } | |
7d31e928 A |
136 | case opCertField: |
137 | { | |
138 | SecCertificateRef cert = mContext->cert(get<int32_t>()); | |
139 | string key = getString(); | |
140 | Match match(*this); | |
141 | return certFieldValue(key, match, cert); | |
142 | } | |
516ae477 A |
143 | case opCertGeneric: |
144 | { | |
145 | SecCertificateRef cert = mContext->cert(get<int32_t>()); | |
146 | string key = getString(); | |
147 | Match match(*this); | |
148 | return certFieldGeneric(key, match, cert); | |
149 | } | |
f60086fc A |
150 | case opCertPolicy: |
151 | { | |
152 | SecCertificateRef cert = mContext->cert(get<int32_t>()); | |
153 | string key = getString(); | |
154 | Match match(*this); | |
155 | return certFieldPolicy(key, match, cert); | |
156 | } | |
7d31e928 A |
157 | case opTrustedCert: |
158 | return trustedCert(get<int32_t>()); | |
159 | case opTrustedCerts: | |
160 | return trustedCerts(); | |
f60086fc A |
161 | case opNamedAnchor: |
162 | return fragments().namedAnchor(getString(), *mContext); | |
163 | case opNamedCode: | |
164 | return fragments().named(getString(), *mContext); | |
7d31e928 A |
165 | default: |
166 | // opcode not recognized - handle generically if possible, fail otherwise | |
167 | if (op & (opGenericFalse | opGenericSkip)) { | |
168 | // unknown opcode, but it has a size field and can be safely bypassed | |
169 | skip(get<uint32_t>()); | |
170 | if (op & opGenericFalse) { | |
d1c1ab47 | 171 | CODESIGN_EVAL_REQINT_UNKNOWN_FALSE(op); |
7d31e928 A |
172 | return false; |
173 | } else { | |
d1c1ab47 | 174 | CODESIGN_EVAL_REQINT_UNKNOWN_SKIPPED(op); |
7d31e928 A |
175 | return evaluate(); |
176 | } | |
177 | } | |
178 | // unrecognized opcode and no way to interpret it | |
179 | secdebug("csinterp", "opcode 0x%x cannot be handled; aborting", op); | |
180 | MacOSError::throwMe(errSecCSUnimplemented); | |
181 | } | |
182 | } | |
183 | ||
184 | ||
185 | // | |
186 | // Evaluate an Info.plist key condition | |
187 | // | |
188 | bool Requirement::Interpreter::infoKeyValue(const string &key, const Match &match) | |
189 | { | |
190 | if (mContext->info) // we have an Info.plist | |
191 | if (CFTypeRef value = CFDictionaryGetValue(mContext->info, CFTempString(key))) | |
192 | return match(value); | |
193 | return false; | |
194 | } | |
195 | ||
196 | ||
516ae477 A |
197 | // |
198 | // Evaluate an entitlement condition | |
199 | // | |
200 | bool Requirement::Interpreter::entitlementValue(const string &key, const Match &match) | |
201 | { | |
202 | if (mContext->entitlements) // we have an Info.plist | |
203 | if (CFTypeRef value = CFDictionaryGetValue(mContext->entitlements, CFTempString(key))) | |
204 | return match(value); | |
205 | return false; | |
206 | } | |
207 | ||
208 | ||
7d31e928 A |
209 | bool Requirement::Interpreter::certFieldValue(const string &key, const Match &match, SecCertificateRef cert) |
210 | { | |
211 | // no cert, no chance | |
212 | if (cert == NULL) | |
213 | return false; | |
214 | ||
215 | // a table of recognized keys for the "certificate[foo]" syntax | |
216 | static const struct CertField { | |
217 | const char *name; | |
218 | const CSSM_OID *oid; | |
219 | } certFields[] = { | |
220 | { "subject.C", &CSSMOID_CountryName }, | |
221 | { "subject.CN", &CSSMOID_CommonName }, | |
222 | { "subject.D", &CSSMOID_Description }, | |
223 | { "subject.L", &CSSMOID_LocalityName }, | |
f60086fc | 224 | // { "subject.C-L", &CSSMOID_CollectiveLocalityName }, // missing from Security.framework headers |
7d31e928 | 225 | { "subject.O", &CSSMOID_OrganizationName }, |
f60086fc | 226 | { "subject.C-O", &CSSMOID_CollectiveOrganizationName }, |
7d31e928 | 227 | { "subject.OU", &CSSMOID_OrganizationalUnitName }, |
f60086fc | 228 | { "subject.C-OU", &CSSMOID_CollectiveOrganizationalUnitName }, |
7d31e928 | 229 | { "subject.ST", &CSSMOID_StateProvinceName }, |
f60086fc | 230 | { "subject.C-ST", &CSSMOID_CollectiveStateProvinceName }, |
7d31e928 | 231 | { "subject.STREET", &CSSMOID_StreetAddress }, |
f60086fc A |
232 | { "subject.C-STREET", &CSSMOID_CollectiveStreetAddress }, |
233 | { "subject.UID", &CSSMOID_UserID }, | |
7d31e928 A |
234 | { NULL, NULL } |
235 | }; | |
7d31e928 A |
236 | |
237 | // DN-component single-value match | |
238 | for (const CertField *cf = certFields; cf->name; cf++) | |
239 | if (cf->name == key) { | |
240 | CFRef<CFStringRef> value; | |
d1c1ab47 | 241 | if (OSStatus rc = SecCertificateCopySubjectComponent(cert, cf->oid, &value.aref())) { |
62e4ed3d | 242 | secdebug("csinterp", "cert %p lookup for DN.%s failed rc=%d", cert, key.c_str(), (int)rc); |
7d31e928 A |
243 | return false; |
244 | } | |
245 | return match(value); | |
246 | } | |
247 | ||
516ae477 A |
248 | // email multi-valued match (any of...) |
249 | if (key == "email") { | |
250 | CFRef<CFArrayRef> value; | |
251 | if (OSStatus rc = SecCertificateCopyEmailAddresses(cert, &value.aref())) { | |
62e4ed3d | 252 | secdebug("csinterp", "cert %p lookup for email failed rc=%d", cert, (int)rc); |
516ae477 A |
253 | return false; |
254 | } | |
255 | return match(value); | |
256 | } | |
257 | ||
7d31e928 A |
258 | // unrecognized key. Fail but do not abort to promote backward compatibility down the road |
259 | secdebug("csinterp", "cert field notation \"%s\" not understood", key.c_str()); | |
260 | return false; | |
261 | } | |
262 | ||
f60086fc | 263 | |
516ae477 A |
264 | bool Requirement::Interpreter::certFieldGeneric(const string &key, const Match &match, SecCertificateRef cert) |
265 | { | |
266 | // the key is actually a (binary) OID value | |
267 | CssmOid oid((char *)key.data(), key.length()); | |
268 | return certFieldGeneric(oid, match, cert); | |
269 | } | |
270 | ||
271 | bool Requirement::Interpreter::certFieldGeneric(const CssmOid &oid, const Match &match, SecCertificateRef cert) | |
272 | { | |
273 | return cert && certificateHasField(cert, oid) && match(kCFBooleanTrue); | |
274 | } | |
275 | ||
f60086fc A |
276 | bool Requirement::Interpreter::certFieldPolicy(const string &key, const Match &match, SecCertificateRef cert) |
277 | { | |
278 | // the key is actually a (binary) OID value | |
279 | CssmOid oid((char *)key.data(), key.length()); | |
280 | return certFieldPolicy(oid, match, cert); | |
281 | } | |
282 | ||
283 | bool Requirement::Interpreter::certFieldPolicy(const CssmOid &oid, const Match &match, SecCertificateRef cert) | |
284 | { | |
285 | return cert && certificateHasPolicy(cert, oid) && match(kCFBooleanTrue); | |
286 | } | |
287 | ||
516ae477 | 288 | |
f6842776 A |
289 | // |
290 | // Check the Apple-signed condition | |
291 | // | |
516ae477 | 292 | bool Requirement::Interpreter::appleAnchored() |
f6842776 A |
293 | { |
294 | if (SecCertificateRef cert = mContext->cert(anchorCert)) | |
295 | if (verifyAnchor(cert, appleAnchorHash()) | |
296 | #if defined(TEST_APPLE_ANCHOR) | |
297 | || verifyAnchor(cert, testAppleAnchorHash()) | |
298 | #endif | |
299 | ) | |
516ae477 A |
300 | return true; |
301 | return false; | |
302 | } | |
303 | ||
304 | bool Requirement::Interpreter::appleSigned() | |
305 | { | |
306 | if (appleAnchored()) | |
d1c1ab47 A |
307 | if (SecCertificateRef intermed = mContext->cert(-2)) // first intermediate |
308 | // first intermediate common name match (exact) | |
309 | if (certFieldValue("subject.CN", Match(appleIntermediateCN, matchEqual), intermed) | |
f6842776 | 310 | && certFieldValue("subject.O", Match(appleIntermediateO, matchEqual), intermed)) |
d1c1ab47 | 311 | return true; |
f6842776 A |
312 | return false; |
313 | } | |
314 | ||
315 | ||
7d31e928 A |
316 | // |
317 | // Verify an anchor requirement against the context | |
318 | // | |
319 | bool Requirement::Interpreter::verifyAnchor(SecCertificateRef cert, const unsigned char *digest) | |
320 | { | |
321 | // get certificate bytes | |
322 | if (cert) { | |
323 | CSSM_DATA certData; | |
324 | MacOSError::check(SecCertificateGetData(cert, &certData)); | |
325 | ||
326 | // verify hash | |
327 | //@@@ should get SHA1(cert(-1).data) precalculated during chain verification | |
328 | SHA1 hasher; | |
329 | hasher(certData.Data, certData.Length); | |
330 | return hasher.verify(digest); | |
331 | } | |
332 | return false; | |
333 | } | |
334 | ||
335 | ||
336 | // | |
337 | // Check one or all certificate(s) in the cert chain against the Trust Settings database. | |
338 | // | |
339 | bool Requirement::Interpreter::trustedCerts() | |
340 | { | |
341 | int anchor = mContext->certCount() - 1; | |
342 | for (int slot = 0; slot <= anchor; slot++) | |
343 | if (SecCertificateRef cert = mContext->cert(slot)) | |
344 | switch (trustSetting(cert, slot == anchor)) { | |
345 | case kSecTrustSettingsResultTrustRoot: | |
346 | case kSecTrustSettingsResultTrustAsRoot: | |
347 | return true; | |
348 | case kSecTrustSettingsResultDeny: | |
349 | return false; | |
350 | case kSecTrustSettingsResultUnspecified: | |
351 | break; | |
352 | default: | |
353 | assert(false); | |
354 | return false; | |
355 | } | |
356 | else | |
357 | return false; | |
358 | return false; | |
359 | } | |
360 | ||
361 | bool Requirement::Interpreter::trustedCert(int slot) | |
362 | { | |
363 | if (SecCertificateRef cert = mContext->cert(slot)) { | |
364 | int anchorSlot = mContext->certCount() - 1; | |
365 | switch (trustSetting(cert, slot == anchorCert || slot == anchorSlot)) { | |
366 | case kSecTrustSettingsResultTrustRoot: | |
367 | case kSecTrustSettingsResultTrustAsRoot: | |
368 | return true; | |
369 | case kSecTrustSettingsResultDeny: | |
370 | case kSecTrustSettingsResultUnspecified: | |
371 | return false; | |
372 | default: | |
373 | assert(false); | |
374 | return false; | |
375 | } | |
376 | } else | |
377 | return false; | |
378 | } | |
379 | ||
380 | ||
381 | // | |
382 | // Explicitly check one certificate against the Trust Settings database and report | |
383 | // the findings. This is a helper for the various Trust Settings evaluators. | |
384 | // | |
385 | SecTrustSettingsResult Requirement::Interpreter::trustSetting(SecCertificateRef cert, bool isAnchor) | |
386 | { | |
387 | // the SPI input is the uppercase hex form of the SHA-1 of the certificate... | |
388 | assert(cert); | |
389 | SHA1::Digest digest; | |
390 | hashOfCertificate(cert, digest); | |
391 | string Certhex = CssmData(digest, sizeof(digest)).toHex(); | |
392 | for (string::iterator it = Certhex.begin(); it != Certhex.end(); ++it) | |
393 | if (islower(*it)) | |
394 | *it = toupper(*it); | |
395 | ||
396 | // call Trust Settings and see what it finds | |
397 | SecTrustSettingsDomain domain; | |
398 | SecTrustSettingsResult result; | |
399 | CSSM_RETURN *errors = NULL; | |
400 | uint32 errorCount = 0; | |
401 | bool foundMatch, foundAny; | |
402 | switch (OSStatus rc = SecTrustSettingsEvaluateCert( | |
403 | CFTempString(Certhex), // settings index | |
404 | &CSSMOID_APPLE_TP_CODE_SIGNING, // standard code signing policy | |
405 | NULL, 0, // policy string (unused) | |
406 | kSecTrustSettingsKeyUseAny, // no restriction on key usage @@@ | |
407 | isAnchor, // consult system default anchor set | |
408 | ||
409 | &domain, // domain of found setting | |
410 | &errors, &errorCount, // error set and maximum count | |
411 | &result, // the actual setting | |
412 | &foundMatch, &foundAny // optimization hints (not used) | |
413 | )) { | |
414 | case noErr: | |
415 | ::free(errors); | |
416 | if (foundMatch) | |
417 | return result; | |
418 | else | |
419 | return kSecTrustSettingsResultUnspecified; | |
420 | default: | |
421 | ::free(errors); | |
422 | MacOSError::throwMe(rc); | |
423 | } | |
424 | } | |
425 | ||
426 | ||
427 | // | |
428 | // Create a Match object from the interpreter stream | |
429 | // | |
430 | Requirement::Interpreter::Match::Match(Interpreter &interp) | |
431 | { | |
432 | switch (mOp = interp.get<MatchOperation>()) { | |
433 | case matchExists: | |
434 | break; | |
435 | case matchEqual: | |
436 | case matchContains: | |
516ae477 A |
437 | case matchBeginsWith: |
438 | case matchEndsWith: | |
439 | case matchLessThan: | |
440 | case matchGreaterThan: | |
441 | case matchLessEqual: | |
442 | case matchGreaterEqual: | |
f60086fc | 443 | mValue.take(makeCFString(interp.getString())); |
7d31e928 A |
444 | break; |
445 | default: | |
516ae477 A |
446 | // Assume this (unknown) match type has a single data argument. |
447 | // This gives us a chance to keep the instruction stream aligned. | |
448 | interp.getString(); // discard | |
7d31e928 A |
449 | break; |
450 | } | |
451 | } | |
452 | ||
453 | ||
454 | // | |
455 | // Execute a match against a candidate value | |
456 | // | |
457 | bool Requirement::Interpreter::Match::operator () (CFTypeRef candidate) const | |
458 | { | |
459 | // null candidates always fail | |
460 | if (!candidate) | |
461 | return false; | |
462 | ||
463 | // interpret an array as matching alternatives (any one succeeds) | |
464 | if (CFGetTypeID(candidate) == CFArrayGetTypeID()) { | |
465 | CFArrayRef array = CFArrayRef(candidate); | |
466 | CFIndex count = CFArrayGetCount(array); | |
467 | for (CFIndex n = 0; n < count; n++) | |
468 | if ((*this)(CFArrayGetValueAtIndex(array, n))) // yes, it's recursive | |
469 | return true; | |
470 | } | |
471 | ||
472 | switch (mOp) { | |
473 | case matchExists: // anything but NULL and boolean false "exists" | |
474 | return !CFEqual(candidate, kCFBooleanFalse); | |
475 | case matchEqual: // equality works for all CF types | |
476 | return CFEqual(candidate, mValue); | |
477 | case matchContains: | |
478 | if (CFGetTypeID(candidate) == CFStringGetTypeID()) { | |
479 | CFStringRef value = CFStringRef(candidate); | |
480 | if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(value)), 0, NULL)) | |
481 | return true; | |
482 | } | |
483 | return false; | |
516ae477 A |
484 | case matchBeginsWith: |
485 | if (CFGetTypeID(candidate) == CFStringGetTypeID()) { | |
486 | CFStringRef value = CFStringRef(candidate); | |
487 | if (CFStringFindWithOptions(value, mValue, CFRangeMake(0, CFStringGetLength(mValue)), 0, NULL)) | |
488 | return true; | |
489 | } | |
490 | return false; | |
491 | case matchEndsWith: | |
492 | if (CFGetTypeID(candidate) == CFStringGetTypeID()) { | |
493 | CFStringRef value = CFStringRef(candidate); | |
494 | CFIndex matchLength = CFStringGetLength(mValue); | |
495 | CFIndex start = CFStringGetLength(value) - matchLength; | |
496 | if (start >= 0) | |
497 | if (CFStringFindWithOptions(value, mValue, CFRangeMake(start, matchLength), 0, NULL)) | |
498 | return true; | |
499 | } | |
500 | return false; | |
501 | case matchLessThan: | |
502 | return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, true); | |
503 | case matchGreaterThan: | |
504 | return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, true); | |
505 | case matchLessEqual: | |
506 | return inequality(candidate, kCFCompareNumerically, kCFCompareGreaterThan, false); | |
507 | case matchGreaterEqual: | |
508 | return inequality(candidate, kCFCompareNumerically, kCFCompareLessThan, false); | |
7d31e928 | 509 | default: |
516ae477 | 510 | // unrecognized match types can never match |
7d31e928 A |
511 | return false; |
512 | } | |
513 | } | |
514 | ||
515 | ||
516ae477 A |
516 | bool Requirement::Interpreter::Match::inequality(CFTypeRef candidate, CFStringCompareFlags flags, |
517 | CFComparisonResult outcome, bool negate) const | |
518 | { | |
519 | if (CFGetTypeID(candidate) == CFStringGetTypeID()) { | |
520 | CFStringRef value = CFStringRef(candidate); | |
521 | if ((CFStringCompare(value, mValue, flags) == outcome) == negate) | |
522 | return true; | |
523 | } | |
524 | return false; | |
525 | } | |
526 | ||
527 | ||
f60086fc A |
528 | // |
529 | // External fragments | |
530 | // | |
531 | Fragments::Fragments() | |
532 | { | |
533 | mMyBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); | |
534 | } | |
535 | ||
536 | ||
537 | bool Fragments::evalNamed(const char *type, const std::string &name, const Requirement::Context &ctx) | |
538 | { | |
539 | if (CFDataRef fragData = fragment(type, name)) { | |
540 | const Requirement *req = (const Requirement *)CFDataGetBytePtr(fragData); // was prevalidated as Requirement | |
541 | return req->validates(ctx); | |
542 | } | |
543 | return false; | |
544 | } | |
545 | ||
546 | ||
547 | CFDataRef Fragments::fragment(const char *type, const std::string &name) | |
548 | { | |
549 | string key = name + "!!" + type; // compound key | |
550 | StLock<Mutex> _(mLock); // lock for cache access | |
551 | FragMap::const_iterator it = mFragments.find(key); | |
552 | if (it == mFragments.end()) { | |
553 | CFRef<CFDataRef> fragData; // will always be set (NULL on any errors) | |
554 | if (CFRef<CFURLRef> fragURL = CFBundleCopyResourceURL(mMyBundle, CFTempString(name), CFSTR("csreq"), CFTempString(type))) | |
555 | if (CFRef<CFDataRef> data = cfLoadFile(fragURL)) { // got data | |
556 | const Requirement *req = (const Requirement *)CFDataGetBytePtr(data); | |
557 | if (req->validateBlob(CFDataGetLength(data))) // looks like a Requirement... | |
558 | fragData = data; // ... so accept it | |
559 | else | |
560 | Syslog::warning("Invalid sub-requirement at %s", cfString(fragURL).c_str()); | |
561 | } | |
562 | if (CODESIGN_EVAL_REQINT_FRAGMENT_LOAD_ENABLED()) | |
563 | CODESIGN_EVAL_REQINT_FRAGMENT_LOAD(type, name.c_str(), fragData ? CFDataGetBytePtr(fragData) : NULL); | |
564 | mFragments[key] = fragData; // cache it, success or failure | |
565 | return fragData; | |
566 | } | |
567 | CODESIGN_EVAL_REQINT_FRAGMENT_HIT(type, name.c_str()); | |
568 | return it->second; | |
569 | } | |
570 | ||
571 | ||
7d31e928 A |
572 | } // CodeSigning |
573 | } // Security |