]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_codesigning/lib/signer.cpp
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / signer.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// signer - Signing operation supervisor and controller
26//
27#include "signer.h"
28#include "resources.h"
29#include "signerutils.h"
30#include "SecCodeSigner.h"
31#include <Security/SecIdentity.h>
32#include <Security/CMSEncoder.h>
33#include <Security/CMSPrivate.h>
34#include <Security/CSCommonPriv.h>
35#include <CoreFoundation/CFBundlePriv.h>
427c49bc 36#include "resources.h"
b1ab9ed8 37#include "machorep.h"
427c49bc
A
38#include "reqparser.h"
39#include "reqdumper.h"
b1ab9ed8
A
40#include "csutilities.h"
41#include <security_utilities/unix++.h>
42#include <security_utilities/unixchild.h>
43#include <security_utilities/cfmunge.h>
d87e1158
A
44#include <security_utilities/dispatch.h>
45#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
b1ab9ed8
A
46
47namespace Security {
48namespace CodeSigning {
49
50
51//
52// Sign some code.
53//
54void SecCodeSigner::Signer::sign(SecCSFlags flags)
55{
56 rep = code->diskRep()->base();
57 this->prepare(flags);
420ff9d9 58
b1ab9ed8 59 PreSigningContext context(*this);
420ff9d9 60
e3d460c9
A
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
71void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context)
72{
420ff9d9
A
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);
e3d460c9 76
420ff9d9
A
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 }
b1ab9ed8 96}
e3d460c9 97
b1ab9ed8
A
98
99//
100// Remove any existing code signature from code
101//
102void 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
e3d460c9 111 MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath());
b1ab9ed8
A
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//
126void SecCodeSigner::Signer::prepare(SecCSFlags flags)
127{
80e23899
A
128 // make sure the rep passes strict validation
129 if (strict)
fa7225c8 130 rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData));
d8f41ccd
A
131
132 // initialize progress/cancellation state
133 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter
80e23899 134
b1ab9ed8
A
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));
427c49bc
A
139
140 uint32_t inherit = code->isSigned() ? state.mPreserveMetadata : 0;
b1ab9ed8
A
141
142 // work out the canonical identifier
143 identifier = state.mIdentifier;
427c49bc
A
144 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier))
145 identifier = code->identifier();
b1ab9ed8 146 if (identifier.empty()) {
e3d460c9 147 identifier = rep->recommendedIdentifier(*this);
b1ab9ed8
A
148 if (identifier.find('.') == string::npos)
149 identifier = state.mIdentifierPrefix + identifier;
e3d460c9 150 if (identifier.find('.') == string::npos && isAdhoc())
b1ab9ed8 151 identifier = identifier + "-" + uniqueName();
fa7225c8 152 secinfo("signer", "using default identifier=%s", identifier.c_str());
b1ab9ed8 153 } else
fa7225c8 154 secinfo("signer", "using explicit identifier=%s", identifier.c_str());
427c49bc 155
420ff9d9
A
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 }
5c19dc3a 162
e3d460c9
A
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
427c49bc
A
168 entitlements = state.mEntitlementData;
169 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements))
170 entitlements = code->component(cdEntitlementSlot);
b1ab9ed8
A
171
172 // work out the CodeDirectory flags word
427c49bc
A
173 bool haveCdFlags = false;
174 if (!haveCdFlags && state.mCdFlagsGiven) {
b1ab9ed8 175 cdFlags = state.mCdFlags;
fa7225c8 176 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags);
427c49bc
A
177 haveCdFlags = true;
178 }
179 if (!haveCdFlags) {
b1ab9ed8
A
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));
fa7225c8 185 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags);
b1ab9ed8
A
186 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
187 cdFlags = cdTextFlags(cfString(CFStringRef(csflags)));
fa7225c8 188 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags);
b1ab9ed8
A
189 } else
190 MacOSError::throwMe(errSecCSBadDictionaryFormat);
427c49bc 191 haveCdFlags = true;
b1ab9ed8
A
192 }
193 }
427c49bc
A
194 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) {
195 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc;
fa7225c8 196 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags);
427c49bc
A
197 haveCdFlags = true;
198 }
199 if (!haveCdFlags)
200 cdFlags = 0;
b1ab9ed8
A
201 if (state.mSigner == SecIdentityRef(kCFNull)) // ad-hoc signing requested...
202 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that
427c49bc
A
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();
b1ab9ed8
A
222
223 // prepare the resource directory, if any
224 string rpath = rep->resourcesRootPath();
e3d460c9
A
225 string rrpath;
226 CFCopyRef<CFDictionaryRef> resourceRules;
b1ab9ed8
A
227 if (!rpath.empty()) {
228 // explicitly given resource rules always win
e3d460c9 229 resourceRules = state.mResourceRules;
b1ab9ed8 230
427c49bc
A
231 // inherited rules come next (overriding embedded ones!)
232 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules))
233 if (CFDictionaryRef oldRules = code->resourceDictionary(false))
234 resourceRules = oldRules;
235
b1ab9ed8
A
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 }
427c49bc
A
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 }
b1ab9ed8
A
253
254 // finally, ask the DiskRep for its default
255 if (!resourceRules)
e3d460c9 256 resourceRules.take(rep->defaultResourceRules(*this));
80e23899
A
257
258 // resource root can optionally be the canonical bundle path,
259 // but sealed resource paths are always relative to rpath
e3d460c9
A
260 rrpath = rpath;
261 if (signingFlags() & kSecCSSignBundleRoot)
262 rrpath = cfStringRelease(rep->copyCanonicalPath());
b1ab9ed8
A
263 }
264
265 // screen and set the signing time
b1ab9ed8 266 if (state.mSigningTime == CFDateRef(kCFNull)) {
fa7225c8 267 emitSigningTime = false; // no time at all
b1ab9ed8 268 } else if (!state.mSigningTime) {
fa7225c8
A
269 emitSigningTime = true;
270 signingTime = 0; // wall clock, established later
b1ab9ed8
A
271 } else {
272 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
fa7225c8 273 if (time > CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature
b1ab9ed8 274 MacOSError::throwMe(errSecCSBadDictionaryFormat);
fa7225c8 275 emitSigningTime = true;
b1ab9ed8
A
276 signingTime = time;
277 }
278
e3d460c9
A
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 }
b1ab9ed8
A
294}
295
296
427c49bc
A
297//
298// Collect the resource seal for a program.
299// This includes both sealed resources and information about nested code.
300//
80e23899 301void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict)
427c49bc
A
302{
303 typedef ResourceBuilder::Rule Rule;
304
fa7225c8 305 secinfo("codesign", "start building resource directory");
427c49bc
A
306 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
307
308 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules");
309 assert(rules);
310
d87e1158
A
311 if (this->state.mLimitedAsync == NULL) {
312 this->state.mLimitedAsync =
60c433a9
A
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);
d87e1158
A
317 }
318
427c49bc 319 CFDictionaryRef files2 = NULL;
e3d460c9 320 if (!(signingFlags() & kSecCSSignV1)) {
427c49bc
A
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"
d8f41ccd 326 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories
427c49bc
A
327 "}", rules);
328 }
d87e1158
A
329
330 Dispatch::Group group;
331 Dispatch::Group &groupRef = group; // (into block)
332
427c49bc
A
333 // build the modern (V2) resource seal
334 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
d87e1158 335 CFMutableDictionaryRef filesRef = files.get(); // (into block)
e3d460c9 336 ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet());
427c49bc
A
337 ResourceBuilder &resources = resourceBuilder; // (into block)
338 rep->adjustResources(resources);
d87e1158
A
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 {
fa7225c8 356 seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
d87e1158
A
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 });
427c49bc 368 });
d87e1158 369 group.wait();
427c49bc
A
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
e3d460c9 376 if (!(signingFlags() & kSecCSSignNoV1)) {
427c49bc
A
377 // build the legacy (V1) resource seal
378 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary();
e3d460c9 379 ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet());
427c49bc
A
380 ResourceBuilder &resources = resourceBuilder;
381 rep->adjustResources(resources); // DiskRep-specific adjustments
d87e1158 382 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) {
427c49bc
A
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)
e3d460c9 393 hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1));
427c49bc 394 if (ruleFlags == 0) { // default case - plain hash
d87e1158 395 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get());
fa7225c8 396 secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule);
427c49bc
A
397 } else { // more complicated - use a sub-dictionary
398 cfadd(files, "{%s={hash=%O,optional=%B}}",
d87e1158 399 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional);
fa7225c8 400 secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule);
427c49bc
A
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//
d87e1158 415CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath)
427c49bc
A
416{
417 // sign nested code and collect nesting information
418 try {
d87e1158 419 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path));
e3d460c9
A
420 if (signingFlags() & kSecCSSignNestedCode)
421 this->state.sign(code, signingFlags());
427c49bc 422 std::string dr = Dumper::dump(code->designatedRequirement());
fa7225c8
A
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);
427c49bc
A
428 } catch (const CommonError &err) {
429 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase()));
430 }
431}
432
433
b1ab9ed8
A
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//
440void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context)
441{
442 // Mach-O executable at the core - perform multi-architecture signing
fa7225c8 443 RefPointer<DiskRep::Writer> writer = rep->writer();
b1ab9ed8
A
444 auto_ptr<ArchEditor> editor(state.mDetached
445 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
fa7225c8 446 : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath()));
b1ab9ed8
A
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));
d8f41ccd
A
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 }
866f8763
A
462
463 bool mainBinary = arch.source.get()->type() == MH_EXECUTE;
464
e3d460c9 465 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context);
b1ab9ed8
A
466 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch
467 populate(arch);
e3d460c9
A
468 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) {
469 arch.eachDigest(^(CodeDirectory::Builder& builder) {
470 populate(builder, arch, arch.ireqs,
866f8763
A
471 arch.source->offset(), arch.source->signingExtent(),
472 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)),
473 unsigned(digestAlgorithms().size()-1));
e3d460c9
A
474 });
475 }
b1ab9ed8
A
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
e3d460c9
A
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);
b1ab9ed8
A
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
e3d460c9
A
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);
b1ab9ed8
A
509
510 // complete the SuperBlob
e3d460c9 511 cdSet.populate(&arch);
b1ab9ed8
A
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
fa7225c8 521 if (!state.mDryRun) {
b1ab9ed8 522 editor->commit();
fa7225c8 523 }
b1ab9ed8
A
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//
531void 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();
e3d460c9
A
536
537 CodeDirectorySet cdSet;
866f8763 538
e3d460c9
A
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);
866f8763
A
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));
e3d460c9
A
548
549 CodeDirectory *cd = builder.build();
550 if (!state.mDryRun)
551 cdSet.add(cd);
552 }
b1ab9ed8
A
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);
b1ab9ed8 558 }
e3d460c9
A
559
560 // write out all CodeDirectories
6b200bc3
A
561 if (!state.mDryRun)
562 cdSet.populate(writer);
e3d460c9
A
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);
641423b6
A
569
570 // commit to storage
571 writer->flush();
b1ab9ed8
A
572}
573
574
575//
576// Global populate - send components to destination buffers ONCE
577//
578void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
579{
427c49bc
A
580 if (resourceDirectory && !state.mDryRun)
581 writer.component(cdResourceDirSlot, resourceDictData);
b1ab9ed8
A
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//
591void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
866f8763
A
592 InternalRequirements &ireqs, size_t offset, size_t length,
593 bool mainBinary, size_t execSegBase, size_t execSegLimit,
594 unsigned alternateDigestCount)
b1ab9ed8
A
595{
596 // fill the CodeDirectory
597 builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
598 builder.flags(cdFlags);
599 builder.identifier(identifier);
420ff9d9 600 builder.teamID(teamID);
5c19dc3a 601 builder.platform(state.mPlatform);
866f8763 602 builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0);
420ff9d9 603
b1ab9ed8
A
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)
427c49bc 612 builder.specialSlot(cdResourceDirSlot, resourceDictData);
427c49bc
A
613 if (entitlements) {
614 writer.component(cdEntitlementSlot, entitlements);
615 builder.specialSlot(cdEntitlementSlot, entitlements);
866f8763
A
616
617 if (mainBinary) {
618 builder.addExecSegFlags(entitlementsToExecSegFlags(entitlements));
619 }
b1ab9ed8 620 }
e3d460c9
A
621 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot))
622 builder.specialSlot(cdRepSpecificSlot, repSpecific);
b1ab9ed8
A
623
624 writer.addDiscretionary(builder);
e3d460c9 625
641423b6
A
626#if 0 // rdar://problem/25720754
627 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) {
e3d460c9
A
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 }
641423b6 642#endif
b1ab9ed8
A
643}
644
e3d460c9 645
b1ab9ed8
A
646#include <security_smime/tsaSupport.h>
647
648//
649// Generate the CMS signature for a (finished) CodeDirectory.
650//
e3d460c9 651CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, CFDataRef hashBag)
b1ab9ed8
A
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);
5c19dc3a 665 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256);
b1ab9ed8
A
666 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
667
fa7225c8 668 if (emitSigningTime) {
b1ab9ed8 669 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
fa7225c8
A
670 CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent();
671 MacOSError::check(CMSEncoderSetSigningTime(cms, time));
b1ab9ed8
A
672 }
673
e3d460c9
A
674 if (hashBag) {
675 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility));
676 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashBag));
677 }
678
b1ab9ed8
A
679 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
680
681 // Set up to call Timestamp server if requested
b1ab9ed8
A
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);
e3d460c9 694 }
b1ab9ed8 695
e3d460c9 696 CmsMessageSetTSAContext(cms, defaultTSContext);
b1ab9ed8 697 }
e3d460c9 698
b1ab9ed8
A
699 CFDataRef signature;
700 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
701
702 return signature;
703}
e3d460c9
A
704
705
706//
707// Our DiskRep::signingContext methods communicate with the signing subsystem
708// in terms those callers can easily understand.
709//
710string 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
719bool SecCodeSigner::Signer::isAdhoc() const
720{
721 return state.mSigner == SecIdentityRef(kCFNull);
722}
723
724SecCSFlags SecCodeSigner::Signer::signingFlags() const
725{
726 return state.mOpFlags;
727}
b1ab9ed8
A
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//
736uint32_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//
766std::string SecCodeSigner::Signer::uniqueName() const
767{
768 CFRef<CFDataRef> identification = rep->identification();
769 const UInt8 *ident = CFDataGetBytePtr(identification);
427c49bc 770 const CFIndex length = CFDataGetLength(identification);
b1ab9ed8 771 string result;
427c49bc 772 for (CFIndex n = 0; n < length; n++) {
b1ab9ed8
A
773 char hex[3];
774 snprintf(hex, sizeof(hex), "%02x", ident[n]);
775 result += hex;
776 }
777 return result;
778}
779
866f8763
A
780bool 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
790uint64_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}
b1ab9ed8
A
823
824} // end namespace CodeSigning
825} // end namespace Security