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