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