]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/lib/StaticCode.cpp
9a12445831cd51cc03761a25825725b60209f450
[apple/security.git] / libsecurity_codesigning / lib / StaticCode.cpp
1 /*
2 * Copyright (c) 2006-2012 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 // StaticCode - SecStaticCode API objects
26 //
27 #include "StaticCode.h"
28 #include "Code.h"
29 #include "reqmaker.h"
30 #include "drmaker.h"
31 #include "reqdumper.h"
32 #include "sigblob.h"
33 #include "resources.h"
34 #include "renum.h"
35 #include "detachedrep.h"
36 #include "csdatabase.h"
37 #include "csutilities.h"
38 #include <CoreFoundation/CFURLAccess.h>
39 #include <Security/SecPolicyPriv.h>
40 #include <Security/SecTrustPriv.h>
41 #include <Security/SecCertificatePriv.h>
42 #include <Security/CMSPrivate.h>
43 #include <Security/SecCmsContentInfo.h>
44 #include <Security/SecCmsSignerInfo.h>
45 #include <Security/SecCmsSignedData.h>
46 #include <Security/cssmapplePriv.h>
47 #include <security_utilities/unix++.h>
48 #include <security_utilities/cfmunge.h>
49 #include <Security/CMSDecoder.h>
50
51
52 namespace Security {
53 namespace CodeSigning {
54
55 using namespace UnixPlusPlus;
56
57
58 //
59 // Construct a SecStaticCode object given a disk representation object
60 //
61 SecStaticCode::SecStaticCode(DiskRep *rep)
62 : mRep(rep),
63 mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
64 mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
65 {
66 CODESIGN_STATIC_CREATE(this, rep);
67 checkForSystemSignature();
68 }
69
70
71 //
72 // Clean up a SecStaticCode object
73 //
74 SecStaticCode::~SecStaticCode() throw()
75 try {
76 ::free(const_cast<Requirement *>(mDesignatedReq));
77 if (mResourcesValidContext)
78 delete mResourcesValidContext;
79 } catch (...) {
80 return;
81 }
82
83
84 //
85 // CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed,
86 // and falls back on comparing canonical paths if (both are) not.
87 //
88 bool SecStaticCode::equal(SecCFObject &secOther)
89 {
90 SecStaticCode *other = static_cast<SecStaticCode *>(&secOther);
91 CFDataRef mine = this->cdHash();
92 CFDataRef his = other->cdHash();
93 if (mine || his)
94 return mine && his && CFEqual(mine, his);
95 else
96 return CFEqual(this->canonicalPath(), other->canonicalPath());
97 }
98
99 CFHashCode SecStaticCode::hash()
100 {
101 if (CFDataRef h = this->cdHash())
102 return CFHash(h);
103 else
104 return CFHash(this->canonicalPath());
105 }
106
107
108 //
109 // Attach a detached signature.
110 //
111 void SecStaticCode::detachedSignature(CFDataRef sigData)
112 {
113 if (sigData) {
114 mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
115 CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
116 } else {
117 mRep = mRep->base();
118 CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
119 }
120 }
121
122
123 //
124 // Consult the system detached signature database to see if it contains
125 // a detached signature for this StaticCode. If it does, fetch and attach it.
126 // We do this only if the code has no signature already attached.
127 //
128 void SecStaticCode::checkForSystemSignature()
129 {
130 if (!this->isSigned())
131 try {
132 if (RefPointer<DiskRep> dsig = signatureDatabase().findCode(mRep)) {
133 CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
134 mRep = dsig;
135 }
136 } catch (...) {
137 }
138 }
139
140
141 //
142 // Return a descriptive string identifying the source of the code signature
143 //
144 string SecStaticCode::signatureSource()
145 {
146 if (!isSigned())
147 return "unsigned";
148 if (DetachedRep *rep = dynamic_cast<DetachedRep *>(mRep.get()))
149 return rep->source();
150 return "embedded";
151 }
152
153
154 //
155 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
156 // (if possible).
157 //
158 SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref)
159 {
160 SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
161 if (SecStaticCode *scode = dynamic_cast<SecStaticCode *>(object))
162 return scode;
163 else if (SecCode *code = dynamic_cast<SecCode *>(object))
164 return code->staticCode();
165 else // neither (a SecSomethingElse)
166 MacOSError::throwMe(errSecCSInvalidObjectRef);
167 }
168
169 SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref)
170 {
171 SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
172 if (dynamic_cast<SecStaticCode *>(object))
173 return NULL;
174 else if (SecCode *code = dynamic_cast<SecCode *>(object))
175 return code;
176 else // neither (a SecSomethingElse)
177 MacOSError::throwMe(errSecCSInvalidObjectRef);
178 }
179
180
181 //
182 // Void all cached validity data.
183 //
184 // We also throw out cached components, because the new signature data may have
185 // a different idea of what components should be present. We could reconcile the
186 // cached data instead, if performance seems to be impacted.
187 //
188 void SecStaticCode::resetValidity()
189 {
190 CODESIGN_EVAL_STATIC_RESET(this);
191 mValidated = false;
192 mExecutableValidated = false;
193 mResourcesValidated = false;
194 if (mResourcesValidContext) {
195 delete mResourcesValidContext;
196 mResourcesValidContext = NULL;
197 }
198 mDir = NULL;
199 mSignature = NULL;
200 for (unsigned n = 0; n < cdSlotCount; n++)
201 mCache[n] = NULL;
202 mInfoDict = NULL;
203 mEntitlements = NULL;
204 mResourceDict = NULL;
205 mDesignatedReq = NULL;
206 mGotResourceBase = false;
207 mTrust = NULL;
208 mCertChain = NULL;
209 mEvalDetails = NULL;
210 mRep->flush();
211
212 // we may just have updated the system database, so check again
213 checkForSystemSignature();
214 }
215
216
217 //
218 // Retrieve a sealed component by special slot index.
219 // If the CodeDirectory has already been validated, validate against that.
220 // Otherwise, retrieve the component without validation (but cache it). Validation
221 // will go through the cache and validate all cached components.
222 //
223 CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
224 {
225 assert(slot <= cdSlotMax);
226
227 CFRef<CFDataRef> &cache = mCache[slot];
228 if (!cache) {
229 if (CFRef<CFDataRef> data = mRep->component(slot)) {
230 if (validated()) // if the directory has been validated...
231 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
232 CFDataGetLength(data), -slot))
233 MacOSError::throwMe(fail); // ... then bail
234 cache = data; // it's okay, cache it
235 } else { // absent, mark so
236 if (validated()) // if directory has been validated...
237 if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
238 MacOSError::throwMe(fail); // was supposed to be there
239 cache = CFDataRef(kCFNull); // white lie
240 }
241 }
242 return (cache == CFDataRef(kCFNull)) ? NULL : cache.get();
243 }
244
245
246 //
247 // Get the CodeDirectory.
248 // Throws (if check==true) or returns NULL (check==false) if there is none.
249 // Always throws if the CodeDirectory exists but is invalid.
250 // NEVER validates against the signature.
251 //
252 const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */)
253 {
254 if (!mDir) {
255 if (mDir.take(mRep->codeDirectory())) {
256 const CodeDirectory *dir = reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
257 dir->checkIntegrity();
258 }
259 }
260 if (mDir)
261 return reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
262 if (check)
263 MacOSError::throwMe(errSecCSUnsigned);
264 return NULL;
265 }
266
267
268 //
269 // Get the hash of the CodeDirectory.
270 // Returns NULL if there is none.
271 //
272 CFDataRef SecStaticCode::cdHash()
273 {
274 if (!mCDHash) {
275 if (const CodeDirectory *cd = codeDirectory(false)) {
276 SHA1 hash;
277 hash(cd, cd->length());
278 SHA1::Digest digest;
279 hash.finish(digest);
280 mCDHash.take(makeCFData(digest, sizeof(digest)));
281 CODESIGN_STATIC_CDHASH(this, digest, sizeof(digest));
282 }
283 }
284 return mCDHash;
285 }
286
287
288 //
289 // Return the CMS signature blob; NULL if none found.
290 //
291 CFDataRef SecStaticCode::signature()
292 {
293 if (!mSignature)
294 mSignature.take(mRep->signature());
295 if (mSignature)
296 return mSignature;
297 MacOSError::throwMe(errSecCSUnsigned);
298 }
299
300
301 //
302 // Verify the signature on the CodeDirectory.
303 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
304 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
305 //
306 void SecStaticCode::validateDirectory()
307 {
308 // echo previous outcome, if any
309 if (!validated())
310 try {
311 // perform validation (or die trying)
312 CODESIGN_EVAL_STATIC_DIRECTORY(this);
313 mValidationExpired = verifySignature();
314 component(cdInfoSlot, errSecCSInfoPlistFailed); // force load of Info Dictionary (if any)
315 for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
316 if (mCache[slot]) // if we already loaded that resource...
317 validateComponent(slot); // ... then check it now
318 mValidated = true; // we've done the deed...
319 mValidationResult = noErr; // ... and it was good
320 } catch (const CommonError &err) {
321 mValidated = true;
322 mValidationResult = err.osStatus();
323 throw;
324 } catch (...) {
325 secdebug("staticCode", "%p validation threw non-common exception", this);
326 mValidated = true;
327 mValidationResult = errSecCSInternalError;
328 throw;
329 }
330 assert(validated());
331 if (mValidationResult == noErr) {
332 if (mValidationExpired)
333 if ((apiFlags() & kSecCSConsiderExpiration)
334 || (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
335 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
336 } else
337 MacOSError::throwMe(mValidationResult);
338 }
339
340
341 //
342 // Load and validate the CodeDirectory and all components *except* those related to the resource envelope.
343 // Those latter components are checked by validateResources().
344 //
345 void SecStaticCode::validateNonResourceComponents()
346 {
347 this->validateDirectory();
348 for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
349 switch (slot) {
350 case cdResourceDirSlot: // validated by validateResources
351 break;
352 default:
353 this->component(slot); // loads and validates
354 break;
355 }
356 }
357
358
359 //
360 // Get the (signed) signing date from the code signature.
361 // Sadly, we need to validate the signature to get the date (as a side benefit).
362 // This means that you can't get the signing time for invalidly signed code.
363 //
364 // We could run the decoder "almost to" verification to avoid this, but there seems
365 // little practical point to such a duplication of effort.
366 //
367 CFAbsoluteTime SecStaticCode::signingTime()
368 {
369 validateDirectory();
370 return mSigningTime;
371 }
372
373 CFAbsoluteTime SecStaticCode::signingTimestamp()
374 {
375 validateDirectory();
376 return mSigningTimestamp;
377 }
378
379
380 //
381 // Verify the CMS signature on the CodeDirectory.
382 // This performs the cryptographic tango. It returns if the signature is valid,
383 // or throws if it is not. As a side effect, a successful return sets up the
384 // cached certificate chain for future use.
385 // Returns true if the signature is expired (the X.509 sense), false if it's not.
386 //
387 bool SecStaticCode::verifySignature()
388 {
389 // ad-hoc signed code is considered validly signed by definition
390 if (flag(kSecCodeSignatureAdhoc)) {
391 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
392 return false;
393 }
394
395 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
396
397 // decode CMS and extract SecTrust for verification
398 CFRef<CMSDecoderRef> cms;
399 MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
400 CFDataRef sig = this->signature();
401 MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
402 this->codeDirectory(); // load CodeDirectory (sets mDir)
403 MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
404 MacOSError::check(CMSDecoderFinalizeMessage(cms));
405 MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
406 CFRef<CFTypeRef> policy = verificationPolicy(apiFlags());
407 CMSSignerStatus status;
408 MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, policy,
409 false, &status, &mTrust.aref(), NULL));
410 if (status != kCMSSignerValid)
411 MacOSError::throwMe(errSecCSSignatureFailed);
412
413 // internal signing time (as specified by the signer; optional)
414 mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
415 switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
416 case noErr:
417 case errSecSigningTimeMissing:
418 break;
419 default:
420 MacOSError::throwMe(rc);
421 }
422
423 // certified signing time (as specified by a TSA; optional)
424 mSigningTimestamp = 0;
425 switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) {
426 case noErr:
427 case errSecTimestampMissing:
428 break;
429 default:
430 MacOSError::throwMe(rc);
431 }
432
433 // set up the environment for SecTrust
434 MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
435 MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
436 CSSM_APPLE_TP_ACTION_DATA actionData = {
437 CSSM_APPLE_TP_ACTION_VERSION, // version of data structure
438 CSSM_TP_ACTION_IMPLICIT_ANCHORS // action flags
439 };
440
441 for (;;) { // at most twice
442 MacOSError::check(SecTrustSetParameters(mTrust,
443 CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
444
445 // evaluate trust and extract results
446 SecTrustResultType trustResult;
447 MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
448 MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
449 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0);
450 switch (trustResult) {
451 case kSecTrustResultProceed:
452 case kSecTrustResultUnspecified:
453 break; // success
454 case kSecTrustResultDeny:
455 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY); // user reject
456 case kSecTrustResultInvalid:
457 assert(false); // should never happen
458 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
459 default:
460 {
461 OSStatus result;
462 MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
463 // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
464 // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
465 if (mSigningTimestamp == 0) // no timestamp available
466 if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
467 && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
468 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
469 actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
470 continue; // retry validation while tolerating expiration
471 }
472 MacOSError::throwMe(result);
473 }
474 }
475 return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
476 }
477 }
478
479
480 //
481 // Return the TP policy used for signature verification.
482 // This may be a simple SecPolicyRef or a CFArray of policies.
483 // The caller owns the return value.
484 //
485 static SecPolicyRef makeCRLPolicy()
486 {
487 CFRef<SecPolicyRef> policy;
488 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
489 CSSM_APPLE_TP_CRL_OPTIONS options;
490 memset(&options, 0, sizeof(options));
491 options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
492 options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
493 CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
494 MacOSError::check(SecPolicySetValue(policy, &optData));
495 return policy.yield();
496 }
497
498 static SecPolicyRef makeOCSPPolicy()
499 {
500 CFRef<SecPolicyRef> policy;
501 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
502 CSSM_APPLE_TP_OCSP_OPTIONS options;
503 memset(&options, 0, sizeof(options));
504 options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
505 options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
506 CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
507 MacOSError::check(SecPolicySetValue(policy, &optData));
508 return policy.yield();
509 }
510
511 CFTypeRef SecStaticCode::verificationPolicy(SecCSFlags flags)
512 {
513 CFRef<SecPolicyRef> core;
514 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
515 &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
516 if (flags & kSecCSEnforceRevocationChecks) {
517 CFRef<SecPolicyRef> crl = makeCRLPolicy();
518 CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
519 return makeCFArray(3, core.get(), crl.get(), ocsp.get());
520 } else {
521 return core.yield();
522 }
523 }
524
525
526 //
527 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
528 // The resource must already have been placed in the cache.
529 // This does NOT perform basic validation.
530 //
531 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
532 {
533 assert(slot <= cdSlotMax);
534 CFDataRef data = mCache[slot];
535 assert(data); // must be cached
536 if (data == CFDataRef(kCFNull)) {
537 if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
538 MacOSError::throwMe(fail); // ... and is missing
539 } else {
540 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
541 MacOSError::throwMe(fail);
542 }
543 }
544
545
546 //
547 // Perform static validation of the main executable.
548 // This reads the main executable from disk and validates it against the
549 // CodeDirectory code slot array.
550 // Note that this is NOT an in-memory validation, and is thus potentially
551 // subject to timing attacks.
552 //
553 void SecStaticCode::validateExecutable()
554 {
555 if (!validatedExecutable()) {
556 try {
557 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
558 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
559 const CodeDirectory *cd = this->codeDirectory();
560 if (!cd)
561 MacOSError::throwMe(errSecCSUnsigned);
562 AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
563 fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass)
564 if (Universal *fat = mRep->mainExecutableImage())
565 fd.seek(fat->archOffset());
566 size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
567 size_t remaining = cd->codeLimit;
568 for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
569 size_t size = min(remaining, pageSize);
570 if (!cd->validateSlot(fd, size, slot)) {
571 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
572 MacOSError::throwMe(errSecCSSignatureFailed);
573 }
574 remaining -= size;
575 }
576 mExecutableValidated = true;
577 mExecutableValidResult = noErr;
578 } catch (const CommonError &err) {
579 mExecutableValidated = true;
580 mExecutableValidResult = err.osStatus();
581 throw;
582 } catch (...) {
583 secdebug("staticCode", "%p executable validation threw non-common exception", this);
584 mExecutableValidated = true;
585 mExecutableValidResult = errSecCSInternalError;
586 throw;
587 }
588 }
589 assert(validatedExecutable());
590 if (mExecutableValidResult != noErr)
591 MacOSError::throwMe(mExecutableValidResult);
592 }
593
594
595 //
596 // Perform static validation of sealed resources.
597 //
598 // This performs a whole-code static resource scan and effectively
599 // computes a concordance between what's on disk and what's in the ResourceDirectory.
600 // Any unsanctioned difference causes an error.
601 //
602 void SecStaticCode::validateResources()
603 {
604 if (!validatedResources()) {
605 try {
606 // sanity first
607 CFDictionaryRef sealedResources = resourceDictionary();
608 if (this->resourceBase()) // disk has resources
609 if (sealedResources)
610 /* go to work below */;
611 else
612 MacOSError::throwMe(errSecCSResourcesNotFound);
613 else // disk has no resources
614 if (sealedResources)
615 MacOSError::throwMe(errSecCSResourcesNotFound);
616 else
617 return; // no resources, not sealed - fine (no work)
618
619 // found resources, and they are sealed
620 CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
621 CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
622 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
623 (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
624
625 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
626 CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
627
628 // scan through the resources on disk, checking each against the resourceDirectory
629 mResourcesValidContext = new CollectingContext(*this); // collect all failures in here
630 ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
631 mRep->adjustResources(resources);
632 string path;
633 ResourceBuilder::Rule *rule;
634
635 while (resources.next(path, rule)) {
636 validateResource(path, *mResourcesValidContext);
637 CFDictionaryRemoveValue(resourceMap, CFTempString(path));
638 }
639
640 if (CFDictionaryGetCount(resourceMap) > 0) {
641 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
642 CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
643 }
644
645 // now check for any errors found in the reporting context
646 mResourcesValidated = true;
647 if (mResourcesValidContext->osStatus() != noErr)
648 mResourcesValidContext->throwMe();
649
650 } catch (const CommonError &err) {
651 mResourcesValidated = true;
652 mResourcesValidResult = err.osStatus();
653 throw;
654 } catch (...) {
655 secdebug("staticCode", "%p executable validation threw non-common exception", this);
656 mResourcesValidated = true;
657 mResourcesValidResult = errSecCSInternalError;
658 throw;
659 }
660 }
661 assert(validatedResources());
662 if (mResourcesValidResult)
663 MacOSError::throwMe(mResourcesValidResult);
664 if (mResourcesValidContext->osStatus() != noErr)
665 mResourcesValidContext->throwMe();
666 }
667
668
669 void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
670 {
671 CollectingContext *ctx = static_cast<CollectingContext *>(context);
672 ResourceSeal seal(value);
673 if (!seal.optional()) {
674 if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
675 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing,
676 CFTempURL(CFStringRef(key), false, ctx->code.resourceBase()));
677 } else {
678 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
679 }
680 }
681 }
682
683
684 //
685 // Load, validate, cache, and return CFDictionary forms of sealed resources.
686 //
687 CFDictionaryRef SecStaticCode::infoDictionary()
688 {
689 if (!mInfoDict) {
690 mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
691 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
692 }
693 return mInfoDict;
694 }
695
696 CFDictionaryRef SecStaticCode::entitlements()
697 {
698 if (!mEntitlements) {
699 validateDirectory();
700 if (CFDataRef entitlementData = component(cdEntitlementSlot)) {
701 validateComponent(cdEntitlementSlot);
702 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
703 if (blob->validateBlob()) {
704 mEntitlements.take(blob->entitlements());
705 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
706 }
707 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
708 }
709 }
710 return mEntitlements;
711 }
712
713 CFDictionaryRef SecStaticCode::resourceDictionary()
714 {
715 if (mResourceDict) // cached
716 return mResourceDict;
717 if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, errSecCSSignatureFailed))
718 if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
719 secdebug("staticCode", "%p loaded ResourceDict %p",
720 this, mResourceDict.get());
721 return mResourceDict = dict;
722 }
723 // bad format
724 return NULL;
725 }
726
727
728 //
729 // Load and cache the resource directory base.
730 // Note that the base is optional for each DiskRep.
731 //
732 CFURLRef SecStaticCode::resourceBase()
733 {
734 if (!mGotResourceBase) {
735 string base = mRep->resourcesRootPath();
736 if (!base.empty())
737 mResourceBase.take(makeCFURL(base, true));
738 mGotResourceBase = true;
739 }
740 return mResourceBase;
741 }
742
743
744 //
745 // Load a component, validate it, convert it to a CFDictionary, and return that.
746 // This will force load and validation, which means that it will perform basic
747 // validation if it hasn't been done yet.
748 //
749 CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
750 {
751 validateDirectory();
752 if (CFDataRef infoData = component(slot, fail)) {
753 validateComponent(slot, fail);
754 if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
755 return dict;
756 else
757 MacOSError::throwMe(errSecCSBadDictionaryFormat);
758 }
759 return NULL;
760 }
761
762
763 //
764 // Load, validate, and return a sealed resource.
765 // The resource data (loaded in to memory as a blob) is returned and becomes
766 // the responsibility of the caller; it is NOT cached by SecStaticCode.
767 //
768 // A resource that is not sealed will not be returned, and an error will be thrown.
769 // A missing resource will cause an error unless it's marked optional in the Directory.
770 // Under no circumstances will a corrupt resource be returned.
771 // NULL will only be returned for a resource that is neither sealed nor present
772 // (or that is sealed, absent, and marked optional).
773 // If the ResourceDictionary itself is not sealed, this function will always fail.
774 //
775 // There is currently no interface for partial retrieval of the resource data.
776 // (Since the ResourceDirectory does not currently support segmentation, all the
777 // data would have to be read anyway, but it could be read into a reusable buffer.)
778 //
779 CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
780 {
781 if (CFDictionaryRef rdict = resourceDictionary()) {
782 if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
783 ResourceSeal seal = file;
784 if (!resourceBase()) // no resources in DiskRep
785 MacOSError::throwMe(errSecCSResourcesNotFound);
786 CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
787 if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
788 MakeHash<CodeDirectory> hasher(this->codeDirectory());
789 hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
790 if (hasher->verify(seal.hash()))
791 return data.yield(); // good
792 else
793 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
794 } else {
795 if (!seal.optional())
796 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
797 else
798 return NULL; // validly missing
799 }
800 } else
801 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
802 return NULL;
803 } else
804 MacOSError::throwMe(errSecCSResourcesNotSealed);
805 }
806
807 CFDataRef SecStaticCode::resource(string path)
808 {
809 ValidationContext ctx;
810 return resource(path, ctx);
811 }
812
813
814 void SecStaticCode::validateResource(string path, ValidationContext &ctx)
815 {
816 if (CFDictionaryRef rdict = resourceDictionary()) {
817 if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
818 ResourceSeal seal = file;
819 if (!resourceBase()) // no resources in DiskRep
820 MacOSError::throwMe(errSecCSResourcesNotFound);
821 CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
822 AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional filee
823 if (fd) {
824 MakeHash<CodeDirectory> hasher(this->codeDirectory());
825 hashFileData(fd, hasher.get());
826 if (hasher->verify(seal.hash()))
827 return; // verify good
828 else
829 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
830 } else {
831 if (!seal.optional())
832 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
833 else
834 return; // validly missing
835 }
836 } else
837 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
838 } else
839 MacOSError::throwMe(errSecCSResourcesNotSealed);
840 }
841
842
843 //
844 // Test a CodeDirectory flag.
845 // Returns false if there is no CodeDirectory.
846 // May throw if the CodeDirectory is present but somehow invalid.
847 //
848 bool SecStaticCode::flag(uint32_t tested)
849 {
850 if (const CodeDirectory *cd = this->codeDirectory(false))
851 return cd->flags & tested;
852 else
853 return false;
854 }
855
856
857 //
858 // Retrieve the full SuperBlob containing all internal requirements.
859 //
860 const Requirements *SecStaticCode::internalRequirements()
861 {
862 if (CFDataRef req = component(cdRequirementsSlot))
863 return (const Requirements *)CFDataGetBytePtr(req);
864 else
865 return NULL;
866 }
867
868
869 //
870 // Retrieve a particular internal requirement by type.
871 //
872 const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
873 {
874 if (const Requirements *reqs = internalRequirements())
875 return reqs->find<Requirement>(type);
876 else
877 return NULL;
878 }
879
880
881 //
882 // Return the Designated Requirement (DR). This can be either explicit in the
883 // Internal Requirements component, or implicitly generated on demand here.
884 // Note that an explicit DR may have been implicitly generated at signing time;
885 // we don't distinguish this case.
886 //
887 const Requirement *SecStaticCode::designatedRequirement()
888 {
889 if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
890 return req; // explicit in signing data
891 } else {
892 if (!mDesignatedReq)
893 mDesignatedReq = defaultDesignatedRequirement();
894 return mDesignatedReq;
895 }
896 }
897
898
899 //
900 // Generate the default Designated Requirement (DR) for this StaticCode.
901 // Ignore any explicit DR it may contain.
902 //
903 const Requirement *SecStaticCode::defaultDesignatedRequirement()
904 {
905 if (flag(kSecCodeSignatureAdhoc)) {
906 // adhoc signature: return a plain cdhash requirement
907 Requirement::Maker maker;
908 SHA1 hash;
909 hash(codeDirectory(), codeDirectory()->length());
910 SHA1::Digest digest;
911 hash.finish(digest);
912 maker.cdhash(digest);
913 return maker.make();
914 } else {
915 // full signature: Gin up full context and let DRMaker do its thing
916 validateDirectory(); // need the cert chain
917 Requirement::Context context(this->certificates(),
918 this->infoDictionary(),
919 this->entitlements(),
920 this->identifier(),
921 this->codeDirectory()
922 );
923 return DRMaker(context).make();
924 }
925 }
926
927
928 //
929 // Validate a SecStaticCode against the internal requirement of a particular type.
930 //
931 void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
932 OSStatus nullError /* = noErr */)
933 {
934 DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
935 if (const Requirement *req = internalRequirement(type))
936 target->validateRequirement(req, nullError ? nullError : errSecCSReqFailed);
937 else if (nullError)
938 MacOSError::throwMe(nullError);
939 else
940 /* accept it */;
941 }
942
943
944 //
945 // Validate this StaticCode against an external Requirement
946 //
947 bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failure)
948 {
949 assert(req);
950 validateDirectory();
951 return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
952 }
953
954 void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure)
955 {
956 if (!this->satisfiesRequirement(req, failure))
957 MacOSError::throwMe(failure);
958 }
959
960
961 //
962 // Retrieve one certificate from the cert chain.
963 // Positive and negative indices can be used:
964 // [ leaf, intermed-1, ..., intermed-n, anchor ]
965 // 0 1 ... -2 -1
966 // Returns NULL if unavailable for any reason.
967 //
968 SecCertificateRef SecStaticCode::cert(int ix)
969 {
970 validateDirectory(); // need cert chain
971 if (mCertChain) {
972 CFIndex length = CFArrayGetCount(mCertChain);
973 if (ix < 0)
974 ix += length;
975 if (ix >= 0 && ix < length)
976 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, ix));
977 }
978 return NULL;
979 }
980
981 CFArrayRef SecStaticCode::certificates()
982 {
983 validateDirectory(); // need cert chain
984 return mCertChain;
985 }
986
987
988 //
989 // Gather (mostly) API-official information about this StaticCode.
990 //
991 // This method lives in the twilight between the API and internal layers,
992 // since it generates API objects (Sec*Refs) for return.
993 //
994 CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
995 {
996 //
997 // Start with the pieces that we return even for unsigned code.
998 // This makes Sec[Static]CodeRefs useful as API-level replacements
999 // of our internal OSXCode objects.
1000 //
1001 CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
1002 kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
1003 );
1004
1005 //
1006 // If we're not signed, this is all you get
1007 //
1008 if (!this->isSigned())
1009 return dict.yield();
1010
1011 //
1012 // Add the generic attributes that we always include
1013 //
1014 CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
1015 CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
1016 CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
1017 CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
1018 CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(this->codeDirectory(false)->hashType));
1019
1020 //
1021 // Deliver any Info.plist only if it looks intact
1022 //
1023 try {
1024 if (CFDictionaryRef info = this->infoDictionary())
1025 CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
1026 } catch (...) { } // don't deliver Info.plist if questionable
1027
1028 //
1029 // kSecCSSigningInformation adds information about signing certificates and chains
1030 //
1031 if (flags & kSecCSSigningInformation) {
1032 if (CFArrayRef certs = this->certificates())
1033 CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
1034 if (CFDataRef sig = this->signature())
1035 CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
1036 if (mTrust)
1037 CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
1038 if (CFAbsoluteTime time = this->signingTime())
1039 if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1040 CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
1041 if (CFAbsoluteTime time = this->signingTimestamp())
1042 if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1043 CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
1044 }
1045
1046 //
1047 // kSecCSRequirementInformation adds information on requirements
1048 //
1049 if (flags & kSecCSRequirementInformation) {
1050 if (const Requirements *reqs = this->internalRequirements()) {
1051 CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
1052 CFTempString(Dumper::dump(reqs)));
1053 CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
1054 }
1055
1056 const Requirement *dreq = this->designatedRequirement();
1057 CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
1058 CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
1059 if (this->internalRequirement(kSecDesignatedRequirementType)) { // explicit
1060 CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1061 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
1062 } else { // implicit
1063 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
1064 }
1065
1066 if (CFDataRef ent = this->component(cdEntitlementSlot)) {
1067 CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
1068 if (CFDictionaryRef entdict = this->entitlements())
1069 CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
1070 }
1071 }
1072
1073 //
1074 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1075 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1076 // to reliably transmit through the API wall so that code outside the Security.framework
1077 // can use it without having to play nasty tricks to get it.
1078 //
1079 if (flags & kSecCSInternalInformation) {
1080 if (mDir)
1081 CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
1082 CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
1083 if (CFDictionaryRef resources = resourceDictionary())
1084 CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, resources);
1085 }
1086
1087
1088 //
1089 // kSecCSContentInformation adds more information about the physical layout
1090 // of the signed code. This is (only) useful for packaging or patching-oriented
1091 // applications.
1092 //
1093 if (flags & kSecCSContentInformation)
1094 if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
1095 CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
1096
1097 return dict.yield();
1098 }
1099
1100
1101 //
1102 // Resource validation contexts.
1103 // The default context simply throws a CSError, rudely terminating the operation.
1104 //
1105 SecStaticCode::ValidationContext::~ValidationContext()
1106 { /* virtual */ }
1107
1108 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1109 {
1110 CSError::throwMe(rc, type, value);
1111 }
1112
1113 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1114 {
1115 if (mStatus == noErr)
1116 mStatus = rc; // record first failure for eventual error return
1117 if (type) {
1118 if (!mCollection)
1119 mCollection.take(makeCFMutableDictionary());
1120 CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
1121 if (!element) {
1122 element = makeCFMutableArray(0);
1123 if (!element)
1124 CFError::throwMe();
1125 CFDictionaryAddValue(mCollection, type, element);
1126 CFRelease(element);
1127 }
1128 CFArrayAppendValue(element, value);
1129 }
1130 }
1131
1132 void SecStaticCode::CollectingContext::throwMe()
1133 {
1134 assert(mStatus != noErr);
1135 throw CSError(mStatus, mCollection.retain());
1136 }
1137
1138
1139 //
1140 // SecStaticCode::AllArchitectures produces SecStaticCode objects separately
1141 // for each architecture represented by a base object.
1142 //
1143 // Performance note: This is a simple, straight-forward implementation that
1144 // does not heroically try to share resources between the code objects produced.
1145 // In practice, this means we'll re-open files and re-read resource files.
1146 // In exchange, we enter all the code paths in the normal way, and do not have
1147 // special sharing paths to worry about.
1148 // If a performance tool brings you here because you have *proof* of a performance
1149 // problem, consider digging up MachO and Universal (for sharing file descriptors),
1150 // and SecStaticCode (for sharing resource iterators). That ought to cover most of
1151 // the big chunks. If you're just offended by the simplicity of this implementation,
1152 // go play somewhere else.
1153 //
1154 SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode *code)
1155 : mBase(code)
1156 {
1157 if (Universal *fat = code->diskRep()->mainExecutableImage()) {
1158 fat->architectures(mArchitectures);
1159 mCurrent = mArchitectures.begin();
1160 mState = fatBinary;
1161 } else {
1162 mState = firstNonFat;
1163 }
1164 }
1165
1166 SecStaticCode *SecStaticCode::AllArchitectures::operator () ()
1167 {
1168 switch (mState) {
1169 case firstNonFat:
1170 mState = atEnd;
1171 return mBase;
1172 case fatBinary:
1173 {
1174 if (mCurrent == mArchitectures.end())
1175 return NULL;
1176 Architecture arch = *mCurrent++;
1177 if (arch == mBase->diskRep()->mainExecutableImage()->bestNativeArch()) {
1178 return mBase;
1179 } else {
1180 DiskRep::Context ctx;
1181 ctx.arch = arch;
1182 return new SecStaticCode(DiskRep::bestGuess(mBase->mainExecutablePath(), &ctx));
1183 }
1184 }
1185 default:
1186 return NULL;
1187 }
1188 }
1189
1190
1191 } // end namespace CodeSigning
1192 } // end namespace Security