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