]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/signer.cpp
2a9aea29707de595c03148d282682eaa244ef361
[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 "der_plist.h"
28 #include "signer.h"
29 #include "resources.h"
30 #include "signerutils.h"
31 #include "SecCodeSigner.h"
32 #include <Security/SecIdentity.h>
33 #include <Security/CMSEncoder.h>
34 #include <Security/CMSPrivate.h>
35 #include <Security/CSCommonPriv.h>
36 #include <CoreFoundation/CFBundlePriv.h>
37 #include "resources.h"
38 #include "machorep.h"
39 #include "reqparser.h"
40 #include "reqdumper.h"
41 #include "csutilities.h"
42 #include <security_utilities/unix++.h>
43 #include <security_utilities/unixchild.h>
44 #include <security_utilities/cfmunge.h>
45 #include <security_utilities/dispatch.h>
46 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
47
48 namespace Security {
49 namespace CodeSigning {
50
51
52 //
53 // Sign some code.
54 //
55 void SecCodeSigner::Signer::sign(SecCSFlags flags)
56 {
57 rep = code->diskRep()->base();
58 this->prepare(flags);
59
60 PreSigningContext context(*this);
61
62 considerTeamID(context);
63
64 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
65 signMachO(fat, context);
66 } else {
67 signArchitectureAgnostic(context);
68 }
69 }
70
71
72 void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context)
73 {
74 /* If an explicit teamID was passed in it must be
75 the same as what came from the cert */
76 std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs);
77
78 if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) {
79 /* If preserving the team identifier, teamID is set previously when the
80 code object is still available */
81 if (!teamIDFromCert.empty() && teamID != teamIDFromCert)
82 MacOSError::throwMe(errSecCSInvalidFlags);
83 } else {
84 if (teamIDFromCert.empty()) {
85 /* state.mTeamID is an explicitly passed teamID */
86 teamID = state.mTeamID;
87 } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) {
88 /* If there was no explicit team ID set, or the explicit team ID matches
89 what is in the cert, use the team ID from the certificate */
90 teamID = teamIDFromCert;
91 } else {
92 /* The caller passed in an explicit team ID that does not match what is
93 in the signing cert, which is an invalid usage */
94 MacOSError::throwMe(errSecCSInvalidFlags);
95 }
96 }
97 }
98
99
100 //
101 // Remove any existing code signature from code
102 //
103 void SecCodeSigner::Signer::remove(SecCSFlags flags)
104 {
105 // can't remove a detached signature
106 if (state.mDetached)
107 MacOSError::throwMe(errSecCSNotSupported);
108
109 rep = code->diskRep();
110 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
111 // architecture-sensitive removal
112 MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath());
113 editor.allocate(); // create copy
114 editor.commit(); // commit change
115 } else {
116 // architecture-agnostic removal
117 RefPointer<DiskRep::Writer> writer = rep->writer();
118 writer->remove();
119 writer->flush();
120 }
121 }
122
123
124 //
125 // Contemplate the object-to-be-signed and set up the Signer state accordingly.
126 //
127 void SecCodeSigner::Signer::prepare(SecCSFlags flags)
128 {
129 // make sure the rep passes strict validation
130 if (strict)
131 rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData));
132
133 // initialize progress/cancellation state
134 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
135
136 // get the Info.plist out of the rep for some creative defaulting
137 CFRef<CFDictionaryRef> infoDict;
138 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
139 infoDict.take(makeCFDictionaryFrom(infoData));
140
141 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
142
143 // work out the canonical identifier
144 identifier = state.mIdentifier;
145 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
146 identifier = code->identifier();
147 if (identifier.empty()) {
148 identifier = rep->recommendedIdentifier(*this);
149 if (identifier.find('.') == string::npos)
150 identifier = state.mIdentifierPrefix + identifier;
151 if (identifier.find('.') == string::npos && isAdhoc())
152 identifier = identifier + "-" + uniqueName();
153 secinfo("signer", "using default identifier=%s", identifier.c_str());
154 } else
155 secinfo("signer", "using explicit identifier=%s", identifier.c_str());
156
157 teamID = state.mTeamID;
158 if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) {
159 const char *c_id = code->teamID();
160 if (c_id)
161 teamID = c_id;
162 }
163
164 // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later.
165 hashAlgorithms = state.mDigestAlgorithms;
166 if (hashAlgorithms.empty() && (inherit & kSecCodeSignerPreserveDigestAlgorithm))
167 hashAlgorithms = code->hashAlgorithms();
168
169 entitlements = state.mEntitlementData;
170 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
171 entitlements = code->component(cdEntitlementSlot);
172
173 generateEntitlementDER = signingFlags() & kSecCSSignGenerateEntitlementDER;
174
175 // work out the CodeDirectory flags word
176 bool haveCdFlags = false;
177 if (!haveCdFlags && state.mCdFlagsGiven) {
178 cdFlags = state.mCdFlags;
179 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags);
180 haveCdFlags = true;
181 }
182 if (!haveCdFlags) {
183 cdFlags = 0;
184 if (infoDict)
185 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) {
186 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
187 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
188 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
189 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
190 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
191 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
192 } else
193 MacOSError::throwMe(errSecCSBadDictionaryFormat);
194 haveCdFlags = true;
195 }
196 }
197 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
198 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
199 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags);
200 haveCdFlags = true;
201 }
202 if (!haveCdFlags)
203 cdFlags = 0;
204 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
205 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
206
207 // prepare the internal requirements input
208 if (state.mRequirements) {
209 if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form
210 const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>());
211 if (!rp->validateBlob())
212 MacOSError::throwMe(errSecCSReqInvalid);
213 requirements = rp->clone();
214 } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form
215 CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>());
216 // substitute $ variable tokens
217 CFRange range = { 0, CFStringGetLength(reqText) };
218 CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0);
219 requirements = parseRequirements(cfString(reqText));
220 } else
221 MacOSError::throwMe(errSecCSInvalidObjectRef);
222 } else if (inherit & kSecCodeSignerPreserveRequirements)
223 if (const Requirements *rp = code->internalRequirements())
224 requirements = rp->clone();
225
226 // prepare the resource directory, if any
227 string rpath = rep->resourcesRootPath();
228 string rrpath;
229 CFCopyRef<CFDictionaryRef> resourceRules;
230 if (!rpath.empty()) {
231 // explicitly given resource rules always win
232 resourceRules = state.mResourceRules;
233
234 // inherited rules come next (overriding embedded ones!)
235 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
236 if (CFDictionaryRef oldRules = code->resourceDictionary(false))
237 resourceRules = oldRules;
238
239 // embedded resource rules come next
240 if (!resourceRules && infoDict)
241 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) {
242 if (CFGetTypeID(spec) == CFStringGetTypeID())
243 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
244 if (CFDictionaryRef dict = makeCFDictionaryFrom(data))
245 resourceRules.take(dict);
246 if (!resourceRules) // embedded rules present but unacceptable
247 MacOSError::throwMe(errSecCSResourceRulesInvalid);
248 }
249
250 // if we got one from anywhere (but the defaults), sanity-check it
251 if (resourceRules) {
252 CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules"));
253 if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID())
254 MacOSError::throwMe(errSecCSResourceRulesInvalid);
255 }
256
257 // finally, ask the DiskRep for its default
258 if (!resourceRules)
259 resourceRules.take(rep->defaultResourceRules(*this));
260
261 // resource root can optionally be the canonical bundle path,
262 // but sealed resource paths are always relative to rpath
263 rrpath = rpath;
264 if (signingFlags() & kSecCSSignBundleRoot)
265 rrpath = cfStringRelease(rep->copyCanonicalPath());
266 }
267
268 // screen and set the signing time
269 if (state.mSigningTime == CFDateRef(kCFNull)) {
270 emitSigningTime = false; // no time at all
271 } else if (!state.mSigningTime) {
272 emitSigningTime = true;
273 signingTime = 0; // wall clock, established later
274 } else {
275 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
276 if (time > CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
277 MacOSError::throwMe(errSecCSBadDictionaryFormat);
278 emitSigningTime = true;
279 signingTime = time;
280 }
281
282 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(*this);
283
284 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults.
285 rep->prepareForSigning(*this);
286
287 // apply some defaults after diskRep intervention
288 if (hashAlgorithms.empty()) { // default to SHA256 + SHA-1
289 hashAlgorithms.insert(kSecCodeSignatureHashSHA1);
290 hashAlgorithms.insert(kSecCodeSignatureHashSHA256);
291 }
292
293 // build the resource directory (once and for all, using the digests determined above)
294 if (!rpath.empty()) {
295 buildResources(rrpath, rpath, resourceRules);
296 }
297
298
299
300 if (inherit & kSecCodeSignerPreservePEH) {
301 /* We need at least one architecture in all cases because we index our
302 * PreEncryptionMaps by architecture. However, only machOs have any
303 * architecture at all, for generic targets there will just be one
304 * PreEncryptionHashMap.
305 * So if the main executable is not a machO, we just choose the local
306 * (signer's) main architecture as dummy value for the first element in our pair. */
307 preEncryptMainArch = (code->diskRep()->mainExecutableIsMachO() ?
308 code->diskRep()->mainExecutableImage()->bestNativeArch() :
309 Architecture::local());
310
311 addPreEncryptHashes(preEncryptHashMaps[preEncryptMainArch], code);
312
313 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
314 Universal *fat = subcode->diskRep()->mainExecutableImage();
315 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
316 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice.
317 addPreEncryptHashes(preEncryptHashMaps[arch], subcode);
318 });
319 }
320
321 if (inherit & kSecCodeSignerPreserveRuntime) {
322 /* We need at least one architecture in all cases because we index our
323 * RuntimeVersionMaps by architecture. However, only machOs have any
324 * architecture at all, for generic targets there will just be one
325 * RuntimeVersionMap.
326 * So if the main executable is not a machO, we just choose the local
327 * (signer's) main architecture as dummy value for the first element in our pair. */
328 runtimeVersionMainArch = (code->diskRep()->mainExecutableIsMachO() ?
329 code->diskRep()->mainExecutableImage()->bestNativeArch() :
330 Architecture::local());
331
332 addRuntimeVersions(runtimeVersionMap[runtimeVersionMainArch], code);
333
334 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) {
335 Universal *fat = subcode->diskRep()->mainExecutableImage();
336 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice.
337 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice.
338 addRuntimeVersions(runtimeVersionMap[arch], subcode);
339 });
340 }
341 }
342
343 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code) {
344 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
345
346 if (cds != NULL) {
347 for(auto const& pair : *cds) {
348 CodeDirectory::HashAlgorithm const alg = pair.first;
349 CFDataRef const cddata = pair.second;
350
351 CodeDirectory const * cd =
352 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
353 if (cd->preEncryptHashes() != NULL) {
354 CFRef<CFDataRef> preEncrypt = makeCFData(cd->preEncryptHashes(),
355 cd->nCodeSlots * cd->hashSize);
356 map[alg] = preEncrypt;
357 }
358 }
359 }
360 }
361
362 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap &map, const SecStaticCode *code)
363 {
364 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories();
365
366 if (cds != NULL) {
367 for(auto const& pair : *cds) {
368 CodeDirectory::HashAlgorithm const alg = pair.first;
369 CFDataRef const cddata = pair.second;
370
371 CodeDirectory const * cd =
372 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata));
373 if (cd->runtimeVersion()) {
374 map[alg] = cd->runtimeVersion();
375 }
376 }
377 }
378 }
379
380 //
381 // Collect the resource seal for a program.
382 // This includes both sealed resources and information about nested code.
383 //
384 void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
385 {
386 typedef ResourceBuilder::Rule Rule;
387
388 secinfo("codesign", "start building resource directory");
389 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
390
391 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
392 assert(rules);
393
394 if (this->state.mLimitedAsync == NULL) {
395 this->state.mLimitedAsync =
396 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently
397 * turned off, because the paths for signing code are not ready for it yet. */
398 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey);
399 new LimitedAsync(false);
400 }
401
402 CFDictionaryRef files2 = NULL;
403 if (!(signingFlags() & kSecCSSignV1)) {
404 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2");
405 if (!rules2) {
406 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules).
407 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them.
408 rules2 = cfmake<CFDictionaryRef>("{+%O"
409 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
410 "}", rules);
411 }
412
413 Dispatch::Group group;
414 Dispatch::Group &groupRef = group; // (into block)
415
416 // build the modern (V2) resource seal
417 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
418 CFMutableDictionaryRef filesRef = files.get(); // (into block)
419 ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet());
420 ResourceBuilder &resources = resourceBuilder; // (into block)
421 rep->adjustResources(resources);
422
423 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
424 bool isSymlink = (ent->fts_info == FTS_SL);
425 const std::string path(ent->fts_path);
426 const std::string accpath(ent->fts_accpath);
427 this->state.mLimitedAsync->perform(groupRef, ^{
428 CFRef<CFMutableDictionaryRef> seal;
429 if (ruleFlags & ResourceBuilder::nested) {
430 seal.take(signNested(path, relpath));
431 } else if (isSymlink) {
432 char target[PATH_MAX];
433 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1);
434 if (len < 0)
435 UnixError::check(-1);
436 target[len] = '\0';
437 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target));
438 } else {
439 seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
440 }
441 if (ruleFlags & ResourceBuilder::optional)
442 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
443 CFTypeRef hash;
444 StLock<Mutex> _(resourceLock);
445 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form
446 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash);
447 else
448 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get());
449 code->reportProgress();
450 });
451 });
452 group.wait();
453 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules());
454 files2 = files;
455 CFDictionaryAddValue(result, CFSTR("files2"), files2);
456 }
457
458 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case
459 if (!(signingFlags() & kSecCSSignNoV1)) {
460 // build the legacy (V1) resource seal
461 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
462 ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet());
463 ResourceBuilder &resources = resourceBuilder;
464 rep->adjustResources(resources); // DiskRep-specific adjustments
465 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
466 if (ent->fts_info == FTS_F) {
467 CFRef<CFDataRef> hash;
468 if (files2) // try to get the hash from a previously-made version
469 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) {
470 if (CFGetTypeID(seal) == CFDataGetTypeID())
471 hash = CFDataRef(seal);
472 else
473 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash")));
474 }
475 if (!hash)
476 hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1));
477 if (ruleFlags == 0) { // default case - plain hash
478 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
479 secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
480 } else { // more complicated - use a sub-dictionary
481 cfadd(files, "{%s={hash=%O,optional=%B}}",
482 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
483 secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
484 }
485 }
486 });
487 CFDictionaryAddValue(result, CFSTR("files"), files.get());
488 }
489
490 resourceDirectory = result.get();
491 resourceDictData.take(makeCFData(resourceDirectory.get()));
492 }
493
494
495 //
496 // Deal with one piece of nested code
497 //
498 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
499 {
500 // sign nested code and collect nesting information
501 try {
502 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
503 if (signingFlags() & kSecCSSignNestedCode)
504 this->state.sign(code, signingFlags());
505 std::string dr = Dumper::dump(code->designatedRequirement());
506 if (CFDataRef hash = code->cdHash())
507 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}",
508 Dumper::dump(code->designatedRequirement()).c_str(),
509 hash);
510 MacOSError::throwMe(errSecCSUnsigned);
511 } catch (const CommonError &err) {
512 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
513 }
514 }
515
516
517 //
518 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce.
519 // Note that this will deal just fine with non-fat Mach-O binaries, but it will
520 // treat them as architectural binaries containing (only) one architecture - that
521 // interpretation is courtesy of the Universal/MachO support classes.
522 //
523 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
524 {
525 // Mach-O executable at the core - perform multi-architecture signing
526 RefPointer<DiskRep::Writer> writer = rep->writer();
527 auto_ptr<ArchEditor> editor(state.mDetached
528 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
529 : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath()));
530 assert(editor->count() > 0);
531 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components
532 populate(*editor);
533
534 // pass 1: prepare signature blobs and calculate sizes
535 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
536 MachOEditor::Arch &arch = *it->second;
537 arch.source.reset(fat->architecture(it->first));
538
539 // library validation is not compatible with i386
540 if (arch.architecture.cpuType() == CPU_TYPE_I386) {
541 if (cdFlags & kSecCodeSignatureLibraryValidation) {
542 MacOSError::throwMe(errSecCSBadLVArch);
543 }
544 }
545
546 bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
547
548 uint32_t runtimeVersion = 0;
549 if (cdFlags & kSecCodeSignatureRuntime) {
550 runtimeVersion = state.mRuntimeVersionOverride ? state.mRuntimeVersionOverride : arch.source.get()->sdkVersion();
551 }
552
553 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
554 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
555 populate(arch);
556 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
557 uint32_t runtimeVersionToUse = runtimeVersion;
558 if ((cdFlags & kSecCodeSignatureRuntime) && runtimeVersionMap.count(arch.architecture)) {
559 if (runtimeVersionMap[arch.architecture].count(*type)) {
560 runtimeVersionToUse = runtimeVersionMap[arch.architecture][*type];
561 }
562 }
563 arch.eachDigest(^(CodeDirectory::Builder& builder) {
564 populate(builder, arch, arch.ireqs,
565 arch.source->offset(), arch.source->signingExtent(),
566 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
567 unsigned(digestAlgorithms().size()-1),
568 preEncryptHashMaps[arch.architecture], runtimeVersionToUse);
569 });
570 }
571
572 // add identification blob (made from this architecture) only if we're making a detached signature
573 if (state.mDetached) {
574 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get());
575 arch.add(cdIdentificationSlot, BlobWrapper::alloc(
576 CFDataGetBytePtr(identification), CFDataGetLength(identification)));
577 }
578
579 // prepare SuperBlob size estimate
580 __block std::vector<size_t> sizes;
581 arch.eachDigest(^(CodeDirectory::Builder& builder){
582 sizes.push_back(builder.size(CodeDirectory::currentVersion));
583 });
584 arch.blobSize = arch.size(sizes, state.mCMSSize, 0);
585 }
586
587 editor->allocate();
588
589 // pass 2: Finish and generate signatures, and write them
590 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
591 MachOEditor::Arch &arch = *it->second;
592 editor->reset(arch);
593
594 // finish CodeDirectories (off new binary) and sign it
595 __block CodeDirectorySet cdSet;
596 arch.eachDigest(^(CodeDirectory::Builder &builder) {
597 CodeDirectory *cd = builder.build();
598 cdSet.add(cd);
599 });
600
601 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
602 CFRef<CFArrayRef> hashList = cdSet.hashList();
603 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
604
605 // complete the SuperBlob
606 cdSet.populate(&arch);
607 arch.add(cdSignatureSlot, BlobWrapper::alloc(
608 CFDataGetBytePtr(signature), CFDataGetLength(signature)));
609 if (!state.mDryRun) {
610 EmbeddedSignatureBlob *blob = arch.make();
611 editor->write(arch, blob); // takes ownership of blob
612 }
613 }
614
615 // done: write edit copy back over the original
616 if (!state.mDryRun) {
617 editor->commit();
618 }
619 }
620
621
622 //
623 // Sign a binary that has no notion of architecture.
624 // That currently means anything that isn't Mach-O format.
625 //
626 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context)
627 {
628 // non-Mach-O executable - single-instance signing
629 RefPointer<DiskRep::Writer> writer = state.mDetached ?
630 (new DetachedBlobWriter(*this)) : rep->writer();
631
632 CodeDirectorySet cdSet;
633
634 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
635 CodeDirectory::Builder builder(*type);
636 InternalRequirements ireqs;
637 ireqs(requirements, rep->defaultRequirements(NULL, *this), context);
638 populate(*writer);
639 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(),
640 false, // only machOs can currently be main binaries
641 rep->execSegBase(NULL), rep->execSegLimit(NULL),
642 unsigned(digestAlgorithms().size()-1),
643 preEncryptHashMaps[preEncryptMainArch], // Only one map, the default.
644 (cdFlags & kSecCodeSignatureRuntime) ? state.mRuntimeVersionOverride : 0);
645
646 CodeDirectory *cd = builder.build();
647 if (!state.mDryRun)
648 cdSet.add(cd);
649 }
650
651 // add identification blob (made from this architecture) only if we're making a detached signature
652 if (state.mDetached) {
653 CFRef<CFDataRef> identification = rep->identification();
654 writer->component(cdIdentificationSlot, identification);
655 }
656
657 // write out all CodeDirectories
658 if (!state.mDryRun)
659 cdSet.populate(writer);
660
661 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict();
662 CFRef<CFArrayRef> hashList = cdSet.hashList();
663 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList);
664 writer->signature(signature);
665
666 // commit to storage
667 writer->flush();
668 }
669
670
671 //
672 // Global populate - send components to destination buffers ONCE
673 //
674 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
675 {
676 if (resourceDirectory && !state.mDryRun)
677 writer.component(cdResourceDirSlot, resourceDictData);
678 }
679
680
681 //
682 // Per-architecture populate - send components to per-architecture buffers
683 // and populate the CodeDirectory for an architecture. In architecture-agnostic
684 // signing operations, the non-architectural binary is considered one (arbitrary) architecture
685 // for the purposes of this call.
686 //
687 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
688 InternalRequirements &ireqs, size_t offset, size_t length,
689 bool mainBinary, size_t execSegBase, size_t execSegLimit,
690 unsigned alternateDigestCount,
691 PreEncryptHashMap const &preEncryptHashMap,
692 uint32_t runtimeVersion)
693 {
694 // fill the CodeDirectory
695 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
696 builder.flags(cdFlags);
697 builder.identifier(identifier);
698 builder.teamID(teamID);
699 builder.platform(state.mPlatform);
700 builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
701 builder.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH);
702 builder.preservePreEncryptHashMap(preEncryptHashMap);
703 builder.runTimeVersion(runtimeVersion);
704
705 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot))
706 builder.specialSlot(cdInfoSlot, data);
707 if (ireqs) {
708 CFRef<CFDataRef> data = makeCFData(*ireqs);
709 writer.component(cdRequirementsSlot, data);
710 builder.specialSlot(cdRequirementsSlot, data);
711 }
712 if (resourceDirectory)
713 builder.specialSlot(cdResourceDirSlot, resourceDictData);
714 if (entitlements) {
715 writer.component(cdEntitlementSlot, entitlements);
716 builder.specialSlot(cdEntitlementSlot, entitlements);
717
718 if (mainBinary) {
719 CFRef<CFDataRef> entitlementDER;
720 uint64_t execSegFlags = 0;
721 cookEntitlements(entitlements, generateEntitlementDER,
722 &execSegFlags, &entitlementDER.aref());
723
724 if (generateEntitlementDER) {
725 writer.component(cdEntitlementDERSlot, entitlementDER);
726 builder.specialSlot(cdEntitlementDERSlot, entitlementDER);
727 }
728
729 builder.addExecSegFlags(execSegFlags);
730 }
731 }
732 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
733 builder.specialSlot(cdRepSpecificSlot, repSpecific);
734
735 writer.addDiscretionary(builder);
736
737 #if 0 // rdar://problem/25720754
738 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
739 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any)
740 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible)
741 std::vector<Endian<uint32_t> > slotVector;
742 slotVector.push_back(cdCodeDirectorySlot); // mandatory
743 std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots();
744 filledSlots.insert(cdTopDirectorySlot); // will be added below
745 copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector));
746 for (unsigned n = 0; n < alternateDigestCount; n++)
747 slotVector.push_back(cdAlternateCodeDirectorySlots + n);
748 slotVector.push_back(cdSignatureSlot);
749 CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0]));
750 writer.component(cdTopDirectorySlot, cfSlotVector);
751 builder.specialSlot(cdTopDirectorySlot, cfSlotVector);
752 }
753 #endif
754 }
755
756
757 #include <security_smime/tsaSupport.h>
758
759 //
760 // Generate the CMS signature for a (finished) CodeDirectory.
761 //
762 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd,
763 CFDictionaryRef hashDict,
764 CFArrayRef hashList)
765 {
766 assert(state.mSigner);
767 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL;
768
769 // a null signer generates a null signature blob
770 if (state.mSigner == SecIdentityRef(kCFNull))
771 return CFDataCreate(NULL, NULL, 0);
772
773 // generate CMS signature
774 CFRef<CMSEncoderRef> cms;
775 MacOSError::check(CMSEncoderCreate(&cms.aref()));
776 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
777 CMSEncoderAddSigners(cms, state.mSigner);
778 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
779 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
780
781 if (emitSigningTime) {
782 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
783 CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent();
784 MacOSError::check(CMSEncoderSetSigningTime(cms, time));
785 }
786
787 if (hashDict != NULL) {
788 assert(hashList != NULL);
789
790 // V2 Hash Agility
791
792 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgilityV2));
793 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms, hashDict));
794
795 // V1 Hash Agility
796
797 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashList);
798 CFRef<CFDataRef> hashAgilityV1Attribute = makeCFData(hashDict.get());
799
800 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
801 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashAgilityV1Attribute));
802 }
803
804 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
805
806 // Set up to call Timestamp server if requested
807 if (state.mWantTimeStamp)
808 {
809 CFRef<CFErrorRef> error = NULL;
810 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref());
811 if (error)
812 MacOSError::throwMe(errSecDataNotAvailable);
813
814 if (state.mNoTimeStampCerts || state.mTimestampService) {
815 if (state.mTimestampService)
816 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService);
817 if (state.mNoTimeStampCerts)
818 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue);
819 }
820
821 CmsMessageSetTSAContext(cms, defaultTSContext);
822 }
823
824 CFDataRef signature;
825 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
826
827 return signature;
828 }
829
830
831 //
832 // Our DiskRep::signingContext methods communicate with the signing subsystem
833 // in terms those callers can easily understand.
834 //
835 string SecCodeSigner::Signer::sdkPath(const std::string &path) const
836 {
837 assert(path[0] == '/'); // need absolute path here
838 if (state.mSDKRoot)
839 return cfString(state.mSDKRoot) + path;
840 else
841 return path;
842 }
843
844 bool SecCodeSigner::Signer::isAdhoc() const
845 {
846 return state.mSigner == SecIdentityRef(kCFNull);
847 }
848
849 SecCSFlags SecCodeSigner::Signer::signingFlags() const
850 {
851 return state.mOpFlags;
852 }
853
854
855 //
856 // Parse a text of the form
857 // flag,...,flag
858 // where each flag is the canonical name of a signable CodeDirectory flag.
859 // No abbreviations are allowed, and internally set flags are not accepted.
860 //
861 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text)
862 {
863 uint32_t flags = 0;
864 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) {
865 string word = (comma == string::npos) ? text : text.substr(0, comma);
866 const SecCodeDirectoryFlagTable *item;
867 for (item = kSecCodeDirectoryFlagTable; item->name; item++)
868 if (item->signable && word == item->name) {
869 flags |= item->value;
870 break;
871 }
872 if (!item->name) // not found
873 MacOSError::throwMe(errSecCSInvalidFlags);
874 if (comma == string::npos) // last word
875 break;
876 }
877 return flags;
878 }
879
880
881 //
882 // Generate a unique string from our underlying DiskRep.
883 // We could get 90%+ of the uniquing benefit by just generating
884 // a random string here. Instead, we pick the (hex string encoding of)
885 // the source rep's unique identifier blob. For universal binaries,
886 // this is the canonical local architecture, which is a bit arbitrary.
887 // This provides us with a consistent unique string for all architectures
888 // of a fat binary, *and* (unlike a random string) is reproducible
889 // for identical inputs, even upon resigning.
890 //
891 std::string SecCodeSigner::Signer::uniqueName() const
892 {
893 CFRef<CFDataRef> identification = rep->identification();
894 const UInt8 *ident = CFDataGetBytePtr(identification);
895 const CFIndex length = CFDataGetLength(identification);
896 string result;
897 for (CFIndex n = 0; n < length; n++) {
898 char hex[3];
899 snprintf(hex, sizeof(hex), "%02x", ident[n]);
900 result += hex;
901 }
902 return result;
903 }
904
905 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) {
906 CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key);
907
908 if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) {
909 return false;
910 }
911
912 return CFBooleanGetValue(entValue);
913 }
914
915 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements, bool generateDER,
916 uint64_t *execSegFlags, CFDataRef *entitlementDER)
917 {
918 if (!entitlements) {
919 return; // nothing to do.
920 }
921
922 EntitlementDERBlob *derBlob = NULL;
923
924 try {
925 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements));
926
927 if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) {
928 MacOSError::throwMe(errSecCSInvalidEntitlements);
929 }
930
931 CFRef<CFDictionaryRef> entDict = blob->entitlements();
932
933 if (generateDER) {
934 CFRef<CFErrorRef> error = NULL;
935 size_t const der_size = der_sizeof_plist(entDict, &error.aref());
936
937 if (der_size == 0) {
938 secerror("Getting DER size for entitlement plist failed: %@", error.get());
939 MacOSError::throwMe(errSecCSInvalidEntitlements);
940 }
941
942 derBlob = EntitlementDERBlob::alloc(der_size);
943
944 if (derBlob == NULL) {
945 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size);
946 MacOSError::throwMe(errSecCSInvalidEntitlements);
947 }
948 uint8_t * const der_end = derBlob->der() + der_size;
949 uint8_t * const der_start = der_encode_plist(entDict, &error.aref(), derBlob->der(), der_end);
950
951 if (der_start != derBlob->der()) {
952 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start - derBlob->der()));
953 free(derBlob);
954 MacOSError::throwMe(errSecCSInvalidEntitlements);
955 }
956
957 *entitlementDER = makeCFData(derBlob, derBlob->length());
958 free(derBlob);
959 derBlob = NULL;
960 }
961
962 if (execSegFlags != NULL) {
963 uint64_t flags = 0;
964
965 flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0;
966 flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0;
967 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0;
968 flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0;
969 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0;
970 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0;
971 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0;
972
973 *execSegFlags = flags;
974 }
975
976 } catch (const CommonError &err) {
977 free(derBlob);
978 // Not fatal if we're not asked to generate DER entitlements.
979
980 secwarning("failed to parse entitlements: %s", err.what());
981 if (generateDER) {
982 throw;
983 }
984 }
985 }
986
987 } // end namespace CodeSigning
988 } // end namespace Security