]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signer.cpp
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signer.cpp
1 /*
2 * Copyright (c) 2006-2014 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 // signer - Signing operation supervisor and controller
26 //
27 #include "der_plist.h"
28 #include "signer.h"
29 #include "resources.h"
30 #include "signerutils.h"
31 #include "SecCodeSigner.h"
32 #include <Security/SecIdentity.h>
33 #include <Security/CMSEncoder.h>
34 #include <Security/CMSPrivate.h>
35 #include <Security/CSCommonPriv.h>
36 #include <CoreFoundation/CFBundlePriv.h>
37 #include "resources.h"
38 #include "machorep.h"
39 #include "reqparser.h"
40 #include "reqdumper.h"
41 #include "csutilities.h"
42 #include <security_utilities/unix++.h>
43 #include <security_utilities/unixchild.h>
44 #include <security_utilities/cfmunge.h>
45 #include <security_utilities/dispatch.h>
46 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
47
48 namespace Security {
49 namespace CodeSigning {
50
51
52 //
53 // Sign some code.
54 //
55 void SecCodeSigner::Signer::sign(SecCSFlags flags)
56 {
57 rep = code->diskRep()->base();
58 this->prepare(flags);
59
60 PreSigningContext context(*this);
61
62 considerTeamID(context);
63
64 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
65 signMachO(fat, context);
66 } else {
67 signArchitectureAgnostic(context);
68 }
69 }
70
71
72 void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context)
73 {
74 /* If an explicit teamID was passed in it must be
75 the same as what came from the cert */
76 std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
77
78 if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
79 /* If preserving the team identifier, teamID is set previously when the
80 code object is still available */
81 if (!teamIDFromCert.empty() && teamID != teamIDFromCert)
82 MacOSError::throwMe(errSecCSInvalidFlags);
83 } else {
84 if (teamIDFromCert.empty()) {
85 /* state.mTeamID is an explicitly passed teamID */
86 teamID = state.mTeamID;
87 } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) {
88 /* If there was no explicit team ID set, or the explicit team ID matches
89 what is in the cert, use the team ID from the certificate */
90 teamID = teamIDFromCert;
91 } else {
92 /* The caller passed in an explicit team ID that does not match what is
93 in the signing cert, which is an invalid usage */
94 MacOSError::throwMe(errSecCSInvalidFlags);
95 }
96 }
97 }
98
99
100 //
101 // Remove any existing code signature from code
102 //
103 void SecCodeSigner::Signer::remove(SecCSFlags flags)
104 {
105 // can't remove a detached signature
106 if (state.mDetached)
107 MacOSError::throwMe(errSecCSNotSupported);
108
109 rep = code->diskRep();
110
111 if (state.mPreserveAFSC)
112 rep->writer()->setPreserveAFSC(state.mPreserveAFSC);
113
114 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
115 // architecture-sensitive removal
116 MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath());
117 editor.allocate(); // create copy
118 editor.commit(); // commit change
119 } else {
120 // architecture-agnostic removal
121 RefPointer<DiskRep::Writer> writer = rep->writer();
122 writer->remove();
123 writer->flush();
124 }
125 }
126
127
128 //
129 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
130 //
131 void SecCodeSigner::Signer::prepare(SecCSFlags flags)
132 {
133 // make sure the rep passes strict validation
134 if (strict)
135 rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData));
136
137 // initialize progress/cancellation state
138 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
139
140 // get the Info.plist out of the rep for some creative defaulting
141 CFRef<CFDictionaryRef> infoDict;
142 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
143 infoDict.take(makeCFDictionaryFrom(infoData));
144
145 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
146
147 // work out the canonical identifier
148 identifier = state.mIdentifier;
149 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
150 identifier = code->identifier();
151 if (identifier.empty()) {
152 identifier = rep->recommendedIdentifier(*this);
153 if (identifier.find('.') == string::npos)
154 identifier = state.mIdentifierPrefix + identifier;
155 if (identifier.find('.') == string::npos && isAdhoc())
156 identifier = identifier + "-" + uniqueName();
157 secinfo("signer", "using default identifier=%s", identifier.c_str());
158 } else
159 secinfo("signer", "using explicit identifier=%s", identifier.c_str());
160
161 teamID = state.mTeamID;
162 if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
163 const char *c_id = code->teamID();
164 if (c_id)
165 teamID = c_id;
166 }
167
168 // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later.
169 hashAlgorithms = state.mDigestAlgorithms;
170 if (hashAlgorithms.empty() && (inherit & kSecCodeSignerPreserveDigestAlgorithm))
171 hashAlgorithms = code->hashAlgorithms();
172
173 entitlements = state.mEntitlementData;
174 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
175 entitlements = code->component(cdEntitlementSlot);
176
177 generateEntitlementDER = signingFlags() & kSecCSSignGenerateEntitlementDER;
178
179 // work out the CodeDirectory flags word
180 bool haveCdFlags = false;
181 if (!haveCdFlags && state.mCdFlagsGiven) {
182 cdFlags = state.mCdFlags;
183 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags);
184 haveCdFlags = true;
185 }
186 if (!haveCdFlags) {
187 cdFlags = 0;
188 if (infoDict)
189 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
190 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
191 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
192 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
193 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
194 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
195 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
196 } else
197 MacOSError::throwMe(errSecCSBadDictionaryFormat);
198 haveCdFlags = true;
199 }
200 }
201 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
202 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
203 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags);
204 haveCdFlags = true;
205 }
206 if (!haveCdFlags)
207 cdFlags = 0;
208 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
209 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
210
211 // prepare the internal requirements input
212 if (state.mRequirements) {
213 if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form
214 const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>());
215 if (!rp->validateBlob())
216 MacOSError::throwMe(errSecCSReqInvalid);
217 requirements = rp->clone();
218 } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form
219 CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>());
220 // substitute $ variable tokens
221 CFRange range = { 0, CFStringGetLength(reqText) };
222 CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0);
223 requirements = parseRequirements(cfString(reqText));
224 } else
225 MacOSError::throwMe(errSecCSInvalidObjectRef);
226 } else if (inherit & kSecCodeSignerPreserveRequirements)
227 if (const Requirements *rp = code->internalRequirements())
228 requirements = rp->clone();
229
230 // prepare the resource directory, if any
231 string rpath = rep->resourcesRootPath();
232 string rrpath;
233 CFCopyRef<CFDictionaryRef> resourceRules;
234 if (!rpath.empty()) {
235 // explicitly given resource rules always win
236 resourceRules = state.mResourceRules;
237
238 // inherited rules come next (overriding embedded ones!)
239 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
240 if (CFDictionaryRef oldRules = code->resourceDictionary(false))
241 resourceRules = oldRules;
242
243 // embedded resource rules come next
244 if (!resourceRules && infoDict)
245 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) {
246 if (CFGetTypeID(spec) == CFStringGetTypeID())
247 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
248 if (CFDictionaryRef dict = makeCFDictionaryFrom(data))
249 resourceRules.take(dict);
250 if (!resourceRules) // embedded rules present but unacceptable
251 MacOSError::throwMe(errSecCSResourceRulesInvalid);
252 }
253
254 // if we got one from anywhere (but the defaults), sanity-check it
255 if (resourceRules) {
256 CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules"));
257 if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID())
258 MacOSError::throwMe(errSecCSResourceRulesInvalid);
259 }
260
261 // finally, ask the DiskRep for its default
262 if (!resourceRules)
263 resourceRules.take(rep->defaultResourceRules(*this));
264
265 // resource root can optionally be the canonical bundle path,
266 // but sealed resource paths are always relative to rpath
267 rrpath = rpath;
268 if (signingFlags() & kSecCSSignBundleRoot)
269 rrpath = cfStringRelease(rep->copyCanonicalPath());
270 }
271
272 // screen and set the signing time
273 if (state.mSigningTime == CFDateRef(kCFNull)) {
274 emitSigningTime = false; // no time at all
275 } else if (!state.mSigningTime) {
276 emitSigningTime = true;
277 signingTime = 0; // wall clock, established later
278 } else {
279 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
280 if (time > CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
281 MacOSError::throwMe(errSecCSBadDictionaryFormat);
282 emitSigningTime = true;
283 signingTime = time;
284 }
285
286 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(*this);
287
288 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
289 rep->prepareForSigning(*this);
290
291 // apply some defaults after diskRep intervention
292 if (hashAlgorithms.empty()) { // default to SHA256 + SHA-1
293 hashAlgorithms.insert(kSecCodeSignatureHashSHA1);
294 hashAlgorithms.insert(kSecCodeSignatureHashSHA256);
295 }
296
297 // build the resource directory (once and for all, using the digests determined above)
298 if (!rpath.empty()) {
299 buildResources(rrpath, rpath, resourceRules);
300 }
301
302
303
304 if (inherit & kSecCodeSignerPreservePEH) {
305 /* We need at least one architecture in all cases because we index our
306 * PreEncryptionMaps by architecture. However, only machOs have any
307 * architecture at all, for generic targets there will just be one
308 * PreEncryptionHashMap.
309 * So if the main executable is not a machO, we just choose the local
310 * (signer's) main architecture as dummy value for the first element in our pair. */
311 preEncryptMainArch = (code->diskRep()->mainExecutableIsMachO() ?
312 code->diskRep()->mainExecutableImage()->bestNativeArch() :
313 Architecture::local());
314
315 addPreEncryptHashes(preEncryptHashMaps[preEncryptMainArch], code);
316
317 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
318 Universal *fat = subcode->diskRep()->mainExecutableImage();
319 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
320 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice.
321 addPreEncryptHashes(preEncryptHashMaps[arch], subcode);
322 });
323 }
324
325 if (inherit & kSecCodeSignerPreserveRuntime) {
326 /* We need at least one architecture in all cases because we index our
327 * RuntimeVersionMaps by architecture. However, only machOs have any
328 * architecture at all, for generic targets there will just be one
329 * RuntimeVersionMap.
330 * So if the main executable is not a machO, we just choose the local
331 * (signer's) main architecture as dummy value for the first element in our pair. */
332 runtimeVersionMainArch = (code->diskRep()->mainExecutableIsMachO() ?
333 code->diskRep()->mainExecutableImage()->bestNativeArch() :
334 Architecture::local());
335
336 addRuntimeVersions(runtimeVersionMap[runtimeVersionMainArch], code);
337
338 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
339 Universal *fat = subcode->diskRep()->mainExecutableImage();
340 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
341 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice.
342 addRuntimeVersions(runtimeVersionMap[arch], subcode);
343 });
344 }
345 }
346
347 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code) {
348 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
349
350 if (cds != NULL) {
351 for(auto const& pair : *cds) {
352 CodeDirectory::HashAlgorithm const alg = pair.first;
353 CFDataRef const cddata = pair.second;
354
355 CodeDirectory const * cd =
356 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
357 if (cd->preEncryptHashes() != NULL) {
358 CFRef<CFDataRef> preEncrypt = makeCFData(cd->preEncryptHashes(),
359 cd->nCodeSlots * cd->hashSize);
360 map[alg] = preEncrypt;
361 }
362 }
363 }
364 }
365
366 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap &map, const SecStaticCode *code)
367 {
368 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
369
370 if (cds != NULL) {
371 for(auto const& pair : *cds) {
372 CodeDirectory::HashAlgorithm const alg = pair.first;
373 CFDataRef const cddata = pair.second;
374
375 CodeDirectory const * cd =
376 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
377 if (cd->runtimeVersion()) {
378 map[alg] = cd->runtimeVersion();
379 }
380 }
381 }
382 }
383
384 //
385 // Collect the resource seal for a program.
386 // This includes both sealed resources and information about nested code.
387 //
388 void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
389 {
390 typedef ResourceBuilder::Rule Rule;
391
392 secinfo("codesign", "start building resource directory");
393 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
394
395 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
396 assert(rules);
397
398 if (this->state.mLimitedAsync == NULL) {
399 this->state.mLimitedAsync =
400 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
401 * turned off, because the paths for signing code are not ready for it yet. */
402 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
403 new LimitedAsync(false);
404 }
405
406 CFDictionaryRef files2 = NULL;
407 if (!(signingFlags() & kSecCSSignV1)) {
408 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
409 if (!rules2) {
410 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules,
411 // because the default weight, according to ResourceBuilder::addRule(), is 1).
412 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
413 rules2 = cfmake<CFDictionaryRef>("{+%O"
414 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
415 "}", rules);
416 }
417
418 Dispatch::Group group;
419 Dispatch::Group &groupRef = group; // (into block)
420
421 // build the modern (V2) resource seal
422 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
423 CFMutableDictionaryRef filesRef = files.get(); // (into block)
424 ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet());
425 ResourceBuilder &resources = resourceBuilder; // (into block)
426 rep->adjustResources(resources);
427
428 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
429 bool isSymlink = (ent->fts_info == FTS_SL);
430 const std::string path(ent->fts_path);
431 const std::string accpath(ent->fts_accpath);
432 this->state.mLimitedAsync->perform(groupRef, ^{
433 CFRef<CFMutableDictionaryRef> seal;
434 if (ruleFlags & ResourceBuilder::nested) {
435 seal.take(signNested(path, relpath));
436 } else if (isSymlink) {
437 char target[PATH_MAX];
438 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1);
439 if (len < 0)
440 UnixError::check(-1);
441 target[len] = '\0';
442 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
443 } else {
444 seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
445 }
446 if (ruleFlags & ResourceBuilder::optional)
447 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
448 CFTypeRef hash;
449 StLock<Mutex> _(resourceLock);
450 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
451 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash);
452 else
453 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get());
454 code->reportProgress();
455 });
456 });
457 group.wait();
458 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
459 files2 = files;
460 CFDictionaryAddValue(result, CFSTR("files2"), files2);
461 }
462
463 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
464 if (!(signingFlags() & kSecCSSignNoV1)) {
465 // build the legacy (V1) resource seal
466 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
467 ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet());
468 ResourceBuilder &resources = resourceBuilder;
469 rep->adjustResources(resources); // DiskRep-specific adjustments
470 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
471 if (ent->fts_info == FTS_F) {
472 CFRef<CFDataRef> hash;
473 if (files2) // try to get the hash from a previously-made version
474 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
475 if (CFGetTypeID(seal) == CFDataGetTypeID())
476 hash = CFDataRef(seal);
477 else
478 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
479 }
480 if (!hash)
481 hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1));
482 if (ruleFlags == 0) { // default case - plain hash
483 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
484 secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
485 } else { // more complicated - use a sub-dictionary
486 cfadd(files, "{%s={hash=%O,optional=%B}}",
487 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
488 secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
489 }
490 }
491 });
492 CFDictionaryAddValue(result, CFSTR("files"), files.get());
493 }
494
495 resourceDirectory = result.get();
496 resourceDictData.take(makeCFData(resourceDirectory.get()));
497 }
498
499
500 //
501 // Deal with one piece of nested code
502 //
503 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
504 {
505 // sign nested code and collect nesting information
506 try {
507 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
508 if (signingFlags() & kSecCSSignNestedCode)
509 this->state.sign(code, signingFlags());
510 std::string dr = Dumper::dump(code->designatedRequirement());
511 if (CFDataRef hash = code->cdHash())
512 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
513 Dumper::dump(code->designatedRequirement()).c_str(),
514 hash);
515 MacOSError::throwMe(errSecCSUnsigned);
516 } catch (const CommonError &err) {
517 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
518 }
519 }
520
521
522 //
523 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
524 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
525 // treat them as architectural binaries containing (only) one architecture - that
526 // interpretation is courtesy of the Universal/MachO support classes.
527 //
528 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
529 {
530 // Mach-O executable at the core - perform multi-architecture signing
531 RefPointer<DiskRep::Writer> writer = rep->writer();
532
533 if (state.mPreserveAFSC)
534 writer->setPreserveAFSC(state.mPreserveAFSC);
535
536 auto_ptr<ArchEditor> editor(state.mDetached
537 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
538 : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath()));
539 assert(editor->count() > 0);
540 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
541 populate(*editor);
542
543 // pass 1: prepare signature blobs and calculate sizes
544 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
545 MachOEditor::Arch &arch = *it->second;
546 arch.source.reset(fat->architecture(it->first));
547
548 // library validation is not compatible with i386
549 if (arch.architecture.cpuType() == CPU_TYPE_I386) {
550 if (cdFlags & kSecCodeSignatureLibraryValidation) {
551 MacOSError::throwMe(errSecCSBadLVArch);
552 }
553 }
554
555 bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
556
557 uint32_t runtimeVersion = 0;
558 if (cdFlags & kSecCodeSignatureRuntime) {
559 runtimeVersion = state.mRuntimeVersionOverride ? state.mRuntimeVersionOverride : arch.source.get()->sdkVersion();
560 }
561
562 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
563 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
564 populate(arch);
565 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
566 uint32_t runtimeVersionToUse = runtimeVersion;
567 if ((cdFlags & kSecCodeSignatureRuntime) && runtimeVersionMap.count(arch.architecture)) {
568 if (runtimeVersionMap[arch.architecture].count(*type)) {
569 runtimeVersionToUse = runtimeVersionMap[arch.architecture][*type];
570 }
571 }
572 arch.eachDigest(^(CodeDirectory::Builder& builder) {
573 populate(builder, arch, arch.ireqs,
574 arch.source->offset(), arch.source->signingExtent(),
575 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
576 unsigned(digestAlgorithms().size()-1),
577 preEncryptHashMaps[arch.architecture], runtimeVersionToUse);
578 });
579 }
580
581 // add identification blob (made from this architecture) only if we're making a detached signature
582 if (state.mDetached) {
583 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
584 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
585 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
586 }
587
588 // prepare SuperBlob size estimate
589 __block std::vector<size_t> sizes;
590 arch.eachDigest(^(CodeDirectory::Builder& builder){
591 sizes.push_back(builder.size(CodeDirectory::currentVersion));
592 });
593 arch.blobSize = arch.size(sizes, state.mCMSSize, 0);
594 }
595
596 editor->allocate();
597
598 // pass 2: Finish and generate signatures, and write them
599 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
600 MachOEditor::Arch &arch = *it->second;
601 editor->reset(arch);
602
603 // finish CodeDirectories (off new binary) and sign it
604 __block CodeDirectorySet cdSet;
605 arch.eachDigest(^(CodeDirectory::Builder &builder) {
606 CodeDirectory *cd = builder.build();
607 cdSet.add(cd);
608 });
609
610 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
611 CFRef<CFArrayRef> hashList = cdSet.hashList();
612 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
613
614 // complete the SuperBlob
615 cdSet.populate(&arch);
616 arch.add(cdSignatureSlot, BlobWrapper::alloc(
617 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
618 if (!state.mDryRun) {
619 EmbeddedSignatureBlob *blob = arch.make();
620 editor->write(arch, blob); // takes ownership of blob
621 }
622 }
623
624 // done: write edit copy back over the original
625 if (!state.mDryRun) {
626 editor->commit();
627 }
628 }
629
630
631 //
632 // Sign a binary that has no notion of architecture.
633 // That currently means anything that isn't Mach-O format.
634 //
635 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
636 {
637 // non-Mach-O executable - single-instance signing
638 RefPointer<DiskRep::Writer> writer = state.mDetached ?
639 (new DetachedBlobWriter(*this)) : rep->writer();
640
641 if(state.mPreserveAFSC)
642 writer->setPreserveAFSC(state.mPreserveAFSC);
643
644 CodeDirectorySet cdSet;
645
646 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
647 CodeDirectory::Builder builder(*type);
648 InternalRequirements ireqs;
649 ireqs(requirements, rep->defaultRequirements(NULL, *this), context);
650 populate(*writer);
651 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(),
652 false, // only machOs can currently be main binaries
653 rep->execSegBase(NULL), rep->execSegLimit(NULL),
654 unsigned(digestAlgorithms().size()-1),
655 preEncryptHashMaps[preEncryptMainArch], // Only one map, the default.
656 (cdFlags & kSecCodeSignatureRuntime) ? state.mRuntimeVersionOverride : 0);
657
658 CodeDirectory *cd = builder.build();
659 if (!state.mDryRun)
660 cdSet.add(cd);
661 }
662
663 // add identification blob (made from this architecture) only if we're making a detached signature
664 if (state.mDetached) {
665 CFRef<CFDataRef> identification = rep->identification();
666 writer->component(cdIdentificationSlot, identification);
667 }
668
669 // write out all CodeDirectories
670 if (!state.mDryRun)
671 cdSet.populate(writer);
672
673 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
674 CFRef<CFArrayRef> hashList = cdSet.hashList();
675 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
676 writer->signature(signature);
677
678 // commit to storage
679 writer->flush();
680 }
681
682
683 //
684 // Global populate - send components to destination buffers ONCE
685 //
686 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
687 {
688 if (resourceDirectory && !state.mDryRun)
689 writer.component(cdResourceDirSlot, resourceDictData);
690 }
691
692
693 //
694 // Per-architecture populate - send components to per-architecture buffers
695 // and populate the CodeDirectory for an architecture. In architecture-agnostic
696 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
697 // for the purposes of this call.
698 //
699 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
700 InternalRequirements &ireqs, size_t offset, size_t length,
701 bool mainBinary, size_t execSegBase, size_t execSegLimit,
702 unsigned alternateDigestCount,
703 PreEncryptHashMap const &preEncryptHashMap,
704 uint32_t runtimeVersion)
705 {
706 // fill the CodeDirectory
707 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
708 builder.flags(cdFlags);
709 builder.identifier(identifier);
710 builder.teamID(teamID);
711 builder.platform(state.mPlatform);
712 builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
713 builder.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH);
714 builder.preservePreEncryptHashMap(preEncryptHashMap);
715 builder.runTimeVersion(runtimeVersion);
716
717 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
718 builder.specialSlot(cdInfoSlot, data);
719 if (ireqs) {
720 CFRef<CFDataRef> data = makeCFData(*ireqs);
721 writer.component(cdRequirementsSlot, data);
722 builder.specialSlot(cdRequirementsSlot, data);
723 }
724 if (resourceDirectory)
725 builder.specialSlot(cdResourceDirSlot, resourceDictData);
726 if (entitlements) {
727 writer.component(cdEntitlementSlot, entitlements);
728 builder.specialSlot(cdEntitlementSlot, entitlements);
729
730 if (mainBinary) {
731 CFRef<CFDataRef> entitlementDER;
732 uint64_t execSegFlags = 0;
733 cookEntitlements(entitlements, generateEntitlementDER,
734 &execSegFlags, &entitlementDER.aref());
735
736 if (generateEntitlementDER) {
737 writer.component(cdEntitlementDERSlot, entitlementDER);
738 builder.specialSlot(cdEntitlementDERSlot, entitlementDER);
739 }
740
741 builder.addExecSegFlags(execSegFlags);
742 }
743 }
744 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
745 builder.specialSlot(cdRepSpecificSlot, repSpecific);
746
747 writer.addDiscretionary(builder);
748
749 #if 0 // rdar://problem/25720754
750 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
751 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
752 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
753 std::vector<Endian<uint32_t> > slotVector;
754 slotVector.push_back(cdCodeDirectorySlot); // mandatory
755 std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots();
756 filledSlots.insert(cdTopDirectorySlot); // will be added below
757 copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector));
758 for (unsigned n = 0; n < alternateDigestCount; n++)
759 slotVector.push_back(cdAlternateCodeDirectorySlots + n);
760 slotVector.push_back(cdSignatureSlot);
761 CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0]));
762 writer.component(cdTopDirectorySlot, cfSlotVector);
763 builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
764 }
765 #endif
766 }
767
768
769 #include <security_smime/tsaSupport.h>
770
771 //
772 // Generate the CMS signature for a (finished) CodeDirectory.
773 //
774 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd,
775 CFDictionaryRef hashDict,
776 CFArrayRef hashList)
777 {
778 assert(state.mSigner);
779 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
780
781 // a null signer generates a null signature blob
782 if (state.mSigner == SecIdentityRef(kCFNull))
783 return CFDataCreate(NULL, NULL, 0);
784
785 // generate CMS signature
786 CFRef<CMSEncoderRef> cms;
787 MacOSError::check(CMSEncoderCreate(&cms.aref()));
788 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
789 CMSEncoderAddSigners(cms, state.mSigner);
790 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
791 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
792
793 if (emitSigningTime) {
794 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
795 CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent();
796 MacOSError::check(CMSEncoderSetSigningTime(cms, time));
797 }
798
799 if (hashDict != NULL) {
800 assert(hashList != NULL);
801
802 // V2 Hash Agility
803
804 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgilityV2));
805 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms, hashDict));
806
807 // V1 Hash Agility
808
809 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashList);
810 CFRef<CFDataRef> hashAgilityV1Attribute = makeCFData(hashDict.get());
811
812 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
813 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashAgilityV1Attribute));
814 }
815
816 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
817
818 // Set up to call Timestamp server if requested
819 if (state.mWantTimeStamp)
820 {
821 CFRef<CFErrorRef> error = NULL;
822 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
823 if (error)
824 MacOSError::throwMe(errSecDataNotAvailable);
825
826 if (state.mNoTimeStampCerts || state.mTimestampService) {
827 if (state.mTimestampService)
828 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
829 if (state.mNoTimeStampCerts)
830 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
831 }
832
833 CmsMessageSetTSAContext(cms, defaultTSContext);
834 }
835
836 CFDataRef signature;
837 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
838
839 return signature;
840 }
841
842
843 //
844 // Our DiskRep::signingContext methods communicate with the signing subsystem
845 // in terms those callers can easily understand.
846 //
847 string SecCodeSigner::Signer::sdkPath(const std::string &path) const
848 {
849 assert(path[0] == '/'); // need absolute path here
850 if (state.mSDKRoot)
851 return cfString(state.mSDKRoot) + path;
852 else
853 return path;
854 }
855
856 bool SecCodeSigner::Signer::isAdhoc() const
857 {
858 return state.mSigner == SecIdentityRef(kCFNull);
859 }
860
861 SecCSFlags SecCodeSigner::Signer::signingFlags() const
862 {
863 return state.mOpFlags;
864 }
865
866
867 //
868 // Parse a text of the form
869 // flag,...,flag
870 // where each flag is the canonical name of a signable CodeDirectory flag.
871 // No abbreviations are allowed, and internally set flags are not accepted.
872 //
873 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
874 {
875 uint32_t flags = 0;
876 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
877 string word = (comma == string::npos) ? text : text.substr(0, comma);
878 const SecCodeDirectoryFlagTable *item;
879 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
880 if (item->signable && word == item->name) {
881 flags |= item->value;
882 break;
883 }
884 if (!item->name) // not found
885 MacOSError::throwMe(errSecCSInvalidFlags);
886 if (comma == string::npos) // last word
887 break;
888 }
889 return flags;
890 }
891
892
893 //
894 // Generate a unique string from our underlying DiskRep.
895 // We could get 90%+ of the uniquing benefit by just generating
896 // a random string here. Instead, we pick the (hex string encoding of)
897 // the source rep's unique identifier blob. For universal binaries,
898 // this is the canonical local architecture, which is a bit arbitrary.
899 // This provides us with a consistent unique string for all architectures
900 // of a fat binary, *and* (unlike a random string) is reproducible
901 // for identical inputs, even upon resigning.
902 //
903 std::string SecCodeSigner::Signer::uniqueName() const
904 {
905 CFRef<CFDataRef> identification = rep->identification();
906 const UInt8 *ident = CFDataGetBytePtr(identification);
907 const CFIndex length = CFDataGetLength(identification);
908 string result;
909 for (CFIndex n = 0; n < length; n++) {
910 char hex[3];
911 snprintf(hex, sizeof(hex), "%02x", ident[n]);
912 result += hex;
913 }
914 return result;
915 }
916
917 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) {
918 CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key);
919
920 if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) {
921 return false;
922 }
923
924 return CFBooleanGetValue(entValue);
925 }
926
927 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements, bool generateDER,
928 uint64_t *execSegFlags, CFDataRef *entitlementDER)
929 {
930 if (!entitlements) {
931 return; // nothing to do.
932 }
933
934 EntitlementDERBlob *derBlob = NULL;
935
936 try {
937 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
938
939 if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
940 MacOSError::throwMe(errSecCSInvalidEntitlements);
941 }
942
943 CFRef<CFDictionaryRef> entDict = blob->entitlements();
944
945 if (generateDER) {
946 CFRef<CFErrorRef> error = NULL;
947 size_t const der_size = der_sizeof_plist(entDict, &error.aref());
948
949 if (der_size == 0) {
950 secerror("Getting DER size for entitlement plist failed: %@", error.get());
951 MacOSError::throwMe(errSecCSInvalidEntitlements);
952 }
953
954 derBlob = EntitlementDERBlob::alloc(der_size);
955
956 if (derBlob == NULL) {
957 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size);
958 MacOSError::throwMe(errSecCSInvalidEntitlements);
959 }
960 uint8_t * const der_end = derBlob->der() + der_size;
961 uint8_t * const der_start = der_encode_plist(entDict, &error.aref(), derBlob->der(), der_end);
962
963 if (der_start != derBlob->der()) {
964 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start - derBlob->der()));
965 free(derBlob);
966 MacOSError::throwMe(errSecCSInvalidEntitlements);
967 }
968
969 *entitlementDER = makeCFData(derBlob, derBlob->length());
970 free(derBlob);
971 derBlob = NULL;
972 }
973
974 if (execSegFlags != NULL) {
975 uint64_t flags = 0;
976
977 flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
978 flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
979 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
980 flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
981 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
982 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
983 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
984
985 *execSegFlags = flags;
986 }
987
988 } catch (const CommonError &err) {
989 free(derBlob);
990 // Not fatal if we're not asked to generate DER entitlements.
991
992 secwarning("failed to parse entitlements: %s", err.what());
993 if (generateDER) {
994 throw;
995 }
996 }
997 }
998
999 } // end namespace CodeSigning
1000 } // end namespace Security