]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signer.cpp
15003b3da5e3221114f8f87d54a80b49b9925e3e
[apple/libsecurity_codesigning.git] / lib / signer.cpp
1 /*
2 * Copyright (c) 2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // signer - Signing operation supervisor and controller
26 //
27 #include "signer.h"
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>
35 #include "renum.h"
36 #include <security_utilities/unix++.h>
37 #include <security_utilities/unixchild.h>
38 #include <security_codesigning/cfmunge.h>
39
40 namespace Security {
41 namespace CodeSigning {
42
43
44 //
45 // Sign some code.
46 //
47 void SecCodeSigner::Signer::sign(SecCSFlags flags)
48 {
49 // set up access to the subject Code
50 rep = code->diskRep();
51
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));
56
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());
64 } else
65 secdebug("signer", "using explicit identifier=%s", identifier.c_str());
66
67 // work out the CodeDirectory flags word
68 if (state.mCdFlagsGiven) {
69 cdFlags = state.mCdFlags;
70 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
71 } else {
72 cdFlags = 0;
73 if (infoDict)
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);
81 } else
82 MacOSError::throwMe(errSecCSBadDictionaryFormat);
83 }
84 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
85 cdFlags |= kSecCodeSignatureAdhoc; // ... so allow that
86
87 // prepare the resource directory, if any
88 string rpath = rep->resourcesRootPath();
89 if (!rpath.empty()) {
90 // explicitly given resource rules always win
91 CFCopyRef<CFDictionaryRef> resourceRules(state.mResourceRules);
92
93 // embedded resource rules come next
94 if (!resourceRules)
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))
99 resourceRules = dict;
100
101 // finally, ask the DiskRep for its default
102 if (!resourceRules)
103 resourceRules.take(rep->defaultResourceRules());
104
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));
110 }
111
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
118 } else {
119 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
120 if (time > now) // not allowed to post-date a signature
121 MacOSError::throwMe(errSecCSBadDictionaryFormat);
122 signingTime = time;
123 }
124
125 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize();
126
127 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
128 signMachO(fat);
129 } else {
130 signArchitectureAgnostic();
131 }
132 }
133
134
135 //
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.
140 //
141 void SecCodeSigner::Signer::signMachO(Universal *fat)
142 {
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
149 populate(*editor);
150
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
157 populate(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);
163 }
164
165 editor->allocate();
166
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;
170 editor->reset(arch);
171
172 // finish CodeDirectory (off new binary) and sign it
173 CodeDirectory *cd = arch.cdbuilder.build();
174 CFRef<CFDataRef> signature = signCodeDirectory(cd);
175
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
183 }
184 }
185
186 // done: write edit copy back over the original
187 if (!state.mDryRun)
188 editor->commit();
189 }
190
191
192 //
193 // Sign a binary that has no notion of architecture.
194 // That currently means anything that isn't Mach-O format.
195 //
196 void SecCodeSigner::Signer::signArchitectureAgnostic()
197 {
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));
204 populate(*writer);
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);
211 writer->flush();
212 }
213 ::free(cd);
214 }
215
216
217 //
218 // Global populate - send components to destination buffers ONCE
219 //
220 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
221 {
222 if (resourceDirectory)
223 writer.component(cdResourceDirSlot, resourceDirectory);
224 }
225
226
227 //
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.
232 //
233 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
234 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
235 {
236 // fill the CodeDirectory
237 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
238 builder.flags(cdFlags);
239 builder.identifier(identifier);
240
241 for (CodeDirectory::Slot slot = cdSlotMax; slot >= 1; --slot)
242 switch (slot) {
243 case cdRequirementsSlot:
244 if (ireqs) {
245 CFRef<CFDataRef> data = makeCFData(*ireqs);
246 writer.component(cdRequirementsSlot, data);
247 builder.special(slot, data);
248 }
249 break;
250 case cdResourceDirSlot:
251 if (resourceDirectory)
252 builder.special(slot, resourceDirectory);
253 break;
254 case cdApplicationSlot:
255 #if NOT_YET
256 if (state.mApplicationData)
257 builder.special(slot, state.mApplicationData);
258 #endif
259 break;
260 case cdEntitlementSlot:
261 if (state.mEntitlementData) {
262 writer.component(cdEntitlementSlot, state.mEntitlementData);
263 builder.special(slot, state.mEntitlementData);
264 }
265 break;
266 default:
267 if (CFDataRef data = rep->component(slot))
268 builder.special(slot, data);
269 break;
270 }
271 }
272
273
274 //
275 // Generate the CMS signature for a (finished) CodeDirectory.
276 //
277 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
278 {
279 assert(state.mSigner);
280
281 // a null signer generates a null signature blob
282 if (state.mSigner == SecIdentityRef(kCFNull))
283 return CFDataCreate(NULL, NULL, 0);
284
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));
291
292 if (signingTime) {
293 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
294 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
295 }
296
297 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
298 CFDataRef signature;
299 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
300 return signature;
301 }
302
303
304 } // end namespace CodeSigning
305 } // end namespace Security