]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signer.cpp
68537d92d53b74240490df944ec5911ba98aadea
[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 <Security/CSCommonPriv.h>
35 #include <CoreFoundation/CFBundlePriv.h>
36 #include "renum.h"
37 #include "machorep.h"
38 #include <security_utilities/unix++.h>
39 #include <security_utilities/unixchild.h>
40 #include <security_utilities/cfmunge.h>
41
42 namespace Security {
43 namespace CodeSigning {
44
45
46 //
47 // Sign some code.
48 //
49 void SecCodeSigner::Signer::sign(SecCSFlags flags)
50 {
51 rep = code->diskRep()->base();
52 this->prepare(flags);
53 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
54 signMachO(fat);
55 } else {
56 signArchitectureAgnostic();
57 }
58 }
59
60
61 //
62 // Remove any existing code signature from code
63 //
64 void SecCodeSigner::Signer::remove(SecCSFlags flags)
65 {
66 // can't remove a detached signature
67 if (state.mDetached)
68 MacOSError::throwMe(errSecCSNotSupported);
69
70 rep = code->diskRep();
71 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
72 // architecture-sensitive removal
73 MachOEditor editor(rep->writer(), *fat, rep->mainExecutablePath());
74 editor.allocate(); // create copy
75 editor.commit(); // commit change
76 } else {
77 // architecture-agnostic removal
78 RefPointer<DiskRep::Writer> writer = rep->writer();
79 writer->remove();
80 writer->flush();
81 }
82 }
83
84
85 //
86 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
87 //
88 void SecCodeSigner::Signer::prepare(SecCSFlags flags)
89 {
90 // get the Info.plist out of the rep for some creative defaulting
91 CFRef<CFDictionaryRef> infoDict;
92 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
93 infoDict.take(makeCFDictionaryFrom(infoData));
94
95 // work out the canonical identifier
96 identifier = state.mIdentifier;
97 if (identifier.empty()) {
98 identifier = rep->recommendedIdentifier();
99 if (identifier.find('.') == string::npos)
100 identifier = state.mIdentifierPrefix + identifier;
101 secdebug("signer", "using default identifier=%s", identifier.c_str());
102 } else
103 secdebug("signer", "using explicit identifier=%s", identifier.c_str());
104
105 // work out the CodeDirectory flags word
106 if (state.mCdFlagsGiven) {
107 cdFlags = state.mCdFlags;
108 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
109 } else {
110 cdFlags = 0;
111 if (infoDict)
112 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags")))
113 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
114 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
115 secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
116 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
117 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
118 secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
119 } else
120 MacOSError::throwMe(errSecCSBadDictionaryFormat);
121 }
122 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
123 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
124
125 // prepare the resource directory, if any
126 string rpath = rep->resourcesRootPath();
127 if (!rpath.empty()) {
128 // explicitly given resource rules always win
129 CFCopyRef<CFDictionaryRef> resourceRules = state.mResourceRules;
130
131 // embedded resource rules come next
132 if (!resourceRules && infoDict)
133 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) {
134 if (CFGetTypeID(spec) == CFStringGetTypeID())
135 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
136 if (CFDictionaryRef dict = makeCFDictionaryFrom(data))
137 resourceRules.take(dict);
138 if (!resourceRules) // embedded rules present but unacceptable
139 MacOSError::throwMe(errSecCSResourceRulesInvalid);
140 }
141
142 // finally, ask the DiskRep for its default
143 if (!resourceRules)
144 resourceRules.take(rep->defaultResourceRules());
145
146 // build the resource directory
147 ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"));
148 rep->adjustResources(resources); // DiskRep-specific adjustments
149 CFRef<CFDictionaryRef> rdir = resources.build();
150 resourceDirectory.take(CFPropertyListCreateXMLData(NULL, rdir));
151 }
152
153 // screen and set the signing time
154 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
155 if (state.mSigningTime == CFDateRef(kCFNull)) {
156 signingTime = 0; // no time at all
157 } else if (!state.mSigningTime) {
158 signingTime = now; // default
159 } else {
160 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
161 if (time > now) // not allowed to post-date a signature
162 MacOSError::throwMe(errSecCSBadDictionaryFormat);
163 signingTime = time;
164 }
165
166 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize();
167 }
168
169
170 //
171 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
172 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
173 // treat them as architectural binaries containing (only) one architecture - that
174 // interpretation is courtesy of the Universal/MachO support classes.
175 //
176 void SecCodeSigner::Signer::signMachO(Universal *fat)
177 {
178 // Mach-O executable at the core - perform multi-architecture signing
179 auto_ptr<ArchEditor> editor(state.mDetached
180 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
181 : new MachOEditor(rep->writer(), *fat, rep->mainExecutablePath()));
182 assert(editor->count() > 0);
183 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
184 populate(*editor);
185
186 // pass 1: prepare signature blobs and calculate sizes
187 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
188 MachOEditor::Arch &arch = *it->second;
189 arch.source.reset(fat->architecture(it->first));
190 arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture));
191 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
192 populate(arch);
193 populate(arch.cdbuilder, arch, arch.ireqs,
194 arch.source->offset(), arch.source->signingExtent());
195
196 // add identification blob (made from this architecture) only if we're making a detached signature
197 if (state.mDetached) {
198 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
199 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
200 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
201 }
202
203 // prepare SuperBlob size estimate
204 size_t cdSize = arch.cdbuilder.size();
205 arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
206 }
207
208 editor->allocate();
209
210 // pass 2: Finish and generate signatures, and write them
211 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
212 MachOEditor::Arch &arch = *it->second;
213 editor->reset(arch);
214
215 // finish CodeDirectory (off new binary) and sign it
216 CodeDirectory *cd = arch.cdbuilder.build();
217 CFRef<CFDataRef> signature = signCodeDirectory(cd);
218
219 // complete the SuperBlob
220 arch.add(cdCodeDirectorySlot, cd); // takes ownership
221 arch.add(cdSignatureSlot, BlobWrapper::alloc(
222 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
223 if (!state.mDryRun) {
224 EmbeddedSignatureBlob *blob = arch.make();
225 editor->write(arch, blob); // takes ownership of blob
226 }
227 }
228
229 // done: write edit copy back over the original
230 if (!state.mDryRun)
231 editor->commit();
232 }
233
234
235 //
236 // Sign a binary that has no notion of architecture.
237 // That currently means anything that isn't Mach-O format.
238 //
239 void SecCodeSigner::Signer::signArchitectureAgnostic()
240 {
241 // non-Mach-O executable - single-instance signing
242 RefPointer<DiskRep::Writer> writer = state.mDetached ?
243 (new DetachedBlobWriter(*this)) : rep->writer();
244 CodeDirectory::Builder builder;
245 InternalRequirements ireqs;
246 ireqs(state.mRequirements, rep->defaultRequirements(NULL));
247 populate(*writer);
248 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
249
250 // add identification blob (made from this architecture) only if we're making a detached signature
251 if (state.mDetached) {
252 CFRef<CFDataRef> identification = rep->identification();
253 writer->component(cdIdentificationSlot, identification);
254 }
255
256 CodeDirectory *cd = builder.build();
257 CFRef<CFDataRef> signature = signCodeDirectory(cd);
258 if (!state.mDryRun) {
259 writer->codeDirectory(cd);
260 writer->signature(signature);
261 writer->flush();
262 }
263 ::free(cd);
264 }
265
266
267 //
268 // Global populate - send components to destination buffers ONCE
269 //
270 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
271 {
272 if (resourceDirectory)
273 writer.component(cdResourceDirSlot, resourceDirectory);
274 }
275
276
277 //
278 // Per-architecture populate - send components to per-architecture buffers
279 // and populate the CodeDirectory for an architecture. In architecture-agnostic
280 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
281 // for the purposes of this call.
282 //
283 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
284 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
285 {
286 // fill the CodeDirectory
287 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
288 builder.flags(cdFlags);
289 builder.identifier(identifier);
290
291 if (CFDataRef data = rep->component(cdInfoSlot))
292 builder.special(cdInfoSlot, data);
293 if (ireqs) {
294 CFRef<CFDataRef> data = makeCFData(*ireqs);
295 writer.component(cdRequirementsSlot, data);
296 builder.special(cdRequirementsSlot, data);
297 }
298 if (resourceDirectory)
299 builder.special(cdResourceDirSlot, resourceDirectory);
300 #if NOT_YET
301 if (state.mApplicationData)
302 builder.special(cdApplicationSlot, state.mApplicationData);
303 #endif
304 if (state.mEntitlementData) {
305 writer.component(cdEntitlementSlot, state.mEntitlementData);
306 builder.special(cdEntitlementSlot, state.mEntitlementData);
307 }
308
309 writer.addDiscretionary(builder);
310 }
311
312
313 //
314 // Generate the CMS signature for a (finished) CodeDirectory.
315 //
316 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
317 {
318 assert(state.mSigner);
319
320 // a null signer generates a null signature blob
321 if (state.mSigner == SecIdentityRef(kCFNull))
322 return CFDataCreate(NULL, NULL, 0);
323
324 // generate CMS signature
325 CFRef<CMSEncoderRef> cms;
326 MacOSError::check(CMSEncoderCreate(&cms.aref()));
327 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
328 CMSEncoderAddSigners(cms, state.mSigner);
329 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
330
331 if (signingTime) {
332 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
333 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
334 }
335
336 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
337 CFDataRef signature;
338 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
339 return signature;
340 }
341
342
343 //
344 // Parse a text of the form
345 // flag,...,flag
346 // where each flag is the canonical name of a signable CodeDirectory flag.
347 // No abbreviations are allowed, and internally set flags are not accepted.
348 //
349 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
350 {
351 uint32_t flags = 0;
352 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
353 string word = (comma == string::npos) ? text : text.substr(0, comma);
354 const SecCodeDirectoryFlagTable *item;
355 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
356 if (item->signable && word == item->name) {
357 flags |= item->value;
358 break;
359 }
360 if (!item->name) // not found
361 MacOSError::throwMe(errSecCSInvalidFlags);
362 if (comma == string::npos) // last word
363 break;
364 }
365 return flags;
366 }
367
368
369 } // end namespace CodeSigning
370 } // end namespace Security