]> git.saurik.com Git - apple/security.git/blob - libsecurity_manifest/lib/Download.cpp
43c91ed9b5b547c199baa24c1ceee5feccf1fd97
[apple/security.git] / libsecurity_manifest / lib / Download.cpp
1 /*
2 * Copyright (c) 2006 Apple Computer, 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 #include <CoreFoundation/CoreFoundation.h>
25 #include <CommonCrypto/CommonDigest.h>
26
27 #include <Security/Security.h>
28 #include <security_utilities/security_utilities.h>
29 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
30 #include <security_cdsa_utilities/cssmbridge.h>
31 #include <Security/cssmapplePriv.h>
32
33 #include "SecureDownload.h"
34 #include "SecureDownloadInternal.h"
35 #include "Download.h"
36
37
38
39 static void CheckCFThingForNULL (CFTypeRef theType)
40 {
41 if (theType == NULL)
42 {
43 CFError::throwMe ();
44 }
45 }
46
47
48
49 Download::Download () : mDict (NULL), mURLs (NULL), mName (NULL), mDate (NULL), mHashes (NULL), mNumHashes (0), mCurrentHash (0), mBytesInCurrentDigest (0)
50 {
51 }
52
53
54
55 static void ReleaseIfNotNull (CFTypeRef theThing)
56 {
57 if (theThing != NULL)
58 {
59 CFRelease (theThing);
60 }
61 }
62
63
64
65 Download::~Download ()
66 {
67 ReleaseIfNotNull (mDict);
68 }
69
70
71
72 CFArrayRef Download::CopyURLs ()
73 {
74 CFRetain (mURLs);
75 return mURLs;
76 }
77
78
79
80 CFStringRef Download::CopyName ()
81 {
82 CFRetain (mName);
83 return mName;
84 }
85
86
87
88 CFDateRef Download::CopyDate ()
89 {
90 CFRetain (mDate);
91 return mDate;
92 }
93
94
95
96 void Download::GoOrNoGo (SecTrustResultType result)
97 {
98 switch (result)
99 {
100 case kSecTrustResultInvalid:
101 case kSecTrustResultDeny:
102 case kSecTrustResultFatalTrustFailure:
103 case kSecTrustResultOtherError:
104 MacOSError::throwMe (errSecureDownloadInvalidTicket);
105
106 case kSecTrustResultProceed:
107 return;
108
109 // we would normally ask for the user's permission in these cases.
110 // we don't in this case, as the Apple signing root had better be
111 // in X509 anchors. I'm leaving this broken out for ease of use
112 // in case we change our minds...
113 case kSecTrustResultConfirm:
114 case kSecTrustResultRecoverableTrustFailure:
115 case kSecTrustResultUnspecified:
116 {
117 MacOSError::throwMe (errSecureDownloadInvalidTicket);
118 }
119
120 default:
121 break;
122 }
123 }
124
125
126
127 SecPolicyRef Download::GetPolicy ()
128 {
129 SecPolicySearchRef search;
130 SecPolicyRef policy;
131 OSStatus result;
132
133 // get the policy for resource signing
134 result = SecPolicySearchCreate (CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_RESOURCE_SIGN, NULL, &search);
135 if (result != noErr)
136 {
137 MacOSError::throwMe (result);
138 }
139
140 result = SecPolicySearchCopyNext (search, &policy);
141 if (result != noErr)
142 {
143 MacOSError::throwMe (result);
144 }
145
146 CFRelease (search);
147
148 return policy;
149 }
150
151
152
153 #define SHA256_NAME CFSTR("SHA-256")
154
155 void Download::ParseTicket (CFDataRef ticket)
156 {
157 // make a propertylist from the ticket
158 CFDictionaryRef mDict = (CFDictionaryRef) _SecureDownloadParseTicketXML (ticket);
159 CheckCFThingForNULL (mDict);
160 CFRetain (mDict);
161
162 mURLs = (CFArrayRef) CFDictionaryGetValue (mDict, SD_XML_URL);
163 CheckCFThingForNULL (mURLs);
164
165 // get the download name
166 mName = (CFStringRef) CFDictionaryGetValue (mDict, SD_XML_NAME);
167 CheckCFThingForNULL (mName);
168
169 // get the download date
170 mDate = (CFDateRef) CFDictionaryGetValue (mDict, SD_XML_CREATED);
171 CheckCFThingForNULL (mDate);
172
173 // get the download size
174 CFNumberRef number = (CFNumberRef) CFDictionaryGetValue (mDict, SD_XML_SIZE);
175 CFNumberGetValue (number, kCFNumberSInt64Type, &mDownloadSize);
176
177 // get the verifications dictionary
178 CFDictionaryRef verifications = (CFDictionaryRef) CFDictionaryGetValue (mDict, SD_XML_VERIFICATIONS);
179
180 // from the verifications dictionary, get the hashing dictionary that we support
181 CFDictionaryRef hashInfo = (CFDictionaryRef) CFDictionaryGetValue (verifications, SHA256_NAME);
182
183 // from the hashing dictionary, get the sector size
184 number = (CFNumberRef) CFDictionaryGetValue (hashInfo, SD_XML_SECTOR_SIZE);
185 CFNumberGetValue (number, kCFNumberSInt32Type, &mSectorSize);
186
187 // get the hashes
188 mHashes = (CFDataRef) CFDictionaryGetValue (hashInfo, SD_XML_DIGESTS);
189 CFIndex hashSize = CFDataGetLength (mHashes);
190 mNumHashes = hashSize / CC_SHA256_DIGEST_LENGTH;
191 mDigests = (Sha256Digest*) CFDataGetBytePtr (mHashes);
192 mCurrentHash = 0;
193 mBytesInCurrentDigest = 0;
194 }
195
196
197
198 void Download::Initialize (CFDataRef ticket,
199 SecureDownloadTrustSetupCallback setup,
200 void* setupContext,
201 SecureDownloadTrustEvaluateCallback evaluate,
202 void* evaluateContext)
203 {
204 // decode the ticket
205 SecCmsMessageRef cmsMessage = GetCmsMessageFromData (ticket);
206
207 // get a policy
208 SecPolicyRef policy = GetPolicy ();
209
210 // parse the CMS message
211 int contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
212 SecCmsSignedDataRef signedData;
213
214 OSStatus result;
215
216 int i = 0;
217 while (i < contentLevelCount)
218 {
219 SecCmsContentInfoRef contentInfo = SecCmsMessageContentLevel (cmsMessage, i++);
220 SECOidTag contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
221
222 if (contentTypeTag != SEC_OID_PKCS7_SIGNED_DATA)
223 {
224 continue;
225 }
226
227 signedData = (SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
228 if (signedData == NULL)
229 {
230 MacOSError::throwMe (errSecureDownloadInvalidTicket);
231 }
232
233 // import the certificates found in the cms message
234 result = SecCmsSignedDataImportCerts (signedData, NULL, certUsageObjectSigner, true);
235 if (result != 0)
236 {
237 MacOSError::throwMe (errSecureDownloadInvalidTicket);
238 }
239
240 int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
241 int j;
242
243 if (numberOfSigners == 0) // no signers? This is a possible attack
244 {
245 MacOSError::throwMe (errSecureDownloadInvalidTicket);
246 }
247
248 for (j = 0; j < numberOfSigners; ++j)
249 {
250 SecTrustResultType resultType;
251
252 // do basic verification of the message
253 SecTrustRef trustRef;
254 result = SecCmsSignedDataVerifySignerInfo (signedData, j, NULL, policy, &trustRef);
255
256 // notify the user of the new trust ref
257 if (setup != NULL)
258 {
259 SecureDownloadTrustCallbackResult tcResult = setup (trustRef, setupContext);
260 switch (tcResult)
261 {
262 case kSecureDownloadDoNotEvaluateSigner:
263 continue;
264
265 case kSecureDownloadFailEvaluation:
266 MacOSError::throwMe (errSecureDownloadInvalidTicket);
267
268 case kSecureDownloadEvaluateSigner:
269 break;
270 }
271 }
272
273 if (result != 0)
274 {
275 MacOSError::throwMe (errSecureDownloadInvalidTicket);
276 }
277
278 result = SecTrustEvaluate (trustRef, &resultType);
279 if (result != noErr)
280 {
281 MacOSError::throwMe (errSecureDownloadInvalidTicket);
282 }
283
284 if (evaluate != NULL)
285 {
286 resultType = evaluate (trustRef, resultType, evaluateContext);
287 }
288
289 GoOrNoGo (resultType);
290 }
291 }
292
293 // extract the message
294 CSSM_DATA_PTR message = SecCmsMessageGetContent (cmsMessage);
295 CFDataRef ticketData = CFDataCreateWithBytesNoCopy (NULL, message->Data, message->Length, kCFAllocatorNull);
296 CheckCFThingForNULL (ticketData);
297
298 ParseTicket (ticketData);
299
300 // setup for hashing
301 CC_SHA256_Init (&mSHA256Context);
302
303 // clean up
304 CFRelease (ticketData);
305 SecCmsMessageDestroy (cmsMessage);
306 }
307
308
309
310 SecCmsMessageRef Download::GetCmsMessageFromData (CFDataRef data)
311 {
312 // setup decoding
313 SecCmsDecoderRef decoderContext;
314 int result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext);
315 if (result)
316 {
317 MacOSError::throwMe (errSecureDownloadInvalidTicket);
318 }
319
320 result = SecCmsDecoderUpdate (decoderContext, CFDataGetBytePtr (data), CFDataGetLength (data));
321 if (result)
322 {
323 SecCmsDecoderDestroy(decoderContext);
324 MacOSError::throwMe (errSecureDownloadInvalidTicket);
325 }
326
327 SecCmsMessageRef message;
328 result = SecCmsDecoderFinish (decoderContext, &message);
329 if (result)
330 {
331 MacOSError::throwMe (errSecureDownloadInvalidTicket);
332 }
333
334 return message;
335 }
336
337
338
339 size_t MinSizeT (size_t a, size_t b)
340 {
341 // return the smaller of a and b
342 return a < b ? a : b;
343 }
344
345
346
347 void Download::FinalizeDigestAndCompare ()
348 {
349 Sha256Digest digest;
350 CC_SHA256_Final (digest, &mSHA256Context);
351
352 // make sure we don't overflow the digest buffer
353 if (mCurrentHash >= mNumHashes || memcmp (digest, mDigests[mCurrentHash++], CC_SHA256_DIGEST_LENGTH) != 0)
354 {
355 // Something's really wrong!
356 MacOSError::throwMe (errSecureDownloadInvalidDownload);
357 }
358
359 // setup for the next receipt of data
360 mBytesInCurrentDigest = 0;
361 CC_SHA256_Init (&mSHA256Context);
362 }
363
364
365
366 void Download::UpdateWithData (CFDataRef data)
367 {
368 // figure out how much data to hash
369 CFIndex dataLength = CFDataGetLength (data);
370 const UInt8* finger = CFDataGetBytePtr (data);
371
372 while (dataLength > 0)
373 {
374 // figure out how many bytes are left to hash
375 size_t bytesLeftToHash = mSectorSize - mBytesInCurrentDigest;
376 size_t bytesToHash = MinSizeT (bytesLeftToHash, dataLength);
377
378 // hash the data
379 CC_SHA256_Update (&mSHA256Context, finger, bytesToHash);
380
381 // update the pointers
382 mBytesInCurrentDigest += bytesToHash;
383 bytesLeftToHash -= bytesToHash;
384 finger += bytesToHash;
385 dataLength -= bytesToHash;
386
387 if (bytesLeftToHash == 0) // is our digest "full"?
388 {
389 FinalizeDigestAndCompare ();
390 }
391 }
392 }
393
394
395
396 void Download::Finalize ()
397 {
398 // are there any bytes left over in the digest?
399 if (mBytesInCurrentDigest != 0)
400 {
401 FinalizeDigestAndCompare ();
402 }
403
404 if (mCurrentHash != mNumHashes) // check for underflow
405 {
406 MacOSError::throwMe (errSecureDownloadInvalidDownload);
407 }
408 }
409