]> git.saurik.com Git - apple/security.git/blob - libsecurity_codesigning/lib/StaticCode.cpp
Security-55179.11.tar.gz
[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 // Expiration is fatal (throws) if a secure timestamp is included, but not otherwise.
387 //
388 bool SecStaticCode::verifySignature()
389 {
390 // ad-hoc signed code is considered validly signed by definition
391 if (flag(kSecCodeSignatureAdhoc)) {
392 CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
393 return false;
394 }
395
396 DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
397
398 // decode CMS and extract SecTrust for verification
399 CFRef<CMSDecoderRef> cms;
400 MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
401 CFDataRef sig = this->signature();
402 MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
403 this->codeDirectory(); // load CodeDirectory (sets mDir)
404 MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
405 MacOSError::check(CMSDecoderFinalizeMessage(cms));
406 MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
407 CFRef<CFTypeRef> policy = verificationPolicy(apiFlags());
408 CMSSignerStatus status;
409 MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, policy,
410 false, &status, &mTrust.aref(), NULL));
411 if (status != kCMSSignerValid)
412 MacOSError::throwMe(errSecCSSignatureFailed);
413
414 // internal signing time (as specified by the signer; optional)
415 mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
416 switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
417 case noErr:
418 case errSecSigningTimeMissing:
419 break;
420 default:
421 MacOSError::throwMe(rc);
422 }
423
424 // certified signing time (as specified by a TSA; optional)
425 mSigningTimestamp = 0;
426 switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) {
427 case noErr:
428 case errSecTimestampMissing:
429 break;
430 default:
431 MacOSError::throwMe(rc);
432 }
433
434 // set up the environment for SecTrust
435 MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
436 MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); // no keychains
437 CSSM_APPLE_TP_ACTION_DATA actionData = {
438 CSSM_APPLE_TP_ACTION_VERSION, // version of data structure
439 CSSM_TP_ACTION_IMPLICIT_ANCHORS // action flags
440 };
441
442 for (;;) { // at most twice
443 MacOSError::check(SecTrustSetParameters(mTrust,
444 CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
445
446 // evaluate trust and extract results
447 SecTrustResultType trustResult;
448 MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
449 MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
450 CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0);
451 switch (trustResult) {
452 case kSecTrustResultProceed:
453 case kSecTrustResultUnspecified:
454 break; // success
455 case kSecTrustResultDeny:
456 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY); // user reject
457 case kSecTrustResultInvalid:
458 assert(false); // should never happen
459 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
460 default:
461 {
462 OSStatus result;
463 MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
464 // if we have a valid timestamp, CMS validates against (that) signing time and all is well.
465 // If we don't have one, may validate against *now*, and must be able to tolerate expiration.
466 if (mSigningTimestamp == 0) // no timestamp available
467 if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
468 && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
469 CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
470 actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; // (this also allows postdated certs)
471 continue; // retry validation while tolerating expiration
472 }
473 MacOSError::throwMe(result);
474 }
475 }
476
477 if (mSigningTimestamp) {
478 CFIndex rootix = CFArrayGetCount(mCertChain);
479 if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
480 if (isAppleCA(mainRoot)) {
481 // impose policy: if the signature itself draws to Apple, then so must the timestamp signature
482 CFRef<CFArrayRef> tsCerts;
483 MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref()));
484 CFIndex tsn = CFArrayGetCount(tsCerts);
485 bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
486 #ifndef WORKAROUND_12007637
487 // TS certificates are reordered weirdly; check them all
488 for (CFIndex n = 0; n < tsn; n++)
489 if (SecCertificateRef tsRoot = SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, n)))
490 if ((good = isAppleCA(tsRoot))) {
491 secdebug("BUG", "Apple root at TS cert %ld", n);
492 break;
493 }
494 #endif //WORKAROUND_12007637
495 if (!good)
496 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
497 }
498 }
499
500 return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
501 }
502 }
503
504
505 //
506 // Return the TP policy used for signature verification.
507 // This may be a simple SecPolicyRef or a CFArray of policies.
508 // The caller owns the return value.
509 //
510 static SecPolicyRef makeCRLPolicy()
511 {
512 CFRef<SecPolicyRef> policy;
513 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
514 CSSM_APPLE_TP_CRL_OPTIONS options;
515 memset(&options, 0, sizeof(options));
516 options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
517 options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
518 CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
519 MacOSError::check(SecPolicySetValue(policy, &optData));
520 return policy.yield();
521 }
522
523 static SecPolicyRef makeOCSPPolicy()
524 {
525 CFRef<SecPolicyRef> policy;
526 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
527 CSSM_APPLE_TP_OCSP_OPTIONS options;
528 memset(&options, 0, sizeof(options));
529 options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
530 options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
531 CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
532 MacOSError::check(SecPolicySetValue(policy, &optData));
533 return policy.yield();
534 }
535
536 CFTypeRef SecStaticCode::verificationPolicy(SecCSFlags flags)
537 {
538 CFRef<SecPolicyRef> core;
539 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
540 &CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
541 if (flags & kSecCSEnforceRevocationChecks) {
542 CFRef<SecPolicyRef> crl = makeCRLPolicy();
543 CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
544 return makeCFArray(3, core.get(), crl.get(), ocsp.get());
545 } else {
546 return core.yield();
547 }
548 }
549
550
551 //
552 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
553 // The resource must already have been placed in the cache.
554 // This does NOT perform basic validation.
555 //
556 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
557 {
558 assert(slot <= cdSlotMax);
559 CFDataRef data = mCache[slot];
560 assert(data); // must be cached
561 if (data == CFDataRef(kCFNull)) {
562 if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
563 MacOSError::throwMe(fail); // ... and is missing
564 } else {
565 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
566 MacOSError::throwMe(fail);
567 }
568 }
569
570
571 //
572 // Perform static validation of the main executable.
573 // This reads the main executable from disk and validates it against the
574 // CodeDirectory code slot array.
575 // Note that this is NOT an in-memory validation, and is thus potentially
576 // subject to timing attacks.
577 //
578 void SecStaticCode::validateExecutable()
579 {
580 if (!validatedExecutable()) {
581 try {
582 DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
583 (char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
584 const CodeDirectory *cd = this->codeDirectory();
585 if (!cd)
586 MacOSError::throwMe(errSecCSUnsigned);
587 AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
588 fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass)
589 if (Universal *fat = mRep->mainExecutableImage())
590 fd.seek(fat->archOffset());
591 size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
592 size_t remaining = cd->codeLimit;
593 for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
594 size_t size = min(remaining, pageSize);
595 if (!cd->validateSlot(fd, size, slot)) {
596 CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
597 MacOSError::throwMe(errSecCSSignatureFailed);
598 }
599 remaining -= size;
600 }
601 mExecutableValidated = true;
602 mExecutableValidResult = noErr;
603 } catch (const CommonError &err) {
604 mExecutableValidated = true;
605 mExecutableValidResult = err.osStatus();
606 throw;
607 } catch (...) {
608 secdebug("staticCode", "%p executable validation threw non-common exception", this);
609 mExecutableValidated = true;
610 mExecutableValidResult = errSecCSInternalError;
611 throw;
612 }
613 }
614 assert(validatedExecutable());
615 if (mExecutableValidResult != noErr)
616 MacOSError::throwMe(mExecutableValidResult);
617 }
618
619
620 //
621 // Perform static validation of sealed resources.
622 //
623 // This performs a whole-code static resource scan and effectively
624 // computes a concordance between what's on disk and what's in the ResourceDirectory.
625 // Any unsanctioned difference causes an error.
626 //
627 void SecStaticCode::validateResources()
628 {
629 if (!validatedResources()) {
630 try {
631 // sanity first
632 CFDictionaryRef sealedResources = resourceDictionary();
633 if (this->resourceBase()) // disk has resources
634 if (sealedResources)
635 /* go to work below */;
636 else
637 MacOSError::throwMe(errSecCSResourcesNotFound);
638 else // disk has no resources
639 if (sealedResources)
640 MacOSError::throwMe(errSecCSResourcesNotFound);
641 else
642 return; // no resources, not sealed - fine (no work)
643
644 // found resources, and they are sealed
645 CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
646 CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
647 DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
648 (char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
649
650 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
651 CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
652
653 // scan through the resources on disk, checking each against the resourceDirectory
654 mResourcesValidContext = new CollectingContext(*this); // collect all failures in here
655 ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
656 mRep->adjustResources(resources);
657 string path;
658 ResourceBuilder::Rule *rule;
659
660 while (resources.next(path, rule)) {
661 validateResource(path, *mResourcesValidContext);
662 CFDictionaryRemoveValue(resourceMap, CFTempString(path));
663 }
664
665 if (CFDictionaryGetCount(resourceMap) > 0) {
666 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
667 CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
668 }
669
670 // now check for any errors found in the reporting context
671 mResourcesValidated = true;
672 if (mResourcesValidContext->osStatus() != noErr)
673 mResourcesValidContext->throwMe();
674
675 } catch (const CommonError &err) {
676 mResourcesValidated = true;
677 mResourcesValidResult = err.osStatus();
678 throw;
679 } catch (...) {
680 secdebug("staticCode", "%p executable validation threw non-common exception", this);
681 mResourcesValidated = true;
682 mResourcesValidResult = errSecCSInternalError;
683 throw;
684 }
685 }
686 assert(validatedResources());
687 if (mResourcesValidResult)
688 MacOSError::throwMe(mResourcesValidResult);
689 if (mResourcesValidContext->osStatus() != noErr)
690 mResourcesValidContext->throwMe();
691 }
692
693
694 void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
695 {
696 CollectingContext *ctx = static_cast<CollectingContext *>(context);
697 ResourceSeal seal(value);
698 if (!seal.optional()) {
699 if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
700 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing,
701 CFTempURL(CFStringRef(key), false, ctx->code.resourceBase()));
702 } else {
703 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
704 }
705 }
706 }
707
708
709 //
710 // Load, validate, cache, and return CFDictionary forms of sealed resources.
711 //
712 CFDictionaryRef SecStaticCode::infoDictionary()
713 {
714 if (!mInfoDict) {
715 mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
716 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
717 }
718 return mInfoDict;
719 }
720
721 CFDictionaryRef SecStaticCode::entitlements()
722 {
723 if (!mEntitlements) {
724 validateDirectory();
725 if (CFDataRef entitlementData = component(cdEntitlementSlot)) {
726 validateComponent(cdEntitlementSlot);
727 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
728 if (blob->validateBlob()) {
729 mEntitlements.take(blob->entitlements());
730 secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
731 }
732 // we do not consider a different blob type to be an error. We think it's a new format we don't understand
733 }
734 }
735 return mEntitlements;
736 }
737
738 CFDictionaryRef SecStaticCode::resourceDictionary()
739 {
740 if (mResourceDict) // cached
741 return mResourceDict;
742 if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, errSecCSSignatureFailed))
743 if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
744 secdebug("staticCode", "%p loaded ResourceDict %p",
745 this, mResourceDict.get());
746 return mResourceDict = dict;
747 }
748 // bad format
749 return NULL;
750 }
751
752
753 //
754 // Load and cache the resource directory base.
755 // Note that the base is optional for each DiskRep.
756 //
757 CFURLRef SecStaticCode::resourceBase()
758 {
759 if (!mGotResourceBase) {
760 string base = mRep->resourcesRootPath();
761 if (!base.empty())
762 mResourceBase.take(makeCFURL(base, true));
763 mGotResourceBase = true;
764 }
765 return mResourceBase;
766 }
767
768
769 //
770 // Load a component, validate it, convert it to a CFDictionary, and return that.
771 // This will force load and validation, which means that it will perform basic
772 // validation if it hasn't been done yet.
773 //
774 CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail /* = errSecCSSignatureFailed */)
775 {
776 validateDirectory();
777 if (CFDataRef infoData = component(slot, fail)) {
778 validateComponent(slot, fail);
779 if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
780 return dict;
781 else
782 MacOSError::throwMe(errSecCSBadDictionaryFormat);
783 }
784 return NULL;
785 }
786
787
788 //
789 // Load, validate, and return a sealed resource.
790 // The resource data (loaded in to memory as a blob) is returned and becomes
791 // the responsibility of the caller; it is NOT cached by SecStaticCode.
792 //
793 // A resource that is not sealed will not be returned, and an error will be thrown.
794 // A missing resource will cause an error unless it's marked optional in the Directory.
795 // Under no circumstances will a corrupt resource be returned.
796 // NULL will only be returned for a resource that is neither sealed nor present
797 // (or that is sealed, absent, and marked optional).
798 // If the ResourceDictionary itself is not sealed, this function will always fail.
799 //
800 // There is currently no interface for partial retrieval of the resource data.
801 // (Since the ResourceDirectory does not currently support segmentation, all the
802 // data would have to be read anyway, but it could be read into a reusable buffer.)
803 //
804 CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
805 {
806 if (CFDictionaryRef rdict = resourceDictionary()) {
807 if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
808 ResourceSeal seal = file;
809 if (!resourceBase()) // no resources in DiskRep
810 MacOSError::throwMe(errSecCSResourcesNotFound);
811 CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
812 if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
813 MakeHash<CodeDirectory> hasher(this->codeDirectory());
814 hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
815 if (hasher->verify(seal.hash()))
816 return data.yield(); // good
817 else
818 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
819 } else {
820 if (!seal.optional())
821 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
822 else
823 return NULL; // validly missing
824 }
825 } else
826 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
827 return NULL;
828 } else
829 MacOSError::throwMe(errSecCSResourcesNotSealed);
830 }
831
832 CFDataRef SecStaticCode::resource(string path)
833 {
834 ValidationContext ctx;
835 return resource(path, ctx);
836 }
837
838
839 void SecStaticCode::validateResource(string path, ValidationContext &ctx)
840 {
841 if (CFDictionaryRef rdict = resourceDictionary()) {
842 if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
843 ResourceSeal seal = file;
844 if (!resourceBase()) // no resources in DiskRep
845 MacOSError::throwMe(errSecCSResourcesNotFound);
846 CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
847 AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); // open optional filee
848 if (fd) {
849 MakeHash<CodeDirectory> hasher(this->codeDirectory());
850 hashFileData(fd, hasher.get());
851 if (hasher->verify(seal.hash()))
852 return; // verify good
853 else
854 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
855 } else {
856 if (!seal.optional())
857 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
858 else
859 return; // validly missing
860 }
861 } else
862 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
863 } else
864 MacOSError::throwMe(errSecCSResourcesNotSealed);
865 }
866
867
868 //
869 // Test a CodeDirectory flag.
870 // Returns false if there is no CodeDirectory.
871 // May throw if the CodeDirectory is present but somehow invalid.
872 //
873 bool SecStaticCode::flag(uint32_t tested)
874 {
875 if (const CodeDirectory *cd = this->codeDirectory(false))
876 return cd->flags & tested;
877 else
878 return false;
879 }
880
881
882 //
883 // Retrieve the full SuperBlob containing all internal requirements.
884 //
885 const Requirements *SecStaticCode::internalRequirements()
886 {
887 if (CFDataRef req = component(cdRequirementsSlot))
888 return (const Requirements *)CFDataGetBytePtr(req);
889 else
890 return NULL;
891 }
892
893
894 //
895 // Retrieve a particular internal requirement by type.
896 //
897 const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
898 {
899 if (const Requirements *reqs = internalRequirements())
900 return reqs->find<Requirement>(type);
901 else
902 return NULL;
903 }
904
905
906 //
907 // Return the Designated Requirement (DR). This can be either explicit in the
908 // Internal Requirements component, or implicitly generated on demand here.
909 // Note that an explicit DR may have been implicitly generated at signing time;
910 // we don't distinguish this case.
911 //
912 const Requirement *SecStaticCode::designatedRequirement()
913 {
914 if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
915 return req; // explicit in signing data
916 } else {
917 if (!mDesignatedReq)
918 mDesignatedReq = defaultDesignatedRequirement();
919 return mDesignatedReq;
920 }
921 }
922
923
924 //
925 // Generate the default Designated Requirement (DR) for this StaticCode.
926 // Ignore any explicit DR it may contain.
927 //
928 const Requirement *SecStaticCode::defaultDesignatedRequirement()
929 {
930 if (flag(kSecCodeSignatureAdhoc)) {
931 // adhoc signature: return a plain cdhash requirement
932 Requirement::Maker maker;
933 SHA1 hash;
934 hash(codeDirectory(), codeDirectory()->length());
935 SHA1::Digest digest;
936 hash.finish(digest);
937 maker.cdhash(digest);
938 return maker.make();
939 } else {
940 // full signature: Gin up full context and let DRMaker do its thing
941 validateDirectory(); // need the cert chain
942 Requirement::Context context(this->certificates(),
943 this->infoDictionary(),
944 this->entitlements(),
945 this->identifier(),
946 this->codeDirectory()
947 );
948 return DRMaker(context).make();
949 }
950 }
951
952
953 //
954 // Validate a SecStaticCode against the internal requirement of a particular type.
955 //
956 void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
957 OSStatus nullError /* = noErr */)
958 {
959 DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
960 if (const Requirement *req = internalRequirement(type))
961 target->validateRequirement(req, nullError ? nullError : errSecCSReqFailed);
962 else if (nullError)
963 MacOSError::throwMe(nullError);
964 else
965 /* accept it */;
966 }
967
968
969 //
970 // Validate this StaticCode against an external Requirement
971 //
972 bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failure)
973 {
974 assert(req);
975 validateDirectory();
976 return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
977 }
978
979 void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure)
980 {
981 if (!this->satisfiesRequirement(req, failure))
982 MacOSError::throwMe(failure);
983 }
984
985
986 //
987 // Retrieve one certificate from the cert chain.
988 // Positive and negative indices can be used:
989 // [ leaf, intermed-1, ..., intermed-n, anchor ]
990 // 0 1 ... -2 -1
991 // Returns NULL if unavailable for any reason.
992 //
993 SecCertificateRef SecStaticCode::cert(int ix)
994 {
995 validateDirectory(); // need cert chain
996 if (mCertChain) {
997 CFIndex length = CFArrayGetCount(mCertChain);
998 if (ix < 0)
999 ix += length;
1000 if (ix >= 0 && ix < length)
1001 return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, ix));
1002 }
1003 return NULL;
1004 }
1005
1006 CFArrayRef SecStaticCode::certificates()
1007 {
1008 validateDirectory(); // need cert chain
1009 return mCertChain;
1010 }
1011
1012
1013 //
1014 // Gather (mostly) API-official information about this StaticCode.
1015 //
1016 // This method lives in the twilight between the API and internal layers,
1017 // since it generates API objects (Sec*Refs) for return.
1018 //
1019 CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
1020 {
1021 //
1022 // Start with the pieces that we return even for unsigned code.
1023 // This makes Sec[Static]CodeRefs useful as API-level replacements
1024 // of our internal OSXCode objects.
1025 //
1026 CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
1027 kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
1028 );
1029
1030 //
1031 // If we're not signed, this is all you get
1032 //
1033 if (!this->isSigned())
1034 return dict.yield();
1035
1036 //
1037 // Add the generic attributes that we always include
1038 //
1039 CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
1040 CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
1041 CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
1042 CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
1043 CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(this->codeDirectory(false)->hashType));
1044
1045 //
1046 // Deliver any Info.plist only if it looks intact
1047 //
1048 try {
1049 if (CFDictionaryRef info = this->infoDictionary())
1050 CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
1051 } catch (...) { } // don't deliver Info.plist if questionable
1052
1053 //
1054 // kSecCSSigningInformation adds information about signing certificates and chains
1055 //
1056 if (flags & kSecCSSigningInformation) {
1057 if (CFArrayRef certs = this->certificates())
1058 CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
1059 if (CFDataRef sig = this->signature())
1060 CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
1061 if (mTrust)
1062 CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
1063 if (CFAbsoluteTime time = this->signingTime())
1064 if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1065 CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
1066 if (CFAbsoluteTime time = this->signingTimestamp())
1067 if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
1068 CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
1069 }
1070
1071 //
1072 // kSecCSRequirementInformation adds information on requirements
1073 //
1074 if (flags & kSecCSRequirementInformation) {
1075 if (const Requirements *reqs = this->internalRequirements()) {
1076 CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
1077 CFTempString(Dumper::dump(reqs)));
1078 CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
1079 }
1080
1081 const Requirement *dreq = this->designatedRequirement();
1082 CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
1083 CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
1084 if (this->internalRequirement(kSecDesignatedRequirementType)) { // explicit
1085 CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
1086 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
1087 } else { // implicit
1088 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
1089 }
1090
1091 if (CFDataRef ent = this->component(cdEntitlementSlot)) {
1092 CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
1093 if (CFDictionaryRef entdict = this->entitlements())
1094 CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
1095 }
1096 }
1097
1098 //
1099 // kSecCSInternalInformation adds internal information meant to be for Apple internal
1100 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
1101 // to reliably transmit through the API wall so that code outside the Security.framework
1102 // can use it without having to play nasty tricks to get it.
1103 //
1104 if (flags & kSecCSInternalInformation) {
1105 if (mDir)
1106 CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
1107 CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
1108 if (CFDictionaryRef resources = resourceDictionary())
1109 CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, resources);
1110 }
1111
1112
1113 //
1114 // kSecCSContentInformation adds more information about the physical layout
1115 // of the signed code. This is (only) useful for packaging or patching-oriented
1116 // applications.
1117 //
1118 if (flags & kSecCSContentInformation)
1119 if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
1120 CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
1121
1122 return dict.yield();
1123 }
1124
1125
1126 //
1127 // Resource validation contexts.
1128 // The default context simply throws a CSError, rudely terminating the operation.
1129 //
1130 SecStaticCode::ValidationContext::~ValidationContext()
1131 { /* virtual */ }
1132
1133 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1134 {
1135 CSError::throwMe(rc, type, value);
1136 }
1137
1138 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
1139 {
1140 if (mStatus == noErr)
1141 mStatus = rc; // record first failure for eventual error return
1142 if (type) {
1143 if (!mCollection)
1144 mCollection.take(makeCFMutableDictionary());
1145 CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
1146 if (!element) {
1147 element = makeCFMutableArray(0);
1148 if (!element)
1149 CFError::throwMe();
1150 CFDictionaryAddValue(mCollection, type, element);
1151 CFRelease(element);
1152 }
1153 CFArrayAppendValue(element, value);
1154 }
1155 }
1156
1157 void SecStaticCode::CollectingContext::throwMe()
1158 {
1159 assert(mStatus != noErr);
1160 throw CSError(mStatus, mCollection.retain());
1161 }
1162
1163
1164 //
1165 // SecStaticCode::AllArchitectures produces SecStaticCode objects separately
1166 // for each architecture represented by a base object.
1167 //
1168 // Performance note: This is a simple, straight-forward implementation that
1169 // does not heroically try to share resources between the code objects produced.
1170 // In practice, this means we'll re-open files and re-read resource files.
1171 // In exchange, we enter all the code paths in the normal way, and do not have
1172 // special sharing paths to worry about.
1173 // If a performance tool brings you here because you have *proof* of a performance
1174 // problem, consider digging up MachO and Universal (for sharing file descriptors),
1175 // and SecStaticCode (for sharing resource iterators). That ought to cover most of
1176 // the big chunks. If you're just offended by the simplicity of this implementation,
1177 // go play somewhere else.
1178 //
1179 SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode *code)
1180 : mBase(code)
1181 {
1182 if (Universal *fat = code->diskRep()->mainExecutableImage()) {
1183 fat->architectures(mArchitectures);
1184 mCurrent = mArchitectures.begin();
1185 mState = fatBinary;
1186 } else {
1187 mState = firstNonFat;
1188 }
1189 }
1190
1191 SecStaticCode *SecStaticCode::AllArchitectures::operator () ()
1192 {
1193 switch (mState) {
1194 case firstNonFat:
1195 mState = atEnd;
1196 return mBase;
1197 case fatBinary:
1198 {
1199 if (mCurrent == mArchitectures.end())
1200 return NULL;
1201 Architecture arch = *mCurrent++;
1202 if (arch == mBase->diskRep()->mainExecutableImage()->bestNativeArch()) {
1203 return mBase;
1204 } else {
1205 DiskRep::Context ctx;
1206 ctx.arch = arch;
1207 return new SecStaticCode(DiskRep::bestGuess(mBase->mainExecutablePath(), &ctx));
1208 }
1209 }
1210 default:
1211 return NULL;
1212 }
1213 }
1214
1215
1216 } // end namespace CodeSigning
1217 } // end namespace Security