]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signer.cpp
Security-58286.51.6.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 bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
464
465 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
466 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
467 populate(arch);
468 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
469 arch.eachDigest(^(CodeDirectory::Builder& builder) {
470 populate(builder, arch, arch.ireqs,
471 arch.source->offset(), arch.source->signingExtent(),
472 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
473 unsigned(digestAlgorithms().size()-1));
474 });
475 }
476
477 // add identification blob (made from this architecture) only if we're making a detached signature
478 if (state.mDetached) {
479 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
480 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
481 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
482 }
483
484 // prepare SuperBlob size estimate
485 __block std::vector<size_t> sizes;
486 arch.eachDigest(^(CodeDirectory::Builder& builder){
487 sizes.push_back(builder.size(CodeDirectory::currentVersion));
488 });
489 arch.blobSize = arch.size(sizes, state.mCMSSize, 0);
490 }
491
492 editor->allocate();
493
494 // pass 2: Finish and generate signatures, and write them
495 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
496 MachOEditor::Arch &arch = *it->second;
497 editor->reset(arch);
498
499 // finish CodeDirectories (off new binary) and sign it
500 __block CodeDirectorySet cdSet;
501 arch.eachDigest(^(CodeDirectory::Builder &builder) {
502 CodeDirectory *cd = builder.build();
503 cdSet.add(cd);
504 });
505 CFRef<CFArrayRef> hashes = cdSet.hashBag();
506 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
507 CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
508 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
509
510 // complete the SuperBlob
511 cdSet.populate(&arch);
512 arch.add(cdSignatureSlot, BlobWrapper::alloc(
513 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
514 if (!state.mDryRun) {
515 EmbeddedSignatureBlob *blob = arch.make();
516 editor->write(arch, blob); // takes ownership of blob
517 }
518 }
519
520 // done: write edit copy back over the original
521 if (!state.mDryRun) {
522 editor->commit();
523 }
524 }
525
526
527 //
528 // Sign a binary that has no notion of architecture.
529 // That currently means anything that isn't Mach-O format.
530 //
531 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
532 {
533 // non-Mach-O executable - single-instance signing
534 RefPointer<DiskRep::Writer> writer = state.mDetached ?
535 (new DetachedBlobWriter(*this)) : rep->writer();
536
537 CodeDirectorySet cdSet;
538
539 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
540 CodeDirectory::Builder builder(*type);
541 InternalRequirements ireqs;
542 ireqs(requirements, rep->defaultRequirements(NULL, *this), context);
543 populate(*writer);
544 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(),
545 false, // only machOs can currently be main binaries
546 rep->execSegBase(NULL), rep->execSegLimit(NULL),
547 unsigned(digestAlgorithms().size()-1));
548
549 CodeDirectory *cd = builder.build();
550 if (!state.mDryRun)
551 cdSet.add(cd);
552 }
553
554 // add identification blob (made from this architecture) only if we're making a detached signature
555 if (state.mDetached) {
556 CFRef<CFDataRef> identification = rep->identification();
557 writer->component(cdIdentificationSlot, identification);
558 }
559
560 // write out all CodeDirectories
561 if (!state.mDryRun)
562 cdSet.populate(writer);
563
564 CFRef<CFArrayRef> hashes = cdSet.hashBag();
565 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashes.get());
566 CFRef<CFDataRef> hashBag = makeCFData(hashDict.get());
567 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashBag);
568 writer->signature(signature);
569
570 // commit to storage
571 writer->flush();
572 }
573
574
575 //
576 // Global populate - send components to destination buffers ONCE
577 //
578 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
579 {
580 if (resourceDirectory && !state.mDryRun)
581 writer.component(cdResourceDirSlot, resourceDictData);
582 }
583
584
585 //
586 // Per-architecture populate - send components to per-architecture buffers
587 // and populate the CodeDirectory for an architecture. In architecture-agnostic
588 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
589 // for the purposes of this call.
590 //
591 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
592 InternalRequirements &ireqs, size_t offset, size_t length,
593 bool mainBinary, size_t execSegBase, size_t execSegLimit,
594 unsigned alternateDigestCount)
595 {
596 // fill the CodeDirectory
597 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
598 builder.flags(cdFlags);
599 builder.identifier(identifier);
600 builder.teamID(teamID);
601 builder.platform(state.mPlatform);
602 builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
603
604 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
605 builder.specialSlot(cdInfoSlot, data);
606 if (ireqs) {
607 CFRef<CFDataRef> data = makeCFData(*ireqs);
608 writer.component(cdRequirementsSlot, data);
609 builder.specialSlot(cdRequirementsSlot, data);
610 }
611 if (resourceDirectory)
612 builder.specialSlot(cdResourceDirSlot, resourceDictData);
613 if (entitlements) {
614 writer.component(cdEntitlementSlot, entitlements);
615 builder.specialSlot(cdEntitlementSlot, entitlements);
616
617 if (mainBinary) {
618 builder.addExecSegFlags(entitlementsToExecSegFlags(entitlements));
619 }
620 }
621 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
622 builder.specialSlot(cdRepSpecificSlot, repSpecific);
623
624 writer.addDiscretionary(builder);
625
626 #if 0 // rdar://problem/25720754
627 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
628 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
629 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
630 std::vector<Endian<uint32_t> > slotVector;
631 slotVector.push_back(cdCodeDirectorySlot); // mandatory
632 std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots();
633 filledSlots.insert(cdTopDirectorySlot); // will be added below
634 copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector));
635 for (unsigned n = 0; n < alternateDigestCount; n++)
636 slotVector.push_back(cdAlternateCodeDirectorySlots + n);
637 slotVector.push_back(cdSignatureSlot);
638 CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0]));
639 writer.component(cdTopDirectorySlot, cfSlotVector);
640 builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
641 }
642 #endif
643 }
644
645
646 #include <security_smime/tsaSupport.h>
647
648 //
649 // Generate the CMS signature for a (finished) CodeDirectory.
650 //
651 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag)
652 {
653 assert(state.mSigner);
654 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
655
656 // a null signer generates a null signature blob
657 if (state.mSigner == SecIdentityRef(kCFNull))
658 return CFDataCreate(NULL, NULL, 0);
659
660 // generate CMS signature
661 CFRef<CMSEncoderRef> cms;
662 MacOSError::check(CMSEncoderCreate(&cms.aref()));
663 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
664 CMSEncoderAddSigners(cms, state.mSigner);
665 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
666 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
667
668 if (emitSigningTime) {
669 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
670 CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent();
671 MacOSError::check(CMSEncoderSetSigningTime(cms, time));
672 }
673
674 if (hashBag) {
675 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
676 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashBag));
677 }
678
679 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
680
681 // Set up to call Timestamp server if requested
682 if (state.mWantTimeStamp)
683 {
684 CFRef<CFErrorRef> error = NULL;
685 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
686 if (error)
687 MacOSError::throwMe(errSecDataNotAvailable);
688
689 if (state.mNoTimeStampCerts || state.mTimestampService) {
690 if (state.mTimestampService)
691 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
692 if (state.mNoTimeStampCerts)
693 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
694 }
695
696 CmsMessageSetTSAContext(cms, defaultTSContext);
697 }
698
699 CFDataRef signature;
700 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
701
702 return signature;
703 }
704
705
706 //
707 // Our DiskRep::signingContext methods communicate with the signing subsystem
708 // in terms those callers can easily understand.
709 //
710 string SecCodeSigner::Signer::sdkPath(const std::string &path) const
711 {
712 assert(path[0] == '/'); // need absolute path here
713 if (state.mSDKRoot)
714 return cfString(state.mSDKRoot) + path;
715 else
716 return path;
717 }
718
719 bool SecCodeSigner::Signer::isAdhoc() const
720 {
721 return state.mSigner == SecIdentityRef(kCFNull);
722 }
723
724 SecCSFlags SecCodeSigner::Signer::signingFlags() const
725 {
726 return state.mOpFlags;
727 }
728
729
730 //
731 // Parse a text of the form
732 // flag,...,flag
733 // where each flag is the canonical name of a signable CodeDirectory flag.
734 // No abbreviations are allowed, and internally set flags are not accepted.
735 //
736 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
737 {
738 uint32_t flags = 0;
739 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
740 string word = (comma == string::npos) ? text : text.substr(0, comma);
741 const SecCodeDirectoryFlagTable *item;
742 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
743 if (item->signable && word == item->name) {
744 flags |= item->value;
745 break;
746 }
747 if (!item->name) // not found
748 MacOSError::throwMe(errSecCSInvalidFlags);
749 if (comma == string::npos) // last word
750 break;
751 }
752 return flags;
753 }
754
755
756 //
757 // Generate a unique string from our underlying DiskRep.
758 // We could get 90%+ of the uniquing benefit by just generating
759 // a random string here. Instead, we pick the (hex string encoding of)
760 // the source rep's unique identifier blob. For universal binaries,
761 // this is the canonical local architecture, which is a bit arbitrary.
762 // This provides us with a consistent unique string for all architectures
763 // of a fat binary, *and* (unlike a random string) is reproducible
764 // for identical inputs, even upon resigning.
765 //
766 std::string SecCodeSigner::Signer::uniqueName() const
767 {
768 CFRef<CFDataRef> identification = rep->identification();
769 const UInt8 *ident = CFDataGetBytePtr(identification);
770 const CFIndex length = CFDataGetLength(identification);
771 string result;
772 for (CFIndex n = 0; n < length; n++) {
773 char hex[3];
774 snprintf(hex, sizeof(hex), "%02x", ident[n]);
775 result += hex;
776 }
777 return result;
778 }
779
780 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) {
781 CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key);
782
783 if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) {
784 return false;
785 }
786
787 return CFBooleanGetValue(entValue);
788 }
789
790 uint64_t SecCodeSigner::Signer::entitlementsToExecSegFlags(CFDataRef entitlements)
791 {
792 if (!entitlements) {
793 return 0;
794 }
795
796 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
797
798 if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
799 return 0;
800 }
801
802 try {
803 CFRef<CFDictionaryRef> entDict = blob->entitlements();
804
805 uint64_t flags = 0;
806
807 flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
808 flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
809 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
810 flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
811 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
812 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
813 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
814
815 return flags;
816
817 } catch (const CommonError &err) {
818 // Not fatal.
819 secwarning("failed to parse entitlements: %s", err.what());
820 return 0;
821 }
822 }
823
824 } // end namespace CodeSigning
825 } // end namespace Security