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