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