]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_manifest/lib/Download.cpp
Security-58286.230.21.tar.gz
[apple/security.git] / OSX / 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 kSecTrustResultRecoverableTrustFailure:
113 case kSecTrustResultUnspecified:
114 {
115 MacOSError::throwMe (errSecureDownloadInvalidTicket);
116 }
117
118 default:
119 break;
120 }
121 }
122
123
124
125 SecPolicyRef Download::GetPolicy ()
126 {
127 SecPolicySearchRef search;
128 SecPolicyRef policy;
129 OSStatus result;
130
131 // get the policy for resource signing
132 result = SecPolicySearchCreate (CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_RESOURCE_SIGN, NULL, &search);
133 if (result != errSecSuccess)
134 {
135 MacOSError::throwMe (result);
136 }
137
138 result = SecPolicySearchCopyNext (search, &policy);
139 if (result != errSecSuccess)
140 {
141 MacOSError::throwMe (result);
142 }
143
144 CFRelease (search);
145
146 return policy;
147 }
148
149
150
151 #define SHA256_NAME CFSTR("SHA-256")
152
153 void Download::ParseTicket (CFDataRef ticket)
154 {
155 // make a propertylist from the ticket
156 CFRef<CFDictionaryRef> mDict = (CFDictionaryRef) _SecureDownloadParseTicketXML (ticket);
157 CheckCFThingForNULL (mDict);
158 CFRetain (mDict);
159
160 mURLs = (CFArrayRef) CFDictionaryGetValue (mDict, SD_XML_URL);
161 CheckCFThingForNULL (mURLs);
162
163 // get the download name
164 mName = (CFStringRef) CFDictionaryGetValue (mDict, SD_XML_NAME);
165 CheckCFThingForNULL (mName);
166
167 // get the download date
168 mDate = (CFDateRef) CFDictionaryGetValue (mDict, SD_XML_CREATED);
169 CheckCFThingForNULL (mDate);
170
171 // get the download size
172 CFNumberRef number = (CFNumberRef) CFDictionaryGetValue (mDict, SD_XML_SIZE);
173 CFNumberGetValue (number, kCFNumberSInt64Type, &mDownloadSize);
174
175 // get the verifications dictionary
176 CFDictionaryRef verifications = (CFDictionaryRef) CFDictionaryGetValue (mDict, SD_XML_VERIFICATIONS);
177
178 // from the verifications dictionary, get the hashing dictionary that we support
179 CFDictionaryRef hashInfo = (CFDictionaryRef) CFDictionaryGetValue (verifications, SHA256_NAME);
180
181 // from the hashing dictionary, get the sector size
182 number = (CFNumberRef) CFDictionaryGetValue (hashInfo, SD_XML_SECTOR_SIZE);
183 CFNumberGetValue (number, kCFNumberSInt64Type, &mSectorSize);
184
185 // get the hashes
186 mHashes = (CFDataRef) CFDictionaryGetValue (hashInfo, SD_XML_DIGESTS);
187 CFIndex hashSize = CFDataGetLength (mHashes);
188 mNumHashes = hashSize / CC_SHA256_DIGEST_LENGTH;
189 mDigests = (Sha256Digest*) CFDataGetBytePtr (mHashes);
190 mCurrentHash = 0;
191 mBytesInCurrentDigest = 0;
192 }
193
194
195
196 void Download::Initialize (CFDataRef ticket,
197 SecureDownloadTrustSetupCallback setup,
198 void* setupContext,
199 SecureDownloadTrustEvaluateCallback evaluate,
200 void* evaluateContext)
201 {
202 // decode the ticket
203 SecCmsMessageRef cmsMessage = GetCmsMessageFromData (ticket);
204
205 // get a policy
206 SecPolicyRef policy = GetPolicy ();
207
208 // parse the CMS message
209 int contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
210 SecCmsSignedDataRef signedData;
211
212 OSStatus result;
213
214 int i = 0;
215 while (i < contentLevelCount)
216 {
217 SecCmsContentInfoRef contentInfo = SecCmsMessageContentLevel (cmsMessage, i++);
218 SECOidTag contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
219
220 if (contentTypeTag != SEC_OID_PKCS7_SIGNED_DATA)
221 {
222 continue;
223 }
224
225 signedData = (SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
226 if (signedData == NULL)
227 {
228 MacOSError::throwMe (errSecureDownloadInvalidTicket);
229 }
230
231 // import the certificates found in the cms message
232 result = SecCmsSignedDataImportCerts (signedData, NULL, certUsageObjectSigner, true);
233 if (result != 0)
234 {
235 MacOSError::throwMe (errSecureDownloadInvalidTicket);
236 }
237
238 int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
239 int j;
240
241 if (numberOfSigners == 0) // no signers? This is a possible attack
242 {
243 MacOSError::throwMe (errSecureDownloadInvalidTicket);
244 }
245
246 for (j = 0; j < numberOfSigners; ++j)
247 {
248 SecTrustResultType resultType;
249
250 // do basic verification of the message
251 SecTrustRef trustRef;
252 result = SecCmsSignedDataVerifySignerInfo (signedData, j, NULL, policy, &trustRef);
253
254 // notify the user of the new trust ref
255 if (setup != NULL)
256 {
257 SecureDownloadTrustCallbackResult tcResult = setup (trustRef, setupContext);
258 switch (tcResult)
259 {
260 case kSecureDownloadDoNotEvaluateSigner:
261 continue;
262
263 case kSecureDownloadFailEvaluation:
264 MacOSError::throwMe (errSecureDownloadInvalidTicket);
265
266 case kSecureDownloadEvaluateSigner:
267 break;
268 }
269 }
270
271 if (result != 0)
272 {
273 MacOSError::throwMe (errSecureDownloadInvalidTicket);
274 }
275
276 result = SecTrustEvaluate (trustRef, &resultType);
277 if (result != errSecSuccess)
278 {
279 MacOSError::throwMe (errSecureDownloadInvalidTicket);
280 }
281
282 if (evaluate != NULL)
283 {
284 resultType = evaluate (trustRef, resultType, evaluateContext);
285 }
286
287 GoOrNoGo (resultType);
288 }
289 }
290
291 // extract the message
292 CSSM_DATA_PTR message = SecCmsMessageGetContent (cmsMessage);
293 CFDataRef ticketData = CFDataCreateWithBytesNoCopy (NULL, message->Data, message->Length, kCFAllocatorNull);
294 CheckCFThingForNULL (ticketData);
295
296 ParseTicket (ticketData);
297
298 // setup for hashing
299 CC_SHA256_Init (&mSHA256Context);
300
301 // clean up
302 CFRelease (ticketData);
303 SecCmsMessageDestroy (cmsMessage);
304 }
305
306
307
308 SecCmsMessageRef Download::GetCmsMessageFromData (CFDataRef data)
309 {
310 // setup decoding
311 SecCmsDecoderRef decoderContext;
312 int result = SecCmsDecoderCreate (NULL, NULL, NULL, NULL, NULL, NULL, NULL, &decoderContext);
313 if (result)
314 {
315 MacOSError::throwMe (errSecureDownloadInvalidTicket);
316 }
317
318 result = SecCmsDecoderUpdate (decoderContext, CFDataGetBytePtr (data), CFDataGetLength (data));
319 if (result)
320 {
321 SecCmsDecoderDestroy(decoderContext);
322 MacOSError::throwMe (errSecureDownloadInvalidTicket);
323 }
324
325 SecCmsMessageRef message;
326 result = SecCmsDecoderFinish (decoderContext, &message);
327 if (result)
328 {
329 MacOSError::throwMe (errSecureDownloadInvalidTicket);
330 }
331
332 return message;
333 }
334
335
336 static
337 size_t MinSizeT (size_t a, size_t b)
338 {
339 // return the smaller of a and b
340 return a < b ? a : b;
341 }
342
343
344
345 void Download::FinalizeDigestAndCompare ()
346 {
347 Sha256Digest digest;
348 CC_SHA256_Final (digest, &mSHA256Context);
349
350 // make sure we don't overflow the digest buffer
351 if (mCurrentHash >= mNumHashes || memcmp (digest, mDigests[mCurrentHash++], CC_SHA256_DIGEST_LENGTH) != 0)
352 {
353 // Something's really wrong!
354 MacOSError::throwMe (errSecureDownloadInvalidDownload);
355 }
356
357 // setup for the next receipt of data
358 mBytesInCurrentDigest = 0;
359 CC_SHA256_Init (&mSHA256Context);
360 }
361
362
363
364 void Download::UpdateWithData (CFDataRef data)
365 {
366 // figure out how much data to hash
367 CFIndex dataLength = CFDataGetLength (data);
368 const UInt8* finger = CFDataGetBytePtr (data);
369
370 while (dataLength > 0)
371 {
372 // figure out how many bytes are left to hash
373 size_t bytesLeftToHash = mSectorSize - mBytesInCurrentDigest;
374 size_t bytesToHash = MinSizeT (bytesLeftToHash, dataLength);
375
376 // hash the data
377 CC_SHA256_Update (&mSHA256Context, finger, (CC_LONG)bytesToHash);
378
379 // update the pointers
380 mBytesInCurrentDigest += bytesToHash;
381 bytesLeftToHash -= bytesToHash;
382 finger += bytesToHash;
383 dataLength -= bytesToHash;
384
385 if (bytesLeftToHash == 0) // is our digest "full"?
386 {
387 FinalizeDigestAndCompare ();
388 }
389 }
390 }
391
392
393
394 void Download::Finalize ()
395 {
396 // are there any bytes left over in the digest?
397 if (mBytesInCurrentDigest != 0)
398 {
399 FinalizeDigestAndCompare ();
400 }
401
402 if (mCurrentHash != mNumHashes) // check for underflow
403 {
404 MacOSError::throwMe (errSecureDownloadInvalidDownload);
405 }
406 }
407