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