2 * Copyright (c) 2007 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
28 #include "resources.h"
29 #include "signerutils.h"
30 #include "SecCodeSigner.h"
31 #include <Security/SecIdentity.h>
32 #include <Security/CMSEncoder.h>
33 #include <Security/CMSPrivate.h>
34 #include <CoreFoundation/CFBundlePriv.h>
36 #include <security_utilities/unix++.h>
37 #include <security_utilities/unixchild.h>
38 #include <security_codesigning/cfmunge.h>
41 namespace CodeSigning
{
47 void SecCodeSigner::Signer::sign(SecCSFlags flags
)
49 // set up access to the subject Code
50 rep
= code
->diskRep()->base();
52 // get the Info.plist out of the rep for some creative defaulting
53 CFRef
<CFDictionaryRef
> infoDict
;
54 if (CFRef
<CFDataRef
> infoData
= rep
->component(cdInfoSlot
))
55 infoDict
.take(makeCFDictionaryFrom(infoData
));
57 // work out the canonical identifier
58 identifier
= state
.mIdentifier
;
59 if (identifier
.empty()) {
60 identifier
= rep
->recommendedIdentifier();
61 if (identifier
.find('.') == string::npos
&& !state
.mIdentifierPrefix
.empty())
62 identifier
= state
.mIdentifierPrefix
+ identifier
;
63 secdebug("signer", "using default identifier=%s", identifier
.c_str());
65 secdebug("signer", "using explicit identifier=%s", identifier
.c_str());
67 // work out the CodeDirectory flags word
68 if (state
.mCdFlagsGiven
) {
69 cdFlags
= state
.mCdFlags
;
70 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags
);
74 if (CFTypeRef csflags
= CFDictionaryGetValue(infoDict
, CFSTR("CSFlags")))
75 if (CFGetTypeID(csflags
) == CFNumberGetTypeID()) {
76 cdFlags
= cfNumber
<uint32_t>(CFNumberRef(csflags
));
77 secdebug("signer", "using numeric cdFlags=0x%x from Info.dict", cdFlags
);
78 } else if (CFGetTypeID(csflags
) == CFStringGetTypeID()) {
79 cdFlags
= CodeDirectory::textFlags(cfString(CFStringRef(csflags
)));
80 secdebug("signer", "using text cdFlags=0x%x from Info.dict", cdFlags
);
82 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
84 if (state
.mSigner
== SecIdentityRef(kCFNull
)) // ad-hoc signing requested...
85 cdFlags
|= kSecCodeSignatureAdhoc
; // ... so allow that
87 // prepare the resource directory, if any
88 string rpath
= rep
->resourcesRootPath();
90 // explicitly given resource rules always win
91 CFCopyRef
<CFDictionaryRef
> resourceRules(state
.mResourceRules
);
93 // embedded resource rules come next
95 if (CFTypeRef spec
= CFDictionaryGetValue(infoDict
, _kCFBundleResourceSpecificationKey
))
96 if (CFGetTypeID(spec
) == CFStringGetTypeID())
97 if (CFRef
<CFDataRef
> data
= cfLoadFile(rpath
+ "/" + cfString(CFStringRef(spec
))))
98 if (CFRef
<CFDictionaryRef
> dict
= makeCFDictionaryFrom(data
))
101 // finally, ask the DiskRep for its default
103 resourceRules
.take(rep
->defaultResourceRules());
105 // build the resource directory
106 ResourceBuilder
resources(rpath
, cfget
<CFDictionaryRef
>(resourceRules
, "rules"));
107 rep
->adjustResources(resources
);
108 CFRef
<CFDictionaryRef
> rdir
= resources
.build();
109 resourceDirectory
.take(CFPropertyListCreateXMLData(NULL
, rdir
));
112 // screen and set the signing time
113 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
114 if (state
.mSigningTime
== CFDateRef(kCFNull
)) {
115 signingTime
= 0; // no time at all
116 } else if (!state
.mSigningTime
) {
117 signingTime
= now
; // default
119 CFAbsoluteTime time
= CFDateGetAbsoluteTime(state
.mSigningTime
);
120 if (time
> now
) // not allowed to post-date a signature
121 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
125 pagesize
= state
.mPageSize
? cfNumber
<size_t>(state
.mPageSize
) : rep
->pageSize();
127 if (Universal
*fat
= state
.mNoMachO
? NULL
: rep
->mainExecutableImage()) {
130 signArchitectureAgnostic();
136 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
137 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
138 // treat them as architectural binaries containing (only) one architecture - that
139 // interpretation is courtesy of the Universal/MachO support classes.
141 void SecCodeSigner::Signer::signMachO(Universal
*fat
)
143 // Mach-O executable at the core - perform multi-architecture signing
144 auto_ptr
<ArchEditor
> editor(state
.mDetached
145 ? static_cast<ArchEditor
*>(new BlobEditor(*fat
, *this))
146 : new MachOEditor(rep
->writer(), *fat
, rep
->mainExecutablePath()));
147 assert(editor
->count() > 0);
148 if (!editor
->attribute(writerNoGlobal
)) // can store architecture-common components
151 // pass 1: prepare signature blobs and calculate sizes
152 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
153 MachOEditor::Arch
&arch
= *it
->second
;
154 arch
.source
.reset(fat
->architecture(it
->first
));
155 arch
.ireqs(state
.mRequirements
, rep
->defaultRequirements(&arch
.architecture
));
156 if (editor
->attribute(writerNoGlobal
)) // can't store globally, add per-arch
158 populate(arch
.cdbuilder
, arch
, arch
.ireqs
,
159 arch
.source
->offset(), arch
.source
->signingExtent());
160 // prepare SuperBlob size estimate
161 size_t cdSize
= arch
.cdbuilder
.size();
162 arch
.blobSize
= arch
.size(cdSize
, state
.mCMSSize
, 0);
167 // pass 2: Finish and generate signatures, and write them
168 for (MachOEditor::Iterator it
= editor
->begin(); it
!= editor
->end(); ++it
) {
169 MachOEditor::Arch
&arch
= *it
->second
;
172 // finish CodeDirectory (off new binary) and sign it
173 CodeDirectory
*cd
= arch
.cdbuilder
.build();
174 CFRef
<CFDataRef
> signature
= signCodeDirectory(cd
);
176 // complete the SuperBlob
177 arch
.add(cdCodeDirectorySlot
, cd
); // takes ownership
178 arch
.add(cdSignatureSlot
, BlobWrapper::alloc(
179 CFDataGetBytePtr(signature
), CFDataGetLength(signature
)));
180 if (!state
.mDryRun
) {
181 EmbeddedSignatureBlob
*blob
= arch
.make();
182 editor
->write(arch
, blob
); // takes ownership of blob
186 // done: write edit copy back over the original
193 // Sign a binary that has no notion of architecture.
194 // That currently means anything that isn't Mach-O format.
196 void SecCodeSigner::Signer::signArchitectureAgnostic()
198 // non-Mach-O executable - single-instance signing
199 RefPointer
<DiskRep::Writer
> writer
= state
.mDetached
?
200 (new DetachedBlobWriter(*this)) : rep
->writer();
201 CodeDirectory::Builder builder
;
202 InternalRequirements ireqs
;
203 ireqs(state
.mRequirements
, rep
->defaultRequirements(NULL
));
205 populate(builder
, *writer
, ireqs
, rep
->signingBase(), rep
->signingLimit());
206 CodeDirectory
*cd
= builder
.build();
207 CFRef
<CFDataRef
> signature
= signCodeDirectory(cd
);
208 if (!state
.mDryRun
) {
209 writer
->codeDirectory(cd
);
210 writer
->signature(signature
);
218 // Global populate - send components to destination buffers ONCE
220 void SecCodeSigner::Signer::populate(DiskRep::Writer
&writer
)
222 if (resourceDirectory
)
223 writer
.component(cdResourceDirSlot
, resourceDirectory
);
228 // Per-architecture populate - send components to per-architecture buffers
229 // and populate the CodeDirectory for an architecture. In architecture-agnostic
230 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
231 // for the purposes of this call.
233 void SecCodeSigner::Signer::populate(CodeDirectory::Builder
&builder
, DiskRep::Writer
&writer
,
234 InternalRequirements
&ireqs
, size_t offset
/* = 0 */, size_t length
/* = 0 */)
236 // fill the CodeDirectory
237 builder
.executable(rep
->mainExecutablePath(), pagesize
, offset
, length
);
238 builder
.flags(cdFlags
);
239 builder
.identifier(identifier
);
241 for (CodeDirectory::Slot slot
= cdSlotMax
; slot
>= 1; --slot
)
243 case cdRequirementsSlot
:
245 CFRef
<CFDataRef
> data
= makeCFData(*ireqs
);
246 writer
.component(cdRequirementsSlot
, data
);
247 builder
.special(slot
, data
);
250 case cdResourceDirSlot
:
251 if (resourceDirectory
)
252 builder
.special(slot
, resourceDirectory
);
254 case cdApplicationSlot
:
256 if (state
.mApplicationData
)
257 builder
.special(slot
, state
.mApplicationData
);
260 case cdEntitlementSlot
:
261 if (state
.mEntitlementData
) {
262 writer
.component(cdEntitlementSlot
, state
.mEntitlementData
);
263 builder
.special(slot
, state
.mEntitlementData
);
267 if (CFDataRef data
= rep
->component(slot
))
268 builder
.special(slot
, data
);
275 // Generate the CMS signature for a (finished) CodeDirectory.
277 CFDataRef
SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory
*cd
)
279 assert(state
.mSigner
);
281 // a null signer generates a null signature blob
282 if (state
.mSigner
== SecIdentityRef(kCFNull
))
283 return CFDataCreate(NULL
, NULL
, 0);
285 // generate CMS signature
286 CFRef
<CMSEncoderRef
> cms
;
287 MacOSError::check(CMSEncoderCreate(&cms
.aref()));
288 MacOSError::check(CMSEncoderSetCertificateChainMode(cms
, kCMSCertificateChainWithRoot
));
289 CMSEncoderAddSigners(cms
, state
.mSigner
);
290 MacOSError::check(CMSEncoderSetHasDetachedContent(cms
, true));
293 MacOSError::check(CMSEncoderAddSignedAttributes(cms
, kCMSAttrSigningTime
));
294 MacOSError::check(CMSEncoderSetSigningTime(cms
, signingTime
));
297 MacOSError::check(CMSEncoderUpdateContent(cms
, cd
, cd
->length()));
299 MacOSError::check(CMSEncoderCopyEncodedContent(cms
, &signature
));
304 } // end namespace CodeSigning
305 } // end namespace Security