2 * Copyright (c) 2006 Apple Computer, 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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
30 #include <security_cdsa_utilities/cssmbridge.h>
31 #include <Security/cssmapplePriv.h>
33 #include "SecureDownload.h"
34 #include "SecureDownloadInternal.h"
39 static void CheckCFThingForNULL (CFTypeRef theType
)
49 Download::Download () : mDict (NULL
), mURLs (NULL
), mName (NULL
), mDate (NULL
), mHashes (NULL
), mNumHashes (0), mCurrentHash (0), mBytesInCurrentDigest (0)
55 static void ReleaseIfNotNull (CFTypeRef theThing
)
65 Download::~Download ()
67 ReleaseIfNotNull (mDict
);
72 CFArrayRef
Download::CopyURLs ()
80 CFStringRef
Download::CopyName ()
88 CFDateRef
Download::CopyDate ()
96 void Download::GoOrNoGo (SecTrustResultType result
)
100 case kSecTrustResultInvalid
:
101 case kSecTrustResultDeny
:
102 case kSecTrustResultFatalTrustFailure
:
103 case kSecTrustResultOtherError
:
104 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
106 case kSecTrustResultProceed
:
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
:
117 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
127 SecPolicyRef
Download::GetPolicy ()
129 SecPolicySearchRef search
;
133 // get the policy for resource signing
134 result
= SecPolicySearchCreate (CSSM_CERT_X_509v3
, &CSSMOID_APPLE_TP_RESOURCE_SIGN
, NULL
, &search
);
137 MacOSError::throwMe (result
);
140 result
= SecPolicySearchCopyNext (search
, &policy
);
143 MacOSError::throwMe (result
);
153 #define SHA256_NAME CFSTR("SHA-256")
155 void Download::ParseTicket (CFDataRef ticket
)
157 // make a propertylist from the ticket
158 CFDictionaryRef mDict
= (CFDictionaryRef
) _SecureDownloadParseTicketXML (ticket
);
159 CheckCFThingForNULL (mDict
);
162 mURLs
= (CFArrayRef
) CFDictionaryGetValue (mDict
, SD_XML_URL
);
163 CheckCFThingForNULL (mURLs
);
165 // get the download name
166 mName
= (CFStringRef
) CFDictionaryGetValue (mDict
, SD_XML_NAME
);
167 CheckCFThingForNULL (mName
);
169 // get the download date
170 mDate
= (CFDateRef
) CFDictionaryGetValue (mDict
, SD_XML_CREATED
);
171 CheckCFThingForNULL (mDate
);
173 // get the download size
174 CFNumberRef number
= (CFNumberRef
) CFDictionaryGetValue (mDict
, SD_XML_SIZE
);
175 CFNumberGetValue (number
, kCFNumberSInt64Type
, &mDownloadSize
);
177 // get the verifications dictionary
178 CFDictionaryRef verifications
= (CFDictionaryRef
) CFDictionaryGetValue (mDict
, SD_XML_VERIFICATIONS
);
180 // from the verifications dictionary, get the hashing dictionary that we support
181 CFDictionaryRef hashInfo
= (CFDictionaryRef
) CFDictionaryGetValue (verifications
, SHA256_NAME
);
183 // from the hashing dictionary, get the sector size
184 number
= (CFNumberRef
) CFDictionaryGetValue (hashInfo
, SD_XML_SECTOR_SIZE
);
185 CFNumberGetValue (number
, kCFNumberSInt32Type
, &mSectorSize
);
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
);
193 mBytesInCurrentDigest
= 0;
198 void Download::Initialize (CFDataRef ticket
,
199 SecureDownloadTrustSetupCallback setup
,
201 SecureDownloadTrustEvaluateCallback evaluate
,
202 void* evaluateContext
)
205 SecCmsMessageRef cmsMessage
= GetCmsMessageFromData (ticket
);
208 SecPolicyRef policy
= GetPolicy ();
210 // parse the CMS message
211 int contentLevelCount
= SecCmsMessageContentLevelCount (cmsMessage
);
212 SecCmsSignedDataRef signedData
;
217 while (i
< contentLevelCount
)
219 SecCmsContentInfoRef contentInfo
= SecCmsMessageContentLevel (cmsMessage
, i
++);
220 SECOidTag contentTypeTag
= SecCmsContentInfoGetContentTypeTag (contentInfo
);
222 if (contentTypeTag
!= SEC_OID_PKCS7_SIGNED_DATA
)
227 signedData
= (SecCmsSignedDataRef
) SecCmsContentInfoGetContent (contentInfo
);
228 if (signedData
== NULL
)
230 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
233 // import the certificates found in the cms message
234 result
= SecCmsSignedDataImportCerts (signedData
, NULL
, certUsageObjectSigner
, true);
237 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
240 int numberOfSigners
= SecCmsSignedDataSignerInfoCount (signedData
);
243 if (numberOfSigners
== 0) // no signers? This is a possible attack
245 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
248 for (j
= 0; j
< numberOfSigners
; ++j
)
250 SecTrustResultType resultType
;
252 // do basic verification of the message
253 SecTrustRef trustRef
;
254 result
= SecCmsSignedDataVerifySignerInfo (signedData
, j
, NULL
, policy
, &trustRef
);
256 // notify the user of the new trust ref
259 SecureDownloadTrustCallbackResult tcResult
= setup (trustRef
, setupContext
);
262 case kSecureDownloadDoNotEvaluateSigner
:
265 case kSecureDownloadFailEvaluation
:
266 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
268 case kSecureDownloadEvaluateSigner
:
275 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
278 result
= SecTrustEvaluate (trustRef
, &resultType
);
281 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
284 if (evaluate
!= NULL
)
286 resultType
= evaluate (trustRef
, resultType
, evaluateContext
);
289 GoOrNoGo (resultType
);
293 // extract the message
294 CSSM_DATA_PTR message
= SecCmsMessageGetContent (cmsMessage
);
295 CFDataRef ticketData
= CFDataCreateWithBytesNoCopy (NULL
, message
->Data
, message
->Length
, kCFAllocatorNull
);
296 CheckCFThingForNULL (ticketData
);
298 ParseTicket (ticketData
);
301 CC_SHA256_Init (&mSHA256Context
);
304 CFRelease (ticketData
);
305 SecCmsMessageDestroy (cmsMessage
);
310 SecCmsMessageRef
Download::GetCmsMessageFromData (CFDataRef data
)
313 SecCmsDecoderRef decoderContext
;
314 int result
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
);
317 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
320 result
= SecCmsDecoderUpdate (decoderContext
, CFDataGetBytePtr (data
), CFDataGetLength (data
));
323 SecCmsDecoderDestroy(decoderContext
);
324 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
327 SecCmsMessageRef message
;
328 result
= SecCmsDecoderFinish (decoderContext
, &message
);
331 MacOSError::throwMe (errSecureDownloadInvalidTicket
);
339 size_t MinSizeT (size_t a
, size_t b
)
341 // return the smaller of a and b
342 return a
< b
? a
: b
;
347 void Download::FinalizeDigestAndCompare ()
350 CC_SHA256_Final (digest
, &mSHA256Context
);
352 // make sure we don't overflow the digest buffer
353 if (mCurrentHash
>= mNumHashes
|| memcmp (digest
, mDigests
[mCurrentHash
++], CC_SHA256_DIGEST_LENGTH
) != 0)
355 // Something's really wrong!
356 MacOSError::throwMe (errSecureDownloadInvalidDownload
);
359 // setup for the next receipt of data
360 mBytesInCurrentDigest
= 0;
361 CC_SHA256_Init (&mSHA256Context
);
366 void Download::UpdateWithData (CFDataRef data
)
368 // figure out how much data to hash
369 CFIndex dataLength
= CFDataGetLength (data
);
370 const UInt8
* finger
= CFDataGetBytePtr (data
);
372 while (dataLength
> 0)
374 // figure out how many bytes are left to hash
375 size_t bytesLeftToHash
= mSectorSize
- mBytesInCurrentDigest
;
376 size_t bytesToHash
= MinSizeT (bytesLeftToHash
, dataLength
);
379 CC_SHA256_Update (&mSHA256Context
, finger
, bytesToHash
);
381 // update the pointers
382 mBytesInCurrentDigest
+= bytesToHash
;
383 bytesLeftToHash
-= bytesToHash
;
384 finger
+= bytesToHash
;
385 dataLength
-= bytesToHash
;
387 if (bytesLeftToHash
== 0) // is our digest "full"?
389 FinalizeDigestAndCompare ();
396 void Download::Finalize ()
398 // are there any bytes left over in the digest?
399 if (mBytesInCurrentDigest
!= 0)
401 FinalizeDigestAndCompare ();
404 if (mCurrentHash
!= mNumHashes
) // check for underflow
406 MacOSError::throwMe (errSecureDownloadInvalidDownload
);