2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // signer - Signing operation supervisor and controller
27 #include "bundlediskrep.h"
28 #include "der_plist.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"
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>
50 namespace CodeSigning
{
56 void SecCodeSigner::Signer::sign(SecCSFlags flags
)
58 rep
= code
->diskRep()->base();
61 PreSigningContext
context(*this);
63 considerTeamID(context
);
65 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
66 signMachO(fat
, context
);
68 signArchitectureAgnostic(context
);
73 void SecCodeSigner::Signer::considerTeamID(const PreSigningContext
& context
)
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
);
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
);
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
;
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
);
102 // Remove any existing code signature from code
104 void SecCodeSigner::Signer::remove(SecCSFlags flags
)
106 // can't remove a detached signature
108 MacOSError::throwMe(errSecCSNotSupported
);
110 rep
= code
->diskRep();
112 if (state
.mPreserveAFSC
)
113 rep
->writer()->setPreserveAFSC(state
.mPreserveAFSC
);
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
121 // architecture-agnostic removal
122 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
130 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
132 void SecCodeSigner::Signer::prepare(SecCSFlags flags
)
134 // make sure the rep passes strict validation
136 rep
->strictValidate(NULL
, MacOSErrorSet(), flags
| (kSecCSQuickCheck
|kSecCSRestrictSidebandData
));
138 // initialize progress/cancellation state
139 code
->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
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
));
146 uint32_t inherit
= code
->isSigned() ? state
.mPreserveMetadata
: 0;
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());
160 secinfo("signer", "using explicit identifier=%s", identifier
.c_str());
162 teamID
= state
.mTeamID
;
163 if (teamID
.empty() && (inherit
& kSecCodeSignerPreserveTeamIdentifier
)) {
164 const char *c_id
= code
->teamID();
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();
174 entitlements
= state
.mEntitlementData
;
175 if (!entitlements
&& (inherit
& kSecCodeSignerPreserveEntitlements
))
176 entitlements
= code
->component(cdEntitlementSlot
);
178 generateEntitlementDER
= signingFlags() & kSecCSSignGenerateEntitlementDER
;
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
);
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
);
198 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
202 if (!haveCdFlags
&& (inherit
& kSecCodeSignerPreserveFlags
)) {
203 cdFlags
= code
->codeDirectory(false)->flags
& ~kSecCodeSignatureAdhoc
;
204 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags
);
209 if ((state
.mSigner
== SecIdentityRef(kCFNull
)) &&
210 !state
.mOmitAdhocFlag
) // ad-hoc signing requested...
211 cdFlags
|= kSecCodeSignatureAdhoc
; // ... so note that
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
));
227 MacOSError::throwMe(errSecCSInvalidObjectRef
);
228 } else if (inherit
& kSecCodeSignerPreserveRequirements
)
229 if (const Requirements
*rp
= code
->internalRequirements())
230 requirements
= rp
->clone();
232 // prepare the resource directory, if any
233 string rpath
= rep
->resourcesRootPath();
235 CFCopyRef
<CFDictionaryRef
> resourceRules
;
236 if (!rpath
.empty()) {
237 // explicitly given resource rules always win
238 resourceRules
= state
.mResourceRules
;
240 // inherited rules come next (overriding embedded ones!)
241 if (!resourceRules
&& (inherit
& kSecCodeSignerPreserveResourceRules
))
242 if (CFDictionaryRef oldRules
= code
->resourceDictionary(false))
243 resourceRules
= oldRules
;
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
);
256 // if we got one from anywhere (but the defaults), sanity-check it
258 CFTypeRef rules
= CFDictionaryGetValue(resourceRules
, CFSTR("rules"));
259 if (!rules
|| CFGetTypeID(rules
) != CFDictionaryGetTypeID())
260 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
263 // finally, ask the DiskRep for its default
265 resourceRules
.take(rep
->defaultResourceRules(*this));
267 // resource root can optionally be the canonical bundle path,
268 // but sealed resource paths are always relative to rpath
270 if (signingFlags() & kSecCSSignBundleRoot
)
271 rrpath
= cfStringRelease(rep
->copyCanonicalPath());
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
281 CFAbsoluteTime time
= CFDateGetAbsoluteTime(state
.mSigningTime
);
282 if (time
> CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
283 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
284 emitSigningTime
= true;
288 pagesize
= state
.mPageSize
? cfNumber
<size_t>(state
.mPageSize
) : rep
->pageSize(*this);
290 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
291 rep
->prepareForSigning(*this);
293 // apply some defaults after diskRep intervention
294 if (hashAlgorithms
.empty()) { // default to SHA256 + SHA-1
295 hashAlgorithms
.insert(kSecCodeSignatureHashSHA1
);
296 hashAlgorithms
.insert(kSecCodeSignatureHashSHA256
);
299 // build the resource directory (once and for all, using the digests determined above)
300 if (!rpath
.empty()) {
301 buildResources(rrpath
, rpath
, resourceRules
);
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());
317 addPreEncryptHashes(preEncryptHashMaps
[preEncryptMainArch
], code
);
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
);
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
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());
338 addRuntimeVersions(runtimeVersionMap
[runtimeVersionMainArch
], code
);
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
);
349 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap
&map
, SecStaticCode
const *code
) {
350 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
353 for(auto const& pair
: *cds
) {
354 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
355 CFDataRef
const cddata
= pair
.second
;
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
;
368 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap
&map
, const SecStaticCode
*code
)
370 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
373 for(auto const& pair
: *cds
) {
374 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
375 CFDataRef
const cddata
= pair
.second
;
377 CodeDirectory
const * cd
=
378 reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cddata
));
379 if (cd
->runtimeVersion()) {
380 map
[alg
] = cd
->runtimeVersion();
387 // Collect the resource seal for a program.
388 // This includes both sealed resources and information about nested code.
390 void SecCodeSigner::Signer::buildResources(std::string root
, std::string relBase
, CFDictionaryRef rulesDict
)
392 typedef ResourceBuilder::Rule Rule
;
394 secinfo("codesign", "start building resource directory");
395 __block CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
397 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(rulesDict
, "rules");
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);
408 CFDictionaryRef files2
= NULL
;
409 if (!(signingFlags() & kSecCSSignV1
)) {
410 CFCopyRef
<CFDictionaryRef
> rules2
= cfget
<CFDictionaryRef
>(rulesDict
, "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
420 Dispatch::Group group
;
421 Dispatch::Group
&groupRef
= group
; // (into block)
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
);
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
;
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);
443 UnixError::check(-1);
445 seal
.take(cfmake
<CFMutableDictionaryRef
>("{symlink=%s}", target
));
447 seal
.take(resources
.hashFile(accpath
.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight
));
449 if (seal
.get() == NULL
) {
450 secerror("Failed to generate sealed resource: %d, %d, %s", isNested
, isSymlink
, accpath
.c_str());
451 MacOSError::throwMe(errSecCSBadResource
);
453 if (ruleFlags
& ResourceBuilder::optional
)
454 CFDictionaryAddValue(seal
, CFSTR("optional"), kCFBooleanTrue
);
456 StLock
<Mutex
> _(resourceLock
);
457 if ((hash
= CFDictionaryGetValue(seal
, CFSTR("hash"))) && CFDictionaryGetCount(seal
) == 1) // simple form
458 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), hash
);
460 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), seal
.get());
461 code
->reportProgress();
465 CFDictionaryAddValue(result
, CFSTR("rules2"), resourceBuilder
.rules());
467 CFDictionaryAddValue(result
, CFSTR("files2"), files2
);
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
);
485 hash
= CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal
), CFSTR("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
);
499 CFDictionaryAddValue(result
, CFSTR("files"), files
.get());
502 resourceDirectory
= result
.get();
503 resourceDictData
.take(makeCFData(resourceDirectory
.get()));
508 // Deal with one piece of nested code
510 CFMutableDictionaryRef
SecCodeSigner::Signer::signNested(const std::string
&path
, const std::string
&relpath
)
512 // sign nested code and collect nesting information
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(),
522 MacOSError::throwMe(errSecCSUnsigned
);
523 } catch (const CommonError
&err
) {
524 CSError::throwMe(err
.osStatus(), kSecCFErrorPath
, CFTempURL(relpath
, false, this->code
->resourceBase()));
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.
535 void SecCodeSigner::Signer::signMachO(Universal
*fat
, const Requirement::Context
&context
)
537 // Mach-O executable at the core - perform multi-architecture signing
538 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
540 if (state
.mPreserveAFSC
)
541 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
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
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
));
555 // library validation is not compatible with i386
556 if (arch
.architecture
.cpuType() == CPU_TYPE_I386
) {
557 if (cdFlags
& kSecCodeSignatureLibraryValidation
) {
558 MacOSError::throwMe(errSecCSBadLVArch
);
562 bool mainBinary
= arch
.source
.get()->type() == MH_EXECUTE
;
564 uint32_t runtimeVersion
= 0;
565 if (cdFlags
& kSecCodeSignatureRuntime
) {
566 runtimeVersion
= state
.mRuntimeVersionOverride
? state
.mRuntimeVersionOverride
: arch
.source
.get()->sdkVersion();
569 arch
.ireqs(requirements
, rep
->defaultRequirements(&arch
.architecture
, *this), context
);
570 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-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
];
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
);
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
)));
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
));
600 arch
.blobSize
= arch
.size(sizes
, state
.mCMSSize
, 0);
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
;
610 // finish CodeDirectories (off new binary) and sign it
611 __block CodeDirectorySet cdSet
;
612 arch
.eachDigest(^(CodeDirectory::Builder
&builder
) {
613 CodeDirectory
*cd
= builder
.build();
617 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
618 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
619 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
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
631 // done: write edit copy back over the original
632 if (!state
.mDryRun
) {
639 // Sign a binary that has no notion of architecture.
640 // That currently means anything that isn't Mach-O format.
642 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
&context
)
644 // non-Mach-O executable - single-instance signing
645 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
646 (new DetachedBlobWriter(*this)) : rep
->writer();
648 if(state
.mPreserveAFSC
)
649 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
651 CodeDirectorySet cdSet
;
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
);
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);
665 CodeDirectory
*cd
= builder
.build();
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
);
676 // write out all CodeDirectories
678 cdSet
.populate(writer
);
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
);
691 // Global populate - send components to destination buffers ONCE
693 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
695 if (resourceDirectory
&& !state
.mDryRun
)
696 writer
.component(cdResourceDirSlot
, resourceDictData
);
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.
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
)
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
);
724 if (CFRef
<CFDataRef
> data
= rep
->component(cdInfoSlot
))
725 builder
.specialSlot(cdInfoSlot
, data
);
727 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
728 writer
.component(cdRequirementsSlot
, data
);
729 builder
.specialSlot(cdRequirementsSlot
, data
);
731 if (resourceDirectory
)
732 builder
.specialSlot(cdResourceDirSlot
, resourceDictData
);
734 writer
.component(cdEntitlementSlot
, entitlements
);
735 builder
.specialSlot(cdEntitlementSlot
, entitlements
);
738 CFRef
<CFDataRef
> entitlementDER
;
739 uint64_t execSegFlags
= 0;
740 cookEntitlements(entitlements
, generateEntitlementDER
,
741 &execSegFlags
, &entitlementDER
.aref());
743 if (generateEntitlementDER
) {
744 writer
.component(cdEntitlementDERSlot
, entitlementDER
);
745 builder
.specialSlot(cdEntitlementDERSlot
, entitlementDER
);
748 builder
.addExecSegFlags(execSegFlags
);
751 if (CFRef
<CFDataRef
> repSpecific
= rep
->component(cdRepSpecificSlot
))
752 builder
.specialSlot(cdRepSpecificSlot
, repSpecific
);
754 writer
.addDiscretionary(builder
);
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
);
776 #include <security_smime/tsaSupport.h>
779 // Generate the CMS signature for a (finished) CodeDirectory.
781 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
,
782 CFDictionaryRef hashDict
,
785 assert(state
.mSigner
);
786 CFRef
<CFMutableDictionaryRef
> defaultTSContext
= NULL
;
788 // a null signer generates a null signature blob
789 if (state
.mSigner
== SecIdentityRef(kCFNull
))
790 return CFDataCreate(NULL
, NULL
, 0);
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));
800 if (emitSigningTime
) {
801 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
802 CFAbsoluteTime time
= signingTime
? signingTime
: CFAbsoluteTimeGetCurrent();
803 MacOSError::check(CMSEncoderSetSigningTime(cms
, time
));
806 if (hashDict
!= NULL
) {
807 assert(hashList
!= NULL
);
811 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgilityV2
));
812 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms
, hashDict
));
816 CFTemp
<CFDictionaryRef
> hashDict("{cdhashes=%O}", hashList
);
817 CFRef
<CFDataRef
> hashAgilityV1Attribute
= makeCFData(hashDict
.get());
819 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgility
));
820 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms
, hashAgilityV1Attribute
));
823 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
825 // Set up to call Timestamp server if requested
826 if (state
.mWantTimeStamp
)
828 CFRef
<CFErrorRef
> error
= NULL
;
829 defaultTSContext
= SecCmsTSAGetDefaultContext(&error
.aref());
831 MacOSError::throwMe(errSecDataNotAvailable
);
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
);
840 CmsMessageSetTSAContext(cms
, defaultTSContext
);
844 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
851 // Our DiskRep::signingContext methods communicate with the signing subsystem
852 // in terms those callers can easily understand.
854 string
SecCodeSigner::Signer::sdkPath(const std::string
&path
) const
856 assert(path
[0] == '/'); // need absolute path here
858 return cfString(state
.mSDKRoot
) + path
;
863 bool SecCodeSigner::Signer::isAdhoc() const
865 return state
.mSigner
== SecIdentityRef(kCFNull
);
868 SecCSFlags
SecCodeSigner::Signer::signingFlags() const
870 return state
.mOpFlags
;
875 // Parse a text of the form
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.
880 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text
)
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
;
891 if (!item
->name
) // not found
892 MacOSError::throwMe(errSecCSInvalidFlags
);
893 if (comma
== string::npos
) // last word
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.
910 std::string
SecCodeSigner::Signer::uniqueName() const
912 CFRef
<CFDataRef
> identification
= rep
->identification();
913 const UInt8
*ident
= CFDataGetBytePtr(identification
);
914 const CFIndex length
= CFDataGetLength(identification
);
916 for (CFIndex n
= 0; n
< length
; n
++) {
918 snprintf(hex
, sizeof(hex
), "%02x", ident
[n
]);
924 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict
, CFStringRef key
) {
925 CFBooleanRef entValue
= (CFBooleanRef
)CFDictionaryGetValue(entDict
, key
);
927 if (entValue
== NULL
|| CFGetTypeID(entValue
) != CFBooleanGetTypeID()) {
931 return CFBooleanGetValue(entValue
);
934 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements
, bool generateDER
,
935 uint64_t *execSegFlags
, CFDataRef
*entitlementDER
)
938 return; // nothing to do.
941 EntitlementDERBlob
*derBlob
= NULL
;
944 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlements
));
946 if (blob
== NULL
|| !blob
->validateBlob(CFDataGetLength(entitlements
))) {
947 MacOSError::throwMe(errSecCSInvalidEntitlements
);
950 CFRef
<CFDictionaryRef
> entDict
= blob
->entitlements();
953 CFRef
<CFErrorRef
> error
= NULL
;
954 size_t const der_size
= der_sizeof_plist(entDict
, &error
.aref());
957 secerror("Getting DER size for entitlement plist failed: %@", error
.get());
958 MacOSError::throwMe(errSecCSInvalidEntitlements
);
961 derBlob
= EntitlementDERBlob::alloc(der_size
);
963 if (derBlob
== NULL
) {
964 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size
);
965 MacOSError::throwMe(errSecCSInvalidEntitlements
);
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
);
970 if (der_start
!= derBlob
->der()) {
971 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start
- derBlob
->der()));
973 MacOSError::throwMe(errSecCSInvalidEntitlements
);
976 *entitlementDER
= makeCFData(derBlob
, derBlob
->length());
981 if (execSegFlags
!= NULL
) {
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;
992 *execSegFlags
= flags
;
995 } catch (const CommonError
&err
) {
997 // Not fatal if we're not asked to generate DER entitlements.
999 secwarning("failed to parse entitlements: %s", err
.what());
1006 //// Signature Editing
1008 void SecCodeSigner::Signer::edit(SecCSFlags flags
)
1010 rep
= code
->diskRep()->base();
1012 Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage();
1014 prepareForEdit(flags
);
1019 editArchitectureAgnostic();
1023 EditableDiskRep
*SecCodeSigner::Signer::editMainExecutableRep(DiskRep
*rep
)
1025 EditableDiskRep
*mainExecRep
= NULL
;
1026 BundleDiskRep
*bundleDiskRep
= dynamic_cast<BundleDiskRep
*>(rep
);
1028 if (bundleDiskRep
) {
1029 mainExecRep
= dynamic_cast<EditableDiskRep
*>(bundleDiskRep
->mainExecRep());
1035 void SecCodeSigner::Signer::prepareForEdit(SecCSFlags flags
) {
1036 setDigestAlgorithms(code
->hashAlgorithms());
1038 Universal
*machO
= (code
->diskRep()->mainExecutableIsMachO() ?
1039 code
->diskRep()->mainExecutableImage() : NULL
);
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
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());
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.
1058 MacOSError::throwMe(errSecCSNotSupported
,
1059 "Signature editing must be performed on universal binary instead of narrow slice (using --edit-arch instead of --arch).");
1062 if (state
.mEditArch
&& !machO
->isUniversal()) {
1063 MacOSError::throwMe(errSecCSInvalidFlags
,
1064 "--edit-arch is only valid for universal binaries.");
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.
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.)
1078 MacOSError::throwMe(errSecCSNotSupported
,
1079 "CMS editing must be performed on specific slice (specified with --edit-arch).");
1083 void (^editArch
)(SecStaticCode
*code
, Architecture arch
) =
1084 ^(SecStaticCode
*code
, Architecture arch
) {
1085 EditableDiskRep
*editRep
= dynamic_cast<EditableDiskRep
*>(code
->diskRep());
1087 if (editRep
== NULL
) {
1088 MacOSError::throwMe(errSecCSNotSupported
,
1089 "Signature editing not supported for code of this type.");
1092 EditableDiskRep
*mainExecRep
= editMainExecutableRep(code
->diskRep());
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
;
1100 editComponents
[arch
] = std::make_unique
<RawComponentMap
>(editRep
->createRawComponents());
1102 if (!state
.mEditArch
|| arch
== state
.mEditArch
) {
1103 if (state
.mEditCMS
) {
1104 CFDataRef cms
= state
.mEditCMS
.get();
1105 (*editComponents
[arch
])[cdSignatureSlot
] = cms
;
1110 editArch(code
, editMainArch
);
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
);
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).
1126 resourceDictData
= rep
->component(cdResourceDirSlot
);
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();
1133 if (state
.mPreserveAFSC
)
1134 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1136 unique_ptr
<ArchEditor
> editor(new MachOEditor(writer
, *fat
,
1137 this->digestAlgorithms(),
1138 rep
->mainExecutablePath()));
1139 assert(editor
->count() > 0);
1141 if (resourceDictData
&& !editor
->attribute(writerNoGlobal
)) {
1142 // For when the resource dict is "global", e.g. for bundles.
1143 editor
->component(cdResourceDirSlot
, resourceDictData
);
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
1150 if (resourceDictData
&& editor
->attribute(writerNoGlobal
)) {
1151 // Technically possible to embed a resource dict in the embedded sig.
1152 arch
.component(cdResourceDirSlot
, resourceDictData
);
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
);
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();
1169 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
1170 MachOEditor::Arch
&arch
= *it
->second
;
1171 editor
->reset(arch
);
1173 if (!state
.mDryRun
) {
1174 EmbeddedSignatureBlob
*blob
= arch
.make();
1175 editor
->write(arch
, blob
); // takes ownership of blob
1179 if (!state
.mDryRun
) {
1185 void SecCodeSigner::Signer::editArchitectureAgnostic()
1187 if (state
.mDryRun
) {
1191 // non-Mach-O executable - single-instance signature editing
1192 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
1194 if(state
.mPreserveAFSC
)
1195 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1197 for (auto const &entry
: *editComponents
[editMainArch
]) {
1198 CodeDirectory::Slot slot
= entry
.first
;
1199 CFDataRef data
= entry
.second
.get();
1201 writer
->component(slot
, data
);
1204 // commit to storage
1208 } // end namespace CodeSigning
1209 } // end namespace Security