]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_codesigning/lib/signer.cpp
Security-57336.10.29.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signer.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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>
427c49bc 36#include "resources.h"
b1ab9ed8 37#include "machorep.h"
427c49bc
A
38#include "reqparser.h"
39#include "reqdumper.h"
b1ab9ed8
A
40#include "csutilities.h"
41#include <security_utilities/unix++.h>
42#include <security_utilities/unixchild.h>
43#include <security_utilities/cfmunge.h>
d87e1158
A
44#include <security_utilities/dispatch.h>
45#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
b1ab9ed8
A
46
47namespace Security {
48namespace CodeSigning {
49
50
51//
52// Sign some code.
53//
54void SecCodeSigner::Signer::sign(SecCSFlags flags)
55{
56 rep = code->diskRep()->base();
57 this->prepare(flags);
420ff9d9 58
b1ab9ed8 59 PreSigningContext context(*this);
420ff9d9
A
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
b1ab9ed8
A
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//
96void 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//
120void SecCodeSigner::Signer::prepare(SecCSFlags flags)
121{
80e23899
A
122 // make sure the rep passes strict validation
123 if (strict)
60c433a9 124 rep->strictValidate(NULL, MacOSErrorSet());
d8f41ccd
A
125
126 // initialize progress/cancellation state
127 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
80e23899 128
b1ab9ed8
A
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));
427c49bc
A
133
134 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
b1ab9ed8
A
135
136 // work out the canonical identifier
137 identifier = state.mIdentifier;
427c49bc
A
138 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
139 identifier = code->identifier();
b1ab9ed8
A
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());
427c49bc 149
420ff9d9
A
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 }
5c19dc3a 156
427c49bc
A
157 entitlements = state.mEntitlementData;
158 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
159 entitlements = code->component(cdEntitlementSlot);
b1ab9ed8
A
160
161 // work out the CodeDirectory flags word
427c49bc
A
162 bool haveCdFlags = false;
163 if (!haveCdFlags && state.mCdFlagsGiven) {
b1ab9ed8
A
164 cdFlags = state.mCdFlags;
165 secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
427c49bc
A
166 haveCdFlags = true;
167 }
168 if (!haveCdFlags) {
b1ab9ed8
A
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);
427c49bc 180 haveCdFlags = true;
b1ab9ed8
A
181 }
182 }
427c49bc
A
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;
b1ab9ed8
A
190 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
191 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
427c49bc
A
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();
b1ab9ed8
A
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
427c49bc
A
218 // inherited rules come next (overriding embedded ones!)
219 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
220 if (CFDictionaryRef oldRules = code->resourceDictionary(false))
221 resourceRules = oldRules;
222
b1ab9ed8
A
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 }
427c49bc
A
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 }
b1ab9ed8
A
240
241 // finally, ask the DiskRep for its default
242 if (!resourceRules)
243 resourceRules.take(rep->defaultResourceRules(state));
80e23899
A
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
b1ab9ed8 251 // build the resource directory
80e23899 252 buildResources(root, rpath, resourceRules);
b1ab9ed8
A
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
427c49bc
A
275//
276// Collect the resource seal for a program.
277// This includes both sealed resources and information about nested code.
278//
80e23899 279void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
427c49bc
A
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
d87e1158
A
289 if (this->state.mLimitedAsync == NULL) {
290 this->state.mLimitedAsync =
60c433a9
A
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);
d87e1158
A
295 }
296
427c49bc
A
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"
d8f41ccd 304 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
427c49bc
A
305 "}", rules);
306 }
d87e1158
A
307
308 Dispatch::Group group;
309 Dispatch::Group &groupRef = group; // (into block)
310
427c49bc
A
311 // build the modern (V2) resource seal
312 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
d87e1158 313 CFMutableDictionaryRef filesRef = files.get(); // (into block)
80e23899 314 ResourceBuilder resourceBuilder(root, relBase, rules2, digestAlgorithm(), strict, MacOSErrorSet());
427c49bc
A
315 ResourceBuilder &resources = resourceBuilder; // (into block)
316 rep->adjustResources(resources);
d87e1158
A
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 });
427c49bc 347 });
d87e1158 348 group.wait();
427c49bc
A
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();
80e23899 358 ResourceBuilder resourceBuilder(root, relBase, rules, digestAlgorithm(), strict, MacOSErrorSet());
427c49bc
A
359 ResourceBuilder &resources = resourceBuilder;
360 rep->adjustResources(resources); // DiskRep-specific adjustments
d87e1158 361 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
427c49bc
A
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
d87e1158
A
374 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
375 secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
427c49bc
A
376 } else { // more complicated - use a sub-dictionary
377 cfadd(files, "{%s={hash=%O,optional=%B}}",
d87e1158
A
378 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
379 secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
427c49bc
A
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//
d87e1158 394CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
427c49bc
A
395{
396 // sign nested code and collect nesting information
397 try {
d87e1158 398 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
427c49bc
A
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
b1ab9ed8
A
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//
417void 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));
d8f41ccd
A
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
427c49bc 439 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, state), context);
b1ab9ed8
A
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
420ff9d9 453 size_t cdSize = arch.cdbuilder.size(CodeDirectory::currentVersion);
b1ab9ed8
A
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//
488void 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;
427c49bc 495 ireqs(requirements, rep->defaultRequirements(NULL, state), context);
b1ab9ed8
A
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);
d8f41ccd 503 }
b1ab9ed8
A
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//
519void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
520{
427c49bc
A
521 if (resourceDirectory && !state.mDryRun)
522 writer.component(cdResourceDirSlot, resourceDictData);
b1ab9ed8
A
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//
532void 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);
420ff9d9 539 builder.teamID(teamID);
5c19dc3a 540 builder.platform(state.mPlatform);
420ff9d9 541
b1ab9ed8
A
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)
427c49bc 550 builder.specialSlot(cdResourceDirSlot, resourceDictData);
b1ab9ed8
A
551#if NOT_YET
552 if (state.mApplicationData)
553 builder.specialSlot(cdApplicationSlot, state.mApplicationData);
554#endif
427c49bc
A
555 if (entitlements) {
556 writer.component(cdEntitlementSlot, entitlements);
557 builder.specialSlot(cdEntitlementSlot, entitlements);
b1ab9ed8
A
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//
568CFDataRef 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);
5c19dc3a 582 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
b1ab9ed8
A
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//
624uint32_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//
654std::string SecCodeSigner::Signer::uniqueName() const
655{
656 CFRef<CFDataRef> identification = rep->identification();
657 const UInt8 *ident = CFDataGetBytePtr(identification);
427c49bc 658 const CFIndex length = CFDataGetLength(identification);
b1ab9ed8 659 string result;
427c49bc 660 for (CFIndex n = 0; n < length; n++) {
b1ab9ed8
A
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