]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signer.cpp
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / 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(NULL, 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 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
292 * turned off, because the paths for signing code are not ready for it yet. */
293 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
294 new LimitedAsync(false);
295 }
296
297 CFDictionaryRef files2 = NULL;
298 if (!(state.signingFlags() & kSecCSSignV1)) {
299 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
300 if (!rules2) {
301 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
302 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
303 rules2 = cfmake<CFDictionaryRef>("{+%O"
304 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
305 "}", rules);
306 }
307
308 Dispatch::Group group;
309 Dispatch::Group &groupRef = group; // (into block)
310
311 // build the modern (V2) resource seal
312 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
313 CFMutableDictionaryRef filesRef = files.get(); // (into block)
314 ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet());
315 ResourceBuilder &resources = resourceBuilder; // (into block)
316 rep->adjustResources(resources);
317
318 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
319 bool isSymlink = (ent->fts_info == FTS_SL);
320 const std::string path(ent->fts_path);
321 const std::string accpath(ent->fts_accpath);
322 this->state.mLimitedAsync->perform(groupRef, ^{
323 CFRef<CFMutableDictionaryRef> seal;
324 if (ruleFlags & ResourceBuilder::nested) {
325 seal.take(signNested(path, relpath));
326 } else if (isSymlink) {
327 char target[PATH_MAX];
328 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1);
329 if (len < 0)
330 UnixError::check(-1);
331 target[len] = '\0';
332 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
333 } else {
334 seal.take(cfmake<CFMutableDictionaryRef>("{hash=%O}",
335 CFRef<CFDataRef>(resources.hashFile(accpath.c_str())).get()));
336 }
337 if (ruleFlags & ResourceBuilder::optional)
338 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
339 CFTypeRef hash;
340 StLock<Mutex> _(resourceLock);
341 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
342 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash);
343 else
344 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get());
345 code->reportProgress();
346 });
347 });
348 group.wait();
349 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
350 files2 = files;
351 CFDictionaryAddValue(result, CFSTR("files2"), files2);
352 }
353
354 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
355 if (!(state.signingFlags() & kSecCSSignNoV1)) {
356 // build the legacy (V1) resource seal
357 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
358 ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet());
359 ResourceBuilder &resources = resourceBuilder;
360 rep->adjustResources(resources); // DiskRep-specific adjustments
361 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
362 if (ent->fts_info == FTS_F) {
363 CFRef<CFDataRef> hash;
364 if (files2) // try to get the hash from a previously-made version
365 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
366 if (CFGetTypeID(seal) == CFDataGetTypeID())
367 hash = CFDataRef(seal);
368 else
369 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
370 }
371 if (!hash)
372 hash.take(resources.hashFile(ent->fts_accpath));
373 if (ruleFlags == 0) { // default case - plain hash
374 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
375 secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
376 } else { // more complicated - use a sub-dictionary
377 cfadd(files, "{%s={hash=%O,optional=%B}}",
378 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
379 secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
380 }
381 }
382 });
383 CFDictionaryAddValue(result, CFSTR("files"), files.get());
384 }
385
386 resourceDirectory = result.get();
387 resourceDictData.take(makeCFData(resourceDirectory.get()));
388 }
389
390
391 //
392 // Deal with one piece of nested code
393 //
394 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
395 {
396 // sign nested code and collect nesting information
397 try {
398 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
399 if (state.signingFlags() & kSecCSSignNestedCode)
400 this->state.sign(code, state.signingFlags());
401 std::string dr = Dumper::dump(code->designatedRequirement());
402 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
403 Dumper::dump(code->designatedRequirement()).c_str(),
404 code->cdHash());
405 } catch (const CommonError &err) {
406 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
407 }
408 }
409
410
411 //
412 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
413 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
414 // treat them as architectural binaries containing (only) one architecture - that
415 // interpretation is courtesy of the Universal/MachO support classes.
416 //
417 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
418 {
419 // Mach-O executable at the core - perform multi-architecture signing
420 auto_ptr<ArchEditor> editor(state.mDetached
421 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
422 : new MachOEditor(rep->writer(), *fat, this->digestAlgorithm(), rep->mainExecutablePath()));
423 assert(editor->count() > 0);
424 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
425 populate(*editor);
426
427 // pass 1: prepare signature blobs and calculate sizes
428 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
429 MachOEditor::Arch &arch = *it->second;
430 arch.source.reset(fat->architecture(it->first));
431
432 // library validation is not compatible with i386
433 if (arch.architecture.cpuType() == CPU_TYPE_I386) {
434 if (cdFlags & kSecCodeSignatureLibraryValidation) {
435 MacOSError::throwMe(errSecCSBadLVArch);
436 }
437 }
438
439 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context);
440 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
441 populate(arch);
442 populate(arch.cdbuilder, arch, arch.ireqs,
443 arch.source->offset(), arch.source->signingExtent());
444
445 // add identification blob (made from this architecture) only if we're making a detached signature
446 if (state.mDetached) {
447 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
448 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
449 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
450 }
451
452 // prepare SuperBlob size estimate
453 size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion);
454 arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
455 }
456
457 editor->allocate();
458
459 // pass 2: Finish and generate signatures, and write them
460 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
461 MachOEditor::Arch &arch = *it->second;
462 editor->reset(arch);
463
464 // finish CodeDirectory (off new binary) and sign it
465 CodeDirectory *cd = arch.cdbuilder.build();
466 CFRef<CFDataRef> signature = signCodeDirectory(cd);
467
468 // complete the SuperBlob
469 arch.add(cdCodeDirectorySlot, cd); // takes ownership
470 arch.add(cdSignatureSlot, BlobWrapper::alloc(
471 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
472 if (!state.mDryRun) {
473 EmbeddedSignatureBlob *blob = arch.make();
474 editor->write(arch, blob); // takes ownership of blob
475 }
476 }
477
478 // done: write edit copy back over the original
479 if (!state.mDryRun)
480 editor->commit();
481 }
482
483
484 //
485 // Sign a binary that has no notion of architecture.
486 // That currently means anything that isn't Mach-O format.
487 //
488 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
489 {
490 // non-Mach-O executable - single-instance signing
491 RefPointer<DiskRep::Writer> writer = state.mDetached ?
492 (new DetachedBlobWriter(*this)) : rep->writer();
493 CodeDirectory::Builder builder(state.mDigestAlgorithm);
494 InternalRequirements ireqs;
495 ireqs(requirements, rep->defaultRequirements(NULL, state), context);
496 populate(*writer);
497 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
498
499 // add identification blob (made from this architecture) only if we're making a detached signature
500 if (state.mDetached) {
501 CFRef<CFDataRef> identification = rep->identification();
502 writer->component(cdIdentificationSlot, identification);
503 }
504
505 CodeDirectory *cd = builder.build();
506 CFRef<CFDataRef> signature = signCodeDirectory(cd);
507 if (!state.mDryRun) {
508 writer->codeDirectory(cd);
509 writer->signature(signature);
510 writer->flush();
511 }
512 ::free(cd);
513 }
514
515
516 //
517 // Global populate - send components to destination buffers ONCE
518 //
519 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
520 {
521 if (resourceDirectory && !state.mDryRun)
522 writer.component(cdResourceDirSlot, resourceDictData);
523 }
524
525
526 //
527 // Per-architecture populate - send components to per-architecture buffers
528 // and populate the CodeDirectory for an architecture. In architecture-agnostic
529 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
530 // for the purposes of this call.
531 //
532 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
533 InternalRequirements &ireqs, size_t offset /* = 0 */, size_t length /* = 0 */)
534 {
535 // fill the CodeDirectory
536 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
537 builder.flags(cdFlags);
538 builder.identifier(identifier);
539 builder.teamID(teamID);
540 builder.platform(state.mPlatform);
541
542 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
543 builder.specialSlot(cdInfoSlot, data);
544 if (ireqs) {
545 CFRef<CFDataRef> data = makeCFData(*ireqs);
546 writer.component(cdRequirementsSlot, data);
547 builder.specialSlot(cdRequirementsSlot, data);
548 }
549 if (resourceDirectory)
550 builder.specialSlot(cdResourceDirSlot, resourceDictData);
551 #if NOT_YET
552 if (state.mApplicationData)
553 builder.specialSlot(cdApplicationSlot, state.mApplicationData);
554 #endif
555 if (entitlements) {
556 writer.component(cdEntitlementSlot, entitlements);
557 builder.specialSlot(cdEntitlementSlot, entitlements);
558 }
559
560 writer.addDiscretionary(builder);
561 }
562
563 #include <security_smime/tsaSupport.h>
564
565 //
566 // Generate the CMS signature for a (finished) CodeDirectory.
567 //
568 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
569 {
570 assert(state.mSigner);
571 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
572
573 // a null signer generates a null signature blob
574 if (state.mSigner == SecIdentityRef(kCFNull))
575 return CFDataCreate(NULL, NULL, 0);
576
577 // generate CMS signature
578 CFRef<CMSEncoderRef> cms;
579 MacOSError::check(CMSEncoderCreate(&cms.aref()));
580 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
581 CMSEncoderAddSigners(cms, state.mSigner);
582 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
583 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
584
585 if (signingTime) {
586 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
587 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
588 }
589
590 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
591
592 // Set up to call Timestamp server if requested
593
594 if (state.mWantTimeStamp)
595 {
596 CFRef<CFErrorRef> error = NULL;
597 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
598 if (error)
599 MacOSError::throwMe(errSecDataNotAvailable);
600
601 if (state.mNoTimeStampCerts || state.mTimestampService) {
602 if (state.mTimestampService)
603 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
604 if (state.mNoTimeStampCerts)
605 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
606 }
607
608 CmsMessageSetTSAContext(cms, defaultTSContext);
609 }
610
611 CFDataRef signature;
612 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
613
614 return signature;
615 }
616
617
618 //
619 // Parse a text of the form
620 // flag,...,flag
621 // where each flag is the canonical name of a signable CodeDirectory flag.
622 // No abbreviations are allowed, and internally set flags are not accepted.
623 //
624 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
625 {
626 uint32_t flags = 0;
627 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
628 string word = (comma == string::npos) ? text : text.substr(0, comma);
629 const SecCodeDirectoryFlagTable *item;
630 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
631 if (item->signable && word == item->name) {
632 flags |= item->value;
633 break;
634 }
635 if (!item->name) // not found
636 MacOSError::throwMe(errSecCSInvalidFlags);
637 if (comma == string::npos) // last word
638 break;
639 }
640 return flags;
641 }
642
643
644 //
645 // Generate a unique string from our underlying DiskRep.
646 // We could get 90%+ of the uniquing benefit by just generating
647 // a random string here. Instead, we pick the (hex string encoding of)
648 // the source rep's unique identifier blob. For universal binaries,
649 // this is the canonical local architecture, which is a bit arbitrary.
650 // This provides us with a consistent unique string for all architectures
651 // of a fat binary, *and* (unlike a random string) is reproducible
652 // for identical inputs, even upon resigning.
653 //
654 std::string SecCodeSigner::Signer::uniqueName() const
655 {
656 CFRef<CFDataRef> identification = rep->identification();
657 const UInt8 *ident = CFDataGetBytePtr(identification);
658 const CFIndex length = CFDataGetLength(identification);
659 string result;
660 for (CFIndex n = 0; n < length; n++) {
661 char hex[3];
662 snprintf(hex, sizeof(hex), "%02x", ident[n]);
663 result += hex;
664 }
665 return result;
666 }
667
668
669 } // end namespace CodeSigning
670 } // end namespace Security