]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/StaticCode.cpp
78d7208d07e750e93fd880986fc6364cb68347b2
[apple/libsecurity_codesigning.git] / lib / StaticCode.cpp
1 /*
2 * Copyright (c) 2006-2007 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 "csutilities.h"
35 #include <CoreFoundation/CFURLAccess.h>
36 #include <Security/SecPolicyPriv.h>
37 #include <Security/SecTrustPriv.h>
38 #include <Security/CMSPrivate.h>
39 #include <Security/SecCmsContentInfo.h>
40 #include <Security/SecCmsSignerInfo.h>
41 #include <Security/SecCmsSignedData.h>
42 #include <security_utilities/unix++.h>
43 #include <security_codesigning/cfmunge.h>
44 #include <Security/CMSDecoder.h>
45
46
47 namespace Security {
48 namespace CodeSigning {
49
50 using namespace UnixPlusPlus;
51
52
53 //
54 // We use a DetachedRep to interpose (filter) the genuine DiskRep representing
55 // the code on disk, *if* a detached signature was set on this object. In this
56 // situation, mRep will point to a (2 element) chain of DiskReps.
57 //
58 // This is a neat way of dealing with the (unusual) detached-signature case
59 // without disturbing things unduly. Consider DetachedDiskRep to be closely
60 // married to SecStaticCode; it's unlikely to work right if you use it elsewhere.
61 //
62 class DetachedRep : public DiskRep {
63 public:
64 DetachedRep(CFDataRef sig, DiskRep *orig);
65
66 const RefPointer<DiskRep> original; // underlying representation
67
68 DiskRep *base() { return original; }
69 CFDataRef component(CodeDirectory::SpecialSlot slot);
70 std::string mainExecutablePath() { return original->mainExecutablePath(); }
71 CFURLRef canonicalPath() { return original->canonicalPath(); }
72 std::string recommendedIdentifier() { return original->recommendedIdentifier(); }
73 std::string resourcesRootPath() { return original->resourcesRootPath(); }
74 CFDictionaryRef defaultResourceRules() { return original->defaultResourceRules(); }
75 Universal *mainExecutableImage() { return original->mainExecutableImage(); }
76 size_t signingBase() { return original->signingBase(); }
77 size_t signingLimit() { return original->signingLimit(); }
78 std::string format() { return original->format(); }
79 FileDesc &fd() { return original->fd(); }
80 void flush() { return original->flush(); }
81
82 private:
83 CFCopyRef<CFDataRef> mSignature;
84 const EmbeddedSignatureBlob *mArch; // current architecture; points into mSignature
85 const EmbeddedSignatureBlob *mGlobal; // shared elements; points into mSignature
86 };
87
88
89 //
90 // Construct a SecStaticCode object given a disk representation object
91 //
92 SecStaticCode::SecStaticCode(DiskRep *rep)
93 : mRep(rep),
94 mValidated(false), mExecutableValidated(false),
95 mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
96 {
97 }
98
99
100 //
101 // Clean up a SecStaticCode object
102 //
103 SecStaticCode::~SecStaticCode() throw()
104 {
105 ::free(const_cast<Requirement *>(mDesignatedReq));
106 }
107
108
109 //
110 // Attach a detached signature.
111 //
112 void SecStaticCode::detachedSignature(CFDataRef sigData)
113 {
114 if (sigData)
115 mRep = new DetachedRep(sigData, mRep->base());
116 else
117 mRep = mRep->base();
118 }
119
120
121 //
122 // Do ::required, but convert incoming SecCodeRefs to their SecStaticCodeRefs
123 // (if possible).
124 //
125 SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref)
126 {
127 SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
128 if (SecStaticCode *scode = dynamic_cast<SecStaticCode *>(object))
129 return scode;
130 else if (SecCode *code = dynamic_cast<SecCode *>(object))
131 return code->staticCode();
132 else // neither (a SecSomethingElse)
133 MacOSError::throwMe(errSecCSInvalidObjectRef);
134 }
135
136 SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref)
137 {
138 SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
139 if (dynamic_cast<SecStaticCode *>(object))
140 return NULL;
141 else if (SecCode *code = dynamic_cast<SecCode *>(object))
142 return code;
143 else // neither (a SecSomethingElse)
144 MacOSError::throwMe(errSecCSInvalidObjectRef);
145 }
146
147
148 //
149 // Void all cached validity data.
150 //
151 // We also throw out cached components, because the new signature data may have
152 // a different idea of what components should be present. We could reconcile the
153 // cached data instead, if performance seems to be impacted.
154 //
155 void SecStaticCode::resetValidity()
156 {
157 secdebug("staticCode", "%p resetting validity status", this);
158 mValidated = false;
159 mExecutableValidated = false;
160 mDir = NULL;
161 mSignature = NULL;
162 for (unsigned n = 0; n < cdSlotCount; n++)
163 mCache[n] = NULL;
164 mInfoDict = NULL;
165 mResourceDict = NULL;
166 mDesignatedReq = NULL;
167 mTrust = NULL;
168 mCertChain = NULL;
169 mEvalDetails = NULL;
170 mRep->flush();
171 }
172
173
174 //
175 // Retrieve a sealed component by special slot index.
176 // If the CodeDirectory has already been validated, validate against that.
177 // Otherwise, retrieve the component without validation (but cache it). Validation
178 // will go through the cache and validate all cached components.
179 //
180 CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot)
181 {
182 assert(slot <= cdSlotMax);
183
184 CFRef<CFDataRef> &cache = mCache[slot];
185 if (!cache) {
186 if (CFRef<CFDataRef> data = mRep->component(slot)) {
187 if (validated()) // if the directory has been validated...
188 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), // ... and it's no good
189 CFDataGetLength(data), -slot))
190 MacOSError::throwMe(errSecCSSignatureFailed); // ... then bail
191 cache = data; // it's okay, cache it
192 } else { // absent, mark so
193 if (validated()) // if directory has been validated...
194 if (codeDirectory()->slotIsPresent(-slot)) // ... and the slot is NOT missing
195 MacOSError::throwMe(errSecCSSignatureFailed); // was supposed to be there
196 cache = CFDataRef(kCFNull); // white lie
197 }
198 }
199 return (cache == CFDataRef(kCFNull)) ? NULL : cache.get();
200 }
201
202
203 //
204 // Get the CodeDirectory.
205 // Throws (if check==true) or returns NULL (check==false) if there is none.
206 // Always throws if the CodeDirectory exists but is invalid.
207 // NEVER validates against the signature.
208 //
209 const CodeDirectory *SecStaticCode::codeDirectory(bool check /* = true */)
210 {
211 if (!mDir) {
212 if (mDir.take(mRep->codeDirectory())) {
213 const CodeDirectory *dir = reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
214 dir->checkVersion();
215 }
216 }
217 if (mDir)
218 return reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
219 if (check)
220 MacOSError::throwMe(errSecCSUnsigned);
221 return NULL;
222 }
223
224
225 //
226 // Return the CMS signature blob; NULL if none found.
227 //
228 CFDataRef SecStaticCode::signature()
229 {
230 if (!mSignature)
231 mSignature.take(mRep->signature());
232 if (mSignature)
233 return mSignature;
234 MacOSError::throwMe(errSecCSUnsigned);
235 }
236
237
238 //
239 // Verify the signature on the CodeDirectory.
240 // If this succeeds (doesn't throw), the CodeDirectory is statically trustworthy.
241 // Any outcome (successful or not) is cached for the lifetime of the StaticCode.
242 //
243 void SecStaticCode::validateDirectory()
244 {
245 // echo previous outcome, if any
246 if (!validated())
247 try {
248 // perform validation (or die trying)
249 secdebug("staticCode", "%p validating directory", this);
250 mValidationExpired = verifySignature();
251 component(cdInfoSlot); // force load of Info Dictionary (if any)
252 for (CodeDirectory::SpecialSlot slot = codeDirectory()->nSpecialSlots;
253 slot >= 1; --slot)
254 if (mCache[slot]) // if we already loaded that resource...
255 validateComponent(slot); // ... then check it now
256 mValidated = true; // we've done the deed...
257 mValidationResult = noErr; // ... and it was good
258 } catch (const CommonError &err) {
259 mValidated = true;
260 mValidationResult = err.osStatus();
261 throw;
262 } catch (...) {
263 secdebug("staticCode", "%p validation threw non-common exception", this);
264 mValidated = true;
265 mValidationResult = errSecCSInternalError;
266 throw;
267 }
268 assert(validated());
269 if (mValidationResult == noErr) {
270 if (mValidationExpired)
271 if ((apiFlags() & kSecCSConsiderExpiration)
272 || (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
273 MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
274 } else
275 MacOSError::throwMe(mValidationResult);
276 }
277
278
279 //
280 // Get the (signed) signing date from the code signature.
281 // Sadly, we need to validate the signature to get the date (as a side benefit).
282 // This means that you can't get the signing time for invalidly signed code.
283 //
284 // We could run the decoder "almost to" verification to avoid this, but there seems
285 // little practical point to such a duplication of effort.
286 //
287 CFAbsoluteTime SecStaticCode::signingTime()
288 {
289 validateDirectory();
290 return mSigningTime;
291 }
292
293
294 //
295 // Verify the CMS signature on the CodeDirectory.
296 // This performs the cryptographic tango. It returns if the signature is valid,
297 // or throws if it is not. As a side effect, a successful return sets up the
298 // cached certificate chain for future use.
299 //
300 bool SecStaticCode::verifySignature()
301 {
302 // ad-hoc signed code is considered validly signed by definition
303 if (flag(kSecCodeSignatureAdhoc)) {
304 secdebug("staticCode", "%p considered verified since it is ad-hoc", this);
305 return false;
306 }
307
308 // decode CMS and extract SecTrust for verification
309 secdebug("staticCode", "%p verifying signature", this);
310 CFRef<CMSDecoderRef> cms;
311 MacOSError::check(CMSDecoderCreate(&cms.aref())); // create decoder
312 CFDataRef sig = this->signature();
313 MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
314 this->codeDirectory(); // load CodeDirectory (sets mDir)
315 MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
316 MacOSError::check(CMSDecoderFinalizeMessage(cms));
317 MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
318 CMSSignerStatus status;
319 MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, verificationPolicy(),
320 false, &status, &mTrust.aref(), NULL));
321
322 // get signing date (we've got the decoder handle right here)
323 mSigningTime = 0; // "not present" marker (nobody could code sign on Jan 1, 2001 :-)
324 SecCmsMessageRef cmsg;
325 MacOSError::check(CMSDecoderGetCmsMessage(cms, &cmsg));
326 SecCmsSignedDataRef signedData = NULL;
327 int numContentInfos = SecCmsMessageContentLevelCount(cmsg);
328 for(int dex = 0; !signedData && dex < numContentInfos; dex++) {
329 SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
330 SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
331 switch(tag) {
332 case SEC_OID_PKCS7_SIGNED_DATA:
333 if (SecCmsSignedDataRef signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci)))
334 if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, 0))
335 SecCmsSignerInfoGetSigningTime(signerInfo, &mSigningTime);
336 break;
337 default:
338 break;
339 }
340 }
341
342 // set up the environment for SecTrust
343 MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); // no anchors
344 CSSM_APPLE_TP_ACTION_DATA actionData = {
345 CSSM_APPLE_TP_ACTION_VERSION, // version of data structure
346 CSSM_TP_ACTION_IMPLICIT_ANCHORS // action flags
347 };
348
349 for (;;) {
350 MacOSError::check(SecTrustSetParameters(mTrust,
351 CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
352
353 // evaluate trust and extract results
354 SecTrustResultType trustResult;
355 MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
356 MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
357 secdebug("staticCode", "%p verification result=%d chain=%ld",
358 this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : -1);
359 switch (trustResult) {
360 case kSecTrustResultProceed:
361 case kSecTrustResultConfirm:
362 case kSecTrustResultUnspecified:
363 break; // success
364 case kSecTrustResultDeny:
365 MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY); // user reject
366 case kSecTrustResultInvalid:
367 assert(false); // should never happen
368 MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
369 case kSecTrustResultRecoverableTrustFailure:
370 case kSecTrustResultFatalTrustFailure:
371 case kSecTrustResultOtherError:
372 {
373 OSStatus result;
374 MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
375 if (result == CSSMERR_TP_CERT_EXPIRED && !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
376 secdebug("staticCode", "expired certificate(s); retrying validation");
377 actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED;
378 continue; // retry validation
379 }
380 MacOSError::throwMe(result);
381 }
382 }
383 return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
384 }
385 }
386
387
388 //
389 // Return the TP policy used for signature verification.
390 // This policy object is cached and reused.
391 //
392 SecPolicyRef SecStaticCode::verificationPolicy()
393 {
394 if (!mPolicy)
395 MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
396 &CSSMOID_APPLE_TP_CODE_SIGNING, &mPolicy.aref()));
397 return mPolicy;
398 }
399
400
401 //
402 // Validate a particular sealed, cached resource against its (special) CodeDirectory slot.
403 // The resource must already have been placed in the cache.
404 // This does NOT perform basic validation.
405 //
406 void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot)
407 {
408 CFDataRef data = mCache[slot];
409 assert(data); // must be cached
410 if (data == CFDataRef(kCFNull)) {
411 if (codeDirectory()->slotIsPresent(-slot)) // was supposed to be there...
412 MacOSError::throwMe(errSecCSSignatureFailed); // ... and is missing
413 } else {
414 if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
415 MacOSError::throwMe(errSecCSSignatureFailed);
416 }
417 }
418
419
420 //
421 // Perform static validation of the main executable.
422 // This reads the main executable from disk and validates it against the
423 // CodeDirectory code slot array.
424 // Note that this is NOT an in-memory validation, and is thus potentially
425 // subject to timing attacks.
426 //
427 void SecStaticCode::validateExecutable()
428 {
429 secdebug("staticCode", "%p performing static main exec validate of %s",
430 this, mainExecutablePath().c_str());
431 const CodeDirectory *cd = this->codeDirectory();
432 if (!cd)
433 MacOSError::throwMe(errSecCSUnsigned);
434 AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
435 fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass)
436 if (Universal *fat = mRep->mainExecutableImage())
437 fd.seek(fat->archOffset());
438 size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
439 size_t remaining = cd->codeLimit;
440 for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
441 size_t size = min(remaining, pageSize);
442 if (!cd->validateSlot(fd, size, slot)) {
443 secdebug("staticCode", "%p failed static validation of code page %zd", this, slot);
444 mExecutableValidated = true; // we tried
445 mExecutableValid = false; // it failed
446 MacOSError::throwMe(errSecCSSignatureFailed);
447 }
448 remaining -= size;
449 }
450 secdebug("staticCode", "%p validated full executable (%d pages)",
451 this, int(cd->nCodeSlots));
452 mExecutableValidated = true; // we tried
453 mExecutableValid = true; // it worked
454 }
455
456
457 //
458 // Perform static validation of sealed resources.
459 //
460 // This performs a whole-code static resource scan and effectively
461 // computes a concordance between what's on disk and what's in the ResourceDirectory.
462 // Any unsanctioned difference causes an error.
463 //
464 void SecStaticCode::validateResources()
465 {
466 // sanity first
467 CFDictionaryRef sealedResources = resourceDictionary();
468 if (this->resourceBase()) // disk has resources
469 if (sealedResources)
470 /* go to work below */;
471 else
472 MacOSError::throwMe(errSecCSResourcesNotFound);
473 else // disk has no resources
474 if (sealedResources)
475 MacOSError::throwMe(errSecCSResourcesNotFound);
476 else
477 return; // no resources, not sealed - fine (no work)
478
479 // found resources, and they are sealed
480 CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
481 CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
482 secdebug("staticCode", "%p verifying %d sealed resources",
483 this, int(CFDictionaryGetCount(files)));
484
485 // make a shallow copy of the ResourceDirectory so we can "check off" what we find
486 CFRef<CFMutableDictionaryRef> resourceMap = CFDictionaryCreateMutableCopy(NULL,
487 CFDictionaryGetCount(files), files);
488 if (!resourceMap)
489 CFError::throwMe();
490
491 // scan through the resources on disk, checking each against the resourceDirectory
492 CollectingContext ctx(*this); // collect all failures in here
493 ResourceBuilder resources(cfString(this->resourceBase()), rules);
494 string path;
495 ResourceBuilder::Rule *rule;
496
497 while (resources.next(path, rule)) {
498 if (CFDataRef value = resource(path, ctx))
499 CFRelease(value);
500 CFDictionaryRemoveValue(resourceMap, CFTempString(path));
501 secdebug("staticCode", "%p validated %s", this, path.c_str());
502 }
503
504 if (CFDictionaryGetCount(resourceMap) > 0) {
505 secdebug("staticCode", "%p sealed resource(s) not found in code", this);
506 CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, &ctx);
507 }
508
509 // now check for any errors found in the reporting context
510 if (ctx)
511 ctx.throwMe();
512
513 secdebug("staticCode", "%p sealed resources okay", this);
514 }
515
516
517 void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
518 {
519 CollectingContext *ctx = static_cast<CollectingContext *>(context);
520 ResourceSeal seal(value);
521 if (!seal.optional())
522 if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
523 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing,
524 CFTempURL(CFStringRef(key), false, ctx->code.resourceBase()));
525 } else
526 ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
527 }
528
529
530 //
531 // Load, validate, cache, and return CFDictionary forms of sealed resources.
532 //
533 CFDictionaryRef SecStaticCode::infoDictionary()
534 {
535 if (!mInfoDict) {
536 mInfoDict.take(getDictionary(cdInfoSlot));
537 secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
538 }
539 return mInfoDict;
540 }
541
542 CFDictionaryRef SecStaticCode::resourceDictionary()
543 {
544 if (mResourceDict) // cached
545 return mResourceDict;
546 if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot))
547 if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
548 secdebug("staticCode", "%p loaded ResourceDict %p",
549 this, mResourceDict.get());
550 return mResourceDict = dict;
551 }
552 // bad format
553 return NULL;
554 }
555
556
557 //
558 // Load and cache the resource directory base.
559 // Note that the base is optional for each DiskRep.
560 //
561 CFURLRef SecStaticCode::resourceBase()
562 {
563 if (!mGotResourceBase) {
564 string base = mRep->resourcesRootPath();
565 if (!base.empty())
566 mResourceBase.take(makeCFURL(base, true));
567 mGotResourceBase = true;
568 }
569 return mResourceBase;
570 }
571
572
573 //
574 // Load a component, validate it, convert it to a CFDictionary, and return that.
575 // This will force load and validation, which means that it will perform basic
576 // validation if it hasn't been done yet.
577 //
578 CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot)
579 {
580 validateDirectory();
581 if (CFDataRef infoData = component(slot)) {
582 validateComponent(slot);
583 if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
584 return dict;
585 else
586 MacOSError::throwMe(errSecCSBadDictionaryFormat);
587 }
588 return NULL;
589 }
590
591
592 //
593 // Load, validate, and return a sealed resource.
594 // The resource data (loaded in to memory as a blob) is returned and becomes
595 // the responsibility of the caller; it is NOT cached by SecStaticCode.
596 //
597 // A resource that is not sealed will not be returned, and an error will be thrown.
598 // A missing resource will cause an error unless it's marked optional in the Directory.
599 // Under no circumstances will a corrupt resource be returned.
600 // NULL will only be returned for a resource that is neither sealed nor present
601 // (or that is sealed, absent, and marked optional).
602 // If the ResourceDictionary itself is not sealed, this function will always fail.
603 //
604 // There is currently no interface for partial retrieval of the resource data.
605 // (Since the ResourceDirectory does not currently support segmentation, all the
606 // data would have to be read anyway, but it could be read into a reusable buffer.)
607 //
608 CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
609 {
610 if (CFDictionaryRef rdict = resourceDictionary()) {
611 if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
612 ResourceSeal seal = file;
613 if (!resourceBase()) // no resources in DiskRep
614 MacOSError::throwMe(errSecCSResourcesNotFound);
615 CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
616 if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
617 SHA1 hasher;
618 hasher(CFDataGetBytePtr(data), CFDataGetLength(data));
619 if (hasher.verify(seal.hash()))
620 return data.yield(); // good
621 else
622 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); // altered
623 } else {
624 if (!seal.optional())
625 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); // was sealed but is now missing
626 else
627 return NULL; // validly missing
628 }
629 } else
630 ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
631 return NULL;
632 } else
633 MacOSError::throwMe(errSecCSResourcesNotSealed);
634 }
635
636 CFDataRef SecStaticCode::resource(string path)
637 {
638 ValidationContext ctx;
639 return resource(path, ctx);
640 }
641
642
643 //
644 // Test a CodeDirectory flag.
645 // Returns false if there is no CodeDirectory.
646 // May throw if the CodeDirectory is present but somehow invalid.
647 //
648 bool SecStaticCode::flag(uint32_t tested)
649 {
650 if (const CodeDirectory *cd = this->codeDirectory(false))
651 return cd->flags & tested;
652 else
653 return false;
654 }
655
656
657 //
658 // Retrieve the full SuperBlob containing all internal requirements.
659 //
660 const Requirements *SecStaticCode::internalRequirements()
661 {
662 if (CFDataRef req = component(cdRequirementsSlot))
663 return (const Requirements *)CFDataGetBytePtr(req);
664 else
665 return NULL;
666 }
667
668
669 //
670 // Retrieve a particular internal requirement by type.
671 //
672 const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
673 {
674 if (const Requirements *reqs = internalRequirements())
675 return reqs->find<Requirement>(type);
676 else
677 return NULL;
678 }
679
680
681 //
682 // Return the Designated Requirement. This can be either explicit in the
683 // Internal Requirements resource, or implicitly generated.
684 //
685 const Requirement *SecStaticCode::designatedRequirement()
686 {
687 if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
688 return req; // explicit in signing data
689 } else {
690 if (!mDesignatedReq)
691 mDesignatedReq = defaultDesignatedRequirement();
692 return mDesignatedReq;
693 }
694 }
695
696
697 //
698 // Generate the default (implicit) Designated Requirement for this StaticCode.
699 // This is a heuristic of sorts, and may change over time (for the better, we hope).
700 //
701 // The current logic is this:
702 // * If the code is ad-hoc signed, use the CodeDirectory hash directory.
703 // * Otherwise, use the form anchor (anchor) and identifier (CodeDirectory identifier).
704 // ** If the root CA is Apple's, we use the "anchor apple" construct. Otherwise,
705 // we default to the leaf (directly signing) certificate.
706 //
707 const Requirement *SecStaticCode::defaultDesignatedRequirement()
708 {
709 validateDirectory(); // need the cert chain
710 Requirement::Maker maker;
711
712 // if this is an ad-hoc (unsigned) object, return a cdhash requirement
713 if (flag(kSecCodeSignatureAdhoc)) {
714 SHA1 hash;
715 hash(codeDirectory(), codeDirectory()->length());
716 SHA1::Digest digest;
717 hash.finish(digest);
718 maker.cdhash(digest);
719 } else {
720 // always require the identifier
721 maker.put(opAnd);
722 maker.ident(codeDirectory()->identifier());
723
724 SHA1::Digest anchorHash;
725 hashOfCertificate(cert(Requirement::anchorCert), anchorHash);
726 if (!memcmp(anchorHash, Requirement::appleAnchorHash(), SHA1::digestLength)
727 #if defined(TEST_APPLE_ANCHOR)
728 || !memcmp(anchorHash, Requirement::testAppleAnchorHash(), SHA1::digestLength)
729 #endif
730 ) {
731 maker.anchor(); // canonical Apple anchor
732 } else {
733 // we don't know anything more, so we'll (conservatively) pick the leaf
734 SHA1::Digest leafHash;
735 hashOfCertificate(cert(Requirement::leafCert), leafHash);
736 maker.anchor(Requirement::leafCert, leafHash);
737 }
738 }
739
740 return maker();
741 }
742
743
744 //
745 // Validate a SecStaticCode against the internal requirement of a particular type.
746 //
747 void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
748 OSStatus nullError /* = noErr */)
749 {
750 secdebug("staticCode", "%p validating %s requirements for %p",
751 this, Requirement::typeNames[type], target);
752 if (const Requirement *req = internalRequirement(type))
753 target->validateRequirements(req, nullError ? nullError : errSecCSReqFailed);
754 else if (nullError) {
755 secdebug("staticCode", "%p NULL validate for %s prohibited",
756 this, Requirement::typeNames[type]);
757 MacOSError::throwMe(nullError);
758 } else
759 secdebug("staticCode", "%p NULL validate (no requirements for %s)",
760 this, Requirement::typeNames[type]);
761 }
762
763
764 //
765 // Validate this StaticCode against an external Requirement
766 //
767 void SecStaticCode::validateRequirements(const Requirement *req, OSStatus failure)
768 {
769 assert(req);
770 validateDirectory();
771 req->validate(Requirement::Context(mCertChain, infoDictionary(), codeDirectory()), failure);
772 }
773
774
775 //
776 // Retrieve one certificate from the cert chain.
777 // Positive and negative indices can be used:
778 // [ leaf, intermed-1, ..., intermed-n, anchor ]
779 // 0 1 ... -2 -1
780 // Returns NULL if unavailable for any reason.
781 //
782 SecCertificateRef SecStaticCode::cert(int ix)
783 {
784 validateDirectory(); // need cert chain
785 if (mCertChain) {
786 if (ix < 0)
787 ix += CFArrayGetCount(mCertChain);
788 if (CFTypeRef element = CFArrayGetValueAtIndex(mCertChain, ix))
789 return SecCertificateRef(element);
790 }
791 return NULL;
792 }
793
794 CFArrayRef SecStaticCode::certificates()
795 {
796 validateDirectory(); // need cert chain
797 return mCertChain;
798 }
799
800
801 //
802 // Gather API-official information about this StaticCode.
803 //
804 // This method lives in the twilight between the API and internal layers,
805 // since it generates API objects (Sec*Refs) for return.
806 //
807 CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
808 {
809 //
810 // Start with the pieces that we return even for unsigned code.
811 // This makes Sec[Static]CodeRefs useful as API-level replacements
812 // of our internal OSXCode objects.
813 //
814 CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
815 kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
816 );
817
818 //
819 // If we're not signed, this is all you get
820 //
821 if (!this->isSigned())
822 return dict.yield();
823
824
825 //
826 // Now add the generic attributes that we always include
827 //
828 CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
829 CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
830 if (CFDictionaryRef info = infoDictionary())
831 CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
832
833 //
834 // kSecCSSigningInformation adds information about signing certificates and chains
835 //
836 if (flags & kSecCSSigningInformation) {
837 if (CFArrayRef certs = certificates())
838 CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
839 if (CFDataRef sig = signature())
840 CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
841 if (mTrust)
842 CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
843 if (CFAbsoluteTime time = signingTime())
844 if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
845 CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
846 }
847
848 //
849 // kSecCSRequirementInformation adds information on requirements
850 //
851 if (flags & kSecCSRequirementInformation) {
852 if (const Requirements *reqs = internalRequirements()) {
853 CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
854 CFTempString(Dumper::dump(reqs)));
855 }
856 const Requirement *ddreq = defaultDesignatedRequirement();
857 CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(ddreq))->handle();
858 const Requirement *dreq = designatedRequirement();
859 if (dreq == ddreq) {
860 CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, ddreqRef);
861 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
862 } else {
863 CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement,
864 CFRef<SecRequirementRef>((new SecRequirement(dreq))->handle()));
865 CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
866 }
867 }
868
869 //
870 // kSecCSInternalInformation adds internal information meant to be for Apple internal
871 // use (SPI), and not guaranteed to be stable. Primarily, this is data we want
872 // to reliably transmit through the API wall so that code outside the Security.framework
873 // can use it without having to play nasty tricks to get it.
874 //
875 if (flags & kSecCSInternalInformation) {
876 if (mDir)
877 CFDictionaryAddValue(dict, CFSTR("CodeDirectory"), mDir);
878 CFDictionaryAddValue(dict, CFSTR("CodeOffset"), CFTempNumber(mRep->signingBase()));
879 }
880
881
882 //
883 // kSecCSContentInformation adds more information about the physical layout
884 // of the signed code. This is (only) useful for packaging or patching-oriented
885 // applications.
886 //
887 if (flags & kSecCSContentInformation)
888 if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
889 CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
890
891 return dict.yield();
892 }
893
894
895 //
896 // Resource validation contexts.
897 // The default context simply throws a CSError, rudely terminating the operation.
898 //
899 SecStaticCode::ValidationContext::~ValidationContext()
900 { /* virtual */ }
901
902 void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
903 {
904 CSError::throwMe(rc, type, value);
905 }
906
907 void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
908 {
909 if (mStatus == noErr)
910 mStatus = rc; // record first failure for eventual error return
911 if (type) {
912 if (!mCollection)
913 mCollection.take(makeCFMutableDictionary(0));
914 CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
915 if (!element) {
916 element = makeCFMutableArray(0);
917 if (!element)
918 CFError::throwMe();
919 CFDictionaryAddValue(mCollection, type, element);
920 CFRelease(element);
921 }
922 CFArrayAppendValue(element, value);
923 }
924 }
925
926 void SecStaticCode::CollectingContext::throwMe()
927 {
928 assert(mStatus != noErr);
929 throw CSError(mStatus, mCollection.yield());
930 }
931
932
933 //
934 // DetachedRep construction
935 //
936 DetachedRep::DetachedRep(CFDataRef sig, DiskRep *orig)
937 : original(orig), mSignature(sig)
938 {
939 const BlobCore *sigBlob = reinterpret_cast<const BlobCore *>(CFDataGetBytePtr(sig));
940 if (sigBlob->is<EmbeddedSignatureBlob>()) { // architecture-less
941 mArch = EmbeddedSignatureBlob::specific(sigBlob);
942 mGlobal = NULL;
943 return;
944 } else if (sigBlob->is<DetachedSignatureBlob>()) { // architecture collection
945 const DetachedSignatureBlob *dsblob = DetachedSignatureBlob::specific(sigBlob);
946 if (Universal *fat = orig->mainExecutableImage()) {
947 if (const BlobCore *blob = dsblob->find(fat->bestNativeArch().cpuType())) {
948 mArch = EmbeddedSignatureBlob::specific(blob);
949 mGlobal = EmbeddedSignatureBlob::specific(dsblob->find(0));
950 return;
951 } else
952 secdebug("staticcode", "detached signature missing architecture %s",
953 fat->bestNativeArch().name());
954 } else
955 secdebug("staticcode", "detached signature requires Mach-O binary");
956 } else
957 secdebug("staticcode", "detached signature bad magic 0x%x", sigBlob->magic());
958 MacOSError::throwMe(errSecCSSignatureInvalid);
959 }
960
961 CFDataRef DetachedRep::component(CodeDirectory::SpecialSlot slot)
962 {
963 if (CFDataRef result = mArch->component(slot))
964 return result;
965 if (mGlobal)
966 if (CFDataRef result = mGlobal->component(slot))
967 return result;
968 return original->component(slot);
969 }
970
971
972 } // end namespace CodeSigning
973 } // end namespace Security