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