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