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
= 0;
148 if (code
->isSigned() && (code
->codeDirectory(false)->flags
& kSecCodeSignatureLinkerSigned
) == 0) {
149 inherit
= state
.mPreserveMetadata
;
152 // work out the canonical identifier
153 identifier
= state
.mIdentifier
;
154 if (identifier
.empty() && (inherit
& kSecCodeSignerPreserveIdentifier
)) {
155 identifier
= code
->identifier();
157 if (identifier
.empty()) {
158 identifier
= rep
->recommendedIdentifier(*this);
159 if (identifier
.find('.') == string::npos
)
160 identifier
= state
.mIdentifierPrefix
+ identifier
;
161 if (identifier
.find('.') == string::npos
&& isAdhoc())
162 identifier
= identifier
+ "-" + uniqueName();
163 secinfo("signer", "using default identifier=%s", identifier
.c_str());
165 secinfo("signer", "using explicit identifier=%s", identifier
.c_str());
167 teamID
= state
.mTeamID
;
168 if (teamID
.empty() && (inherit
& kSecCodeSignerPreserveTeamIdentifier
)) {
169 const char *c_id
= code
->teamID();
174 // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later.
175 hashAlgorithms
= state
.mDigestAlgorithms
;
176 if (hashAlgorithms
.empty() && (inherit
& kSecCodeSignerPreserveDigestAlgorithm
))
177 hashAlgorithms
= code
->hashAlgorithms();
179 entitlements
= state
.mEntitlementData
;
180 if (!entitlements
&& (inherit
& kSecCodeSignerPreserveEntitlements
))
181 entitlements
= code
->component(cdEntitlementSlot
);
183 // work out the CodeDirectory flags word
184 bool haveCdFlags
= false;
185 if (!haveCdFlags
&& state
.mCdFlagsGiven
) {
186 cdFlags
= state
.mCdFlags
;
187 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags
);
193 if (CFTypeRef csflags
= CFDictionaryGetValue(infoDict
, CFSTR("CSFlags"))) {
194 if (CFGetTypeID(csflags
) == CFNumberGetTypeID()) {
195 cdFlags
= cfNumber
<uint32_t>(CFNumberRef(csflags
));
196 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags
);
197 } else if (CFGetTypeID(csflags
) == CFStringGetTypeID()) {
198 cdFlags
= cdTextFlags(cfString(CFStringRef(csflags
)));
199 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags
);
201 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
205 if (!haveCdFlags
&& (inherit
& kSecCodeSignerPreserveFlags
)) {
206 cdFlags
= code
->codeDirectory(false)->flags
& ~kSecCodeSignatureAdhoc
;
207 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags
);
212 if ((state
.mSigner
== SecIdentityRef(kCFNull
)) &&
213 !state
.mOmitAdhocFlag
) // ad-hoc signing requested...
214 cdFlags
|= kSecCodeSignatureAdhoc
; // ... so note that
216 // prepare the internal requirements input
217 if (state
.mRequirements
) {
218 if (CFGetTypeID(state
.mRequirements
) == CFDataGetTypeID()) { // binary form
219 const Requirements
*rp
= (const Requirements
*)CFDataGetBytePtr(state
.mRequirements
.as
<CFDataRef
>());
220 if (!rp
->validateBlob())
221 MacOSError::throwMe(errSecCSReqInvalid
);
222 requirements
= rp
->clone();
223 } else if (CFGetTypeID(state
.mRequirements
) == CFStringGetTypeID()) { // text form
224 CFRef
<CFMutableStringRef
> reqText
= CFStringCreateMutableCopy(NULL
, 0, state
.mRequirements
.as
<CFStringRef
>());
225 // substitute $ variable tokens
226 CFRange range
= { 0, CFStringGetLength(reqText
) };
227 CFStringFindAndReplace(reqText
, CFSTR("$self.identifier"), CFTempString(identifier
), range
, 0);
228 requirements
= parseRequirements(cfString(reqText
));
230 MacOSError::throwMe(errSecCSInvalidObjectRef
);
231 } else if (inherit
& kSecCodeSignerPreserveRequirements
)
232 if (const Requirements
*rp
= code
->internalRequirements())
233 requirements
= rp
->clone();
235 // prepare the resource directory, if any
236 string rpath
= rep
->resourcesRootPath();
238 CFCopyRef
<CFDictionaryRef
> resourceRules
;
239 if (!rpath
.empty()) {
240 // explicitly given resource rules always win
241 resourceRules
= state
.mResourceRules
;
243 // inherited rules come next (overriding embedded ones!)
244 if (!resourceRules
&& (inherit
& kSecCodeSignerPreserveResourceRules
))
245 if (CFDictionaryRef oldRules
= code
->resourceDictionary(false))
246 resourceRules
= oldRules
;
248 // embedded resource rules come next
249 if (!resourceRules
&& infoDict
)
250 if (CFTypeRef spec
= CFDictionaryGetValue(infoDict
, _kCFBundleResourceSpecificationKey
)) {
251 if (CFGetTypeID(spec
) == CFStringGetTypeID())
252 if (CFRef
<CFDataRef
> data
= cfLoadFile(rpath
+ "/" + cfString(CFStringRef(spec
))))
253 if (CFDictionaryRef dict
= makeCFDictionaryFrom(data
))
254 resourceRules
.take(dict
);
255 if (!resourceRules
) // embedded rules present but unacceptable
256 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
259 // if we got one from anywhere (but the defaults), sanity-check it
261 CFTypeRef rules
= CFDictionaryGetValue(resourceRules
, CFSTR("rules"));
262 if (!rules
|| CFGetTypeID(rules
) != CFDictionaryGetTypeID())
263 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
266 // finally, ask the DiskRep for its default
268 resourceRules
.take(rep
->defaultResourceRules(*this));
270 // resource root can optionally be the canonical bundle path,
271 // but sealed resource paths are always relative to rpath
273 if (signingFlags() & kSecCSSignBundleRoot
)
274 rrpath
= cfStringRelease(rep
->copyCanonicalPath());
277 // screen and set the signing time
278 if (state
.mSigningTime
== CFDateRef(kCFNull
)) {
279 emitSigningTime
= false; // no time at all
280 } else if (!state
.mSigningTime
) {
281 emitSigningTime
= true;
282 signingTime
= 0; // wall clock, established later
284 CFAbsoluteTime time
= CFDateGetAbsoluteTime(state
.mSigningTime
);
285 if (time
> CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
286 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
287 emitSigningTime
= true;
291 pagesize
= state
.mPageSize
? cfNumber
<size_t>(state
.mPageSize
) : rep
->pageSize(*this);
293 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
294 rep
->prepareForSigning(*this);
296 // apply some defaults after diskRep intervention
297 if (hashAlgorithms
.empty()) { // default to SHA256 + SHA-1
298 hashAlgorithms
.insert(kSecCodeSignatureHashSHA1
);
299 hashAlgorithms
.insert(kSecCodeSignatureHashSHA256
);
302 // build the resource directory (once and for all, using the digests determined above)
303 if (!rpath
.empty()) {
304 buildResources(rrpath
, rpath
, resourceRules
);
309 if (inherit
& kSecCodeSignerPreservePEH
) {
310 /* We need at least one architecture in all cases because we index our
311 * PreEncryptionMaps by architecture. However, only machOs have any
312 * architecture at all, for generic targets there will just be one
313 * PreEncryptionHashMap.
314 * So if the main executable is not a machO, we just choose the local
315 * (signer's) main architecture as dummy value for the first element in our pair. */
316 preEncryptMainArch
= (code
->diskRep()->mainExecutableIsMachO() ?
317 code
->diskRep()->mainExecutableImage()->bestNativeArch() :
318 Architecture::local());
320 addPreEncryptHashes(preEncryptHashMaps
[preEncryptMainArch
], code
);
322 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
323 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
324 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
325 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
326 addPreEncryptHashes(preEncryptHashMaps
[arch
], subcode
);
330 if (inherit
& kSecCodeSignerPreserveRuntime
) {
331 /* We need at least one architecture in all cases because we index our
332 * RuntimeVersionMaps by architecture. However, only machOs have any
333 * architecture at all, for generic targets there will just be one
335 * So if the main executable is not a machO, we just choose the local
336 * (signer's) main architecture as dummy value for the first element in our pair. */
337 runtimeVersionMainArch
= (code
->diskRep()->mainExecutableIsMachO() ?
338 code
->diskRep()->mainExecutableImage()->bestNativeArch() :
339 Architecture::local());
341 addRuntimeVersions(runtimeVersionMap
[runtimeVersionMainArch
], code
);
343 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
344 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
345 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
346 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
347 addRuntimeVersions(runtimeVersionMap
[arch
], subcode
);
352 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap
&map
, SecStaticCode
const *code
) {
353 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
356 for(auto const& pair
: *cds
) {
357 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
358 CFDataRef
const cddata
= pair
.second
;
360 CodeDirectory
const * cd
=
361 reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cddata
));
362 if (cd
->preEncryptHashes() != NULL
) {
363 CFRef
<CFDataRef
> preEncrypt
= makeCFData(cd
->preEncryptHashes(),
364 cd
->nCodeSlots
* cd
->hashSize
);
365 map
[alg
] = preEncrypt
;
371 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap
&map
, const SecStaticCode
*code
)
373 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
376 for(auto const& pair
: *cds
) {
377 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
378 CFDataRef
const cddata
= pair
.second
;
380 CodeDirectory
const * cd
=
381 reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cddata
));
382 if (cd
->runtimeVersion()) {
383 map
[alg
] = cd
->runtimeVersion();
390 // Collect the resource seal for a program.
391 // This includes both sealed resources and information about nested code.
393 void SecCodeSigner::Signer::buildResources(std::string root
, std::string relBase
, CFDictionaryRef rulesDict
)
395 typedef ResourceBuilder::Rule Rule
;
397 secinfo("codesign", "start building resource directory");
398 __block CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
400 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(rulesDict
, "rules");
403 if (this->state
.mLimitedAsync
== NULL
) {
404 this->state
.mLimitedAsync
=
405 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
406 * turned off, because the paths for signing code are not ready for it yet. */
407 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
408 new LimitedAsync(false);
411 CFDictionaryRef files2
= NULL
;
412 if (!(signingFlags() & kSecCSSignV1
)) {
413 CFCopyRef
<CFDictionaryRef
> rules2
= cfget
<CFDictionaryRef
>(rulesDict
, "rules2");
415 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules,
416 // because the default weight, according to ResourceBuilder::addRule(), is 1).
417 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
418 rules2
.take(cfmake
<CFDictionaryRef
>("{+%O"
419 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
423 Dispatch::Group group
;
424 Dispatch::Group
&groupRef
= group
; // (into block)
426 // build the modern (V2) resource seal
427 __block CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
428 CFMutableDictionaryRef filesRef
= files
.get(); // (into block)
429 ResourceBuilder
resourceBuilder(root
, relBase
, rules2
, strict
, MacOSErrorSet());
430 ResourceBuilder
&resources
= resourceBuilder
; // (into block)
431 rep
->adjustResources(resources
);
433 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, const std::string relpath
, Rule
*rule
) {
434 bool isSymlink
= (ent
->fts_info
== FTS_SL
);
435 bool isNested
= (ruleFlags
& ResourceBuilder::nested
);
436 const std::string
path(ent
->fts_path
);
437 const std::string
accpath(ent
->fts_accpath
);
438 this->state
.mLimitedAsync
->perform(groupRef
, ^{
439 CFRef
<CFMutableDictionaryRef
> seal
;
441 seal
.take(signNested(path
, relpath
));
442 } else if (isSymlink
) {
443 char target
[PATH_MAX
];
444 ssize_t len
= ::readlink(accpath
.c_str(), target
, sizeof(target
)-1);
446 UnixError::check(-1);
448 seal
.take(cfmake
<CFMutableDictionaryRef
>("{symlink=%s}", target
));
450 seal
.take(resources
.hashFile(accpath
.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight
));
452 if (seal
.get() == NULL
) {
453 secerror("Failed to generate sealed resource: %d, %d, %s", isNested
, isSymlink
, accpath
.c_str());
454 MacOSError::throwMe(errSecCSBadResource
);
456 if (ruleFlags
& ResourceBuilder::optional
)
457 CFDictionaryAddValue(seal
, CFSTR("optional"), kCFBooleanTrue
);
459 StLock
<Mutex
> _(resourceLock
);
460 if ((hash
= CFDictionaryGetValue(seal
, CFSTR("hash"))) && CFDictionaryGetCount(seal
) == 1) // simple form
461 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), hash
);
463 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), seal
.get());
464 code
->reportProgress();
468 CFDictionaryAddValue(result
, CFSTR("rules2"), resourceBuilder
.rules());
470 CFDictionaryAddValue(result
, CFSTR("files2"), files2
);
473 CFDictionaryAddValue(result
, CFSTR("rules"), rules
); // preserve V1 rules in any case
474 if (!(signingFlags() & kSecCSSignNoV1
)) {
475 // build the legacy (V1) resource seal
476 __block CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
477 ResourceBuilder
resourceBuilder(root
, relBase
, rules
, strict
, MacOSErrorSet());
478 ResourceBuilder
&resources
= resourceBuilder
;
479 rep
->adjustResources(resources
); // DiskRep-specific adjustments
480 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, std::string relpath
, Rule
*rule
) {
481 if (ent
->fts_info
== FTS_F
) {
482 CFRef
<CFDataRef
> hash
;
483 if (files2
) // try to get the hash from a previously-made version
484 if (CFTypeRef seal
= CFDictionaryGetValue(files2
, CFTempString(relpath
))) {
485 if (CFGetTypeID(seal
) == CFDataGetTypeID())
486 hash
= CFDataRef(seal
);
488 hash
= CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal
), CFSTR("hash")));
491 hash
.take(resources
.hashFile(ent
->fts_accpath
, kSecCodeSignatureHashSHA1
));
492 if (ruleFlags
== 0) { // default case - plain hash
493 cfadd(files
, "{%s=%O}", relpath
.c_str(), hash
.get());
494 secinfo("csresource", "%s added simple (rule %p)", relpath
.c_str(), rule
);
495 } else { // more complicated - use a sub-dictionary
496 cfadd(files
, "{%s={hash=%O,optional=%B}}",
497 relpath
.c_str(), hash
.get(), ruleFlags
& ResourceBuilder::optional
);
498 secinfo("csresource", "%s added complex (rule %p)", relpath
.c_str(), rule
);
502 CFDictionaryAddValue(result
, CFSTR("files"), files
.get());
505 resourceDirectory
= result
.get();
506 resourceDictData
.take(makeCFData(resourceDirectory
.get()));
511 // Deal with one piece of nested code
513 CFMutableDictionaryRef
SecCodeSigner::Signer::signNested(const std::string
&path
, const std::string
&relpath
)
515 // sign nested code and collect nesting information
517 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(path
));
518 if (signingFlags() & kSecCSSignNestedCode
)
519 this->state
.sign(code
, signingFlags());
520 std::string dr
= Dumper::dump(code
->designatedRequirement());
521 if (CFDataRef hash
= code
->cdHash())
522 return cfmake
<CFMutableDictionaryRef
>("{requirement=%s,cdhash=%O}",
523 Dumper::dump(code
->designatedRequirement()).c_str(),
525 MacOSError::throwMe(errSecCSUnsigned
);
526 } catch (const CommonError
&err
) {
527 CSError::throwMe(err
.osStatus(), kSecCFErrorPath
, CFTempURL(relpath
, false, this->code
->resourceBase()));
533 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
534 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
535 // treat them as architectural binaries containing (only) one architecture - that
536 // interpretation is courtesy of the Universal/MachO support classes.
538 void SecCodeSigner::Signer::signMachO(Universal
*fat
, const Requirement::Context
&context
)
540 // Mach-O executable at the core - perform multi-architecture signing
541 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
543 if (state
.mPreserveAFSC
)
544 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
546 unique_ptr
<ArchEditor
> editor(state
.mDetached
547 ? static_cast<ArchEditor
*>(new BlobEditor(*fat
, *this))
548 : new MachOEditor(writer
, *fat
, this->digestAlgorithms(), rep
->mainExecutablePath()));
549 assert(editor
->count() > 0);
550 if (!editor
->attribute(writerNoGlobal
)) // can store architecture-common components
553 // pass 1: prepare signature blobs and calculate sizes
554 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
555 MachOEditor::Arch
&arch
= *it
->second
;
556 arch
.source
.reset(fat
->architecture(it
->first
));
558 // library validation is not compatible with i386
559 if (arch
.architecture
.cpuType() == CPU_TYPE_I386
) {
560 if (cdFlags
& kSecCodeSignatureLibraryValidation
) {
561 MacOSError::throwMe(errSecCSBadLVArch
);
565 bool generateEntitlementDER
= false;
566 if (signingFlags() & kSecCSSignGenerateEntitlementDER
) {
567 generateEntitlementDER
= true;
569 uint32_t platform
= arch
.source
->platform();
571 case PLATFORM_WATCHOS
:
572 case PLATFORM_BRIDGEOS
:
573 generateEntitlementDER
= false;
576 generateEntitlementDER
= true;
581 bool mainBinary
= arch
.source
.get()->type() == MH_EXECUTE
;
583 uint32_t runtimeVersion
= 0;
584 if (cdFlags
& kSecCodeSignatureRuntime
) {
585 runtimeVersion
= state
.mRuntimeVersionOverride
? state
.mRuntimeVersionOverride
: arch
.source
.get()->sdkVersion();
588 arch
.ireqs(requirements
, rep
->defaultRequirements(&arch
.architecture
, *this), context
);
589 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-arch
591 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
592 uint32_t runtimeVersionToUse
= runtimeVersion
;
593 if ((cdFlags
& kSecCodeSignatureRuntime
) && runtimeVersionMap
.count(arch
.architecture
)) {
594 if (runtimeVersionMap
[arch
.architecture
].count(*type
)) {
595 runtimeVersionToUse
= runtimeVersionMap
[arch
.architecture
][*type
];
598 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
599 populate(builder
, arch
, arch
.ireqs
,
600 arch
.source
->offset(), arch
.source
->signingExtent(),
601 mainBinary
, rep
->execSegBase(&(arch
.architecture
)), rep
->execSegLimit(&(arch
.architecture
)),
602 unsigned(digestAlgorithms().size()-1),
603 preEncryptHashMaps
[arch
.architecture
], runtimeVersionToUse
, generateEntitlementDER
);
607 // add identification blob (made from this architecture) only if we're making a detached signature
608 if (state
.mDetached
) {
609 CFRef
<CFDataRef
> identification
= MachORep::identificationFor(arch
.source
.get());
610 arch
.add(cdIdentificationSlot
, BlobWrapper::alloc(
611 CFDataGetBytePtr(identification
), CFDataGetLength(identification
)));
614 // prepare SuperBlob size estimate
615 __block
std::vector
<size_t> sizes
;
616 arch
.eachDigest(^(CodeDirectory::Builder
& builder
){
617 sizes
.push_back(builder
.size(CodeDirectory::currentVersion
));
619 arch
.blobSize
= arch
.size(sizes
, state
.mCMSSize
, 0);
624 // pass 2: Finish and generate signatures, and write them
625 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
626 MachOEditor::Arch
&arch
= *it
->second
;
629 // finish CodeDirectories (off new binary) and sign it
630 __block CodeDirectorySet cdSet
;
631 arch
.eachDigest(^(CodeDirectory::Builder
&builder
) {
632 CodeDirectory
*cd
= builder
.build();
636 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
637 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
638 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
640 // complete the SuperBlob
641 cdSet
.populate(&arch
);
642 arch
.add(cdSignatureSlot
, BlobWrapper::alloc(
643 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)));
644 if (!state
.mDryRun
) {
645 EmbeddedSignatureBlob
*blob
= arch
.make();
646 editor
->write(arch
, blob
); // takes ownership of blob
650 // done: write edit copy back over the original
651 if (!state
.mDryRun
) {
658 // Sign a binary that has no notion of architecture.
659 // That currently means anything that isn't Mach-O format.
661 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
&context
)
663 // non-Mach-O executable - single-instance signing
664 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
665 (new DetachedBlobWriter(*this)) : rep
->writer();
667 if(state
.mPreserveAFSC
)
668 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
670 CodeDirectorySet cdSet
;
672 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
673 CodeDirectory::Builder
builder(*type
);
674 InternalRequirements ireqs
;
675 ireqs(requirements
, rep
->defaultRequirements(NULL
, *this), context
);
677 populate(builder
, *writer
, ireqs
, rep
->signingBase(), rep
->signingLimit(),
678 false, // only machOs can currently be main binaries
679 rep
->execSegBase(NULL
), rep
->execSegLimit(NULL
),
680 unsigned(digestAlgorithms().size()-1),
681 preEncryptHashMaps
[preEncryptMainArch
], // Only one map, the default.
682 (cdFlags
& kSecCodeSignatureRuntime
) ? state
.mRuntimeVersionOverride
: 0,
683 signingFlags() & kSecCSSignGenerateEntitlementDER
);
685 CodeDirectory
*cd
= builder
.build();
690 // add identification blob (made from this architecture) only if we're making a detached signature
691 if (state
.mDetached
) {
692 CFRef
<CFDataRef
> identification
= rep
->identification();
693 writer
->component(cdIdentificationSlot
, identification
);
696 // write out all CodeDirectories
698 cdSet
.populate(writer
);
700 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
701 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
702 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
703 writer
->signature(signature
);
711 // Global populate - send components to destination buffers ONCE
713 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
715 if (resourceDirectory
&& !state
.mDryRun
)
716 writer
.component(cdResourceDirSlot
, resourceDictData
);
721 // Per-architecture populate - send components to per-architecture buffers
722 // and populate the CodeDirectory for an architecture. In architecture-agnostic
723 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
724 // for the purposes of this call.
726 void SecCodeSigner::Signer::populate(CodeDirectory::Builder
&builder
, DiskRep::Writer
&writer
,
727 InternalRequirements
&ireqs
, size_t offset
, size_t length
,
728 bool mainBinary
, size_t execSegBase
, size_t execSegLimit
,
729 unsigned alternateDigestCount
,
730 PreEncryptHashMap
const &preEncryptHashMap
,
731 uint32_t runtimeVersion
, bool generateEntitlementDER
)
733 // fill the CodeDirectory
734 builder
.executable(rep
->mainExecutablePath(), pagesize
, offset
, length
);
735 builder
.flags(cdFlags
);
736 builder
.identifier(identifier
);
737 builder
.teamID(teamID
);
738 builder
.platform(state
.mPlatform
);
739 builder
.execSeg(execSegBase
, execSegLimit
, mainBinary
? kSecCodeExecSegMainBinary
: 0);
740 builder
.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH
);
741 builder
.preservePreEncryptHashMap(preEncryptHashMap
);
742 builder
.runTimeVersion(runtimeVersion
);
744 if (CFRef
<CFDataRef
> data
= rep
->component(cdInfoSlot
))
745 builder
.specialSlot(cdInfoSlot
, data
);
747 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
748 writer
.component(cdRequirementsSlot
, data
);
749 builder
.specialSlot(cdRequirementsSlot
, data
);
751 if (resourceDirectory
)
752 builder
.specialSlot(cdResourceDirSlot
, resourceDictData
);
754 writer
.component(cdEntitlementSlot
, entitlements
);
755 builder
.specialSlot(cdEntitlementSlot
, entitlements
);
758 CFRef
<CFDataRef
> entitlementDER
;
759 uint64_t execSegFlags
= 0;
760 cookEntitlements(entitlements
, generateEntitlementDER
,
761 &execSegFlags
, &entitlementDER
.aref());
763 if (generateEntitlementDER
) {
764 writer
.component(cdEntitlementDERSlot
, entitlementDER
);
765 builder
.specialSlot(cdEntitlementDERSlot
, entitlementDER
);
768 builder
.addExecSegFlags(execSegFlags
);
771 if (CFRef
<CFDataRef
> repSpecific
= rep
->component(cdRepSpecificSlot
))
772 builder
.specialSlot(cdRepSpecificSlot
, repSpecific
);
774 writer
.addDiscretionary(builder
);
776 #if 0 // rdar://problem/25720754
777 if ((signingFlags() & (kSecCSSignOpaque
|kSecCSSignV1
)) == 0 && builder
.hashType() != kSecCodeSignatureHashSHA1
) {
778 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
779 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
780 std::vector
<Endian
<uint32_t> > slotVector
;
781 slotVector
.push_back(cdCodeDirectorySlot
); // mandatory
782 std::set
<CodeDirectory::Slot
> filledSlots
= builder
.filledSpecialSlots();
783 filledSlots
.insert(cdTopDirectorySlot
); // will be added below
784 copy(filledSlots
.begin(), filledSlots
.end(), back_inserter(slotVector
));
785 for (unsigned n
= 0; n
< alternateDigestCount
; n
++)
786 slotVector
.push_back(cdAlternateCodeDirectorySlots
+ n
);
787 slotVector
.push_back(cdSignatureSlot
);
788 CFTempData
cfSlotVector(&slotVector
[0], slotVector
.size() * sizeof(slotVector
[0]));
789 writer
.component(cdTopDirectorySlot
, cfSlotVector
);
790 builder
.specialSlot(cdTopDirectorySlot
, cfSlotVector
);
796 #include <security_smime/tsaSupport.h>
799 // Generate the CMS signature for a (finished) CodeDirectory.
801 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
,
802 CFDictionaryRef hashDict
,
805 assert(state
.mSigner
);
806 CFRef
<CFMutableDictionaryRef
> defaultTSContext
= NULL
;
808 // a null signer generates a null signature blob
809 if (state
.mSigner
== SecIdentityRef(kCFNull
))
810 return CFDataCreate(NULL
, NULL
, 0);
812 // generate CMS signature
813 CFRef
<CMSEncoderRef
> cms
;
814 MacOSError::check(CMSEncoderCreate(&cms
.aref()));
815 MacOSError::check(CMSEncoderSetCertificateChainMode(cms
, kCMSCertificateChainWithRootOrFail
));
816 CMSEncoderAddSigners(cms
, state
.mSigner
);
817 CMSEncoderSetSignerAlgorithm(cms
, kCMSEncoderDigestAlgorithmSHA256
);
818 MacOSError::check(CMSEncoderSetHasDetachedContent(cms
, true));
820 if (emitSigningTime
) {
821 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
822 CFAbsoluteTime time
= signingTime
? signingTime
: CFAbsoluteTimeGetCurrent();
823 MacOSError::check(CMSEncoderSetSigningTime(cms
, time
));
826 if (hashDict
!= NULL
) {
827 assert(hashList
!= NULL
);
831 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgilityV2
));
832 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms
, hashDict
));
836 CFTemp
<CFDictionaryRef
> hashDict("{cdhashes=%O}", hashList
);
837 CFRef
<CFDataRef
> hashAgilityV1Attribute
= makeCFData(hashDict
.get());
839 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgility
));
840 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms
, hashAgilityV1Attribute
));
843 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
845 // Set up to call Timestamp server if requested
846 if (state
.mWantTimeStamp
)
848 CFRef
<CFErrorRef
> error
= NULL
;
849 defaultTSContext
= SecCmsTSAGetDefaultContext(&error
.aref());
851 MacOSError::throwMe(errSecDataNotAvailable
);
853 if (state
.mNoTimeStampCerts
|| state
.mTimestampService
) {
854 if (state
.mTimestampService
)
855 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyURL
, state
.mTimestampService
);
856 if (state
.mNoTimeStampCerts
)
857 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyNoCerts
, kCFBooleanTrue
);
860 CmsMessageSetTSAContext(cms
, defaultTSContext
);
864 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
871 // Our DiskRep::signingContext methods communicate with the signing subsystem
872 // in terms those callers can easily understand.
874 string
SecCodeSigner::Signer::sdkPath(const std::string
&path
) const
876 assert(path
[0] == '/'); // need absolute path here
878 return cfString(state
.mSDKRoot
) + path
;
883 bool SecCodeSigner::Signer::isAdhoc() const
885 return state
.mSigner
== SecIdentityRef(kCFNull
);
888 SecCSFlags
SecCodeSigner::Signer::signingFlags() const
890 return state
.mOpFlags
;
895 // Parse a text of the form
897 // where each flag is the canonical name of a signable CodeDirectory flag.
898 // No abbreviations are allowed, and internally set flags are not accepted.
900 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text
)
903 for (string::size_type comma
= text
.find(','); ; text
= text
.substr(comma
+1), comma
= text
.find(',')) {
904 string word
= (comma
== string::npos
) ? text
: text
.substr(0, comma
);
905 const SecCodeDirectoryFlagTable
*item
;
906 for (item
= kSecCodeDirectoryFlagTable
; item
->name
; item
++)
907 if (item
->signable
&& word
== item
->name
) {
908 flags
|= item
->value
;
911 if (!item
->name
) // not found
912 MacOSError::throwMe(errSecCSInvalidFlags
);
913 if (comma
== string::npos
) // last word
921 // Generate a unique string from our underlying DiskRep.
922 // We could get 90%+ of the uniquing benefit by just generating
923 // a random string here. Instead, we pick the (hex string encoding of)
924 // the source rep's unique identifier blob. For universal binaries,
925 // this is the canonical local architecture, which is a bit arbitrary.
926 // This provides us with a consistent unique string for all architectures
927 // of a fat binary, *and* (unlike a random string) is reproducible
928 // for identical inputs, even upon resigning.
930 std::string
SecCodeSigner::Signer::uniqueName() const
932 CFRef
<CFDataRef
> identification
= rep
->identification();
933 const UInt8
*ident
= CFDataGetBytePtr(identification
);
934 const CFIndex length
= CFDataGetLength(identification
);
936 for (CFIndex n
= 0; n
< length
; n
++) {
938 snprintf(hex
, sizeof(hex
), "%02x", ident
[n
]);
944 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict
, CFStringRef key
) {
945 CFBooleanRef entValue
= (CFBooleanRef
)CFDictionaryGetValue(entDict
, key
);
947 if (entValue
== NULL
|| CFGetTypeID(entValue
) != CFBooleanGetTypeID()) {
951 return CFBooleanGetValue(entValue
);
954 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements
, bool generateDER
,
955 uint64_t *execSegFlags
, CFDataRef
*entitlementDER
)
958 return; // nothing to do.
961 EntitlementDERBlob
*derBlob
= NULL
;
964 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlements
));
966 if (blob
== NULL
|| !blob
->validateBlob(CFDataGetLength(entitlements
))) {
967 MacOSError::throwMe(errSecCSInvalidEntitlements
);
970 CFRef
<CFDictionaryRef
> entDict
= blob
->entitlements();
973 CFRef
<CFErrorRef
> error
= NULL
;
974 size_t const der_size
= der_sizeof_plist(entDict
, &error
.aref());
977 secerror("Getting DER size for entitlement plist failed: %@", error
.get());
978 MacOSError::throwMe(errSecCSInvalidEntitlements
);
981 derBlob
= EntitlementDERBlob::alloc(der_size
);
983 if (derBlob
== NULL
) {
984 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size
);
985 MacOSError::throwMe(errSecCSInvalidEntitlements
);
987 uint8_t * const der_end
= derBlob
->der() + der_size
;
988 uint8_t * const der_start
= der_encode_plist(entDict
, &error
.aref(), derBlob
->der(), der_end
);
990 if (der_start
!= derBlob
->der()) {
991 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start
- derBlob
->der()));
993 MacOSError::throwMe(errSecCSInvalidEntitlements
);
996 *entitlementDER
= makeCFData(derBlob
, derBlob
->length());
1001 if (execSegFlags
!= NULL
) {
1004 flags
|= booleanEntitlement(entDict
, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned
: 0;
1005 flags
|= booleanEntitlement(entDict
, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned
: 0;
1006 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger
: 0;
1007 flags
|= booleanEntitlement(entDict
, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit
: 0;
1008 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal
: 0;
1009 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash
: 0;
1010 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash
: 0;
1012 *execSegFlags
= flags
;
1015 } catch (const CommonError
&err
) {
1017 // Not fatal if we're not asked to generate DER entitlements.
1019 secwarning("failed to parse entitlements: %s", err
.what());
1026 //// Signature Editing
1028 void SecCodeSigner::Signer::edit(SecCSFlags flags
)
1030 rep
= code
->diskRep()->base();
1032 Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage();
1034 prepareForEdit(flags
);
1039 editArchitectureAgnostic();
1043 EditableDiskRep
*SecCodeSigner::Signer::editMainExecutableRep(DiskRep
*rep
)
1045 EditableDiskRep
*mainExecRep
= NULL
;
1046 BundleDiskRep
*bundleDiskRep
= dynamic_cast<BundleDiskRep
*>(rep
);
1048 if (bundleDiskRep
) {
1049 mainExecRep
= dynamic_cast<EditableDiskRep
*>(bundleDiskRep
->mainExecRep());
1055 void SecCodeSigner::Signer::prepareForEdit(SecCSFlags flags
) {
1056 setDigestAlgorithms(code
->hashAlgorithms());
1058 Universal
*machO
= (code
->diskRep()->mainExecutableIsMachO() ?
1059 code
->diskRep()->mainExecutableImage() : NULL
);
1061 /* We need at least one architecture in all cases because we index our
1062 * RawComponentMaps by architecture. However, only machOs have any
1063 * architecture at all, for generic targets there will just be one
1065 * So if the main executable is not a machO, we just choose the local
1066 * (signer's) main architecture as dummy value for the first element in our pair. */
1067 editMainArch
= (machO
!= NULL
? machO
->bestNativeArch() : Architecture::local());
1069 if (machO
!= NULL
) {
1070 if (machO
->narrowed()) {
1071 /* --arch gives us a narrowed SecStaticCode, but because
1072 * codesign_allocate always creates or replaces signatures
1073 * for all slices, we must operate on the universal
1074 * SecStaticCode. Instead, we provide --edit-arch to specify
1075 * which slices to edit, the others have their code signature
1076 * copied without modifications.
1078 MacOSError::throwMe(errSecCSNotSupported
,
1079 "Signature editing must be performed on universal binary instead of narrow slice (using --edit-arch instead of --arch).");
1082 if (state
.mEditArch
&& !machO
->isUniversal()) {
1083 MacOSError::throwMe(errSecCSInvalidFlags
,
1084 "--edit-arch is only valid for universal binaries.");
1087 if (state
.mEditCMS
&& machO
->isUniversal() && !state
.mEditArch
) {
1088 /* Each slice has its own distinct code signature,
1089 * so a CMS blob is only valid for its one slice.
1090 * Therefore, replacing all CMS blobs in all slices
1091 * with the same blob is rather nonsensical, and we refuse.
1093 * (Universal binaries with only one slice can exist,
1094 * and in that case the slice to operate on would be
1095 * umambiguous, but we are not treating those binaries
1096 * specially and still want --edit-arch for consistency.)
1098 MacOSError::throwMe(errSecCSNotSupported
,
1099 "CMS editing must be performed on specific slice (specified with --edit-arch).");
1103 void (^editArch
)(SecStaticCode
*code
, Architecture arch
) =
1104 ^(SecStaticCode
*code
, Architecture arch
) {
1105 EditableDiskRep
*editRep
= dynamic_cast<EditableDiskRep
*>(code
->diskRep());
1107 if (editRep
== NULL
) {
1108 MacOSError::throwMe(errSecCSNotSupported
,
1109 "Signature editing not supported for code of this type.");
1112 EditableDiskRep
*mainExecRep
= editMainExecutableRep(code
->diskRep());
1114 if (mainExecRep
!= NULL
) {
1115 // Delegate editing to the main executable if it is an EditableDiskRep.
1116 //(Which is the case for machOs.)
1117 editRep
= mainExecRep
;
1120 editComponents
[arch
] = std::make_unique
<RawComponentMap
>(editRep
->createRawComponents());
1122 if (!state
.mEditArch
|| arch
== state
.mEditArch
) {
1123 if (state
.mEditCMS
) {
1124 CFDataRef cms
= state
.mEditCMS
.get();
1125 (*editComponents
[arch
])[cdSignatureSlot
] = cms
;
1130 editArch(code
, editMainArch
);
1132 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
1133 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
1134 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
1135 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
1136 editArch(subcode
, arch
);
1139 /* The resource dictionary is special, because it is
1140 * considered "global" instead of per architecture.
1141 * For editing, that means it's usually not embedded
1142 * in the main executable's signature if it exists,
1143 * but in the containing disk rep (e.g. the
1144 * CodeResources file if the rep is a Bundle).
1146 resourceDictData
= rep
->component(cdResourceDirSlot
);
1149 void SecCodeSigner::Signer::editMachO(Universal
*fat
) {
1150 // Mach-O executable at the core - perform multi-architecture signature editing
1151 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
1153 if (state
.mPreserveAFSC
)
1154 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1156 unique_ptr
<ArchEditor
> editor(new MachOEditor(writer
, *fat
,
1157 this->digestAlgorithms(),
1158 rep
->mainExecutablePath()));
1159 assert(editor
->count() > 0);
1161 if (resourceDictData
&& !editor
->attribute(writerNoGlobal
)) {
1162 // For when the resource dict is "global", e.g. for bundles.
1163 editor
->component(cdResourceDirSlot
, resourceDictData
);
1166 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
1167 MachOEditor::Arch
&arch
= *it
->second
;
1168 arch
.source
.reset(fat
->architecture(it
->first
)); // transfer ownership
1170 if (resourceDictData
&& editor
->attribute(writerNoGlobal
)) {
1171 // Technically possible to embed a resource dict in the embedded sig.
1172 arch
.component(cdResourceDirSlot
, resourceDictData
);
1175 for (auto const &entry
: *editComponents
[arch
.architecture
]) {
1176 CodeDirectory::Slot slot
= entry
.first
;
1177 CFDataRef data
= entry
.second
.get();
1178 arch
.component(slot
, data
);
1181 /* We must preserve the original superblob's size, as the size is
1182 * also in the macho's load commands, which are itself covered
1183 * by the signature. */
1184 arch
.blobSize
= arch
.source
->signingLength();
1189 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
1190 MachOEditor::Arch
&arch
= *it
->second
;
1191 editor
->reset(arch
);
1193 if (!state
.mDryRun
) {
1194 EmbeddedSignatureBlob
*blob
= arch
.make();
1195 editor
->write(arch
, blob
); // takes ownership of blob
1199 if (!state
.mDryRun
) {
1205 void SecCodeSigner::Signer::editArchitectureAgnostic()
1207 if (state
.mDryRun
) {
1211 // non-Mach-O executable - single-instance signature editing
1212 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
1214 if(state
.mPreserveAFSC
)
1215 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
1217 for (auto const &entry
: *editComponents
[editMainArch
]) {
1218 CodeDirectory::Slot slot
= entry
.first
;
1219 CFDataRef data
= entry
.second
.get();
1221 writer
->component(slot
, data
);
1224 // commit to storage
1228 } // end namespace CodeSigning
1229 } // end namespace Security