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 kSecTrustResultRecoverableTrustFailure
: 
 113                 case kSecTrustResultUnspecified
: 
 115                         MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 125 SecPolicyRef 
Download::GetPolicy () 
 127         SecPolicySearchRef search
; 
 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
) 
 135                 MacOSError::throwMe (result
); 
 138         result 
= SecPolicySearchCopyNext (search
, &policy
); 
 139         if (result 
!= errSecSuccess
) 
 141                 MacOSError::throwMe (result
); 
 151 #define SHA256_NAME CFSTR("SHA-256") 
 153 void Download::ParseTicket (CFDataRef ticket
) 
 155         // make a propertylist from the ticket 
 156         CFRef
<CFDictionaryRef
> mDict 
= (CFDictionaryRef
) _SecureDownloadParseTicketXML (ticket
); 
 157         CheckCFThingForNULL (mDict
); 
 160         mURLs 
= (CFArrayRef
) CFDictionaryGetValue (mDict
, SD_XML_URL
); 
 161         CheckCFThingForNULL (mURLs
); 
 163         // get the download name 
 164         mName 
= (CFStringRef
) CFDictionaryGetValue (mDict
, SD_XML_NAME
); 
 165         CheckCFThingForNULL (mName
); 
 167         // get the download date 
 168         mDate 
= (CFDateRef
) CFDictionaryGetValue (mDict
, SD_XML_CREATED
); 
 169         CheckCFThingForNULL (mDate
); 
 171         // get the download size 
 172         CFNumberRef number 
= (CFNumberRef
) CFDictionaryGetValue (mDict
, SD_XML_SIZE
); 
 173         CFNumberGetValue (number
, kCFNumberSInt64Type
, &mDownloadSize
); 
 175         // get the verifications dictionary 
 176         CFDictionaryRef verifications 
= (CFDictionaryRef
) CFDictionaryGetValue (mDict
, SD_XML_VERIFICATIONS
); 
 178         // from the verifications dictionary, get the hashing dictionary that we support 
 179         CFDictionaryRef hashInfo 
= (CFDictionaryRef
) CFDictionaryGetValue (verifications
, SHA256_NAME
); 
 181         // from the hashing dictionary, get the sector size 
 182         number 
= (CFNumberRef
) CFDictionaryGetValue (hashInfo
, SD_XML_SECTOR_SIZE
); 
 183         CFNumberGetValue (number
, kCFNumberSInt64Type
, &mSectorSize
); 
 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
); 
 191         mBytesInCurrentDigest 
= 0; 
 196 void Download::Initialize (CFDataRef ticket
, 
 197                                                    SecureDownloadTrustSetupCallback setup
, 
 199                                                    SecureDownloadTrustEvaluateCallback evaluate
, 
 200                                                    void* evaluateContext
) 
 203         SecCmsMessageRef cmsMessage 
= GetCmsMessageFromData (ticket
); 
 206         SecPolicyRef policy 
= GetPolicy (); 
 208         // parse the CMS message 
 209         int contentLevelCount 
= SecCmsMessageContentLevelCount (cmsMessage
); 
 210         SecCmsSignedDataRef signedData
; 
 215         while (i 
< contentLevelCount
) 
 217                 SecCmsContentInfoRef contentInfo 
= SecCmsMessageContentLevel (cmsMessage
, i
++); 
 218                 SECOidTag contentTypeTag 
= SecCmsContentInfoGetContentTypeTag (contentInfo
); 
 220                 if (contentTypeTag 
!= SEC_OID_PKCS7_SIGNED_DATA
) 
 225                 signedData 
= (SecCmsSignedDataRef
) SecCmsContentInfoGetContent (contentInfo
); 
 226                 if (signedData 
== NULL
) 
 228                         MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 231                 // import the certificates found in the cms message 
 232                 result 
= SecCmsSignedDataImportCerts (signedData
, NULL
, certUsageObjectSigner
, true); 
 235                         MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 238                 int numberOfSigners 
= SecCmsSignedDataSignerInfoCount (signedData
); 
 241                 if (numberOfSigners 
== 0) // no signers?  This is a possible attack 
 243                         MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 246                 for (j 
= 0; j 
< numberOfSigners
; ++j
) 
 248                         SecTrustResultType resultType
; 
 250                         // do basic verification of the message 
 251                         SecTrustRef trustRef
; 
 252                         result 
= SecCmsSignedDataVerifySignerInfo (signedData
, j
, NULL
, policy
, &trustRef
); 
 254                         // notify the user of the new trust ref 
 257                                 SecureDownloadTrustCallbackResult tcResult 
= setup (trustRef
, setupContext
); 
 260                                         case kSecureDownloadDoNotEvaluateSigner
: 
 263                                         case kSecureDownloadFailEvaluation
: 
 264                                                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 266                                         case kSecureDownloadEvaluateSigner
: 
 273                                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 276                         result 
= SecTrustEvaluate (trustRef
, &resultType
); 
 277                         if (result 
!= errSecSuccess
) 
 279                                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 282                         if (evaluate 
!= NULL
) 
 284                                 resultType 
= evaluate (trustRef
, resultType
, evaluateContext
); 
 287                         GoOrNoGo (resultType
); 
 291         // extract the message  
 292         CSSM_DATA_PTR message 
= SecCmsMessageGetContent (cmsMessage
); 
 293         CFDataRef ticketData 
= CFDataCreateWithBytesNoCopy (NULL
, message
->Data
, message
->Length
, kCFAllocatorNull
); 
 294         CheckCFThingForNULL (ticketData
); 
 296         ParseTicket (ticketData
); 
 299         CC_SHA256_Init (&mSHA256Context
); 
 302         CFRelease (ticketData
); 
 303         SecCmsMessageDestroy (cmsMessage
); 
 308 SecCmsMessageRef 
Download::GetCmsMessageFromData (CFDataRef data
) 
 311         SecCmsDecoderRef decoderContext
; 
 312         int result 
= SecCmsDecoderCreate (NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &decoderContext
); 
 315                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 318         result 
= SecCmsDecoderUpdate (decoderContext
, CFDataGetBytePtr (data
), CFDataGetLength (data
)); 
 321         SecCmsDecoderDestroy(decoderContext
); 
 322                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 325     SecCmsMessageRef message
; 
 326         result 
= SecCmsDecoderFinish (decoderContext
, &message
); 
 329                 MacOSError::throwMe (errSecureDownloadInvalidTicket
); 
 337 size_t MinSizeT (size_t a
, size_t b
) 
 339         // return the smaller of a and b 
 340         return a 
< b 
? a 
: b
; 
 345 void Download::FinalizeDigestAndCompare () 
 348         CC_SHA256_Final (digest
, &mSHA256Context
); 
 350         // make sure we don't overflow the digest buffer 
 351         if (mCurrentHash 
>= mNumHashes 
|| memcmp (digest
, mDigests
[mCurrentHash
++], CC_SHA256_DIGEST_LENGTH
) != 0) 
 353                 // Something's really wrong! 
 354                 MacOSError::throwMe (errSecureDownloadInvalidDownload
); 
 357         // setup for the next receipt of data 
 358         mBytesInCurrentDigest 
= 0; 
 359         CC_SHA256_Init (&mSHA256Context
); 
 364 void Download::UpdateWithData (CFDataRef data
) 
 366         // figure out how much data to hash 
 367         CFIndex dataLength 
= CFDataGetLength (data
); 
 368         const UInt8
* finger 
= CFDataGetBytePtr (data
); 
 370         while (dataLength 
> 0) 
 372                 // figure out how many bytes are left to hash 
 373                 size_t bytesLeftToHash 
= mSectorSize 
- mBytesInCurrentDigest
; 
 374                 size_t bytesToHash 
= MinSizeT (bytesLeftToHash
, dataLength
); 
 377                 CC_SHA256_Update (&mSHA256Context
, finger
, (CC_LONG
)bytesToHash
); 
 379                 // update the pointers 
 380                 mBytesInCurrentDigest 
+= bytesToHash
; 
 381                 bytesLeftToHash 
-= bytesToHash
; 
 382                 finger 
+= bytesToHash
; 
 383                 dataLength 
-= bytesToHash
; 
 385                 if (bytesLeftToHash 
== 0) // is our digest "full"? 
 387                         FinalizeDigestAndCompare (); 
 394 void Download::Finalize () 
 396         // are there any bytes left over in the digest? 
 397         if (mBytesInCurrentDigest 
!= 0) 
 399                 FinalizeDigestAndCompare (); 
 402         if (mCurrentHash 
!= mNumHashes
) // check for underflow 
 404                 MacOSError::throwMe (errSecureDownloadInvalidDownload
);