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