/*
 * Obtain the timestamp of signer 'signerIndex' of a CMS message, if
 * present. This timestamp is an authenticated timestamp provided by
 * a timestamping authority.
 *
 * Returns errSecParam if the CMS message was not signed or if signerIndex
 * is greater than the number of signers of the message minus one.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called.
 */
OSStatus CMSDecoderCopySignerTimestamp(
                                       CMSDecoderRef		cmsDecoder,
                                       size_t				signerIndex,        /* usually 0 */
                                       CFAbsoluteTime      *timestamp)			/* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    
    require(cmsDecoder && timestamp, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit);
    numContentInfos = SecCmsMessageContentLevelCount(cmsg);
    for (int dex = 0; !signedData && dex < numContentInfos; dex++)
    {
        SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
        SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
        if (tag == SEC_OID_PKCS7_SIGNED_DATA)
            if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
                if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
                {
                    status = SecCmsSignerInfoGetTimestampTime(signerInfo, timestamp);
                    break;
                }
    }
    
xit:
    return status;
}
/*
 * Indicate that no more CMSDecoderUpdateMessage() calls are forthcoming;
 * finish decoding the message. We parse the message as best we can, up to
 * but not including verifying individual signerInfos.
 */
OSStatus CMSDecoderFinalizeMessage(
                                   CMSDecoderRef		cmsDecoder)
{
	if(cmsDecoder == NULL) {
		return errSecParam;
	}
	if(cmsDecoder->decState != DS_Updating) {
		return errSecParam;
	}
	ASSERT(cmsDecoder->decoder != NULL);
	OSStatus ortn = SecCmsDecoderFinish(cmsDecoder->decoder, &cmsDecoder->cmsMsg);
	cmsDecoder->decState = DS_Final;
	
	/* SecCmsDecoderFinish destroyed the decoder even on failure */
	cmsDecoder->decoder = NULL;
	
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat);
		CSSM_PERROR("SecCmsDecoderFinish", ortn);
		return ortn;
	}
	
	ASSERT(cmsDecoder->cmsMsg != NULL);
	cmsDecoder->wasEncrypted = SecCmsMessageIsEncrypted(cmsDecoder->cmsMsg);
	
	/* Look for a SignedData */
	int numContentInfos = SecCmsMessageContentLevelCount(cmsDecoder->cmsMsg);
	int dex;
	for(dex=0; dex<numContentInfos; dex++) {
		SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsDecoder->cmsMsg, dex);
		SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
		switch(tag) {
			case SEC_OID_PKCS7_SIGNED_DATA:
				cmsDecoder->signedData =
                (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci);
				/* dig down one more layer for eContentType */
				ci = SecCmsSignedDataGetContentInfo(cmsDecoder->signedData);
				cmsDecoder->eContentType = SecCmsContentInfoGetContentTypeOID(ci);
				break;
			default:
				break;
		}
		if(cmsDecoder->signedData != NULL) {
			break;
		}
        
	}
	
	/* minimal processing of optional signedData... */
	if(cmsDecoder->signedData != NULL) {
		cmsDecoder->numSigners = (size_t)
        SecCmsSignedDataSignerInfoCount(cmsDecoder->signedData);
		if(cmsDecoder->detachedContent != NULL) {
			/* time to calculate digests from detached content */
			ortn = cmsDigestDetachedContent(cmsDecoder);
		}
	}
	return ortn;
}
/*
 * Obtain an array of the certificates in a timestamp response. Elements of the
 * returned array are SecCertificateRefs. The caller must CFRelease the returned
 * array. This timestamp is an authenticated timestamp provided by
 * a timestamping authority.
 *
 * Returns errSecParam if the CMS message was not signed or if signerIndex
 * is greater than the number of signers of the message minus one. It returns
 * errSecItemNotFound if no certificates were found.
 *
 * This cannot be called until after CMSDecoderFinalizeMessage() is called. 
 */
OSStatus CMSDecoderCopySignerTimestampCertificates(
                                                   CMSDecoderRef		cmsDecoder,
                                                   size_t				signerIndex,            /* usually 0 */
                                                   CFArrayRef          *certificateRefs)       /* RETURNED */
{
    OSStatus status = errSecParam;
	SecCmsMessageRef cmsg = NULL;
	SecCmsSignedDataRef signedData = NULL;
    int numContentInfos = 0;
    CFIndex tsn = 0;
    bool good = false;
    
    require(cmsDecoder && certificateRefs, xit);
	require_noerr(CMSDecoderGetCmsMessage(cmsDecoder, &cmsg), xit);
    numContentInfos = SecCmsMessageContentLevelCount(cmsg);
    for (int dex = 0; !signedData && dex < numContentInfos; dex++)
    {
        SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
        SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
        if (tag == SEC_OID_PKCS7_SIGNED_DATA)
            if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
                if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
                {
                    CFArrayRef certList = SecCmsSignerInfoGetTimestampCertList(signerInfo);
                    require_action(certList, xit, status = errSecItemNotFound);
                    CFMutableArrayRef certs = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(certList), certList);
                    
                    if(certs){
                        //reorder certificates:
                        tsn = CFArrayGetCount(certs);
                        good = tsn > 0 && Security::CodeSigning::isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(certs, tsn-1)));
                        
                        if ( good == false )
                        {
                            //change TS certificate ordering.
                            for (CFIndex n = 0; n < tsn; n++)
                            {
                                if (SecCertificateRef tsRoot = SecCertificateRef(CFArrayGetValueAtIndex(certs, n)))
                                    if ((good = Security::CodeSigning::isAppleCA(tsRoot))) {
                                        CFArrayExchangeValuesAtIndices(certs, n, tsn-1);
                                        break;
                                    }
                            }
                        }
                        
                        *certificateRefs = CFArrayCreateCopy(kCFAllocatorDefault, certs);
                        CFRelease(certs);
                        status = errSecSuccess;
                    }
                    break;
                }
    }
    
    
    xit:
        return status;
    }
Exemplo n.º 4
0
void Download::Initialize (CFDataRef ticket,
						   SecureDownloadTrustSetupCallback setup,
						   void* setupContext,
						   SecureDownloadTrustEvaluateCallback evaluate,
						   void* evaluateContext)
{
	// decode the ticket
	SecCmsMessageRef cmsMessage = GetCmsMessageFromData (ticket);
	
	// get a policy
	SecPolicyRef policy = GetPolicy ();

	// parse the CMS message
	int contentLevelCount = SecCmsMessageContentLevelCount (cmsMessage);
	SecCmsSignedDataRef signedData;

	OSStatus result;
	
	int i = 0;
	while (i < contentLevelCount)
	{
		SecCmsContentInfoRef contentInfo = SecCmsMessageContentLevel (cmsMessage, i++);
		SECOidTag contentTypeTag = SecCmsContentInfoGetContentTypeTag (contentInfo);
		
		if (contentTypeTag != SEC_OID_PKCS7_SIGNED_DATA)
		{
			continue;
		}

		signedData = (SecCmsSignedDataRef) SecCmsContentInfoGetContent (contentInfo);
		if (signedData == NULL)
		{
			MacOSError::throwMe (errSecureDownloadInvalidTicket);
		}
		
		// import the certificates found in the cms message
		result = SecCmsSignedDataImportCerts (signedData, NULL, certUsageObjectSigner, true);
		if (result != 0)
		{
			MacOSError::throwMe (errSecureDownloadInvalidTicket);
		}
		
		int numberOfSigners = SecCmsSignedDataSignerInfoCount (signedData);
		int j;
		
		if (numberOfSigners == 0) // no signers?  This is a possible attack
		{
			MacOSError::throwMe (errSecureDownloadInvalidTicket);
		}
		
		for (j = 0; j < numberOfSigners; ++j)
		{
			SecTrustResultType resultType;
			
			// do basic verification of the message
			SecTrustRef trustRef;
			result = SecCmsSignedDataVerifySignerInfo (signedData, j, NULL, policy, &trustRef);
			
			// notify the user of the new trust ref
			if (setup != NULL)
			{
				SecureDownloadTrustCallbackResult tcResult = setup (trustRef, setupContext);
				switch (tcResult)
				{
					case kSecureDownloadDoNotEvaluateSigner:
						continue;
					
					case kSecureDownloadFailEvaluation:
						MacOSError::throwMe (errSecureDownloadInvalidTicket);
					
					case kSecureDownloadEvaluateSigner:
					break;
				}
			}
			
			if (result != 0)
			{
				MacOSError::throwMe (errSecureDownloadInvalidTicket);
			}
			
			result = SecTrustEvaluate (trustRef, &resultType);
			if (result != noErr)
			{
				MacOSError::throwMe (errSecureDownloadInvalidTicket);
			}
			
			if (evaluate != NULL)
			{
				resultType = evaluate (trustRef, resultType, evaluateContext);
			}
			
			GoOrNoGo (resultType);
		}
	}
	
	// extract the message 
	CSSM_DATA_PTR message = SecCmsMessageGetContent (cmsMessage);
	CFDataRef ticketData = CFDataCreateWithBytesNoCopy (NULL, message->Data, message->Length, kCFAllocatorNull);
	CheckCFThingForNULL (ticketData);
	
	ParseTicket (ticketData);

	// setup for hashing
	CC_SHA256_Init (&mSHA256Context);
	
	// clean up
	CFRelease (ticketData);
	SecCmsMessageDestroy (cmsMessage);
}