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