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// CodeSigner - SecCodeSigner API objects
26//
27#include "CodeSigner.h"
28#include "signer.h"
29#include "csdatabase.h"
30#include "drmaker.h"
31#include "csutilities.h"
32#include <security_utilities/unix++.h>
33#include <security_utilities/unixchild.h>
34#include <Security/SecCertificate.h>
35#include <Security/SecCertificatePriv.h>
36#include <vector>
37
38namespace Security {
39
40__SEC_CFTYPE(SecIdentity)
41
42namespace CodeSigning {
43
44using namespace UnixPlusPlus;
45
46
47//
48// A helper for parsing out a CFDictionary signing-data specification
49//
50class SecCodeSigner::Parser : CFDictionary {
51public:
52	Parser(SecCodeSigner &signer, CFDictionaryRef parameters);
53
54	bool getBool(CFStringRef key) const
55	{
56		if (CFBooleanRef flag = get<CFBooleanRef>(key))
57			return flag == kCFBooleanTrue;
58		else
59			return false;
60	}
61};
62
63
64//
65// Construct a SecCodeSigner
66//
67SecCodeSigner::SecCodeSigner(SecCSFlags flags)
68	: mOpFlags(flags), mDigestAlgorithm(kSecCodeSignatureDefaultDigestAlgorithm)
69{
70}
71
72
73//
74// Clean up a SecCodeSigner
75//
76SecCodeSigner::~SecCodeSigner() throw()
77try {
78} catch (...) {
79	return;
80}
81
82
83//
84// Parse an input parameter dictionary and set ready-to-use parameters
85//
86void SecCodeSigner::parameters(CFDictionaryRef paramDict)
87{
88	Parser(*this, paramDict);
89	if (!valid())
90		MacOSError::throwMe(errSecCSInvalidObjectRef);
91}
92
93//
94// Retrieve the team ID from the signing certificate if and only if
95// it is an apple developer signing cert
96//
97std::string SecCodeSigner::getTeamIDFromSigner(CFArrayRef certs)
98{
99	if (mSigner && mSigner != SecIdentityRef(kCFNull)) {
100		CFRef<SecCertificateRef> signerCert;
101		MacOSError::check(SecIdentityCopyCertificate(mSigner, &signerCert.aref()));
102
103		/* Make sure the certificate looks like an Apple certificate, because we do not
104			extract the team ID from a non Apple certificate */
105		if (SecStaticCode::isAppleDeveloperCert(certs)) {
106			CFRef<CFStringRef> teamIDFromCert;
107
108			MacOSError::check(SecCertificateCopySubjectComponent(signerCert.get(), &CSSMOID_OrganizationalUnitName, &teamIDFromCert.aref()));
109
110			if (teamIDFromCert)
111				return cfString(teamIDFromCert);
112		}
113	}
114
115	return "";
116}
117
118//
119// Roughly check for validity.
120// This isn't thorough; it just sees if if looks like we've set up the object appropriately.
121//
122bool SecCodeSigner::valid() const
123{
124	if (mOpFlags & kSecCSRemoveSignature)
125		return true;
126	return mSigner;
127}
128
129
130//
131// Sign code
132//
133void SecCodeSigner::sign(SecStaticCode *code, SecCSFlags flags)
134{
135	code->setValidationFlags(flags);
136	if (code->isSigned() && (flags & kSecCSSignPreserveSignature))
137		return;
138	Signer operation(*this, code);
139	if ((flags | mOpFlags) & kSecCSRemoveSignature) {
140		secdebug("signer", "%p will remove signature from %p", this, code);
141		operation.remove(flags);
142	} else {
143		if (!valid())
144			MacOSError::throwMe(errSecCSInvalidObjectRef);
145		secdebug("signer", "%p will sign %p (flags 0x%x)", this, code, flags);
146		operation.sign(flags);
147	}
148	code->resetValidity();
149}
150
151
152//
153// ReturnDetachedSignature is called by writers or editors that try to return
154// detached signature data (rather than annotate the target).
155//
156void SecCodeSigner::returnDetachedSignature(BlobCore *blob, Signer &signer)
157{
158	assert(mDetached);
159	if (CFGetTypeID(mDetached) == CFURLGetTypeID()) {
160		// URL to destination file
161		AutoFileDesc fd(cfString(CFURLRef(mDetached.get())), O_WRONLY | O_CREAT | O_TRUNC);
162		fd.writeAll(*blob);
163	} else if (CFGetTypeID(mDetached) == CFDataGetTypeID()) {
164		CFDataAppendBytes(CFMutableDataRef(mDetached.get()),
165			(const UInt8 *)blob, blob->length());
166	} else if (CFGetTypeID(mDetached) == CFNullGetTypeID()) {
167		SignatureDatabaseWriter db;
168		db.storeCode(blob, signer.path().c_str());
169	} else
170		assert(false);
171}
172
173
174//
175// Our DiskRep::signingContext methods communicate with the signing subsystem
176// in terms those callers can easily understand.
177//
178string SecCodeSigner::sdkPath(const std::string &path) const
179{
180	assert(path[0] == '/');	// need absolute path here
181	if (mSDKRoot)
182		return cfString(mSDKRoot) + path;
183	else
184		return path;
185}
186
187bool SecCodeSigner::isAdhoc() const
188{
189	return mSigner == SecIdentityRef(kCFNull);
190}
191
192SecCSFlags SecCodeSigner::signingFlags() const
193{
194	return mOpFlags;
195}
196
197
198//
199// The actual parsing operation is done in the Parser class.
200//
201// Note that we need to copy or retain all incoming data. The caller has no requirement
202// to keep the parameters dictionary around.
203//
204SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
205	: CFDictionary(parameters, errSecCSBadDictionaryFormat)
206{
207	// the signer may be an identity or null
208	state.mSigner = SecIdentityRef(get<CFTypeRef>(kSecCodeSignerIdentity));
209	if (state.mSigner)
210		if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull))
211			MacOSError::throwMe(errSecCSInvalidObjectRef);
212
213	// the flags need some augmentation
214	if (CFNumberRef flags = get<CFNumberRef>(kSecCodeSignerFlags)) {
215		state.mCdFlagsGiven = true;
216		state.mCdFlags = cfNumber<uint32_t>(flags);
217	} else
218		state.mCdFlagsGiven = false;
219
220	// digest algorithms are specified as a numeric code
221	if (CFNumberRef digestAlgorithm = get<CFNumberRef>(kSecCodeSignerDigestAlgorithm))
222		state.mDigestAlgorithm = cfNumber<unsigned int>(digestAlgorithm);
223
224	if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
225		state.mCMSSize = cfNumber<size_t>(cmsSize);
226	else
227		state.mCMSSize = 9000;	// likely big enough
228
229	// metadata preservation options
230	if (CFNumberRef preserve = get<CFNumberRef>(kSecCodeSignerPreserveMetadata)) {
231		state.mPreserveMetadata = cfNumber<uint32_t>(preserve);
232	} else
233		state.mPreserveMetadata = 0;
234
235
236	// signing time can be a CFDateRef or null
237	if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime)) {
238		if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
239			state.mSigningTime = CFDateRef(time);
240		else
241			MacOSError::throwMe(errSecCSInvalidObjectRef);
242	}
243
244	if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
245		state.mIdentifier = cfString(ident);
246
247	if (CFStringRef teamid = get<CFStringRef>(kSecCodeSignerTeamIdentifier))
248		state.mTeamID = cfString(teamid);
249
250	if (CFStringRef prefix = get<CFStringRef>(kSecCodeSignerIdentifierPrefix))
251		state.mIdentifierPrefix = cfString(prefix);
252
253	// Requirements can be binary or string (to be compiled).
254	// We must pass them along to the signer for possible text substitution
255	if (CFTypeRef reqs = get<CFTypeRef>(kSecCodeSignerRequirements)) {
256		if (CFGetTypeID(reqs) == CFDataGetTypeID() || CFGetTypeID(reqs) == CFStringGetTypeID())
257			state.mRequirements = reqs;
258		else
259			MacOSError::throwMe(errSecCSInvalidObjectRef);
260	} else
261		state.mRequirements = NULL;
262
263	state.mNoMachO = getBool(CFSTR("no-macho"));
264
265	state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize);
266
267	// detached can be (destination) file URL or (mutable) Data to be appended-to
268	if ((state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached))) {
269		CFTypeID type = CFGetTypeID(state.mDetached);
270		if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID())
271			MacOSError::throwMe(errSecCSInvalidObjectRef);
272	}
273
274	state.mDryRun = getBool(kSecCodeSignerDryRun);
275
276	state.mResourceRules = get<CFDictionaryRef>(kSecCodeSignerResourceRules);
277
278	state.mApplicationData = get<CFDataRef>(kSecCodeSignerApplicationData);
279	state.mEntitlementData = get<CFDataRef>(kSecCodeSignerEntitlements);
280
281	state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot);
282
283	if (CFBooleanRef timestampRequest = get<CFBooleanRef>(kSecCodeSignerRequireTimestamp)) {
284		state.mWantTimeStamp = timestampRequest == kCFBooleanTrue;
285	} else {	// pick default
286		state.mWantTimeStamp = false;
287		if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) {
288			CFRef<SecCertificateRef> signerCert;
289			MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref()));
290			if (certificateHasField(signerCert, devIdLeafMarkerOID))
291				state.mWantTimeStamp = true;
292		}
293	}
294	state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication);
295	state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer);
296	state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates);
297}
298
299
300} // end namespace CodeSigning
301} // end namespace Security
302