]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signer.cpp
Security-57337.50.23.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);
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 secdebug("signer", "using default identifier=%s", identifier.c_str());
153 } else
154 secdebug("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 secdebug("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 secdebug("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
186 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
187 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
188 secdebug("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 secdebug("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 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
267 if (state.mSigningTime == CFDateRef(kCFNull)) {
268 signingTime = 0; // no time at all
269 } else if (!state.mSigningTime) {
270 signingTime = now; // default
271 } else {
272 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
273 if (time > now) // not allowed to post-date a signature
274 MacOSError::throwMe(errSecCSBadDictionaryFormat);
275 signingTime = time;
276 }
277
278 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(*this);
279
280 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
281 rep->prepareForSigning(*this);
282
283 // apply some defaults after diskRep intervention
284 if (hashAlgorithms.empty()) { // default to SHA256 + SHA-1
285 hashAlgorithms.insert(kSecCodeSignatureHashSHA1);
286 hashAlgorithms.insert(kSecCodeSignatureHashSHA256);
287 }
288
289 // build the resource directory (once and for all, using the digests determined above)
290 if (!rpath.empty()) {
291 buildResources(rrpath, rpath, resourceRules);
292 }
293 }
294
295
296 //
297 // Collect the resource seal for a program.
298 // This includes both sealed resources and information about nested code.
299 //
300 void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
301 {
302 typedef ResourceBuilder::Rule Rule;
303
304 secdebug("codesign", "start building resource directory");
305 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
306
307 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
308 assert(rules);
309
310 if (this->state.mLimitedAsync == NULL) {
311 this->state.mLimitedAsync =
312 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
313 * turned off, because the paths for signing code are not ready for it yet. */
314 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
315 new LimitedAsync(false);
316 }
317
318 CFDictionaryRef files2 = NULL;
319 if (!(signingFlags() & kSecCSSignV1)) {
320 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
321 if (!rules2) {
322 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
323 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
324 rules2 = cfmake<CFDictionaryRef>("{+%O"
325 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
326 "}", rules);
327 }
328
329 Dispatch::Group group;
330 Dispatch::Group &groupRef = group; // (into block)
331
332 // build the modern (V2) resource seal
333 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
334 CFMutableDictionaryRef filesRef = files.get(); // (into block)
335 ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet());
336 ResourceBuilder &resources = resourceBuilder; // (into block)
337 rep->adjustResources(resources);
338
339 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
340 bool isSymlink = (ent->fts_info == FTS_SL);
341 const std::string path(ent->fts_path);
342 const std::string accpath(ent->fts_accpath);
343 this->state.mLimitedAsync->perform(groupRef, ^{
344 CFRef<CFMutableDictionaryRef> seal;
345 if (ruleFlags & ResourceBuilder::nested) {
346 seal.take(signNested(path, relpath));
347 } else if (isSymlink) {
348 char target[PATH_MAX];
349 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1);
350 if (len < 0)
351 UnixError::check(-1);
352 target[len] = '\0';
353 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
354 } else {
355 seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms()));
356 }
357 if (ruleFlags & ResourceBuilder::optional)
358 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
359 CFTypeRef hash;
360 StLock<Mutex> _(resourceLock);
361 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
362 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash);
363 else
364 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get());
365 code->reportProgress();
366 });
367 });
368 group.wait();
369 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
370 files2 = files;
371 CFDictionaryAddValue(result, CFSTR("files2"), files2);
372 }
373
374 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
375 if (!(signingFlags() & kSecCSSignNoV1)) {
376 // build the legacy (V1) resource seal
377 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
378 ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet());
379 ResourceBuilder &resources = resourceBuilder;
380 rep->adjustResources(resources); // DiskRep-specific adjustments
381 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
382 if (ent->fts_info == FTS_F) {
383 CFRef<CFDataRef> hash;
384 if (files2) // try to get the hash from a previously-made version
385 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
386 if (CFGetTypeID(seal) == CFDataGetTypeID())
387 hash = CFDataRef(seal);
388 else
389 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
390 }
391 if (!hash)
392 hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1));
393 if (ruleFlags == 0) { // default case - plain hash
394 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
395 secdebug("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
396 } else { // more complicated - use a sub-dictionary
397 cfadd(files, "{%s={hash=%O,optional=%B}}",
398 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
399 secdebug("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
400 }
401 }
402 });
403 CFDictionaryAddValue(result, CFSTR("files"), files.get());
404 }
405
406 resourceDirectory = result.get();
407 resourceDictData.take(makeCFData(resourceDirectory.get()));
408 }
409
410
411 //
412 // Deal with one piece of nested code
413 //
414 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
415 {
416 // sign nested code and collect nesting information
417 try {
418 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
419 if (signingFlags() & kSecCSSignNestedCode)
420 this->state.sign(code, signingFlags());
421 std::string dr = Dumper::dump(code->designatedRequirement());
422 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
423 Dumper::dump(code->designatedRequirement()).c_str(),
424 code->cdHash());
425 } catch (const CommonError &err) {
426 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
427 }
428 }
429
430
431 //
432 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
433 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
434 // treat them as architectural binaries containing (only) one architecture - that
435 // interpretation is courtesy of the Universal/MachO support classes.
436 //
437 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
438 {
439 // Mach-O executable at the core - perform multi-architecture signing
440 auto_ptr<ArchEditor> editor(state.mDetached
441 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
442 : new MachOEditor(rep->writer(), *fat, this->digestAlgorithms(), rep->mainExecutablePath()));
443 assert(editor->count() > 0);
444 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
445 populate(*editor);
446
447 // pass 1: prepare signature blobs and calculate sizes
448 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
449 MachOEditor::Arch &arch = *it->second;
450 arch.source.reset(fat->architecture(it->first));
451
452 // library validation is not compatible with i386
453 if (arch.architecture.cpuType() == CPU_TYPE_I386) {
454 if (cdFlags & kSecCodeSignatureLibraryValidation) {
455 MacOSError::throwMe(errSecCSBadLVArch);
456 }
457 }
458
459 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
460 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
461 populate(arch);
462 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
463 arch.eachDigest(^(CodeDirectory::Builder& builder) {
464 populate(builder, arch, arch.ireqs,
465 arch.source->offset(), arch.source->signingExtent(), unsigned(digestAlgorithms().size()-1));
466 });
467 }
468
469 // add identification blob (made from this architecture) only if we're making a detached signature
470 if (state.mDetached) {
471 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
472 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
473 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
474 }
475
476 // prepare SuperBlob size estimate
477 __block std::vector<size_t> sizes;
478 arch.eachDigest(^(CodeDirectory::Builder& builder){
479 sizes.push_back(builder.size(CodeDirectory::currentVersion));
480 });
481 arch.blobSize = arch.size(sizes, state.mCMSSize, 0);
482 }
483
484 editor->allocate();
485
486 // pass 2: Finish and generate signatures, and write them
487 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
488 MachOEditor::Arch &arch = *it->second;
489 editor->reset(arch);
490
491 // finish CodeDirectories (off new binary) and sign it
492 __block CodeDirectorySet cdSet;
493 arch.eachDigest(^(CodeDirectory::Builder &builder) {
494 CodeDirectory *cd = builder.build();
495 cdSet.add(cd);
496 });
497 CFRef<CFArrayRef> hashes = cdSet.hashBag();
498 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
499 CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
500 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
501
502 // complete the SuperBlob
503 cdSet.populate(&arch);
504 arch.add(cdSignatureSlot, BlobWrapper::alloc(
505 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
506 if (!state.mDryRun) {
507 EmbeddedSignatureBlob *blob = arch.make();
508 editor->write(arch, blob); // takes ownership of blob
509 }
510 }
511
512 // done: write edit copy back over the original
513 if (!state.mDryRun)
514 editor->commit();
515 }
516
517
518 //
519 // Sign a binary that has no notion of architecture.
520 // That currently means anything that isn't Mach-O format.
521 //
522 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
523 {
524 // non-Mach-O executable - single-instance signing
525 RefPointer<DiskRep::Writer> writer = state.mDetached ?
526 (new DetachedBlobWriter(*this)) : rep->writer();
527
528 CodeDirectorySet cdSet;
529 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
530 CodeDirectory::Builder builder(*type);
531 InternalRequirements ireqs;
532 ireqs(requirements, rep->defaultRequirements(NULL, *this), context);
533 populate(*writer);
534 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(), unsigned(digestAlgorithms().size()-1));
535
536 CodeDirectory *cd = builder.build();
537 if (!state.mDryRun)
538 cdSet.add(cd);
539 }
540
541 // add identification blob (made from this architecture) only if we're making a detached signature
542 if (state.mDetached) {
543 CFRef<CFDataRef> identification = rep->identification();
544 writer->component(cdIdentificationSlot, identification);
545 }
546
547 // write out all CodeDirectories
548 cdSet.populate(writer);
549
550 CFRef<CFArrayRef> hashes = cdSet.hashBag();
551 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
552 CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
553 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
554 writer->signature(signature);
555
556 // commit to storage
557 writer->flush();
558 }
559
560
561 //
562 // Global populate - send components to destination buffers ONCE
563 //
564 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
565 {
566 if (resourceDirectory && !state.mDryRun)
567 writer.component(cdResourceDirSlot, resourceDictData);
568 }
569
570
571 //
572 // Per-architecture populate - send components to per-architecture buffers
573 // and populate the CodeDirectory for an architecture. In architecture-agnostic
574 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
575 // for the purposes of this call.
576 //
577 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
578 InternalRequirements &ireqs, size_t offset, size_t length, unsigned alternateDigestCount)
579 {
580 // fill the CodeDirectory
581 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
582 builder.flags(cdFlags);
583 builder.identifier(identifier);
584 builder.teamID(teamID);
585 builder.platform(state.mPlatform);
586
587 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
588 builder.specialSlot(cdInfoSlot, data);
589 if (ireqs) {
590 CFRef<CFDataRef> data = makeCFData(*ireqs);
591 writer.component(cdRequirementsSlot, data);
592 builder.specialSlot(cdRequirementsSlot, data);
593 }
594 if (resourceDirectory)
595 builder.specialSlot(cdResourceDirSlot, resourceDictData);
596 if (entitlements) {
597 writer.component(cdEntitlementSlot, entitlements);
598 builder.specialSlot(cdEntitlementSlot, entitlements);
599 }
600 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
601 builder.specialSlot(cdRepSpecificSlot, repSpecific);
602
603 writer.addDiscretionary(builder);
604
605 #if 0 // rdar://problem/25720754
606 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
607 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
608 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
609 std::vector<Endian<uint32_t> > slotVector;
610 slotVector.push_back(cdCodeDirectorySlot); // mandatory
611 std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots();
612 filledSlots.insert(cdTopDirectorySlot); // will be added below
613 copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector));
614 for (unsigned n = 0; n < alternateDigestCount; n++)
615 slotVector.push_back(cdAlternateCodeDirectorySlots + n);
616 slotVector.push_back(cdSignatureSlot);
617 CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0]));
618 writer.component(cdTopDirectorySlot, cfSlotVector);
619 builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
620 }
621 #endif
622 }
623
624
625 #include <security_smime/tsaSupport.h>
626
627 //
628 // Generate the CMS signature for a (finished) CodeDirectory.
629 //
630 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag)
631 {
632 assert(state.mSigner);
633 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
634
635 // a null signer generates a null signature blob
636 if (state.mSigner == SecIdentityRef(kCFNull))
637 return CFDataCreate(NULL, NULL, 0);
638
639 // generate CMS signature
640 CFRef<CMSEncoderRef> cms;
641 MacOSError::check(CMSEncoderCreate(&cms.aref()));
642 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
643 CMSEncoderAddSigners(cms, state.mSigner);
644 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
645 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
646
647 if (signingTime) {
648 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
649 MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
650 }
651
652 if (hashBag) {
653 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
654 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashBag));
655 }
656
657 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
658
659 // Set up to call Timestamp server if requested
660 if (state.mWantTimeStamp)
661 {
662 CFRef<CFErrorRef> error = NULL;
663 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
664 if (error)
665 MacOSError::throwMe(errSecDataNotAvailable);
666
667 if (state.mNoTimeStampCerts || state.mTimestampService) {
668 if (state.mTimestampService)
669 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
670 if (state.mNoTimeStampCerts)
671 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
672 }
673
674 CmsMessageSetTSAContext(cms, defaultTSContext);
675 }
676
677 CFDataRef signature;
678 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
679
680 return signature;
681 }
682
683
684 //
685 // Our DiskRep::signingContext methods communicate with the signing subsystem
686 // in terms those callers can easily understand.
687 //
688 string SecCodeSigner::Signer::sdkPath(const std::string &path) const
689 {
690 assert(path[0] == '/'); // need absolute path here
691 if (state.mSDKRoot)
692 return cfString(state.mSDKRoot) + path;
693 else
694 return path;
695 }
696
697 bool SecCodeSigner::Signer::isAdhoc() const
698 {
699 return state.mSigner == SecIdentityRef(kCFNull);
700 }
701
702 SecCSFlags SecCodeSigner::Signer::signingFlags() const
703 {
704 return state.mOpFlags;
705 }
706
707
708 //
709 // Parse a text of the form
710 // flag,...,flag
711 // where each flag is the canonical name of a signable CodeDirectory flag.
712 // No abbreviations are allowed, and internally set flags are not accepted.
713 //
714 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
715 {
716 uint32_t flags = 0;
717 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
718 string word = (comma == string::npos) ? text : text.substr(0, comma);
719 const SecCodeDirectoryFlagTable *item;
720 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
721 if (item->signable && word == item->name) {
722 flags |= item->value;
723 break;
724 }
725 if (!item->name) // not found
726 MacOSError::throwMe(errSecCSInvalidFlags);
727 if (comma == string::npos) // last word
728 break;
729 }
730 return flags;
731 }
732
733
734 //
735 // Generate a unique string from our underlying DiskRep.
736 // We could get 90%+ of the uniquing benefit by just generating
737 // a random string here. Instead, we pick the (hex string encoding of)
738 // the source rep's unique identifier blob. For universal binaries,
739 // this is the canonical local architecture, which is a bit arbitrary.
740 // This provides us with a consistent unique string for all architectures
741 // of a fat binary, *and* (unlike a random string) is reproducible
742 // for identical inputs, even upon resigning.
743 //
744 std::string SecCodeSigner::Signer::uniqueName() const
745 {
746 CFRef<CFDataRef> identification = rep->identification();
747 const UInt8 *ident = CFDataGetBytePtr(identification);
748 const CFIndex length = CFDataGetLength(identification);
749 string result;
750 for (CFIndex n = 0; n < length; n++) {
751 char hex[3];
752 snprintf(hex, sizeof(hex), "%02x", ident[n]);
753 result += hex;
754 }
755 return result;
756 }
757
758
759 } // end namespace CodeSigning
760 } // end namespace Security