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 "der_plist.h"
29 #include "resources.h"
30 #include "signerutils.h"
31 #include "SecCodeSigner.h"
32 #include <Security/SecIdentity.h>
33 #include <Security/CMSEncoder.h>
34 #include <Security/CMSPrivate.h>
35 #include <Security/CSCommonPriv.h>
36 #include <CoreFoundation/CFBundlePriv.h>
37 #include "resources.h"
39 #include "reqparser.h"
40 #include "reqdumper.h"
41 #include "csutilities.h"
42 #include <security_utilities/unix++.h>
43 #include <security_utilities/unixchild.h>
44 #include <security_utilities/cfmunge.h>
45 #include <security_utilities/dispatch.h>
46 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
49 namespace CodeSigning
{
55 void SecCodeSigner::Signer::sign(SecCSFlags flags
)
57 rep
= code
->diskRep()->base();
60 PreSigningContext
context(*this);
62 considerTeamID(context
);
64 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
65 signMachO(fat
, context
);
67 signArchitectureAgnostic(context
);
72 void SecCodeSigner::Signer::considerTeamID(const PreSigningContext
& context
)
74 /* If an explicit teamID was passed in it must be
75 the same as what came from the cert */
76 std::string teamIDFromCert
= state
.getTeamIDFromSigner(context
.certs
);
78 if (state
.mPreserveMetadata
& kSecCodeSignerPreserveTeamIdentifier
) {
79 /* If preserving the team identifier, teamID is set previously when the
80 code object is still available */
81 if (!teamIDFromCert
.empty() && teamID
!= teamIDFromCert
)
82 MacOSError::throwMe(errSecCSInvalidFlags
);
84 if (teamIDFromCert
.empty()) {
85 /* state.mTeamID is an explicitly passed teamID */
86 teamID
= state
.mTeamID
;
87 } else if (state
.mTeamID
.empty() || (state
.mTeamID
== teamIDFromCert
)) {
88 /* If there was no explicit team ID set, or the explicit team ID matches
89 what is in the cert, use the team ID from the certificate */
90 teamID
= teamIDFromCert
;
92 /* The caller passed in an explicit team ID that does not match what is
93 in the signing cert, which is an invalid usage */
94 MacOSError::throwMe(errSecCSInvalidFlags
);
101 // Remove any existing code signature from code
103 void SecCodeSigner::Signer::remove(SecCSFlags flags
)
105 // can't remove a detached signature
107 MacOSError::throwMe(errSecCSNotSupported
);
109 rep
= code
->diskRep();
111 if (state
.mPreserveAFSC
)
112 rep
->writer()->setPreserveAFSC(state
.mPreserveAFSC
);
114 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
115 // architecture-sensitive removal
116 MachOEditor
editor(rep
->writer(), *fat
, digestAlgorithms(), rep
->mainExecutablePath());
117 editor
.allocate(); // create copy
118 editor
.commit(); // commit change
120 // architecture-agnostic removal
121 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
129 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
131 void SecCodeSigner::Signer::prepare(SecCSFlags flags
)
133 // make sure the rep passes strict validation
135 rep
->strictValidate(NULL
, MacOSErrorSet(), flags
| (kSecCSQuickCheck
|kSecCSRestrictSidebandData
));
137 // initialize progress/cancellation state
138 code
->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
140 // get the Info.plist out of the rep for some creative defaulting
141 CFRef
<CFDictionaryRef
> infoDict
;
142 if (CFRef
<CFDataRef
> infoData
= rep
->component(cdInfoSlot
))
143 infoDict
.take(makeCFDictionaryFrom(infoData
));
145 uint32_t inherit
= code
->isSigned() ? state
.mPreserveMetadata
: 0;
147 // work out the canonical identifier
148 identifier
= state
.mIdentifier
;
149 if (identifier
.empty() && (inherit
& kSecCodeSignerPreserveIdentifier
))
150 identifier
= code
->identifier();
151 if (identifier
.empty()) {
152 identifier
= rep
->recommendedIdentifier(*this);
153 if (identifier
.find('.') == string::npos
)
154 identifier
= state
.mIdentifierPrefix
+ identifier
;
155 if (identifier
.find('.') == string::npos
&& isAdhoc())
156 identifier
= identifier
+ "-" + uniqueName();
157 secinfo("signer", "using default identifier=%s", identifier
.c_str());
159 secinfo("signer", "using explicit identifier=%s", identifier
.c_str());
161 teamID
= state
.mTeamID
;
162 if (teamID
.empty() && (inherit
& kSecCodeSignerPreserveTeamIdentifier
)) {
163 const char *c_id
= code
->teamID();
168 // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later.
169 hashAlgorithms
= state
.mDigestAlgorithms
;
170 if (hashAlgorithms
.empty() && (inherit
& kSecCodeSignerPreserveDigestAlgorithm
))
171 hashAlgorithms
= code
->hashAlgorithms();
173 entitlements
= state
.mEntitlementData
;
174 if (!entitlements
&& (inherit
& kSecCodeSignerPreserveEntitlements
))
175 entitlements
= code
->component(cdEntitlementSlot
);
177 generateEntitlementDER
= signingFlags() & kSecCSSignGenerateEntitlementDER
;
179 // work out the CodeDirectory flags word
180 bool haveCdFlags
= false;
181 if (!haveCdFlags
&& state
.mCdFlagsGiven
) {
182 cdFlags
= state
.mCdFlags
;
183 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags
);
189 if (CFTypeRef csflags
= CFDictionaryGetValue(infoDict
, CFSTR("CSFlags"))) {
190 if (CFGetTypeID(csflags
) == CFNumberGetTypeID()) {
191 cdFlags
= cfNumber
<uint32_t>(CFNumberRef(csflags
));
192 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags
);
193 } else if (CFGetTypeID(csflags
) == CFStringGetTypeID()) {
194 cdFlags
= cdTextFlags(cfString(CFStringRef(csflags
)));
195 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags
);
197 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
201 if (!haveCdFlags
&& (inherit
& kSecCodeSignerPreserveFlags
)) {
202 cdFlags
= code
->codeDirectory(false)->flags
& ~kSecCodeSignatureAdhoc
;
203 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags
);
208 if (state
.mSigner
== SecIdentityRef(kCFNull
)) // ad-hoc signing requested...
209 cdFlags
|= kSecCodeSignatureAdhoc
; // ... so note that
211 // prepare the internal requirements input
212 if (state
.mRequirements
) {
213 if (CFGetTypeID(state
.mRequirements
) == CFDataGetTypeID()) { // binary form
214 const Requirements
*rp
= (const Requirements
*)CFDataGetBytePtr(state
.mRequirements
.as
<CFDataRef
>());
215 if (!rp
->validateBlob())
216 MacOSError::throwMe(errSecCSReqInvalid
);
217 requirements
= rp
->clone();
218 } else if (CFGetTypeID(state
.mRequirements
) == CFStringGetTypeID()) { // text form
219 CFRef
<CFMutableStringRef
> reqText
= CFStringCreateMutableCopy(NULL
, 0, state
.mRequirements
.as
<CFStringRef
>());
220 // substitute $ variable tokens
221 CFRange range
= { 0, CFStringGetLength(reqText
) };
222 CFStringFindAndReplace(reqText
, CFSTR("$self.identifier"), CFTempString(identifier
), range
, 0);
223 requirements
= parseRequirements(cfString(reqText
));
225 MacOSError::throwMe(errSecCSInvalidObjectRef
);
226 } else if (inherit
& kSecCodeSignerPreserveRequirements
)
227 if (const Requirements
*rp
= code
->internalRequirements())
228 requirements
= rp
->clone();
230 // prepare the resource directory, if any
231 string rpath
= rep
->resourcesRootPath();
233 CFCopyRef
<CFDictionaryRef
> resourceRules
;
234 if (!rpath
.empty()) {
235 // explicitly given resource rules always win
236 resourceRules
= state
.mResourceRules
;
238 // inherited rules come next (overriding embedded ones!)
239 if (!resourceRules
&& (inherit
& kSecCodeSignerPreserveResourceRules
))
240 if (CFDictionaryRef oldRules
= code
->resourceDictionary(false))
241 resourceRules
= oldRules
;
243 // embedded resource rules come next
244 if (!resourceRules
&& infoDict
)
245 if (CFTypeRef spec
= CFDictionaryGetValue(infoDict
, _kCFBundleResourceSpecificationKey
)) {
246 if (CFGetTypeID(spec
) == CFStringGetTypeID())
247 if (CFRef
<CFDataRef
> data
= cfLoadFile(rpath
+ "/" + cfString(CFStringRef(spec
))))
248 if (CFDictionaryRef dict
= makeCFDictionaryFrom(data
))
249 resourceRules
.take(dict
);
250 if (!resourceRules
) // embedded rules present but unacceptable
251 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
254 // if we got one from anywhere (but the defaults), sanity-check it
256 CFTypeRef rules
= CFDictionaryGetValue(resourceRules
, CFSTR("rules"));
257 if (!rules
|| CFGetTypeID(rules
) != CFDictionaryGetTypeID())
258 MacOSError::throwMe(errSecCSResourceRulesInvalid
);
261 // finally, ask the DiskRep for its default
263 resourceRules
.take(rep
->defaultResourceRules(*this));
265 // resource root can optionally be the canonical bundle path,
266 // but sealed resource paths are always relative to rpath
268 if (signingFlags() & kSecCSSignBundleRoot
)
269 rrpath
= cfStringRelease(rep
->copyCanonicalPath());
272 // screen and set the signing time
273 if (state
.mSigningTime
== CFDateRef(kCFNull
)) {
274 emitSigningTime
= false; // no time at all
275 } else if (!state
.mSigningTime
) {
276 emitSigningTime
= true;
277 signingTime
= 0; // wall clock, established later
279 CFAbsoluteTime time
= CFDateGetAbsoluteTime(state
.mSigningTime
);
280 if (time
> CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
281 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
282 emitSigningTime
= true;
286 pagesize
= state
.mPageSize
? cfNumber
<size_t>(state
.mPageSize
) : rep
->pageSize(*this);
288 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
289 rep
->prepareForSigning(*this);
291 // apply some defaults after diskRep intervention
292 if (hashAlgorithms
.empty()) { // default to SHA256 + SHA-1
293 hashAlgorithms
.insert(kSecCodeSignatureHashSHA1
);
294 hashAlgorithms
.insert(kSecCodeSignatureHashSHA256
);
297 // build the resource directory (once and for all, using the digests determined above)
298 if (!rpath
.empty()) {
299 buildResources(rrpath
, rpath
, resourceRules
);
304 if (inherit
& kSecCodeSignerPreservePEH
) {
305 /* We need at least one architecture in all cases because we index our
306 * PreEncryptionMaps by architecture. However, only machOs have any
307 * architecture at all, for generic targets there will just be one
308 * PreEncryptionHashMap.
309 * So if the main executable is not a machO, we just choose the local
310 * (signer's) main architecture as dummy value for the first element in our pair. */
311 preEncryptMainArch
= (code
->diskRep()->mainExecutableIsMachO() ?
312 code
->diskRep()->mainExecutableImage()->bestNativeArch() :
313 Architecture::local());
315 addPreEncryptHashes(preEncryptHashMaps
[preEncryptMainArch
], code
);
317 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
318 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
319 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
320 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
321 addPreEncryptHashes(preEncryptHashMaps
[arch
], subcode
);
325 if (inherit
& kSecCodeSignerPreserveRuntime
) {
326 /* We need at least one architecture in all cases because we index our
327 * RuntimeVersionMaps by architecture. However, only machOs have any
328 * architecture at all, for generic targets there will just be one
330 * So if the main executable is not a machO, we just choose the local
331 * (signer's) main architecture as dummy value for the first element in our pair. */
332 runtimeVersionMainArch
= (code
->diskRep()->mainExecutableIsMachO() ?
333 code
->diskRep()->mainExecutableImage()->bestNativeArch() :
334 Architecture::local());
336 addRuntimeVersions(runtimeVersionMap
[runtimeVersionMainArch
], code
);
338 code
->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode
*subcode
) {
339 Universal
*fat
= subcode
->diskRep()->mainExecutableImage();
340 assert(fat
&& fat
->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
341 Architecture arch
= fat
->bestNativeArch(); // actually, only architecture for this slice.
342 addRuntimeVersions(runtimeVersionMap
[arch
], subcode
);
347 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap
&map
, SecStaticCode
const *code
) {
348 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
351 for(auto const& pair
: *cds
) {
352 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
353 CFDataRef
const cddata
= pair
.second
;
355 CodeDirectory
const * cd
=
356 reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cddata
));
357 if (cd
->preEncryptHashes() != NULL
) {
358 CFRef
<CFDataRef
> preEncrypt
= makeCFData(cd
->preEncryptHashes(),
359 cd
->nCodeSlots
* cd
->hashSize
);
360 map
[alg
] = preEncrypt
;
366 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap
&map
, const SecStaticCode
*code
)
368 SecStaticCode::CodeDirectoryMap
const *cds
= code
->codeDirectories();
371 for(auto const& pair
: *cds
) {
372 CodeDirectory::HashAlgorithm
const alg
= pair
.first
;
373 CFDataRef
const cddata
= pair
.second
;
375 CodeDirectory
const * cd
=
376 reinterpret_cast<const CodeDirectory
*>(CFDataGetBytePtr(cddata
));
377 if (cd
->runtimeVersion()) {
378 map
[alg
] = cd
->runtimeVersion();
385 // Collect the resource seal for a program.
386 // This includes both sealed resources and information about nested code.
388 void SecCodeSigner::Signer::buildResources(std::string root
, std::string relBase
, CFDictionaryRef rulesDict
)
390 typedef ResourceBuilder::Rule Rule
;
392 secinfo("codesign", "start building resource directory");
393 __block CFRef
<CFMutableDictionaryRef
> result
= makeCFMutableDictionary();
395 CFDictionaryRef rules
= cfget
<CFDictionaryRef
>(rulesDict
, "rules");
398 if (this->state
.mLimitedAsync
== NULL
) {
399 this->state
.mLimitedAsync
=
400 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
401 * turned off, because the paths for signing code are not ready for it yet. */
402 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
403 new LimitedAsync(false);
406 CFDictionaryRef files2
= NULL
;
407 if (!(signingFlags() & kSecCSSignV1
)) {
408 CFCopyRef
<CFDictionaryRef
> rules2
= cfget
<CFDictionaryRef
>(rulesDict
, "rules2");
410 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules,
411 // because the default weight, according to ResourceBuilder::addRule(), is 1).
412 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
413 rules2
= cfmake
<CFDictionaryRef
>("{+%O"
414 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
418 Dispatch::Group group
;
419 Dispatch::Group
&groupRef
= group
; // (into block)
421 // build the modern (V2) resource seal
422 __block CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
423 CFMutableDictionaryRef filesRef
= files
.get(); // (into block)
424 ResourceBuilder
resourceBuilder(root
, relBase
, rules2
, strict
, MacOSErrorSet());
425 ResourceBuilder
&resources
= resourceBuilder
; // (into block)
426 rep
->adjustResources(resources
);
428 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, const std::string relpath
, Rule
*rule
) {
429 bool isSymlink
= (ent
->fts_info
== FTS_SL
);
430 const std::string
path(ent
->fts_path
);
431 const std::string
accpath(ent
->fts_accpath
);
432 this->state
.mLimitedAsync
->perform(groupRef
, ^{
433 CFRef
<CFMutableDictionaryRef
> seal
;
434 if (ruleFlags
& ResourceBuilder::nested
) {
435 seal
.take(signNested(path
, relpath
));
436 } else if (isSymlink
) {
437 char target
[PATH_MAX
];
438 ssize_t len
= ::readlink(accpath
.c_str(), target
, sizeof(target
)-1);
440 UnixError::check(-1);
442 seal
.take(cfmake
<CFMutableDictionaryRef
>("{symlink=%s}", target
));
444 seal
.take(resources
.hashFile(accpath
.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight
));
446 if (ruleFlags
& ResourceBuilder::optional
)
447 CFDictionaryAddValue(seal
, CFSTR("optional"), kCFBooleanTrue
);
449 StLock
<Mutex
> _(resourceLock
);
450 if ((hash
= CFDictionaryGetValue(seal
, CFSTR("hash"))) && CFDictionaryGetCount(seal
) == 1) // simple form
451 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), hash
);
453 CFDictionaryAddValue(filesRef
, CFTempString(relpath
).get(), seal
.get());
454 code
->reportProgress();
458 CFDictionaryAddValue(result
, CFSTR("rules2"), resourceBuilder
.rules());
460 CFDictionaryAddValue(result
, CFSTR("files2"), files2
);
463 CFDictionaryAddValue(result
, CFSTR("rules"), rules
); // preserve V1 rules in any case
464 if (!(signingFlags() & kSecCSSignNoV1
)) {
465 // build the legacy (V1) resource seal
466 __block CFRef
<CFMutableDictionaryRef
> files
= makeCFMutableDictionary();
467 ResourceBuilder
resourceBuilder(root
, relBase
, rules
, strict
, MacOSErrorSet());
468 ResourceBuilder
&resources
= resourceBuilder
;
469 rep
->adjustResources(resources
); // DiskRep-specific adjustments
470 resources
.scan(^(FTSENT
*ent
, uint32_t ruleFlags
, std::string relpath
, Rule
*rule
) {
471 if (ent
->fts_info
== FTS_F
) {
472 CFRef
<CFDataRef
> hash
;
473 if (files2
) // try to get the hash from a previously-made version
474 if (CFTypeRef seal
= CFDictionaryGetValue(files2
, CFTempString(relpath
))) {
475 if (CFGetTypeID(seal
) == CFDataGetTypeID())
476 hash
= CFDataRef(seal
);
478 hash
= CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal
), CFSTR("hash")));
481 hash
.take(resources
.hashFile(ent
->fts_accpath
, kSecCodeSignatureHashSHA1
));
482 if (ruleFlags
== 0) { // default case - plain hash
483 cfadd(files
, "{%s=%O}", relpath
.c_str(), hash
.get());
484 secinfo("csresource", "%s added simple (rule %p)", relpath
.c_str(), rule
);
485 } else { // more complicated - use a sub-dictionary
486 cfadd(files
, "{%s={hash=%O,optional=%B}}",
487 relpath
.c_str(), hash
.get(), ruleFlags
& ResourceBuilder::optional
);
488 secinfo("csresource", "%s added complex (rule %p)", relpath
.c_str(), rule
);
492 CFDictionaryAddValue(result
, CFSTR("files"), files
.get());
495 resourceDirectory
= result
.get();
496 resourceDictData
.take(makeCFData(resourceDirectory
.get()));
501 // Deal with one piece of nested code
503 CFMutableDictionaryRef
SecCodeSigner::Signer::signNested(const std::string
&path
, const std::string
&relpath
)
505 // sign nested code and collect nesting information
507 SecPointer
<SecStaticCode
> code
= new SecStaticCode(DiskRep::bestGuess(path
));
508 if (signingFlags() & kSecCSSignNestedCode
)
509 this->state
.sign(code
, signingFlags());
510 std::string dr
= Dumper::dump(code
->designatedRequirement());
511 if (CFDataRef hash
= code
->cdHash())
512 return cfmake
<CFMutableDictionaryRef
>("{requirement=%s,cdhash=%O}",
513 Dumper::dump(code
->designatedRequirement()).c_str(),
515 MacOSError::throwMe(errSecCSUnsigned
);
516 } catch (const CommonError
&err
) {
517 CSError::throwMe(err
.osStatus(), kSecCFErrorPath
, CFTempURL(relpath
, false, this->code
->resourceBase()));
523 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
524 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
525 // treat them as architectural binaries containing (only) one architecture - that
526 // interpretation is courtesy of the Universal/MachO support classes.
528 void SecCodeSigner::Signer::signMachO(Universal
*fat
, const Requirement::Context
&context
)
530 // Mach-O executable at the core - perform multi-architecture signing
531 RefPointer
<DiskRep::Writer
> writer
= rep
->writer();
533 if (state
.mPreserveAFSC
)
534 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
536 auto_ptr
<ArchEditor
> editor(state
.mDetached
537 ? static_cast<ArchEditor
*>(new BlobEditor(*fat
, *this))
538 : new MachOEditor(writer
, *fat
, this->digestAlgorithms(), rep
->mainExecutablePath()));
539 assert(editor
->count() > 0);
540 if (!editor
->attribute(writerNoGlobal
)) // can store architecture-common components
543 // pass 1: prepare signature blobs and calculate sizes
544 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
545 MachOEditor::Arch
&arch
= *it
->second
;
546 arch
.source
.reset(fat
->architecture(it
->first
));
548 // library validation is not compatible with i386
549 if (arch
.architecture
.cpuType() == CPU_TYPE_I386
) {
550 if (cdFlags
& kSecCodeSignatureLibraryValidation
) {
551 MacOSError::throwMe(errSecCSBadLVArch
);
555 bool mainBinary
= arch
.source
.get()->type() == MH_EXECUTE
;
557 uint32_t runtimeVersion
= 0;
558 if (cdFlags
& kSecCodeSignatureRuntime
) {
559 runtimeVersion
= state
.mRuntimeVersionOverride
? state
.mRuntimeVersionOverride
: arch
.source
.get()->sdkVersion();
562 arch
.ireqs(requirements
, rep
->defaultRequirements(&arch
.architecture
, *this), context
);
563 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-arch
565 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
566 uint32_t runtimeVersionToUse
= runtimeVersion
;
567 if ((cdFlags
& kSecCodeSignatureRuntime
) && runtimeVersionMap
.count(arch
.architecture
)) {
568 if (runtimeVersionMap
[arch
.architecture
].count(*type
)) {
569 runtimeVersionToUse
= runtimeVersionMap
[arch
.architecture
][*type
];
572 arch
.eachDigest(^(CodeDirectory::Builder
& builder
) {
573 populate(builder
, arch
, arch
.ireqs
,
574 arch
.source
->offset(), arch
.source
->signingExtent(),
575 mainBinary
, rep
->execSegBase(&(arch
.architecture
)), rep
->execSegLimit(&(arch
.architecture
)),
576 unsigned(digestAlgorithms().size()-1),
577 preEncryptHashMaps
[arch
.architecture
], runtimeVersionToUse
);
581 // add identification blob (made from this architecture) only if we're making a detached signature
582 if (state
.mDetached
) {
583 CFRef
<CFDataRef
> identification
= MachORep::identificationFor(arch
.source
.get());
584 arch
.add(cdIdentificationSlot
, BlobWrapper::alloc(
585 CFDataGetBytePtr(identification
), CFDataGetLength(identification
)));
588 // prepare SuperBlob size estimate
589 __block
std::vector
<size_t> sizes
;
590 arch
.eachDigest(^(CodeDirectory::Builder
& builder
){
591 sizes
.push_back(builder
.size(CodeDirectory::currentVersion
));
593 arch
.blobSize
= arch
.size(sizes
, state
.mCMSSize
, 0);
598 // pass 2: Finish and generate signatures, and write them
599 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
600 MachOEditor::Arch
&arch
= *it
->second
;
603 // finish CodeDirectories (off new binary) and sign it
604 __block CodeDirectorySet cdSet
;
605 arch
.eachDigest(^(CodeDirectory::Builder
&builder
) {
606 CodeDirectory
*cd
= builder
.build();
610 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
611 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
612 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
614 // complete the SuperBlob
615 cdSet
.populate(&arch
);
616 arch
.add(cdSignatureSlot
, BlobWrapper::alloc(
617 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)));
618 if (!state
.mDryRun
) {
619 EmbeddedSignatureBlob
*blob
= arch
.make();
620 editor
->write(arch
, blob
); // takes ownership of blob
624 // done: write edit copy back over the original
625 if (!state
.mDryRun
) {
632 // Sign a binary that has no notion of architecture.
633 // That currently means anything that isn't Mach-O format.
635 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context
&context
)
637 // non-Mach-O executable - single-instance signing
638 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
639 (new DetachedBlobWriter(*this)) : rep
->writer();
641 if(state
.mPreserveAFSC
)
642 writer
->setPreserveAFSC(state
.mPreserveAFSC
);
644 CodeDirectorySet cdSet
;
646 for (auto type
= digestAlgorithms().begin(); type
!= digestAlgorithms().end(); ++type
) {
647 CodeDirectory::Builder
builder(*type
);
648 InternalRequirements ireqs
;
649 ireqs(requirements
, rep
->defaultRequirements(NULL
, *this), context
);
651 populate(builder
, *writer
, ireqs
, rep
->signingBase(), rep
->signingLimit(),
652 false, // only machOs can currently be main binaries
653 rep
->execSegBase(NULL
), rep
->execSegLimit(NULL
),
654 unsigned(digestAlgorithms().size()-1),
655 preEncryptHashMaps
[preEncryptMainArch
], // Only one map, the default.
656 (cdFlags
& kSecCodeSignatureRuntime
) ? state
.mRuntimeVersionOverride
: 0);
658 CodeDirectory
*cd
= builder
.build();
663 // add identification blob (made from this architecture) only if we're making a detached signature
664 if (state
.mDetached
) {
665 CFRef
<CFDataRef
> identification
= rep
->identification();
666 writer
->component(cdIdentificationSlot
, identification
);
669 // write out all CodeDirectories
671 cdSet
.populate(writer
);
673 CFRef
<CFDictionaryRef
> hashDict
= cdSet
.hashDict();
674 CFRef
<CFArrayRef
> hashList
= cdSet
.hashList();
675 CFRef
<CFDataRef
> signature
= signCodeDirectory(cdSet
.primary(), hashDict
, hashList
);
676 writer
->signature(signature
);
684 // Global populate - send components to destination buffers ONCE
686 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
688 if (resourceDirectory
&& !state
.mDryRun
)
689 writer
.component(cdResourceDirSlot
, resourceDictData
);
694 // Per-architecture populate - send components to per-architecture buffers
695 // and populate the CodeDirectory for an architecture. In architecture-agnostic
696 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
697 // for the purposes of this call.
699 void SecCodeSigner::Signer::populate(CodeDirectory::Builder
&builder
, DiskRep::Writer
&writer
,
700 InternalRequirements
&ireqs
, size_t offset
, size_t length
,
701 bool mainBinary
, size_t execSegBase
, size_t execSegLimit
,
702 unsigned alternateDigestCount
,
703 PreEncryptHashMap
const &preEncryptHashMap
,
704 uint32_t runtimeVersion
)
706 // fill the CodeDirectory
707 builder
.executable(rep
->mainExecutablePath(), pagesize
, offset
, length
);
708 builder
.flags(cdFlags
);
709 builder
.identifier(identifier
);
710 builder
.teamID(teamID
);
711 builder
.platform(state
.mPlatform
);
712 builder
.execSeg(execSegBase
, execSegLimit
, mainBinary
? kSecCodeExecSegMainBinary
: 0);
713 builder
.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH
);
714 builder
.preservePreEncryptHashMap(preEncryptHashMap
);
715 builder
.runTimeVersion(runtimeVersion
);
717 if (CFRef
<CFDataRef
> data
= rep
->component(cdInfoSlot
))
718 builder
.specialSlot(cdInfoSlot
, data
);
720 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
721 writer
.component(cdRequirementsSlot
, data
);
722 builder
.specialSlot(cdRequirementsSlot
, data
);
724 if (resourceDirectory
)
725 builder
.specialSlot(cdResourceDirSlot
, resourceDictData
);
727 writer
.component(cdEntitlementSlot
, entitlements
);
728 builder
.specialSlot(cdEntitlementSlot
, entitlements
);
731 CFRef
<CFDataRef
> entitlementDER
;
732 uint64_t execSegFlags
= 0;
733 cookEntitlements(entitlements
, generateEntitlementDER
,
734 &execSegFlags
, &entitlementDER
.aref());
736 if (generateEntitlementDER
) {
737 writer
.component(cdEntitlementDERSlot
, entitlementDER
);
738 builder
.specialSlot(cdEntitlementDERSlot
, entitlementDER
);
741 builder
.addExecSegFlags(execSegFlags
);
744 if (CFRef
<CFDataRef
> repSpecific
= rep
->component(cdRepSpecificSlot
))
745 builder
.specialSlot(cdRepSpecificSlot
, repSpecific
);
747 writer
.addDiscretionary(builder
);
749 #if 0 // rdar://problem/25720754
750 if ((signingFlags() & (kSecCSSignOpaque
|kSecCSSignV1
)) == 0 && builder
.hashType() != kSecCodeSignatureHashSHA1
) {
751 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
752 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
753 std::vector
<Endian
<uint32_t> > slotVector
;
754 slotVector
.push_back(cdCodeDirectorySlot
); // mandatory
755 std::set
<CodeDirectory::Slot
> filledSlots
= builder
.filledSpecialSlots();
756 filledSlots
.insert(cdTopDirectorySlot
); // will be added below
757 copy(filledSlots
.begin(), filledSlots
.end(), back_inserter(slotVector
));
758 for (unsigned n
= 0; n
< alternateDigestCount
; n
++)
759 slotVector
.push_back(cdAlternateCodeDirectorySlots
+ n
);
760 slotVector
.push_back(cdSignatureSlot
);
761 CFTempData
cfSlotVector(&slotVector
[0], slotVector
.size() * sizeof(slotVector
[0]));
762 writer
.component(cdTopDirectorySlot
, cfSlotVector
);
763 builder
.specialSlot(cdTopDirectorySlot
, cfSlotVector
);
769 #include <security_smime/tsaSupport.h>
772 // Generate the CMS signature for a (finished) CodeDirectory.
774 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
,
775 CFDictionaryRef hashDict
,
778 assert(state
.mSigner
);
779 CFRef
<CFMutableDictionaryRef
> defaultTSContext
= NULL
;
781 // a null signer generates a null signature blob
782 if (state
.mSigner
== SecIdentityRef(kCFNull
))
783 return CFDataCreate(NULL
, NULL
, 0);
785 // generate CMS signature
786 CFRef
<CMSEncoderRef
> cms
;
787 MacOSError::check(CMSEncoderCreate(&cms
.aref()));
788 MacOSError::check(CMSEncoderSetCertificateChainMode(cms
, kCMSCertificateChainWithRoot
));
789 CMSEncoderAddSigners(cms
, state
.mSigner
);
790 CMSEncoderSetSignerAlgorithm(cms
, kCMSEncoderDigestAlgorithmSHA256
);
791 MacOSError::check(CMSEncoderSetHasDetachedContent(cms
, true));
793 if (emitSigningTime
) {
794 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
795 CFAbsoluteTime time
= signingTime
? signingTime
: CFAbsoluteTimeGetCurrent();
796 MacOSError::check(CMSEncoderSetSigningTime(cms
, time
));
799 if (hashDict
!= NULL
) {
800 assert(hashList
!= NULL
);
804 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgilityV2
));
805 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms
, hashDict
));
809 CFTemp
<CFDictionaryRef
> hashDict("{cdhashes=%O}", hashList
);
810 CFRef
<CFDataRef
> hashAgilityV1Attribute
= makeCFData(hashDict
.get());
812 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrAppleCodesigningHashAgility
));
813 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms
, hashAgilityV1Attribute
));
816 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
818 // Set up to call Timestamp server if requested
819 if (state
.mWantTimeStamp
)
821 CFRef
<CFErrorRef
> error
= NULL
;
822 defaultTSContext
= SecCmsTSAGetDefaultContext(&error
.aref());
824 MacOSError::throwMe(errSecDataNotAvailable
);
826 if (state
.mNoTimeStampCerts
|| state
.mTimestampService
) {
827 if (state
.mTimestampService
)
828 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyURL
, state
.mTimestampService
);
829 if (state
.mNoTimeStampCerts
)
830 CFDictionarySetValue(defaultTSContext
, kTSAContextKeyNoCerts
, kCFBooleanTrue
);
833 CmsMessageSetTSAContext(cms
, defaultTSContext
);
837 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
844 // Our DiskRep::signingContext methods communicate with the signing subsystem
845 // in terms those callers can easily understand.
847 string
SecCodeSigner::Signer::sdkPath(const std::string
&path
) const
849 assert(path
[0] == '/'); // need absolute path here
851 return cfString(state
.mSDKRoot
) + path
;
856 bool SecCodeSigner::Signer::isAdhoc() const
858 return state
.mSigner
== SecIdentityRef(kCFNull
);
861 SecCSFlags
SecCodeSigner::Signer::signingFlags() const
863 return state
.mOpFlags
;
868 // Parse a text of the form
870 // where each flag is the canonical name of a signable CodeDirectory flag.
871 // No abbreviations are allowed, and internally set flags are not accepted.
873 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text
)
876 for (string::size_type comma
= text
.find(','); ; text
= text
.substr(comma
+1), comma
= text
.find(',')) {
877 string word
= (comma
== string::npos
) ? text
: text
.substr(0, comma
);
878 const SecCodeDirectoryFlagTable
*item
;
879 for (item
= kSecCodeDirectoryFlagTable
; item
->name
; item
++)
880 if (item
->signable
&& word
== item
->name
) {
881 flags
|= item
->value
;
884 if (!item
->name
) // not found
885 MacOSError::throwMe(errSecCSInvalidFlags
);
886 if (comma
== string::npos
) // last word
894 // Generate a unique string from our underlying DiskRep.
895 // We could get 90%+ of the uniquing benefit by just generating
896 // a random string here. Instead, we pick the (hex string encoding of)
897 // the source rep's unique identifier blob. For universal binaries,
898 // this is the canonical local architecture, which is a bit arbitrary.
899 // This provides us with a consistent unique string for all architectures
900 // of a fat binary, *and* (unlike a random string) is reproducible
901 // for identical inputs, even upon resigning.
903 std::string
SecCodeSigner::Signer::uniqueName() const
905 CFRef
<CFDataRef
> identification
= rep
->identification();
906 const UInt8
*ident
= CFDataGetBytePtr(identification
);
907 const CFIndex length
= CFDataGetLength(identification
);
909 for (CFIndex n
= 0; n
< length
; n
++) {
911 snprintf(hex
, sizeof(hex
), "%02x", ident
[n
]);
917 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict
, CFStringRef key
) {
918 CFBooleanRef entValue
= (CFBooleanRef
)CFDictionaryGetValue(entDict
, key
);
920 if (entValue
== NULL
|| CFGetTypeID(entValue
) != CFBooleanGetTypeID()) {
924 return CFBooleanGetValue(entValue
);
927 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements
, bool generateDER
,
928 uint64_t *execSegFlags
, CFDataRef
*entitlementDER
)
931 return; // nothing to do.
934 EntitlementDERBlob
*derBlob
= NULL
;
937 const EntitlementBlob
*blob
= reinterpret_cast<const EntitlementBlob
*>(CFDataGetBytePtr(entitlements
));
939 if (blob
== NULL
|| !blob
->validateBlob(CFDataGetLength(entitlements
))) {
940 MacOSError::throwMe(errSecCSInvalidEntitlements
);
943 CFRef
<CFDictionaryRef
> entDict
= blob
->entitlements();
946 CFRef
<CFErrorRef
> error
= NULL
;
947 size_t const der_size
= der_sizeof_plist(entDict
, &error
.aref());
950 secerror("Getting DER size for entitlement plist failed: %@", error
.get());
951 MacOSError::throwMe(errSecCSInvalidEntitlements
);
954 derBlob
= EntitlementDERBlob::alloc(der_size
);
956 if (derBlob
== NULL
) {
957 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size
);
958 MacOSError::throwMe(errSecCSInvalidEntitlements
);
960 uint8_t * const der_end
= derBlob
->der() + der_size
;
961 uint8_t * const der_start
= der_encode_plist(entDict
, &error
.aref(), derBlob
->der(), der_end
);
963 if (der_start
!= derBlob
->der()) {
964 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start
- derBlob
->der()));
966 MacOSError::throwMe(errSecCSInvalidEntitlements
);
969 *entitlementDER
= makeCFData(derBlob
, derBlob
->length());
974 if (execSegFlags
!= NULL
) {
977 flags
|= booleanEntitlement(entDict
, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned
: 0;
978 flags
|= booleanEntitlement(entDict
, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned
: 0;
979 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger
: 0;
980 flags
|= booleanEntitlement(entDict
, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit
: 0;
981 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal
: 0;
982 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash
: 0;
983 flags
|= booleanEntitlement(entDict
, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash
: 0;
985 *execSegFlags
= flags
;
988 } catch (const CommonError
&err
) {
990 // Not fatal if we're not asked to generate DER entitlements.
992 secwarning("failed to parse entitlements: %s", err
.what());
999 } // end namespace CodeSigning
1000 } // end namespace Security