2 * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <CommonCrypto/CommonDigest.h>
27 #include <Security/Security.h>
28 #include <security_utilities/security_utilities.h>
29 #include <security_cdsa_utilities/cssmbridge.h>
30 #include <Security/cssmapplePriv.h>
32 #include "SecureDownload.h"
33 #include "SecureDownloadInternal.h"
38 static void CheckCFThingForNULL (CFTypeRef theType
)
48 Download::Download () : mDict (NULL
), mURLs (NULL
), mName (NULL
), mDate (NULL
), mHashes (NULL
), mNumHashes (0), mCurrentHash (0), mBytesInCurrentDigest (0)
54 static void ReleaseIfNotNull (CFTypeRef theThing
)
64 Download::~Download ()
66 ReleaseIfNotNull (mDict
);
71 CFArrayRef
Download::CopyURLs ()
79 CFStringRef
Download::CopyName ()
87 CFDateRef
Download::CopyDate ()
95 void Download::GoOrNoGo (SecTrustResultType result
)
99 case kSecTrustResultInvalid
:
100 case kSecTrustResultDeny
:
101 case kSecTrustResultFatalTrustFailure
:
102 case kSecTrustResultOtherError
:
103 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
105 case kSecTrustResultProceed
:
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
:
116 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
126 SecPolicyRef
Download::GetPolicy ()
128 SecPolicySearchRef search
;
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
)
136 MacOSError::throwMe (result
);
139 result
= SecPolicySearchCopyNext (search
, &policy
);
140 if (result
!= errSecSuccess
)
142 MacOSError::throwMe (result
);
152 #define SHA256_NAME CFSTR("SHA-256")
154 void Download::ParseTicket (CFDataRef ticket
)
156 // make a propertylist from the ticket
157 CFDictionaryRef mDict
= (CFDictionaryRef
) _SecureDownloadParseTicketXML (ticket
);
158 CheckCFThingForNULL (mDict
);
161 mURLs
= (CFArrayRef
) CFDictionaryGetValue (mDict
, SD_XML_URL
);
162 CheckCFThingForNULL (mURLs
);
164 // get the download name
165 mName
= (CFStringRef
) CFDictionaryGetValue (mDict
, SD_XML_NAME
);
166 CheckCFThingForNULL (mName
);
168 // get the download date
169 mDate
= (CFDateRef
) CFDictionaryGetValue (mDict
, SD_XML_CREATED
);
170 CheckCFThingForNULL (mDate
);
172 // get the download size
173 CFNumberRef number
= (CFNumberRef
) CFDictionaryGetValue (mDict
, SD_XML_SIZE
);
174 CFNumberGetValue (number
, kCFNumberSInt64Type
, &mDownloadSize
);
176 // get the verifications dictionary
177 CFDictionaryRef verifications
= (CFDictionaryRef
) CFDictionaryGetValue (mDict
, SD_XML_VERIFICATIONS
);
179 // from the verifications dictionary, get the hashing dictionary that we support
180 CFDictionaryRef hashInfo
= (CFDictionaryRef
) CFDictionaryGetValue (verifications
, SHA256_NAME
);
182 // from the hashing dictionary, get the sector size
183 number
= (CFNumberRef
) CFDictionaryGetValue (hashInfo
, SD_XML_SECTOR_SIZE
);
184 CFNumberGetValue (number
, kCFNumberSInt32Type
, &mSectorSize
);
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
);
192 mBytesInCurrentDigest
= 0;
197 void Download::Initialize (CFDataRef ticket
,
198 SecureDownloadTrustSetupCallback setup
,
200 SecureDownloadTrustEvaluateCallback evaluate
,
201 void* evaluateContext
)
204 SecCmsMessageRef cmsMessage
= GetCmsMessageFromData (ticket
);
207 SecPolicyRef policy
= GetPolicy ();
209 // parse the CMS message
210 int contentLevelCount
= SecCmsMessageContentLevelCount (cmsMessage
);
211 SecCmsSignedDataRef signedData
;
216 while (i
< contentLevelCount
)
218 SecCmsContentInfoRef contentInfo
= SecCmsMessageContentLevel (cmsMessage
, i
++);
219 SECOidTag contentTypeTag
= SecCmsContentInfoGetContentTypeTag (contentInfo
);
221 if (contentTypeTag
!= SEC_OID_PKCS7_SIGNED_DATA
)
226 signedData
= (SecCmsSignedDataRef
) SecCmsContentInfoGetContent (contentInfo
);
227 if (signedData
== NULL
)
229 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
232 // import the certificates found in the cms message
233 result
= SecCmsSignedDataImportCerts (signedData
, NULL
, certUsageObjectSigner
, true);
236 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
239 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
242 if (numberOfSigners
== 0) // no signers? This is a possible attack
244 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
247 for (j
= 0; j
< numberOfSigners
; ++j
)
249 SecTrustResultType resultType
;
251 // do basic verification of the message
252 SecTrustRef trustRef
;
253 result
= SecCmsSignedDataVerifySignerInfo (signedData
, j
, NULL
, policy
, &trustRef
);
255 // notify the user of the new trust ref
258 SecureDownloadTrustCallbackResult tcResult
= setup (trustRef
, setupContext
);
261 case kSecureDownloadDoNotEvaluateSigner
:
264 case kSecureDownloadFailEvaluation
:
265 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
267 case kSecureDownloadEvaluateSigner
:
274 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
277 result
= SecTrustEvaluate (trustRef
, &resultType
);
278 if (result
!= errSecSuccess
)
280 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
283 if (evaluate
!= NULL
)
285 resultType
= evaluate (trustRef
, resultType
, evaluateContext
);
288 GoOrNoGo (resultType
);
292 // extract the message
293 CSSM_DATA_PTR message
= SecCmsMessageGetContent (cmsMessage
);
294 CFDataRef ticketData
= CFDataCreateWithBytesNoCopy (NULL
, message
->Data
, message
->Length
, kCFAllocatorNull
);
295 CheckCFThingForNULL (ticketData
);
297 ParseTicket (ticketData
);
300 CC_SHA256_Init (&mSHA256Context
);
303 CFRelease (ticketData
);
304 SecCmsMessageDestroy (cmsMessage
);
309 SecCmsMessageRef
Download::GetCmsMessageFromData (CFDataRef data
)
312 SecCmsDecoderRef decoderContext
;
313 int result
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
);
316 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
319 result
= SecCmsDecoderUpdate (decoderContext
, CFDataGetBytePtr (data
), CFDataGetLength (data
));
322 SecCmsDecoderDestroy(decoderContext
);
323 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
326 SecCmsMessageRef message
;
327 result
= SecCmsDecoderFinish (decoderContext
, &message
);
330 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
338 size_t MinSizeT (size_t a
, size_t b
)
340 // return the smaller of a and b
341 return a
< b
? a
: b
;
346 void Download::FinalizeDigestAndCompare ()
349 CC_SHA256_Final (digest
, &mSHA256Context
);
351 // make sure we don't overflow the digest buffer
352 if (mCurrentHash
>= mNumHashes
|| memcmp (digest
, mDigests
[mCurrentHash
++], CC_SHA256_DIGEST_LENGTH
) != 0)
354 // Something's really wrong!
355 MacOSError::throwMe (errSecureDownloadInvalidDownload
);
358 // setup for the next receipt of data
359 mBytesInCurrentDigest
= 0;
360 CC_SHA256_Init (&mSHA256Context
);
365 void Download::UpdateWithData (CFDataRef data
)
367 // figure out how much data to hash
368 CFIndex dataLength
= CFDataGetLength (data
);
369 const UInt8
* finger
= CFDataGetBytePtr (data
);
371 while (dataLength
> 0)
373 // figure out how many bytes are left to hash
374 size_t bytesLeftToHash
= mSectorSize
- mBytesInCurrentDigest
;
375 size_t bytesToHash
= MinSizeT (bytesLeftToHash
, dataLength
);
378 CC_SHA256_Update (&mSHA256Context
, finger
, (CC_LONG
)bytesToHash
);
380 // update the pointers
381 mBytesInCurrentDigest
+= bytesToHash
;
382 bytesLeftToHash
-= bytesToHash
;
383 finger
+= bytesToHash
;
384 dataLength
-= bytesToHash
;
386 if (bytesLeftToHash
== 0) // is our digest "full"?
388 FinalizeDigestAndCompare ();
395 void Download::Finalize ()
397 // are there any bytes left over in the digest?
398 if (mBytesInCurrentDigest
!= 0)
400 FinalizeDigestAndCompare ();
403 if (mCurrentHash
!= mNumHashes
) // check for underflow
405 MacOSError::throwMe (errSecureDownloadInvalidDownload
);