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
45namespace Security {
46namespace CodeSigning {
47
48
49//
50// Sign some code.
51//
52void 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//
94void 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//
118void 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//
277void 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//
371CFMutableDictionaryRef 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//
394void 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//
465void 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//
496void 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//
509void 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//
544CFDataRef 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//
599uint32_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//
629std::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
646