1/*
2 * Copyright (c) 2006-2010 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// cs_sign - codesign signing operation
26//
27#include "codesign.h"
28#include "cs_utils.h"
29#include <Security/Security.h>
30#include <Security/SecCodeSigner.h>
31#include <Security/SecCodePriv.h>
32#include <Security/SecRequirementPriv.h>
33#include <Security/CSCommonPriv.h>
34#include <security_utilities/blob.h>
35#include <security_utilities/cfmunge.h>
36#include <cstdio>
37#include <cmath>
38
39using namespace UnixPlusPlus;
40
41
42//
43// One-time preparation
44//
45static CFMutableDictionaryRef parameters;		// common signing parameters
46static SecCodeSignerRef signerRef;				// global signer object
47
48void prepareToSign()
49{
50	parameters = makeCFMutableDictionary();
51	SecCSFlags flags = signOptions;
52
53	if (!force)
54		flags |= kSecCSSignPreserveSignature;
55
56	if (signer)
57		CFDictionaryAddValue(parameters,
58			kSecCodeSignerIdentity, signer);
59	else
60		flags |= kSecCSRemoveSignature;
61
62	if (uniqueIdentifier)
63		CFDictionaryAddValue(parameters,
64			kSecCodeSignerIdentifier, CFTempString(uniqueIdentifier));
65	if (identifierPrefix)
66		CFDictionaryAddValue(parameters,
67			kSecCodeSignerIdentifierPrefix, CFTempString(identifierPrefix));
68
69	if (teamID) {
70		CFDictionaryAddValue(parameters,
71                             kSecCodeSignerTeamIdentifier, CFTempString(teamID));
72	}
73
74	if (internalReq)
75		CFDictionaryAddValue(parameters,
76			kSecCodeSignerRequirements, readRequirement(internalReq, 0));
77
78	if (signatureSize)
79		CFDictionaryAddValue(parameters, CFSTR("cmssize"), CFTempNumber(signatureSize));
80	if (pagesize != pagesizeUnspecified)
81		CFDictionaryAddValue(parameters, kSecCodeSignerPageSize, CFTempNumber(pagesize));
82	if (cdFlags)
83		CFDictionaryAddValue(parameters, kSecCodeSignerFlags, CFTempNumber(cdFlags));
84	if (digestAlgorithm)
85		CFDictionaryAddValue(parameters, kSecCodeSignerDigestAlgorithm, CFTempNumber(digestAlgorithm));
86	if (preserveMetadata)
87		CFDictionaryAddValue(parameters, kSecCodeSignerPreserveMetadata, CFTempNumber(preserveMetadata));
88	if (signingTime)
89		CFDictionaryAddValue(parameters, kSecCodeSignerSigningTime, signingTime);
90
91	if (detached)
92		CFDictionaryAddValue(parameters, kSecCodeSignerDetached, CFTempURL(detached));
93	else if (detachedDb)
94		CFDictionaryAddValue(parameters, kSecCodeSignerDetached, kCFNull);
95
96    if (timestampRequest) {
97		CFDictionaryAddValue(parameters, kSecCodeSignerRequireTimestamp, timestampRequest);
98		if (signingTime && signingTime != CFDateRef(kCFNull))
99			fail("explicit signing time not allowed with timestamp service");
100	}
101	if (tsaURL)
102		CFDictionaryAddValue(parameters, kSecCodeSignerTimestampServer, CFTempURL(tsaURL));
103    if (noTSAcerts)
104		CFDictionaryAddValue(parameters, kSecCodeSignerTimestampOmitCertificates, kCFBooleanTrue);
105
106	if (resourceRules) {
107		if (CFRef<CFDataRef> data = cfLoadFile(resourceRules)) {
108			CFDictionaryAddValue(parameters, kSecCodeSignerResourceRules,
109				CFRef<CFDictionaryRef>(makeCFDictionaryFrom(data)));
110		} else
111			fail("%s: cannot read resources", resourceRules);
112	}
113
114	if (!sdkRoot)
115		if (const char *envroot = getenv("SDKDIR"))
116			sdkRoot = envroot;
117	if (sdkRoot) {
118		struct stat st;
119		if (::stat(sdkRoot, &st))
120			fail("%s: %s", sdkRoot, strerror(errno));
121		CFDictionaryAddValue(parameters, kSecCodeSignerSDKRoot, CFTempURL(sdkRoot, true));
122	}
123
124	if (entitlements) {
125		if (CFRef<CFDataRef> data = cfLoadFile(entitlements)) {	// load the proposed entitlement blob
126			if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(data)) {
127				// plain plist - (silently) wrap into canonical blob form
128				BlobWrapper *wrap = BlobWrapper::alloc(CFDataGetBytePtr(data), CFDataGetLength(data), kSecCodeMagicEntitlement);
129				CFDictionaryAddValue(parameters, kSecCodeSignerEntitlements, CFTempData(*(BlobCore*)wrap));
130				::free(wrap);
131			} else {
132				const BlobCore *blob = reinterpret_cast<const BlobCore *>(CFDataGetBytePtr(data));
133				if (blob->magic() != kSecCodeMagicEntitlement)
134					note(0, "%s: unrecognized blob type (accepting blindly)", entitlements);
135				if (blob->length() != CFDataGetLength(data))
136					fail("%s: invalid length in entitlement blob", entitlements);
137				CFDictionaryAddValue(parameters, kSecCodeSignerEntitlements, CFTempData(*blob));
138			}
139		} else
140			fail("%s: cannot read entitlement data", entitlements);
141	}
142
143	if (dryrun)
144		CFDictionaryAddValue(parameters, kSecCodeSignerDryRun, kCFBooleanTrue);
145
146	MacOSError::check(SecCodeSignerCreate(parameters, flags, &signerRef));
147}
148
149
150//
151// Sign a code object.
152//
153void sign(const char *target)
154{
155	secdebug("codesign", "BEGIN SIGNING %s", target);
156
157	CFRef<SecStaticCodeRef> code = staticCodePath(target, architecture, bundleVersion);
158
159	// check previous signature (if any) and collect some data from it
160	CFRef<CFDictionaryRef> dict;
161	switch (OSStatus rc = SecCodeCopySigningInformation(code,
162		preserveMetadata ? SecCSFlags(kSecCSRequirementInformation|kSecCSInternalInformation) : kSecCSDefaultFlags,
163		&dict.aref())) {
164	case noErr:
165		if (CFDictionaryGetValue(dict, kSecCodeInfoIdentifier)) {	// binary is signed
166			if (detached || detachedDb)
167				note(0, "%s: not disturbing embedded signature", target);
168			else if (force)
169				note(0, "%s: replacing existing signature", target);
170			else if (signer)
171				fail("%s: is already signed", target);
172		}
173		break;
174	default:
175		if (detached)
176			note(0, "%s: ignoring invalid embedded signature", target);
177		else if (force)
178			note(0, "%s: replacing invalid existing signature", target);
179		else if (signer)
180			fail("%s: is already signed", target);
181		break;
182	}
183
184	// add target-specific signing inputs, mostly carried from a prior signature
185	CFCopyRef<SecCodeSignerRef> currentSigner = signerRef;		// the one we prepared during setup
186
187	// do the deed
188	ErrorCheck check;
189	check(SecCodeSignerAddSignatureWithErrors(currentSigner, code, kSecCSDefaultFlags, check));
190
191	// collect some resulting information and deliver it to the user
192	SecCSFlags flags = kSecCSSigningInformation;
193	if (modifiedFiles)
194		flags |= kSecCSContentInformation;
195	MacOSError::check(SecCodeCopySigningInformation(code, flags, &dict.aref()));
196	note(1, "%s: signed %s [%s]", target,
197		cfString(CFStringRef(CFDictionaryGetValue(dict, kSecCodeInfoFormat))).c_str(),
198		cfString(CFStringRef(CFDictionaryGetValue(dict, kSecCodeInfoIdentifier))).c_str()
199	);
200
201	CFRef<CFLocaleRef> userLocale = CFLocaleCopyCurrent();
202	CFRef<CFDateFormatterRef> format = CFDateFormatterCreate(NULL, userLocale,
203		kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
204	CFDateRef softTime = CFDateRef(CFDictionaryGetValue(dict, kSecCodeInfoTime));
205	CFDateRef hardTime = CFDateRef(CFDictionaryGetValue(dict, kSecCodeInfoTimestamp));
206	if (hardTime) {
207		if (softTime) {
208			CFAbsoluteTime slop = abs(CFDateGetAbsoluteTime(softTime) - CFDateGetAbsoluteTime(hardTime));
209			if (slop > timestampSlop)
210				fail("%s: timestamps differ by %g seconds - check your system clock", target, slop);
211		} else {
212			CFAbsoluteTime slop = abs(CFAbsoluteTimeGetCurrent() - CFDateGetAbsoluteTime(hardTime));
213			if (slop > timestampSlop)
214				note(0, "%s: WARNING: local time diverges from timestamp by %g seconds - check your system clock", target, slop);
215		}
216	}
217
218	if (modifiedFiles)
219		writeFileList(CFArrayRef(CFDictionaryGetValue(dict, kSecCodeInfoChangedFiles)), modifiedFiles, "a");
220}
221