]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/signer.cpp
d3f964c12c27cfaf5622c0259a361a048296bdd2
[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 "renum.h"
35 #include <security_utilities/unix++.h>
36 #include <security_utilities/unixchild.h>
37 #include <security_codesigning/cfmunge.h>
38
39 namespace Security {
40 namespace CodeSigning {
41
42
43 //
44 // Sign some code.
45 //
46 void SecCodeSigner::Signer::sign(SecCSFlags flags)
47 {
48 // set up access to the subject Code
49 rep = code->diskRep();
50
51 // get the Info.plist out of the rep for some creative defaulting
52 CFRef<CFDictionaryRef> infoDict;
53 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
54 infoDict.take(makeCFDictionaryFrom(infoData));
55
56 // work out the canonical identifier
57 identifier = state.mIdentifier;
58 if (identifier.empty()) {
59 identifier = rep->recommendedIdentifier();
60 if (identifier.find('.') == string::npos && !state.mIdentifierPrefix.empty())
61 identifier = state.mIdentifierPrefix + identifier;
62 secdebug("signer", "using default identifier=%s", identifier.c_str());
63 } else
64 secdebug("signer", "using explicit identifier=%s", identifier.c_str());
65
66 // work out the CodeDirectory flags word
67 if (state.mCdFlagsGiven) {
68 cdFlags = state.mCdFlags;
69 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
70 } else {
71 cdFlags = 0;
72 if (infoDict)
73 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags")))
74 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
75 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
76 secdebug("signer", "using numeric cdFlags=0x%x from Info.dict", cdFlags);
77 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
78 cdFlags = CodeDirectory::textFlags(cfString(CFStringRef(csflags)));
79 secdebug("signer", "using text cdFlags=0x%x from Info.dict", cdFlags);
80 } else
81 MacOSError::throwMe(errSecCSBadDictionaryFormat);
82 }
83 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
84 cdFlags |= kSecCodeSignatureAdhoc; // ... so allow that
85
86 // prepare the resource directory, if any
87 string rpath = rep->resourcesRootPath();
88 if (!rpath.empty()) {
89 CFCopyRef<CFDictionaryRef> resourceRules(state.mResourceRules);
90 if (!resourceRules)
91 resourceRules.take(rep->defaultResourceRules());
92 ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"));
93 CFRef<CFDictionaryRef> rdir = resources.build();
94 resourceDirectory.take(CFPropertyListCreateXMLData(NULL, rdir));
95 }
96
97 // screen and set the signing time
98 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
99 if (state.mSigningTime == CFDateRef(kCFNull)) {
100 signingTime = 0; // no time at all
101 } else if (!state.mSigningTime) {
102 signingTime = now; // default
103 } else {
104 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
105 if (time > now) // not allowed to post-date a signature
106 MacOSError::throwMe(errSecCSBadDictionaryFormat);
107 signingTime = time;
108 }
109
110 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize();
111
112 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
113 signMachO(fat);
114 } else {
115 signArchitectureAgnostic();
116 }
117 }
118
119
120 //
121 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
122 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
123 // treat them as architectural binaries containing (only) one architecture - that
124 // interpretation is courtesy of the Universal/MachO support classes.
125 //
126 void SecCodeSigner::Signer::signMachO(Universal *fat)
127 {
128 // Mach-O executable at the core - perform multi-architecture signing
129 auto_ptr<ArchEditor> editor(state.mDetached
130 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
131 : new MachOEditor(rep->writer(), *fat, rep->mainExecutablePath()));
132 assert(editor->count() > 0);
133 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
134 populate(*editor);
135
136 // pass 1: prepare signature blobs and calculate sizes
137 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
138 MachOEditor::Arch &arch = *it->second;
139 arch.source.reset(fat->architecture(it->first));
140 arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture));
141 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
142 populate(arch);
143 populate(arch.cdbuilder, arch, arch.ireqs,
144 arch.source->offset(), arch.source->signingExtent());
145 // prepare SuperBlob size estimate
146 size_t cdSize = arch.cdbuilder.size();
147 arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
148 }
149
150 editor->allocate();
151
152 // pass 2: Finish and generate signatures, and write them
153 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
154 MachOEditor::Arch &arch = *it->second;
155 editor->reset(arch);
156
157 // finish CodeDirectory (off new binary) and sign it
158 CodeDirectory *cd = arch.cdbuilder.build();
159 CFRef<CFDataRef> signature = signCodeDirectory(cd);
160
161 // complete the SuperBlob
162 arch.add(cdCodeDirectorySlot, cd); // takes ownership
163 arch.add(cdSignatureSlot, BlobWrapper::alloc(
164 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
165 if (!state.mDryRun) {
166 EmbeddedSignatureBlob *blob = arch.make();
167 editor->write(arch, blob); // takes ownership of blob
168 }
169 }
170
171 // done: write edit copy back over the original
172 if (!state.mDryRun)
173 editor->commit();
174 }
175
176
177 //
178 // Sign a binary that has no notion of architecture.
179 // That currently means anything that isn't Mach-O format.
180 //
181 void SecCodeSigner::Signer::signArchitectureAgnostic()
182 {
183 // non-Mach-O executable - single-instance signing
184 RefPointer<DiskRep::Writer> writer = state.mDetached ?
185 (new DetachedBlobWriter(*this)) : rep->writer();
186 CodeDirectory::Builder builder;
187 InternalRequirements ireqs;
188 ireqs(state.mRequirements, rep->defaultRequirements(NULL));
189 populate(*writer);
190 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
191 CodeDirectory *cd = builder.build();
192 CFRef<CFDataRef> signature = signCodeDirectory(cd);
193 if (!state.mDryRun) {
194 writer->codeDirectory(cd);
195 writer->signature(signature);
196 writer->flush();
197 }
198 ::free(cd);
199 }
200
201
202 //
203 // Global populate - send components to destination buffers ONCE
204 //
205 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
206 {
207 if (resourceDirectory)
208 writer.component(cdResourceDirSlot, resourceDirectory);
209 }
210
211
212 //
213 // Per-architecture populate - send components to per-architecture buffers
214 // and populate the CodeDirectory for an architecture. In architecture-agnostic
215 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
216 // for the purposes of this call.
217 //
218 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
219 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
220 {
221 // fill the CodeDirectory
222 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
223 builder.flags(cdFlags);
224 builder.identifier(identifier);
225
226 for (CodeDirectory::Slot slot = cdSlotMax; slot >= 1; --slot)
227 switch (slot) {
228 case cdRequirementsSlot:
229 if (ireqs) {
230 CFRef<CFDataRef> data = makeCFData(*ireqs);
231 writer.component(cdRequirementsSlot, data);
232 builder.special(slot, data);
233 }
234 break;
235 case cdResourceDirSlot:
236 if (resourceDirectory)
237 builder.special(slot, resourceDirectory);
238 break;
239 case cdApplicationSlot:
240 #if NOT_YET
241 if (state.mApplicationData)
242 builder.special(slot, state.mApplicationData);
243 #endif
244 break;
245 default:
246 if (CFDataRef data = rep->component(slot))
247 builder.special(slot, data);
248 break;
249 }
250 }
251
252
253 //
254 // Generate the CMS signature for a (finished) CodeDirectory.
255 //
256 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
257 {
258 assert(state.mSigner);
259
260 // a null signer generates a null signature blob
261 if (state.mSigner == SecIdentityRef(kCFNull))
262 return CFDataCreate(NULL, NULL, 0);
263
264 // generate CMS signature
265 CFRef<CMSEncoderRef> cms;
266 MacOSError::check(CMSEncoderCreate(&cms.aref()));
267 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
268 CMSEncoderAddSigners(cms, state.mSigner);
269 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
270
271 if (signingTime) {
272 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
273 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
274 }
275
276 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
277 CFDataRef signature;
278 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
279 return signature;
280 }
281
282
283 } // end namespace CodeSigning
284 } // end namespace Security