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