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 const std::string
path(ent
->fts_path
);
433 const std::string
accpath(ent
->fts_accpath
);
434 this->state
.mLimitedAsync
->perform(groupRef
, ^{
435 CFRef
<CFMutableDictionaryRef
> seal
;
436 if (ruleFlags
& ResourceBuilder::nested
) {
437 seal
.take(signNested(path
, relpath
));
438 } else if (isSymlink
) {
439 char target
[PATH_MAX
];
440 ssize_t len
= ::readlink(accpath
.c_str(), target
, sizeof(target
)-1);
442 UnixError::check(-1);
444 seal
.take(cfmake
<CFMutableDictionaryRef
>("{symlink=%s}", target
));
446 seal
.take(resources
.hashFile(accpath
.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight
));
448 if (ruleFlags
& ResourceBuilder::optional
)
449 CFDictionaryAddValue(seal
, CFSTR("optional"), kCFBooleanTrue
);
451 StLock
<Mutex
> _(resourceLock
);
452 if ((hash
= CFDictionaryGetValue(seal
, CFSTR("hash"))) && CFDictionaryGetCount(seal
) == 1) // simple form
453 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), hash
);
455 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), seal
.get());
456 code
->reportProgress();
460 CFDictionaryAddValue(result
, CFSTR("rules2"), resourceBuilder
.rules());
462 CFDictionaryAddValue(result
, CFSTR("files2"), files2
);
465 CFDictionaryAddValue(result
, CFSTR("rules"), rules
); // preserve V1 rules in any case
466 if (!(signingFlags() & kSecCSSignNoV1
)) {
467 // build the legacy (V1) resource seal
468 __block CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
469 ResourceBuilder
resourceBuilder(root
, relBase
, rules
, strict
, MacOSErrorSet());
470 ResourceBuilder
&resources
= resourceBuilder
;
471 rep
->adjustResources(resources
); // DiskRep-specific adjustments
472 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, std::string relpath
, Rule
*rule
) {
473 if (ent
->fts_info
== FTS_F
) {
474 CFRef
<CFDataRef
> hash
;
475 if (files2
) // try to get the hash from a previously-made version
476 if (CFTypeRef seal
= CFDictionaryGetValue(files2
, CFTempString(relpath
))) {
477 if (CFGetTypeID(seal
) == CFDataGetTypeID())
478 hash
= CFDataRef(seal
);
480 hash
= CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal
), CFSTR("hash")));
483 hash
.take(resources
.hashFile(ent
->fts_accpath
, kSecCodeSignatureHashSHA1
));
484 if (ruleFlags
== 0) { // default case - plain hash
485 cfadd(files
, "{%s=%O}", relpath
.c_str(), hash
.get());
486 secinfo("csresource", "%s added simple (rule %p)", relpath
.c_str(), rule
);
487 } else { // more complicated - use a sub-dictionary
488 cfadd(files
, "{%s={hash=%O,optional=%B}}",
489 relpath
.c_str(), hash
.get(), ruleFlags
& ResourceBuilder::optional
);
490 secinfo("csresource", "%s added complex (rule %p)", relpath
.c_str(), rule
);
494 CFDictionaryAddValue(result
, CFSTR("files"), files
.get());
497 resourceDirectory
= result
.get();
498 resourceDictData
.take(makeCFData(resourceDirectory
.get()));
503 // Deal with one piece of nested code
505 CFMutableDictionaryRef
SecCodeSigner::Signer::signNested(const std::string
&path
, const std::string
&relpath
)
507 // sign nested code and collect nesting information
509 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(path
));
510 if (signingFlags() & kSecCSSignNestedCode
)
511 this->state
.sign(code
, signingFlags());
512 std::string dr
= Dumper::dump(code
->designatedRequirement());
513 if (CFDataRef hash
= code
->cdHash())
514 return cfmake
<CFMutableDictionaryRef
>("{requirement=%s,cdhash=%O}",
515 Dumper::dump(code
->designatedRequirement()).c_str(),
517 MacOSError::throwMe(errSecCSUnsigned
);
518 } catch (const CommonError
&err
) {
519 CSError::throwMe(err
.osStatus(), kSecCFErrorPath
, CFTempURL(relpath
, false, this->code
->resourceBase()));
525 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
526 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
527 // treat them as architectural binaries containing (only) one architecture - that
528 // interpretation is courtesy of the Universal/MachO support classes.
530 void SecCodeSigner::Signer::signMachO(Universal
*fat
, const Requirement::Context
&context
)
532 // Mach-O executable at the core - perform multi-architecture signing
533 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
535 if (state
.mPreserveAFSC
)
536 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
538 auto_ptr
<ArchEditor
> editor(state
.mDetached
539 ? static_cast<ArchEditor
*>(new BlobEditor(*fat
, *this))
540 : new MachOEditor(writer
, *fat
, this->digestAlgorithms(), rep
->mainExecutablePath()));
541 assert(editor
->count() > 0);
542 if (!editor
->attribute(writerNoGlobal
)) // can store architecture-common components
545 // pass 1: prepare signature blobs and calculate sizes
546 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
547 MachOEditor::Arch
&arch
= *it
->second
;
548 arch
.source
.reset(fat
->architecture(it
->first
));
550 // library validation is not compatible with i386
551 if (arch
.architecture
.cpuType() == CPU_TYPE_I386
) {
552 if (cdFlags
& kSecCodeSignatureLibraryValidation
) {
553 MacOSError::throwMe(errSecCSBadLVArch
);
557 bool mainBinary
= arch
.source
.get()->type() == MH_EXECUTE
;
559 uint32_t runtimeVersion
= 0;
560 if (cdFlags
& kSecCodeSignatureRuntime
) {
561 runtimeVersion
= state
.mRuntimeVersionOverride
? state
.mRuntimeVersionOverride
: arch
.source
.get()->sdkVersion();
564 arch
.ireqs(requirements
, rep
->defaultRequirements(&arch
.architecture
, *this), context
);
565 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-arch
567 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
568 uint32_t runtimeVersionToUse
= runtimeVersion
;
569 if ((cdFlags
& kSecCodeSignatureRuntime
) && runtimeVersionMap
.count(arch
.architecture
)) {
570 if (runtimeVersionMap
[arch
.architecture
].count(*type
)) {
571 runtimeVersionToUse
= runtimeVersionMap
[arch
.architecture
][*type
];
574 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
575 populate(builder
, arch
, arch
.ireqs
,
576 arch
.source
->offset(), arch
.source
->signingExtent(),
577 mainBinary
, rep
->execSegBase(&(arch
.architecture
)), rep
->execSegLimit(&(arch
.architecture
)),
578 unsigned(digestAlgorithms().size()-1),
579 preEncryptHashMaps
[arch
.architecture
], runtimeVersionToUse
);
583 // add identification blob (made from this architecture) only if we're making a detached signature
584 if (state
.mDetached
) {
585 CFRef
<CFDataRef
> identification
= MachORep::identificationFor(arch
.source
.get());
586 arch
.add(cdIdentificationSlot
, BlobWrapper::alloc(
587 CFDataGetBytePtr(identification
), CFDataGetLength(identification
)));
590 // prepare SuperBlob size estimate
591 __block
std::vector
<size_t> sizes
;
592 arch
.eachDigest(^(CodeDirectory::Builder
& builder
){
593 sizes
.push_back(builder
.size(CodeDirectory::currentVersion
));
595 arch
.blobSize
= arch
.size(sizes
, state
.mCMSSize
, 0);
600 // pass 2: Finish and generate signatures, and write them
601 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
602 MachOEditor::Arch
&arch
= *it
->second
;
605 // finish CodeDirectories (off new binary) and sign it
606 __block CodeDirectorySet cdSet
;
607 arch
.eachDigest(^(CodeDirectory::Builder
&builder
) {
608 CodeDirectory
*cd
= builder
.build();
612 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
613 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
614 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
616 // complete the SuperBlob
617 cdSet
.populate(&arch
);
618 arch
.add(cdSignatureSlot
, BlobWrapper::alloc(
619 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)));
620 if (!state
.mDryRun
) {
621 EmbeddedSignatureBlob
*blob
= arch
.make();
622 editor
->write(arch
, blob
); // takes ownership of blob
626 // done: write edit copy back over the original
627 if (!state
.mDryRun
) {
634 // Sign a binary that has no notion of architecture.
635 // That currently means anything that isn't Mach-O format.
637 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
&context
)
639 // non-Mach-O executable - single-instance signing
640 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
641 (new DetachedBlobWriter(*this)) : rep
->writer();
643 if(state
.mPreserveAFSC
)
644 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
646 CodeDirectorySet cdSet
;
648 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
649 CodeDirectory::Builder
builder(*type
);
650 InternalRequirements ireqs
;
651 ireqs(requirements
, rep
->defaultRequirements(NULL
, *this), context
);
653 populate(builder
, *writer
, ireqs
, rep
->signingBase(), rep
->signingLimit(),
654 false, // only machOs can currently be main binaries
655 rep
->execSegBase(NULL
), rep
->execSegLimit(NULL
),
656 unsigned(digestAlgorithms().size()-1),
657 preEncryptHashMaps
[preEncryptMainArch
], // Only one map, the default.
658 (cdFlags
& kSecCodeSignatureRuntime
) ? state
.mRuntimeVersionOverride
: 0);
660 CodeDirectory
*cd
= builder
.build();
665 // add identification blob (made from this architecture) only if we're making a detached signature
666 if (state
.mDetached
) {
667 CFRef
<CFDataRef
> identification
= rep
->identification();
668 writer
->component(cdIdentificationSlot
, identification
);
671 // write out all CodeDirectories
673 cdSet
.populate(writer
);
675 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
676 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
677 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
678 writer
->signature(signature
);
686 // Global populate - send components to destination buffers ONCE
688 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
690 if (resourceDirectory
&& !state
.mDryRun
)
691 writer
.component(cdResourceDirSlot
, resourceDictData
);
696 // Per-architecture populate - send components to per-architecture buffers
697 // and populate the CodeDirectory for an architecture. In architecture-agnostic
698 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
699 // for the purposes of this call.
701 void SecCodeSigner::Signer::populate(CodeDirectory::Builder
&builder
, DiskRep::Writer
&writer
,
702 InternalRequirements
&ireqs
, size_t offset
, size_t length
,
703 bool mainBinary
, size_t execSegBase
, size_t execSegLimit
,
704 unsigned alternateDigestCount
,
705 PreEncryptHashMap
const &preEncryptHashMap
,
706 uint32_t runtimeVersion
)
708 // fill the CodeDirectory
709 builder
.executable(rep
->mainExecutablePath(), pagesize
, offset
, length
);
710 builder
.flags(cdFlags
);
711 builder
.identifier(identifier
);
712 builder
.teamID(teamID
);
713 builder
.platform(state
.mPlatform
);
714 builder
.execSeg(execSegBase
, execSegLimit
, mainBinary
? kSecCodeExecSegMainBinary
: 0);
715 builder
.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH
);
716 builder
.preservePreEncryptHashMap(preEncryptHashMap
);
717 builder
.runTimeVersion(runtimeVersion
);
719 if (CFRef
<CFDataRef
> data
= rep
->component(cdInfoSlot
))
720 builder
.specialSlot(cdInfoSlot
, data
);
722 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
723 writer
.component(cdRequirementsSlot
, data
);
724 builder
.specialSlot(cdRequirementsSlot
, data
);
726 if (resourceDirectory
)
727 builder
.specialSlot(cdResourceDirSlot
, resourceDictData
);
729 writer
.component(cdEntitlementSlot
, entitlements
);
730 builder
.specialSlot(cdEntitlementSlot
, entitlements
);
733 CFRef
<CFDataRef
> entitlementDER
;
734 uint64_t execSegFlags
= 0;
735 cookEntitlements(entitlements
, generateEntitlementDER
,
736 &execSegFlags
, &entitlementDER
.aref());
738 if (generateEntitlementDER
) {
739 writer
.component(cdEntitlementDERSlot
, entitlementDER
);
740 builder
.specialSlot(cdEntitlementDERSlot
, entitlementDER
);
743 builder
.addExecSegFlags(execSegFlags
);
746 if (CFRef
<CFDataRef
> repSpecific
= rep
->component(cdRepSpecificSlot
))
747 builder
.specialSlot(cdRepSpecificSlot
, repSpecific
);
749 writer
.addDiscretionary(builder
);
751 #if 0 // rdar://problem/25720754
752 if ((signingFlags() & (kSecCSSignOpaque
|kSecCSSignV1
)) == 0 && builder
.hashType() != kSecCodeSignatureHashSHA1
) {
753 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
754 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
755 std::vector
<Endian
<uint32_t> > slotVector
;
756 slotVector
.push_back(cdCodeDirectorySlot
); // mandatory
757 std::set
<CodeDirectory::Slot
> filledSlots
= builder
.filledSpecialSlots();
758 filledSlots
.insert(cdTopDirectorySlot
); // will be added below
759 copy(filledSlots
.begin(), filledSlots
.end(), back_inserter(slotVector
));
760 for (unsigned n
= 0; n
< alternateDigestCount
; n
++)
761 slotVector
.push_back(cdAlternateCodeDirectorySlots
+ n
);
762 slotVector
.push_back(cdSignatureSlot
);
763 CFTempData
cfSlotVector(&slotVector
[0], slotVector
.size() * sizeof(slotVector
[0]));
764 writer
.component(cdTopDirectorySlot
, cfSlotVector
);
765 builder
.specialSlot(cdTopDirectorySlot
, cfSlotVector
);
771 #include <security_smime/tsaSupport.h>
774 // Generate the CMS signature for a (finished) CodeDirectory.
776 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
,
777 CFDictionaryRef hashDict
,
780 assert(state
.mSigner
);
781 CFRef
<CFMutableDictionaryRef
> defaultTSContext
= NULL
;
783 // a null signer generates a null signature blob
784 if (state
.mSigner
== SecIdentityRef(kCFNull
))
785 return CFDataCreate(NULL
, NULL
, 0);
787 // generate CMS signature
788 CFRef
<CMSEncoderRef
> cms
;
789 MacOSError::check(CMSEncoderCreate(&cms
.aref()));
790 MacOSError::check(CMSEncoderSetCertificateChainMode(cms
, kCMSCertificateChainWithRoot
));
791 CMSEncoderAddSigners(cms
, state
.mSigner
);
792 CMSEncoderSetSignerAlgorithm(cms
, kCMSEncoderDigestAlgorithmSHA256
);
793 MacOSError::check(CMSEncoderSetHasDetachedContent(cms
, true));
795 if (emitSigningTime
) {
796 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
797 CFAbsoluteTime time
= signingTime
? signingTime
: CFAbsoluteTimeGetCurrent();
798 MacOSError::check(CMSEncoderSetSigningTime(cms
, time
));
801 if (hashDict
!= NULL
) {
802 assert(hashList
!= NULL
);
806 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgilityV2
));
807 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms
, hashDict
));
811 CFTemp
<CFDictionaryRef
> hashDict("{cdhashes=%O}", hashList
);
812 CFRef
<CFDataRef
> hashAgilityV1Attribute
= makeCFData(hashDict
.get());
814 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgility
));
815 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms
, hashAgilityV1Attribute
));
818 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
820 // Set up to call Timestamp server if requested
821 if (state
.mWantTimeStamp
)
823 CFRef
<CFErrorRef
> error
= NULL
;
824 defaultTSContext
= SecCmsTSAGetDefaultContext(&error
.aref());
826 MacOSError::throwMe(errSecDataNotAvailable
);
828 if (state
.mNoTimeStampCerts
|| state
.mTimestampService
) {
829 if (state
.mTimestampService
)
830 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyURL
, state
.mTimestampService
);
831 if (state
.mNoTimeStampCerts
)
832 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyNoCerts
, kCFBooleanTrue
);
835 CmsMessageSetTSAContext(cms
, defaultTSContext
);
839 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
846 // Our DiskRep::signingContext methods communicate with the signing subsystem
847 // in terms those callers can easily understand.
849 string
SecCodeSigner::Signer::sdkPath(const std::string
&path
) const
851 assert(path
[0] == '/'); // need absolute path here
853 return cfString(state
.mSDKRoot
) + path
;
858 bool SecCodeSigner::Signer::isAdhoc() const
860 return state
.mSigner
== SecIdentityRef(kCFNull
);
863 SecCSFlags
SecCodeSigner::Signer::signingFlags() const
865 return state
.mOpFlags
;
870 // Parse a text of the form
872 // where each flag is the canonical name of a signable CodeDirectory flag.
873 // No abbreviations are allowed, and internally set flags are not accepted.
875 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text
)
878 for (string::size_type comma
= text
.find(','); ; text
= text
.substr(comma
+1), comma
= text
.find(',')) {
879 string word
= (comma
== string::npos
) ? text
: text
.substr(0, comma
);
880 const SecCodeDirectoryFlagTable
*item
;
881 for (item
= kSecCodeDirectoryFlagTable
; item
->name
; item
++)
882 if (item
->signable
&& word
== item
->name
) {
883 flags
|= item
->value
;
886 if (!item
->name
) // not found
887 MacOSError::throwMe(errSecCSInvalidFlags
);
888 if (comma
== string::npos
) // last word
896 // Generate a unique string from our underlying DiskRep.
897 // We could get 90%+ of the uniquing benefit by just generating
898 // a random string here. Instead, we pick the (hex string encoding of)
899 // the source rep's unique identifier blob. For universal binaries,
900 // this is the canonical local architecture, which is a bit arbitrary.
901 // This provides us with a consistent unique string for all architectures
902 // of a fat binary, *and* (unlike a random string) is reproducible
903 // for identical inputs, even upon resigning.
905 std::string
SecCodeSigner::Signer::uniqueName() const
907 CFRef
<CFDataRef
> identification
= rep
->identification();
908 const UInt8
*ident
= CFDataGetBytePtr(identification
);
909 const CFIndex length
= CFDataGetLength(identification
);
911 for (CFIndex n
= 0; n
< length
; n
++) {
913 snprintf(hex
, sizeof(hex
), "%02x", ident
[n
]);
919 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict
, CFStringRef key
) {
920 CFBooleanRef entValue
= (CFBooleanRef
)CFDictionaryGetValue(entDict
, key
);
922 if (entValue
== NULL
|| CFGetTypeID(entValue
) != CFBooleanGetTypeID()) {
926 return CFBooleanGetValue(entValue
);
929 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements
, bool generateDER
,
930 uint64_t *execSegFlags
, CFDataRef
*entitlementDER
)
933 return; // nothing to do.
936 EntitlementDERBlob
*derBlob
= NULL
;
939 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlements
));
941 if (blob
== NULL
|| !blob
->validateBlob(CFDataGetLength(entitlements
))) {
942 MacOSError::throwMe(errSecCSInvalidEntitlements
);
945 CFRef
<CFDictionaryRef
> entDict
= blob
->entitlements();
948 CFRef
<CFErrorRef
> error
= NULL
;
949 size_t const der_size
= der_sizeof_plist(entDict
, &error
.aref());
952 secerror("Getting DER size for entitlement plist failed: %@", error
.get());
953 MacOSError::throwMe(errSecCSInvalidEntitlements
);
956 derBlob
= EntitlementDERBlob::alloc(der_size
);
958 if (derBlob
== NULL
) {
959 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size
);
960 MacOSError::throwMe(errSecCSInvalidEntitlements
);
962 uint8_t * const der_end
= derBlob
->der() + der_size
;
963 uint8_t * const der_start
= der_encode_plist(entDict
, &error
.aref(), derBlob
->der(), der_end
);
965 if (der_start
!= derBlob
->der()) {
966 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start
- derBlob
->der()));
968 MacOSError::throwMe(errSecCSInvalidEntitlements
);
971 *entitlementDER
= makeCFData(derBlob
, derBlob
->length());
976 if (execSegFlags
!= NULL
) {
979 flags
|= booleanEntitlement(entDict
, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned
: 0;
980 flags
|= booleanEntitlement(entDict
, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned
: 0;
981 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger
: 0;
982 flags
|= booleanEntitlement(entDict
, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit
: 0;
983 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal
: 0;
984 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash
: 0;
985 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash
: 0;
987 *execSegFlags
= flags
;
990 } catch (const CommonError
&err
) {
992 // Not fatal if we're not asked to generate DER entitlements.
994 secwarning("failed to parse entitlements: %s", err
.what());
1001 //// Signature Editing
1003 void SecCodeSigner::Signer::edit(SecCSFlags flags
)
1005 rep
= code
->diskRep()->base();
1007 Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage();
1009 prepareForEdit(flags
);
1014 editArchitectureAgnostic();
1018 EditableDiskRep
*SecCodeSigner::Signer::editMainExecutableRep(DiskRep
*rep
)
1020 EditableDiskRep
*mainExecRep
= NULL
;
1021 BundleDiskRep
*bundleDiskRep
= dynamic_cast<BundleDiskRep
*>(rep
);
1023 if (bundleDiskRep
) {
1024 mainExecRep
= dynamic_cast<EditableDiskRep
*>(bundleDiskRep
->mainExecRep());
1030 void SecCodeSigner::Signer::prepareForEdit(SecCSFlags flags
) {
1031 setDigestAlgorithms(code
->hashAlgorithms());
1033 Universal
*machO
= (code
->diskRep()->mainExecutableIsMachO() ?
1034 code
->diskRep()->mainExecutableImage() : NULL
);
1036 /* We need at least one architecture in all cases because we index our
1037 * RawComponentMaps by architecture. However, only machOs have any
1038 * architecture at all, for generic targets there will just be one
1040 * So if the main executable is not a machO, we just choose the local
1041 * (signer's) main architecture as dummy value for the first element in our pair. */
1042 editMainArch
= (machO
!= NULL
? machO
->bestNativeArch() : Architecture::local());
1044 if (machO
!= NULL
) {
1045 if (machO
->narrowed()) {
1046 /* --arch gives us a narrowed SecStaticCode, but because
1047 * codesign_allocate always creates or replaces signatures
1048 * for all slices, we must operate on the universal
1049 * SecStaticCode. Instead, we provide --edit-arch to specify
1050 * which slices to edit, the others have their code signature
1051 * copied without modifications.
1053 MacOSError::throwMe(errSecCSNotSupported
,
1054 "Signature editing must be performed on universal binary instead of narrow slice (using --edit-arch instead of --arch).");
1057 if (state
.mEditArch
&& !machO
->isUniversal()) {
1058 MacOSError::throwMe(errSecCSInvalidFlags
,
1059 "--edit-arch is only valid for universal binaries.");
1062 if (state
.mEditCMS
&& machO
->isUniversal() && !state
.mEditArch
) {
1063 /* Each slice has its own distinct code signature,
1064 * so a CMS blob is only valid for its one slice.
1065 * Therefore, replacing all CMS blobs in all slices
1066 * with the same blob is rather nonsensical, and we refuse.
1068 * (Universal binaries with only one slice can exist,
1069 * and in that case the slice to operate on would be
1070 * umambiguous, but we are not treating those binaries
1071 * specially and still want --edit-arch for consistency.)
1073 MacOSError::throwMe(errSecCSNotSupported
,
1074 "CMS editing must be performed on specific slice (specified with --edit-arch).");
1078 void (^editArch
)(SecStaticCode
*code
, Architecture arch
) =
1079 ^(SecStaticCode
*code
, Architecture arch
) {
1080 EditableDiskRep
*editRep
= dynamic_cast<EditableDiskRep
*>(code
->diskRep());
1082 if (editRep
== NULL
) {
1083 MacOSError::throwMe(errSecCSNotSupported
,
1084 "Signature editing not supported for code of this type.");
1087 EditableDiskRep
*mainExecRep
= editMainExecutableRep(code
->diskRep());
1089 if (mainExecRep
!= NULL
) {
1090 // Delegate editing to the main executable if it is an EditableDiskRep.
1091 //(Which is the case for machOs.)
1092 editRep
= mainExecRep
;
1095 editComponents
[arch
] = std::make_unique
<RawComponentMap
>(editRep
->createRawComponents());
1097 if (!state
.mEditArch
|| arch
== state
.mEditArch
) {
1098 if (state
.mEditCMS
) {
1099 CFDataRef cms
= state
.mEditCMS
.get();
1100 (*editComponents
[arch
])[cdSignatureSlot
] = cms
;
1105 editArch(code
, editMainArch
);
1107 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
1108 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
1109 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
1110 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
1111 editArch(subcode
, arch
);
1114 /* The resource dictionary is special, because it is
1115 * considered "global" instead of per architecture.
1116 * For editing, that means it's usually not embedded
1117 * in the main executable's signature if it exists,
1118 * but in the containing disk rep (e.g. the
1119 * CodeResources file if the rep is a Bundle).
1121 resourceDictData
= rep
->component(cdResourceDirSlot
);
1124 void SecCodeSigner::Signer::editMachO(Universal
*fat
) {
1125 // Mach-O executable at the core - perform multi-architecture signature editing
1126 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
1128 if (state
.mPreserveAFSC
)
1129 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1131 unique_ptr
<ArchEditor
> editor(new MachOEditor(writer
, *fat
,
1132 this->digestAlgorithms(),
1133 rep
->mainExecutablePath()));
1134 assert(editor
->count() > 0);
1136 if (resourceDictData
&& !editor
->attribute(writerNoGlobal
)) {
1137 // For when the resource dict is "global", e.g. for bundles.
1138 editor
->component(cdResourceDirSlot
, resourceDictData
);
1141 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
1142 MachOEditor::Arch
&arch
= *it
->second
;
1143 arch
.source
.reset(fat
->architecture(it
->first
)); // transfer ownership
1145 if (resourceDictData
&& editor
->attribute(writerNoGlobal
)) {
1146 // Technically possible to embed a resource dict in the embedded sig.
1147 arch
.component(cdResourceDirSlot
, resourceDictData
);
1150 for (auto const &entry
: *editComponents
[arch
.architecture
]) {
1151 CodeDirectory::Slot slot
= entry
.first
;
1152 CFDataRef data
= entry
.second
.get();
1153 arch
.component(slot
, data
);
1156 /* We must preserve the original superblob's size, as the size is
1157 * also in the macho's load commands, which are itself covered
1158 * by the signature. */
1159 arch
.blobSize
= arch
.source
->signingLength();
1164 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
1165 MachOEditor::Arch
&arch
= *it
->second
;
1166 editor
->reset(arch
);
1168 if (!state
.mDryRun
) {
1169 EmbeddedSignatureBlob
*blob
= arch
.make();
1170 editor
->write(arch
, blob
); // takes ownership of blob
1174 if (!state
.mDryRun
) {
1180 void SecCodeSigner::Signer::editArchitectureAgnostic()
1182 if (state
.mDryRun
) {
1186 // non-Mach-O executable - single-instance signature editing
1187 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
1189 if(state
.mPreserveAFSC
)
1190 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1192 for (auto const &entry
: *editComponents
[editMainArch
]) {
1193 CodeDirectory::Slot slot
= entry
.first
;
1194 CFDataRef data
= entry
.second
.get();
1196 writer
->component(slot
, data
);
1199 // commit to storage
1203 } // end namespace CodeSigning
1204 } // end namespace Security