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