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