]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/CodeSigner.cpp
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / CodeSigner.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 // 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 #include <errno.h>
38
39 namespace Security {
40
41 __SEC_CFTYPE(SecIdentity)
42
43 namespace CodeSigning {
44
45 using namespace UnixPlusPlus;
46
47
48 //
49 // A helper for parsing out a CFDictionary signing-data specification
50 //
51 class SecCodeSigner::Parser : CFDictionary {
52 public:
53 Parser(SecCodeSigner &signer, CFDictionaryRef parameters);
54
55 bool getBool(CFStringRef key) const
56 {
57 if (CFBooleanRef flag = get<CFBooleanRef>(key))
58 return flag == kCFBooleanTrue;
59 else
60 return false;
61 }
62
63 uint32_t parseRuntimeVersion(std::string& runtime)
64 {
65 uint32_t version = 0;
66 char* cursor = const_cast<char*>(runtime.c_str());
67 char* end = cursor + runtime.length();
68 char* nxt = NULL;
69 long component = 0;
70 int component_shift = 16;
71
72 // x should convert to 0x00XX0000
73 // x.y should convert to 0x00XXYY00
74 // x.y.z should covert to 0x00XXYYZZ
75 // 0, 0.0, and 0.0.0 are rejected
76 // anything else should be rejected
77 while (cursor < end) {
78 nxt = NULL;
79 errno = 0;
80 component = strtol(cursor, &nxt, 10);
81 if (cursor == nxt ||
82 (errno != 0) ||
83 (component < 0 || component > UINT8_MAX)) {
84 secdebug("signer", "Runtime version: %s is invalid", runtime.c_str());
85 MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
86 }
87 version |= (component & 0xff) << component_shift;
88 component_shift -= 8;
89
90 if (*nxt == '\0') {
91 break;
92 }
93
94 if (*nxt != '.' || component_shift < 0 || (nxt + 1) == end) {
95 // Catch a trailing "."
96 secdebug("signer", "Runtime version: %s is invalid", runtime.c_str());
97 MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
98 }
99 cursor = nxt + 1;
100 }
101
102 if (version == 0) {
103 secdebug("signer","Runtime version: %s is a version of zero", runtime.c_str());
104 MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
105 }
106
107 return version;
108 }
109 };
110
111
112 //
113 // Construct a SecCodeSigner
114 //
115 SecCodeSigner::SecCodeSigner(SecCSFlags flags)
116 : mOpFlags(flags), mLimitedAsync(NULL), mRuntimeVersionOverride(0)
117 {
118 }
119
120
121 //
122 // Clean up a SecCodeSigner
123 //
124 SecCodeSigner::~SecCodeSigner() _NOEXCEPT
125 try {
126 delete mLimitedAsync;
127 } catch (...) {
128 return;
129 }
130
131
132 //
133 // Parse an input parameter dictionary and set ready-to-use parameters
134 //
135 void SecCodeSigner::parameters(CFDictionaryRef paramDict)
136 {
137 Parser(*this, paramDict);
138 if (!valid())
139 MacOSError::throwMe(errSecCSInvalidObjectRef);
140 }
141
142 //
143 // Retrieve the team ID from the signing certificate if and only if
144 // it is an apple developer signing cert
145 //
146 std::string SecCodeSigner::getTeamIDFromSigner(CFArrayRef certs)
147 {
148 if (mSigner && mSigner != SecIdentityRef(kCFNull)) {
149 CFRef<SecCertificateRef> signerCert;
150 MacOSError::check(SecIdentityCopyCertificate(mSigner, &signerCert.aref()));
151
152 /* Make sure the certificate looks like an Apple certificate, because we do not
153 extract the team ID from a non Apple certificate */
154 if (SecStaticCode::isAppleDeveloperCert(certs)) {
155 CFRef<CFStringRef> teamIDFromCert;
156
157 MacOSError::check(SecCertificateCopySubjectComponent(signerCert.get(), &CSSMOID_OrganizationalUnitName, &teamIDFromCert.aref()));
158
159 if (teamIDFromCert)
160 return cfString(teamIDFromCert);
161 }
162 }
163
164 return "";
165 }
166
167 //
168 // Roughly check for validity.
169 // This isn't thorough; it just sees if if looks like we've set up the object appropriately.
170 //
171 bool SecCodeSigner::valid() const
172 {
173 if (mOpFlags & (kSecCSRemoveSignature | kSecCSEditSignature)) {
174 return true;
175 }
176 return mSigner;
177 }
178
179
180 //
181 // Sign code
182 //
183 void SecCodeSigner::sign(SecStaticCode *code, SecCSFlags flags)
184 {
185 //Never preserve a linker signature.
186 if (code->isSigned() &&
187 (flags & kSecCSSignPreserveSignature) &&
188 !code->flag(kSecCodeSignatureLinkerSigned)) {
189 return;
190 }
191 code->setValidationFlags(flags);
192 Signer operation(*this, code);
193 if ((flags | mOpFlags) & kSecCSRemoveSignature) {
194 secinfo("signer", "%p will remove signature from %p", this, code);
195 operation.remove(flags);
196 } else if ((flags | mOpFlags) & kSecCSEditSignature) {
197 secinfo("signer", "%p will edit signature of %p", this, code);
198 operation.edit(flags);
199 } else {
200 if (!valid())
201 MacOSError::throwMe(errSecCSInvalidObjectRef);
202 secinfo("signer", "%p will sign %p (flags 0x%x)", this, code, flags);
203 operation.sign(flags);
204 }
205 code->resetValidity();
206 }
207
208
209 //
210 // ReturnDetachedSignature is called by writers or editors that try to return
211 // detached signature data (rather than annotate the target).
212 //
213 void SecCodeSigner::returnDetachedSignature(BlobCore *blob, Signer &signer)
214 {
215 assert(mDetached);
216 if (CFGetTypeID(mDetached) == CFURLGetTypeID()) {
217 // URL to destination file
218 AutoFileDesc fd(cfString(CFURLRef(mDetached.get())), O_WRONLY | O_CREAT | O_TRUNC);
219 fd.writeAll(*blob);
220 } else if (CFGetTypeID(mDetached) == CFDataGetTypeID()) {
221 CFDataAppendBytes(CFMutableDataRef(mDetached.get()),
222 (const UInt8 *)blob, blob->length());
223 } else if (CFGetTypeID(mDetached) == CFNullGetTypeID()) {
224 SignatureDatabaseWriter db;
225 db.storeCode(blob, signer.path().c_str());
226 } else
227 assert(false);
228 }
229
230
231 //
232 // The actual parsing operation is done in the Parser class.
233 //
234 // Note that we need to copy or retain all incoming data. The caller has no requirement
235 // to keep the parameters dictionary around.
236 //
237 SecCodeSigner::Parser::Parser(SecCodeSigner &state, CFDictionaryRef parameters)
238 : CFDictionary(parameters, errSecCSBadDictionaryFormat)
239 {
240 CFNumberRef editCpuType = get<CFNumberRef>(kSecCodeSignerEditCpuType);
241 CFNumberRef editCpuSubtype = get<CFNumberRef>(kSecCodeSignerEditCpuSubtype);
242 if (editCpuType != NULL && editCpuSubtype != NULL) {
243 state.mEditArch = Architecture(cfNumber<uint32_t>(editCpuType),
244 cfNumber<uint32_t>(editCpuSubtype));
245 }
246
247 state.mEditCMS = get<CFDataRef>(kSecCodeSignerEditCMS);
248
249 state.mDryRun = getBool(kSecCodeSignerDryRun);
250
251 state.mSDKRoot = get<CFURLRef>(kSecCodeSignerSDKRoot);
252
253 state.mPreserveAFSC = getBool(kSecCodeSignerPreserveAFSC);
254
255 if (state.mOpFlags & kSecCSEditSignature) {
256 return;
257 /* Everything below this point is irrelevant for
258 * Signature Editing, which does not create any
259 * parts of the signature, only replaces them.
260 */
261 }
262
263 // the signer may be an identity or null
264 state.mSigner = SecIdentityRef(get<CFTypeRef>(kSecCodeSignerIdentity));
265 if (state.mSigner)
266 if (CFGetTypeID(state.mSigner) != SecIdentityGetTypeID() && !CFEqual(state.mSigner, kCFNull))
267 MacOSError::throwMe(errSecCSInvalidObjectRef);
268
269 // the flags need some augmentation
270 if (CFNumberRef flags = get<CFNumberRef>(kSecCodeSignerFlags)) {
271 state.mCdFlagsGiven = true;
272 state.mCdFlags = cfNumber<uint32_t>(flags);
273 } else
274 state.mCdFlagsGiven = false;
275
276 // digest algorithms are specified as a numeric code
277 if (CFCopyRef<CFTypeRef> digestAlgorithms = get<CFTypeRef>(kSecCodeSignerDigestAlgorithm)) {
278 CFRef<CFArrayRef> array = cfArrayize(digestAlgorithms);
279 CFToVector<CodeDirectory::HashAlgorithm, CFNumberRef, cfNumber<CodeDirectory::HashAlgorithm> > digests(array);
280 std::copy(digests.begin(), digests.end(), std::inserter(state.mDigestAlgorithms, state.mDigestAlgorithms.begin()));
281 }
282
283 if (CFNumberRef cmsSize = get<CFNumberRef>(CFSTR("cmssize")))
284 state.mCMSSize = cfNumber<size_t>(cmsSize);
285 else
286 state.mCMSSize = 18000; // big enough for now, not forever.
287
288 // metadata preservation options
289 if (CFNumberRef preserve = get<CFNumberRef>(kSecCodeSignerPreserveMetadata)) {
290 state.mPreserveMetadata = cfNumber<uint32_t>(preserve);
291 } else
292 state.mPreserveMetadata = 0;
293
294 // signing time can be a CFDateRef or null
295 if (CFTypeRef time = get<CFTypeRef>(kSecCodeSignerSigningTime)) {
296 if (CFGetTypeID(time) == CFDateGetTypeID() || time == kCFNull)
297 state.mSigningTime = CFDateRef(time);
298 else
299 MacOSError::throwMe(errSecCSInvalidObjectRef);
300 }
301
302 if (CFStringRef ident = get<CFStringRef>(kSecCodeSignerIdentifier))
303 state.mIdentifier = cfString(ident);
304
305 if (CFStringRef teamid = get<CFStringRef>(kSecCodeSignerTeamIdentifier))
306 state.mTeamID = cfString(teamid);
307
308 if (CFNumberRef platform = get<CFNumberRef>(kSecCodeSignerPlatformIdentifier)) {
309 int64_t ident = cfNumber<int64_t>(platform);
310 if (ident < 0 || ident > maxPlatform) // overflow
311 MacOSError::throwMe(errSecCSInvalidPlatform);
312 state.mPlatform = ident;
313 }
314
315 if (CFStringRef prefix = get<CFStringRef>(kSecCodeSignerIdentifierPrefix))
316 state.mIdentifierPrefix = cfString(prefix);
317
318 // Requirements can be binary or string (to be compiled).
319 // We must pass them along to the signer for possible text substitution
320 if (CFTypeRef reqs = get<CFTypeRef>(kSecCodeSignerRequirements)) {
321 if (CFGetTypeID(reqs) == CFDataGetTypeID() || CFGetTypeID(reqs) == CFStringGetTypeID())
322 state.mRequirements = reqs;
323 else
324 MacOSError::throwMe(errSecCSInvalidObjectRef);
325 } else
326 state.mRequirements = NULL;
327
328 state.mNoMachO = getBool(CFSTR("no-macho"));
329
330 state.mPageSize = get<CFNumberRef>(kSecCodeSignerPageSize);
331
332 // detached can be (destination) file URL or (mutable) Data to be appended-to
333 if ((state.mDetached = get<CFTypeRef>(kSecCodeSignerDetached))) {
334 CFTypeID type = CFGetTypeID(state.mDetached);
335 if (type != CFURLGetTypeID() && type != CFDataGetTypeID() && type != CFNullGetTypeID())
336 MacOSError::throwMe(errSecCSInvalidObjectRef);
337 }
338
339 state.mResourceRules = get<CFDictionaryRef>(kSecCodeSignerResourceRules);
340
341 state.mApplicationData = get<CFDataRef>(kSecCodeSignerApplicationData);
342 state.mEntitlementData = get<CFDataRef>(kSecCodeSignerEntitlements);
343
344 if (CFBooleanRef timestampRequest = get<CFBooleanRef>(kSecCodeSignerRequireTimestamp)) {
345 state.mWantTimeStamp = timestampRequest == kCFBooleanTrue;
346 } else { // pick default
347 state.mWantTimeStamp = false;
348 if (state.mSigner && state.mSigner != SecIdentityRef(kCFNull)) {
349 CFRef<SecCertificateRef> signerCert;
350 MacOSError::check(SecIdentityCopyCertificate(state.mSigner, &signerCert.aref()));
351 if (certificateHasField(signerCert, devIdLeafMarkerOID))
352 state.mWantTimeStamp = true;
353 }
354 }
355 state.mTimestampAuthentication = get<SecIdentityRef>(kSecCodeSignerTimestampAuthentication);
356 state.mTimestampService = get<CFURLRef>(kSecCodeSignerTimestampServer);
357 state.mNoTimeStampCerts = getBool(kSecCodeSignerTimestampOmitCertificates);
358
359 if (CFStringRef runtimeVersionOverride = get<CFStringRef>(kSecCodeSignerRuntimeVersion)) {
360 std::string runtime = cfString(runtimeVersionOverride);
361 if (runtime.empty()) {
362 MacOSError::throwMe(errSecCSInvalidRuntimeVersion);
363 }
364 state.mRuntimeVersionOverride = parseRuntimeVersion(runtime);
365 }
366
367 // Don't add the adhoc flag, even if no signer identity was specified.
368 // Useful for editing in the CMS at a later point.
369 state.mOmitAdhocFlag = getBool(kSecCodeSignerOmitAdhocFlag);
370 }
371
372
373 } // end namespace CodeSigning
374 } // end namespace Security