]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_codesigning/lib/signer.cpp
68b02b39f9054decba7eeece4eaced798a78f6f3
[apple/security.git] / Security / libsecurity_codesigning / lib / signer.cpp
1 /*
2 * Copyright (c) 2006-2014 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 "resources.h"
37 #include "machorep.h"
38 #include "reqparser.h"
39 #include "reqdumper.h"
40 #include "csutilities.h"
41 #include <security_utilities/unix++.h>
42 #include <security_utilities/unixchild.h>
43 #include <security_utilities/cfmunge.h>
44 #include <security_utilities/dispatch.h>
45 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
46
47 namespace Security {
48 namespace CodeSigning {
49
50
51 //
52 // Sign some code.
53 //
54 void SecCodeSigner::Signer::sign(SecCSFlags flags)
55 {
56 rep = code->diskRep()->base();
57 this->prepare(flags);
58
59 PreSigningContext context(*this);
60
61 /* If an explicit teamID was passed in it must be
62 the same as what came from the cert */
63 std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
64
65 if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
66 /* If preserving the team identifier, teamID is set previously when the
67 code object is still available */
68 if (!teamIDFromCert.empty() && teamID != teamIDFromCert)
69 MacOSError::throwMe(errSecCSInvalidFlags);
70 } else {
71 if (teamIDFromCert.empty()) {
72 /* state.mTeamID is an explicitly passed teamID */
73 teamID = state.mTeamID;
74 } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) {
75 /* If there was no explicit team ID set, or the explicit team ID matches
76 what is in the cert, use the team ID from the certificate */
77 teamID = teamIDFromCert;
78 } else {
79 /* The caller passed in an explicit team ID that does not match what is
80 in the signing cert, which is an invalid usage */
81 MacOSError::throwMe(errSecCSInvalidFlags);
82 }
83 }
84
85 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
86 signMachO(fat, context);
87 } else {
88 signArchitectureAgnostic(context);
89 }
90 }
91
92
93 //
94 // Remove any existing code signature from code
95 //
96 void SecCodeSigner::Signer::remove(SecCSFlags flags)
97 {
98 // can't remove a detached signature
99 if (state.mDetached)
100 MacOSError::throwMe(errSecCSNotSupported);
101
102 rep = code->diskRep();
103 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
104 // architecture-sensitive removal
105 MachOEditor editor(rep->writer(), *fat, kSecCodeSignatureNoHash, rep->mainExecutablePath());
106 editor.allocate(); // create copy
107 editor.commit(); // commit change
108 } else {
109 // architecture-agnostic removal
110 RefPointer<DiskRep::Writer> writer = rep->writer();
111 writer->remove();
112 writer->flush();
113 }
114 }
115
116
117 //
118 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
119 //
120 void SecCodeSigner::Signer::prepare(SecCSFlags flags)
121 {
122 // make sure the rep passes strict validation
123 if (strict)
124 rep->strictValidate(MacOSErrorSet());
125
126 // initialize progress/cancellation state
127 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
128
129 // get the Info.plist out of the rep for some creative defaulting
130 CFRef<CFDictionaryRef> infoDict;
131 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
132 infoDict.take(makeCFDictionaryFrom(infoData));
133
134 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
135
136 // work out the canonical identifier
137 identifier = state.mIdentifier;
138 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
139 identifier = code->identifier();
140 if (identifier.empty()) {
141 identifier = rep->recommendedIdentifier(state);
142 if (identifier.find('.') == string::npos)
143 identifier = state.mIdentifierPrefix + identifier;
144 if (identifier.find('.') == string::npos && state.isAdhoc())
145 identifier = identifier + "-" + uniqueName();
146 secdebug("signer", "using default identifier=%s", identifier.c_str());
147 } else
148 secdebug("signer", "using explicit identifier=%s", identifier.c_str());
149
150 teamID = state.mTeamID;
151 if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
152 const char *c_id = code->teamID();
153 if (c_id)
154 teamID = c_id;
155 }
156
157 entitlements = state.mEntitlementData;
158 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
159 entitlements = code->component(cdEntitlementSlot);
160
161 // work out the CodeDirectory flags word
162 bool haveCdFlags = false;
163 if (!haveCdFlags && state.mCdFlagsGiven) {
164 cdFlags = state.mCdFlags;
165 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
166 haveCdFlags = true;
167 }
168 if (!haveCdFlags) {
169 cdFlags = 0;
170 if (infoDict)
171 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
172 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
173 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
174 secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
175 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
176 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
177 secdebug("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
178 } else
179 MacOSError::throwMe(errSecCSBadDictionaryFormat);
180 haveCdFlags = true;
181 }
182 }
183 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
184 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
185 secdebug("signer", "using inherited cdFlags=0x%x", cdFlags);
186 haveCdFlags = true;
187 }
188 if (!haveCdFlags)
189 cdFlags = 0;
190 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
191 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
192
193 // prepare the internal requirements input
194 if (state.mRequirements) {
195 if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form
196 const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>());
197 if (!rp->validateBlob())
198 MacOSError::throwMe(errSecCSReqInvalid);
199 requirements = rp->clone();
200 } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form
201 CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>());
202 // substitute $ variable tokens
203 CFRange range = { 0, CFStringGetLength(reqText) };
204 CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0);
205 requirements = parseRequirements(cfString(reqText));
206 } else
207 MacOSError::throwMe(errSecCSInvalidObjectRef);
208 } else if (inherit & kSecCodeSignerPreserveRequirements)
209 if (const Requirements *rp = code->internalRequirements())
210 requirements = rp->clone();
211
212 // prepare the resource directory, if any
213 string rpath = rep->resourcesRootPath();
214 if (!rpath.empty()) {
215 // explicitly given resource rules always win
216 CFCopyRef<CFDictionaryRef> resourceRules = state.mResourceRules;
217
218 // inherited rules come next (overriding embedded ones!)
219 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
220 if (CFDictionaryRef oldRules = code->resourceDictionary(false))
221 resourceRules = oldRules;
222
223 // embedded resource rules come next
224 if (!resourceRules && infoDict)
225 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) {
226 if (CFGetTypeID(spec) == CFStringGetTypeID())
227 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
228 if (CFDictionaryRef dict = makeCFDictionaryFrom(data))
229 resourceRules.take(dict);
230 if (!resourceRules) // embedded rules present but unacceptable
231 MacOSError::throwMe(errSecCSResourceRulesInvalid);
232 }
233
234 // if we got one from anywhere (but the defaults), sanity-check it
235 if (resourceRules) {
236 CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules"));
237 if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID())
238 MacOSError::throwMe(errSecCSResourceRulesInvalid);
239 }
240
241 // finally, ask the DiskRep for its default
242 if (!resourceRules)
243 resourceRules.take(rep->defaultResourceRules(state));
244
245 // resource root can optionally be the canonical bundle path,
246 // but sealed resource paths are always relative to rpath
247 string root = rpath;
248 if (state.signingFlags() & kSecCSSignBundleRoot)
249 root = cfStringRelease(rep->copyCanonicalPath());
250
251 // build the resource directory
252 buildResources(root, rpath, resourceRules);
253 }
254
255 // screen and set the signing time
256 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
257 if (state.mSigningTime == CFDateRef(kCFNull)) {
258 signingTime = 0; // no time at all
259 } else if (!state.mSigningTime) {
260 signingTime = now; // default
261 } else {
262 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
263 if (time > now) // not allowed to post-date a signature
264 MacOSError::throwMe(errSecCSBadDictionaryFormat);
265 signingTime = time;
266 }
267
268 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(state);
269
270 // Timestamping setup
271 CFRef<SecIdentityRef> mTSAuth; // identity for client-side authentication to the Timestamp server
272 }
273
274
275 //
276 // Collect the resource seal for a program.
277 // This includes both sealed resources and information about nested code.
278 //
279 void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
280 {
281 typedef ResourceBuilder::Rule Rule;
282
283 secdebug("codesign", "start building resource directory");
284 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
285
286 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
287 assert(rules);
288
289 if (this->state.mLimitedAsync == NULL) {
290 this->state.mLimitedAsync =
291 new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
292 }
293
294 CFDictionaryRef files2 = NULL;
295 if (!(state.signingFlags() & kSecCSSignV1)) {
296 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
297 if (!rules2) {
298 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
299 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
300 rules2 = cfmake<CFDictionaryRef>("{+%O"
301 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
302 "}", rules);
303 }
304
305 Dispatch::Group group;
306 Dispatch::Group &groupRef = group; // (into block)
307
308 // build the modern (V2) resource seal
309 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
310 CFMutableDictionaryRef filesRef = files.get(); // (into block)
311 ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet());
312 ResourceBuilder &resources = resourceBuilder; // (into block)
313 rep->adjustResources(resources);
314
315 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
316 bool isSymlink = (ent->fts_info == FTS_SL);
317 const std::string path(ent->fts_path);
318 const std::string accpath(ent->fts_accpath);
319 this->state.mLimitedAsync->perform(groupRef, ^{
320 CFRef<CFMutableDictionaryRef> seal;
321 if (ruleFlags & ResourceBuilder::nested) {
322 seal.take(signNested(path, relpath));
323 } else if (isSymlink) {
324 char target[PATH_MAX];
325 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1);
326 if (len < 0)
327 UnixError::check(-1);
328 target[len] = '\0';
329 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
330 } else {
331 seal.take(cfmake<CFMutableDictionaryRef>("{hash=%O}",
332 CFRef<CFDataRef>(resources.hashFile(accpath.c_str())).get()));
333 }
334 if (ruleFlags & ResourceBuilder::optional)
335 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
336 CFTypeRef hash;
337 StLock<Mutex> _(resourceLock);
338 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
339 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash);
340 else
341 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get());
342 code->reportProgress();
343 });
344 });
345 group.wait();
346 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
347 files2 = files;
348 CFDictionaryAddValue(result, CFSTR("files2"), files2);
349 }
350
351 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
352 if (!(state.signingFlags() & kSecCSSignNoV1)) {
353 // build the legacy (V1) resource seal
354 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
355 ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet());
356 ResourceBuilder &resources = resourceBuilder;
357 rep->adjustResources(resources); // DiskRep-specific adjustments
358 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
359 if (ent->fts_info == FTS_F) {
360 CFRef<CFDataRef> hash;
361 if (files2) // try to get the hash from a previously-made version
362 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
363 if (CFGetTypeID(seal) == CFDataGetTypeID())
364 hash = CFDataRef(seal);
365 else
366 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
367 }
368 if (!hash)
369 hash.take(resources.hashFile(ent->fts_accpath));
370 if (ruleFlags == 0) { // default case - plain hash
371 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
372 secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
373 } else { // more complicated - use a sub-dictionary
374 cfadd(files, "{%s={hash=%O,optional=%B}}",
375 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
376 secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
377 }
378 }
379 });
380 CFDictionaryAddValue(result, CFSTR("files"), files.get());
381 }
382
383 resourceDirectory = result.get();
384 resourceDictData.take(makeCFData(resourceDirectory.get()));
385 }
386
387
388 //
389 // Deal with one piece of nested code
390 //
391 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
392 {
393 // sign nested code and collect nesting information
394 try {
395 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
396 if (state.signingFlags() & kSecCSSignNestedCode)
397 this->state.sign(code, state.signingFlags());
398 std::string dr = Dumper::dump(code->designatedRequirement());
399 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
400 Dumper::dump(code->designatedRequirement()).c_str(),
401 code->cdHash());
402 } catch (const CommonError &err) {
403 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
404 }
405 }
406
407
408 //
409 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
410 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
411 // treat them as architectural binaries containing (only) one architecture - that
412 // interpretation is courtesy of the Universal/MachO support classes.
413 //
414 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
415 {
416 // Mach-O executable at the core - perform multi-architecture signing
417 auto_ptr<ArchEditor> editor(state.mDetached
418 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
419 : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath()));
420 assert(editor->count() > 0);
421 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
422 populate(*editor);
423
424 // pass 1: prepare signature blobs and calculate sizes
425 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
426 MachOEditor::Arch &arch = *it->second;
427 arch.source.reset(fat->architecture(it->first));
428
429 // library validation is not compatible with i386
430 if (arch.architecture.cpuType() == CPU_TYPE_I386) {
431 if (cdFlags & kSecCodeSignatureLibraryValidation) {
432 MacOSError::throwMe(errSecCSBadLVArch);
433 }
434 }
435
436 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context);
437 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
438 populate(arch);
439 populate(arch.cdbuilder, arch, arch.ireqs,
440 arch.source->offset(), arch.source->signingExtent());
441
442 // add identification blob (made from this architecture) only if we're making a detached signature
443 if (state.mDetached) {
444 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
445 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
446 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
447 }
448
449 // prepare SuperBlob size estimate
450 size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion);
451 arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
452 }
453
454 editor->allocate();
455
456 // pass 2: Finish and generate signatures, and write them
457 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
458 MachOEditor::Arch &arch = *it->second;
459 editor->reset(arch);
460
461 // finish CodeDirectory (off new binary) and sign it
462 CodeDirectory *cd = arch.cdbuilder.build();
463 CFRef<CFDataRef> signature = signCodeDirectory(cd);
464
465 // complete the SuperBlob
466 arch.add(cdCodeDirectorySlot, cd); // takes ownership
467 arch.add(cdSignatureSlot, BlobWrapper::alloc(
468 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
469 if (!state.mDryRun) {
470 EmbeddedSignatureBlob *blob = arch.make();
471 editor->write(arch, blob); // takes ownership of blob
472 }
473 }
474
475 // done: write edit copy back over the original
476 if (!state.mDryRun)
477 editor->commit();
478 }
479
480
481 //
482 // Sign a binary that has no notion of architecture.
483 // That currently means anything that isn't Mach-O format.
484 //
485 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
486 {
487 // non-Mach-O executable - single-instance signing
488 RefPointer<DiskRep::Writer> writer = state.mDetached ?
489 (new DetachedBlobWriter(*this)) : rep->writer();
490 CodeDirectory::Builder builder(state.mDigestAlgorithm);
491 InternalRequirements ireqs;
492 ireqs(requirements, rep->defaultRequirements(NULL, state), context);
493 populate(*writer);
494 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
495
496 // add identification blob (made from this architecture) only if we're making a detached signature
497 if (state.mDetached) {
498 CFRef<CFDataRef> identification = rep->identification();
499 writer->component(cdIdentificationSlot, identification);
500 }
501
502 CodeDirectory *cd = builder.build();
503 CFRef<CFDataRef> signature = signCodeDirectory(cd);
504 if (!state.mDryRun) {
505 writer->codeDirectory(cd);
506 writer->signature(signature);
507 writer->flush();
508 }
509 ::free(cd);
510 }
511
512
513 //
514 // Global populate - send components to destination buffers ONCE
515 //
516 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
517 {
518 if (resourceDirectory && !state.mDryRun)
519 writer.component(cdResourceDirSlot, resourceDictData);
520 }
521
522
523 //
524 // Per-architecture populate - send components to per-architecture buffers
525 // and populate the CodeDirectory for an architecture. In architecture-agnostic
526 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
527 // for the purposes of this call.
528 //
529 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
530 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
531 {
532 // fill the CodeDirectory
533 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
534 builder.flags(cdFlags);
535 builder.identifier(identifier);
536 builder.teamID(teamID);
537
538 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
539 builder.specialSlot(cdInfoSlot, data);
540 if (ireqs) {
541 CFRef<CFDataRef> data = makeCFData(*ireqs);
542 writer.component(cdRequirementsSlot, data);
543 builder.specialSlot(cdRequirementsSlot, data);
544 }
545 if (resourceDirectory)
546 builder.specialSlot(cdResourceDirSlot, resourceDictData);
547 #if NOT_YET
548 if (state.mApplicationData)
549 builder.specialSlot(cdApplicationSlot, state.mApplicationData);
550 #endif
551 if (entitlements) {
552 writer.component(cdEntitlementSlot, entitlements);
553 builder.specialSlot(cdEntitlementSlot, entitlements);
554 }
555
556 writer.addDiscretionary(builder);
557 }
558
559 #include <security_smime/tsaSupport.h>
560
561 //
562 // Generate the CMS signature for a (finished) CodeDirectory.
563 //
564 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
565 {
566 assert(state.mSigner);
567 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
568
569 // a null signer generates a null signature blob
570 if (state.mSigner == SecIdentityRef(kCFNull))
571 return CFDataCreate(NULL, NULL, 0);
572
573 // generate CMS signature
574 CFRef<CMSEncoderRef> cms;
575 MacOSError::check(CMSEncoderCreate(&cms.aref()));
576 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
577 CMSEncoderAddSigners(cms, state.mSigner);
578 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
579
580 if (signingTime) {
581 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
582 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
583 }
584
585 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
586
587 // Set up to call Timestamp server if requested
588
589 if (state.mWantTimeStamp)
590 {
591 CFRef<CFErrorRef> error = NULL;
592 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
593 if (error)
594 MacOSError::throwMe(errSecDataNotAvailable);
595
596 if (state.mNoTimeStampCerts || state.mTimestampService) {
597 if (state.mTimestampService)
598 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
599 if (state.mNoTimeStampCerts)
600 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
601 }
602
603 CmsMessageSetTSAContext(cms, defaultTSContext);
604 }
605
606 CFDataRef signature;
607 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
608
609 return signature;
610 }
611
612
613 //
614 // Parse a text of the form
615 // flag,...,flag
616 // where each flag is the canonical name of a signable CodeDirectory flag.
617 // No abbreviations are allowed, and internally set flags are not accepted.
618 //
619 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
620 {
621 uint32_t flags = 0;
622 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
623 string word = (comma == string::npos) ? text : text.substr(0, comma);
624 const SecCodeDirectoryFlagTable *item;
625 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
626 if (item->signable && word == item->name) {
627 flags |= item->value;
628 break;
629 }
630 if (!item->name) // not found
631 MacOSError::throwMe(errSecCSInvalidFlags);
632 if (comma == string::npos) // last word
633 break;
634 }
635 return flags;
636 }
637
638
639 //
640 // Generate a unique string from our underlying DiskRep.
641 // We could get 90%+ of the uniquing benefit by just generating
642 // a random string here. Instead, we pick the (hex string encoding of)
643 // the source rep's unique identifier blob. For universal binaries,
644 // this is the canonical local architecture, which is a bit arbitrary.
645 // This provides us with a consistent unique string for all architectures
646 // of a fat binary, *and* (unlike a random string) is reproducible
647 // for identical inputs, even upon resigning.
648 //
649 std::string SecCodeSigner::Signer::uniqueName() const
650 {
651 CFRef<CFDataRef> identification = rep->identification();
652 const UInt8 *ident = CFDataGetBytePtr(identification);
653 const CFIndex length = CFDataGetLength(identification);
654 string result;
655 for (CFIndex n = 0; n < length; n++) {
656 char hex[3];
657 snprintf(hex, sizeof(hex), "%02x", ident[n]);
658 result += hex;
659 }
660 return result;
661 }
662
663
664 } // end namespace CodeSigning
665 } // end namespace Security