--- /dev/null
+/*
+ * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * CMSDecoder.cpp - Interface for decoding CMS messages.
+ */
+
+#include "CMSDecoder.h"
+#include "CMSPrivate.h"
+#include "CMSUtils.h"
+#include <../libsecurity_codesigning/lib/csutilities.h>
+
+#include <Security/SecCmsDecoder.h>
+#include <Security/SecCmsEnvelopedData.h>
+#include <Security/SecCmsMessage.h>
+#include <Security/SecCmsSignedData.h>
+#include <Security/SecCmsSignerInfo.h>
+#include <Security/SecCmsContentInfo.h>
+#include <Security/SecCmsDigestContext.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecSMIME.h>
+#include <Security/oidsattr.h>
+#include <Security/SecTrustPriv.h>
+#include <CoreFoundation/CFRuntime.h>
+#include <pthread.h>
+#include <AssertMacros.h>
+
+#pragma mark --- Private types and definitions ---
+
+/*
+ * Decoder state.
+ */
+typedef enum {
+ DS_Init, /* between CMSDecoderCreate and CMSDecoderUpdateMessage */
+ DS_Updating, /* between first CMSDecoderUpdateMessage and CMSDecoderFinalizeMessage */
+ DS_Final /* CMSDecoderFinalizeMessage has been called */
+} CMSDecoderState;
+
+/*
+ * Caller's CMSDecoderRef points to one of these.
+ */
+struct _CMSDecoder {
+ CFRuntimeBase base;
+ CMSDecoderState decState;
+ SecArenaPoolRef arena; /* the decoder's arena */
+ SecCmsDecoderRef decoder;
+ CFDataRef detachedContent;
+ CFTypeRef keychainOrArray; /* from CMSDecoderSetSearchKeychain() */
+
+ /*
+ * The following are valid (and quiescent) after CMSDecoderFinalizeMessage().
+ */
+ SecCmsMessageRef cmsMsg;
+ Boolean wasEncrypted; /* valid after CMSDecoderFinalizeMessage() */
+ SecCmsSignedDataRef signedData; /* if there is one... */
+ /* only non-NULL if we found a signedData */
+ size_t numSigners;
+ CSSM_OID *eContentType;
+ /* etc. */
+};
+
+static void cmsDecoderInit(CFTypeRef dec);
+static void cmsDecoderFinalize(CFTypeRef dec);
+
+static CFRuntimeClass cmsDecoderRuntimeClass =
+{
+ 0, /* version */
+ "CMSDecoder",
+ cmsDecoderInit,
+ NULL, /* copy */
+ cmsDecoderFinalize,
+ NULL, /* equal - just use pointer equality */
+ NULL, /* hash, ditto */
+ NULL, /* copyFormattingDesc */
+ NULL /* copyDebugDesc */
+};
+
+#pragma mark --- Private Routines ---
+
+static CFTypeID cmsDecoderTypeID = _kCFRuntimeNotATypeID;
+
+/* one time only class init, called via pthread_once() in CMSDecoderGetTypeID() */
+static void cmsDecoderClassInitialize(void)
+{
+ cmsDecoderTypeID =
+ _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsDecoderRuntimeClass);
+}
+
+/* init called out from _CFRuntimeCreateInstance() */
+static void cmsDecoderInit(CFTypeRef dec)
+{
+ char *start = ((char *)dec) + sizeof(CFRuntimeBase);
+ memset(start, 0, sizeof(struct _CMSDecoder) - sizeof(CFRuntimeBase));
+}
+
+/*
+ * Dispose of a CMSDecoder. Called out from CFRelease().
+ */
+static void cmsDecoderFinalize(
+ CFTypeRef dec)
+{
+ CMSDecoderRef cmsDecoder = (CMSDecoderRef)dec;
+ if(cmsDecoder == NULL) {
+ return;
+ }
+ if(cmsDecoder->decoder != NULL) {
+ /*
+ * Normally this gets freed in SecCmsDecoderFinish - this is
+ * an error case.
+ * FIXME: SecCmsDecoderDestroy() appears to destroy the
+ * cmsMsg too! Plus there's a comment there re: a leak...
+ */
+ SecCmsDecoderDestroy(cmsDecoder->decoder);
+ }
+ CFRELEASE(cmsDecoder->detachedContent);
+ CFRELEASE(cmsDecoder->keychainOrArray);
+ if(cmsDecoder->cmsMsg != NULL) {
+ SecCmsMessageDestroy(cmsDecoder->cmsMsg);
+ }
+ if(cmsDecoder->arena != NULL) {
+ SecArenaPoolFree(cmsDecoder->arena, false);
+ }
+}
+
+
+/*
+ * Given detached content and a valid (decoded) SignedData, digest the detached
+ * content. This occurs at the later of {CMSDecoderFinalizeMessage() finding a
+ * SignedData when already have detachedContent, or CMSDecoderSetDetachedContent()
+ * when we already have a SignedData).
+ */
+static OSStatus cmsDigestDetachedContent(
+ CMSDecoderRef cmsDecoder)
+{
+ ASSERT((cmsDecoder->signedData != NULL) && (cmsDecoder->detachedContent != NULL));
+
+ SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(cmsDecoder->signedData);
+ if(digestAlgorithms == NULL) {
+ return errSecUnknownFormat;
+ }
+ SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms);
+ if(digcx == NULL) {
+ return errSecAllocate;
+ }
+ CSSM_DATA **digests = NULL;
+
+ SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(cmsDecoder->detachedContent),
+ CFDataGetLength(cmsDecoder->detachedContent));
+ /* note this frees the digest content regardless */
+ OSStatus ortn = SecCmsDigestContextFinishMultiple(digcx, cmsDecoder->arena, &digests);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsDigestContextFinishMultiple", ortn);
+ return ortn;
+ }
+ ortn = SecCmsSignedDataSetDigests(cmsDecoder->signedData, digestAlgorithms, digests);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsSignedDataSetDigests", ortn);
+ }
+ return ortn;
+}
+
+#pragma mark --- Start of Public API ---
+
+CFTypeID CMSDecoderGetTypeID(void)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+ if(cmsDecoderTypeID == _kCFRuntimeNotATypeID) {
+ pthread_once(&once, &cmsDecoderClassInitialize);
+ }
+ return cmsDecoderTypeID;
+}
+
+/*
+ * Create a CMSDecoder. Result must eventually be freed via CFRelease().
+ */
+OSStatus CMSDecoderCreate(
+ CMSDecoderRef *cmsDecoderOut) /* RETURNED */
+{
+ CMSDecoderRef cmsDecoder = NULL;
+
+ uint32_t extra = sizeof(*cmsDecoder) - sizeof(cmsDecoder->base);
+ cmsDecoder = (CMSDecoderRef)_CFRuntimeCreateInstance(NULL, CMSDecoderGetTypeID(),
+ extra, NULL);
+ if(cmsDecoder == NULL) {
+ return errSecAllocate;
+ }
+ cmsDecoder->decState = DS_Init;
+ *cmsDecoderOut = cmsDecoder;
+ return errSecSuccess;
+}
+
+/*
+ * Feed raw bytes of the message to be decoded into the decoder. Can be called
+ * multiple times.
+ */
+OSStatus CMSDecoderUpdateMessage(
+ CMSDecoderRef cmsDecoder,
+ const void *msgBytes,
+ size_t msgBytesLen)
+{
+ if(cmsDecoder == NULL) {
+ return errSecParam;
+ }
+
+ OSStatus ortn;
+ switch(cmsDecoder->decState) {
+ case DS_Init:
+ /* First time through; set up */
+ ASSERT(cmsDecoder->decoder == NULL);
+ ASSERT(cmsDecoder->arena == NULL);
+ ortn = SecArenaPoolCreate(1024, &cmsDecoder->arena);
+ if(ortn) {
+ return cmsRtnToOSStatus(ortn);
+ }
+ ortn = SecCmsDecoderCreate(cmsDecoder->arena,
+ NULL, NULL, NULL, NULL, NULL, NULL, &cmsDecoder->decoder);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn);
+ CSSM_PERROR("SecCmsDecoderCreate", ortn);
+ return ortn;
+ }
+ cmsDecoder->decState = DS_Updating;
+ break;
+
+ case DS_Updating:
+ ASSERT(cmsDecoder->decoder != NULL);
+ break;
+
+ case DS_Final:
+ /* Too late for another update */
+ return errSecParam;
+
+ default:
+ dprintf("CMSDecoderUpdateMessage: bad decState\n");
+ return errSecInternalComponent;
+ }
+
+ /* FIXME - CFIndex same size as size_t on 64bit? */
+ ortn = SecCmsDecoderUpdate(cmsDecoder->decoder, msgBytes, (CFIndex)msgBytesLen);
+ if(ortn) {
+ ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat);
+ CSSM_PERROR("SecCmsDecoderUpdate", ortn);
+ }
+ return ortn;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * A signed CMS message optionally includes the data which was signed. If the
+ * message does not include the signed data, caller specifies the signed data
+ * (the "detached content") here.
+ *
+ * This can be called either before or after the actual decoding of the message
+ * (via CMSDecoderUpdateMessage() and CMSDecoderFinalizeMessage()); the only
+ * restriction is that, if detached content is required, this function must
+ * be called befoere successfully ascertaining the signature status via
+ * CMSDecoderCopySignerStatus().
+ */
+OSStatus CMSDecoderSetDetachedContent(
+ CMSDecoderRef cmsDecoder,
+ CFDataRef detachedContent)
+{
+ if((cmsDecoder == NULL) || (detachedContent == NULL)) {
+ return errSecParam;
+ }
+ cmsDecoder->detachedContent = detachedContent;
+ CFRetain(detachedContent);
+
+ if(cmsDecoder->signedData != NULL) {
+ /* time to calculate digests from detached content */
+ ASSERT(cmsDecoder->decState == DS_Final);
+ return cmsDigestDetachedContent(cmsDecoder);
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the detached content specified in CMSDecoderSetDetachedContent().
+ * Returns a NULL detachedContent if no detached content has been specified.
+ * Caller must CFRelease() the result.
+ */
+OSStatus CMSDecoderCopyDetachedContent(
+ CMSDecoderRef cmsDecoder,
+ CFDataRef *detachedContent) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (detachedContent == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->detachedContent != NULL) {
+ CFRetain(cmsDecoder->detachedContent);
+ }
+ *detachedContent = cmsDecoder->detachedContent;
+ return errSecSuccess;
+}
+
+/*
+ * Optionally specify a SecKeychainRef, or an array of them, containing
+ * intermediate certs to be used in verifying a signed message's signer
+ * certs. By default, the default keychain search list is used for this.
+ * Specify an empty CFArrayRef to search *no* keychains for intermediate
+ * certs.
+ * IF this is called, it must be called before CMSDecoderCopySignerStatus().
+ */
+OSStatus CMSDecoderSetSearchKeychain(
+ CMSDecoderRef cmsDecoder,
+ CFTypeRef keychainOrArray)
+{
+ if(cmsDecoder == NULL) {
+ return errSecParam;
+ }
+ cmsDecoder->keychainOrArray = keychainOrArray;
+ if(keychainOrArray) {
+ CFRetain(keychainOrArray);
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the number of signers of a message. A result of zero indicates that
+ * the message was not signed.
+ */
+OSStatus CMSDecoderGetNumSigners(
+ CMSDecoderRef cmsDecoder,
+ size_t *numSigners) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (numSigners == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->decState != DS_Final) {
+ return errSecParam;
+ }
+ *numSigners = cmsDecoder->numSigners;
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the status of a CMS message's signature. A CMS message can
+ * be signed my multiple signers; this function returns the status
+ * associated with signer 'n' as indicated by the signerIndex parameter.
+ */
+OSStatus CMSDecoderCopySignerStatus(
+ CMSDecoderRef cmsDecoder,
+ size_t signerIndex,
+ CFTypeRef policyOrArray,
+ Boolean evaluateSecTrust,
+ CMSSignerStatus *signerStatus, /* optional; RETURNED */
+ SecTrustRef *secTrust, /* optional; RETURNED */
+ OSStatus *certVerifyResultCode) /* optional; RETURNED */
+{
+ if((cmsDecoder == NULL) || (cmsDecoder->decState != DS_Final)) {
+ return errSecParam;
+ }
+
+ /* initialize return values */
+ if(signerStatus) {
+ *signerStatus = kCMSSignerUnsigned;
+ }
+ if(secTrust) {
+ *secTrust = NULL;
+ }
+ if(certVerifyResultCode) {
+ *certVerifyResultCode = 0;
+ }
+
+ if(cmsDecoder->signedData == NULL) {
+ *signerStatus = kCMSSignerUnsigned; /* redundant, I know, but explicit */
+ return errSecSuccess;
+ }
+ ASSERT(cmsDecoder->numSigners > 0);
+ if(signerIndex >= cmsDecoder->numSigners) {
+ *signerStatus = kCMSSignerInvalidIndex;
+ return errSecSuccess;
+ }
+ if(!SecCmsSignedDataHasDigests(cmsDecoder->signedData)) {
+ *signerStatus = kCMSSignerNeedsDetachedContent;
+ return errSecSuccess;
+ }
+
+ /*
+ * OK, we should be able to verify this signerInfo.
+ * I think we have to do the SecCmsSignedDataVerifySignerInfo first
+ * in order get all the cert pieces into place before returning them
+ * to the caller.
+ */
+ SecTrustRef theTrust = NULL;
+ OSStatus vfyRtn = SecCmsSignedDataVerifySignerInfo(cmsDecoder->signedData,
+ (int)signerIndex,
+ /*
+ * FIXME this cast should not be necessary, but libsecurity_smime
+ * declares this argument as a SecKeychainRef
+ */
+ (SecKeychainRef)cmsDecoder->keychainOrArray,
+ policyOrArray,
+ &theTrust);
+ /* Subsequent errors to errOut: */
+
+ /*
+ * NOTE the smime lib did NOT evaluate that SecTrust - it only does
+ * SecTrustEvaluate() if we don't ask for a copy.
+ *
+ * FIXME deal with multitudes of status returns here...for now, proceed with
+ * obtaining components the caller wants and assume that a nonzero vfyRtn
+ * means "bad signature".
+ */
+ OSStatus ortn = errSecSuccess;
+ SecTrustResultType secTrustResult;
+ CSSM_RETURN tpVfyStatus = CSSM_OK;
+ OSStatus evalRtn;
+
+ if(secTrust != NULL) {
+ *secTrust = theTrust;
+ /* we'll release our reference at the end */
+ if (theTrust)
+ CFRetain(theTrust);
+ }
+ SecCmsSignerInfoRef signerInfo =
+ SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
+ if(signerInfo == NULL) {
+ /* should never happen */
+ ASSERT(0);
+ dprintf("CMSDecoderCopySignerStatus: no signerInfo\n");
+ ortn = errSecInternalComponent;
+ goto errOut;
+ }
+
+ /* now do the actual cert verify */
+ if(evaluateSecTrust) {
+ evalRtn = SecTrustEvaluate(theTrust, &secTrustResult);
+ if(evalRtn) {
+ /* should never happen */
+ CSSM_PERROR("SecTrustEvaluate", evalRtn);
+ dprintf("CMSDecoderCopySignerStatus: SecTrustEvaluate error\n");
+ ortn = errSecInternalComponent;
+ goto errOut;
+ }
+ switch(secTrustResult) {
+ case kSecTrustResultUnspecified:
+ /* cert chain valid, no special UserTrust assignments */
+ case kSecTrustResultProceed:
+ /* cert chain valid AND user explicitly trusts this */
+ break;
+ case kSecTrustResultDeny:
+ tpVfyStatus = CSSMERR_APPLETP_TRUST_SETTING_DENY;
+ break;
+ case kSecTrustResultConfirm:
+ dprintf("SecTrustEvaluate reported confirm\n");
+ tpVfyStatus = CSSMERR_TP_NOT_TRUSTED;
+ break;
+ default:
+ {
+ /* get low-level TP error */
+ OSStatus tpStatus;
+ ortn = SecTrustGetCssmResultCode(theTrust, &tpStatus);
+ if(ortn) {
+ CSSM_PERROR("SecTrustGetCssmResultCode", ortn);
+ }
+ else {
+ tpVfyStatus = tpStatus;
+ }
+ CSSM_PERROR("TP status after SecTrustEvaluate", tpVfyStatus);
+ break;
+ }
+ } /* switch(secTrustResult) */
+ } /* evaluateSecTrust true */
+ if(certVerifyResultCode != NULL) {
+ *certVerifyResultCode = tpVfyStatus;
+ }
+
+ /* cook up global status based on vfyRtn and tpVfyStatus */
+ if(signerStatus != NULL) {
+ if((vfyRtn == errSecSuccess) && (tpVfyStatus == CSSM_OK)) {
+ *signerStatus = kCMSSignerValid;
+ }
+ else if(vfyRtn != errSecSuccess) {
+ /* this could mean other things, but for now... */
+ *signerStatus = kCMSSignerInvalidSignature;
+ }
+ else {
+ *signerStatus = kCMSSignerInvalidCert;
+ }
+ }
+errOut:
+ CFRELEASE(theTrust);
+ return ortn;
+}
+
+/*
+ * Obtain the email address of signer 'signerIndex' of a CMS message, if
+ * present.
+ *
+ * This cannot be called until after CMSDecoderFinalizeMessage() is called.
+ */
+OSStatus CMSDecoderCopySignerEmailAddress(
+ CMSDecoderRef cmsDecoder,
+ size_t signerIndex,
+ CFStringRef *signerEmailAddress) /* RETURNED */
+{
+ if((cmsDecoder == NULL) ||
+ (signerEmailAddress == NULL) ||
+ (cmsDecoder->signedData == NULL) || /* not signed */
+ (signerIndex >= cmsDecoder->numSigners) || /* index out of range */
+ (cmsDecoder->decState != DS_Final)) {
+ return errSecParam;
+ }
+
+ SecCmsSignerInfoRef signerInfo =
+ SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
+ if(signerInfo == NULL) {
+ /* should never happen */
+ ASSERT(0);
+ dprintf("CMSDecoderCopySignerEmailAddress: no signerInfo\n");
+ return errSecInternalComponent;
+ }
+
+ /*
+ * This is leaking memory in libsecurityKeychain per Radar 4412699.
+ */
+ *signerEmailAddress = SecCmsSignerInfoGetSignerEmailAddress(signerInfo);
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the certificate of signer 'signerIndex' of a CMS message, if
+ * present.
+ *
+ * This cannot be called until after CMSDecoderFinalizeMessage() is called.
+ */
+OSStatus CMSDecoderCopySignerCert(
+ CMSDecoderRef cmsDecoder,
+ size_t signerIndex,
+ SecCertificateRef *signerCert) /* RETURNED */
+{
+ if((cmsDecoder == NULL) ||
+ (signerCert == NULL) ||
+ (cmsDecoder->signedData == NULL) || /* not signed */
+ (signerIndex >= cmsDecoder->numSigners) || /* index out of range */
+ (cmsDecoder->decState != DS_Final)) {
+ return errSecParam;
+ }
+
+ SecCmsSignerInfoRef signerInfo =
+ SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, (int)signerIndex);
+ if(signerInfo == NULL) {
+ /* should never happen */
+ ASSERT(0);
+ dprintf("CMSDecoderCopySignerCertificate: no signerInfo\n");
+ return errSecInternalComponent;
+ }
+ *signerCert = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL);
+ /* libsecurity_smime does NOT retain that */
+ if(*signerCert == NULL) {
+ /* should never happen */
+ ASSERT(0);
+ dprintf("CMSDecoderCopySignerCertificate: no signerCert\n");
+ return errSecInternalComponent;
+ }
+ CFRetain(*signerCert);
+ return errSecSuccess;
+}
+
+/*
+ * Determine whether a CMS message was encrypted, and if so, whether we were
+ * able to decrypt it.
+ */
+OSStatus CMSDecoderIsContentEncrypted(
+ CMSDecoderRef cmsDecoder,
+ Boolean *wasEncrypted)
+{
+ if((cmsDecoder == NULL) || (wasEncrypted == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->decState != DS_Final) {
+ return errSecParam;
+ }
+ *wasEncrypted = cmsDecoder->wasEncrypted;
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the eContentType OID for a SignedData's EncapsulatedContentType, if
+ * present.
+ */
+OSStatus CMSDecoderCopyEncapsulatedContentType(
+ CMSDecoderRef cmsDecoder,
+ CFDataRef *eContentType) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (eContentType == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->decState != DS_Final) {
+ return errSecParam;
+ }
+ if(cmsDecoder->signedData == NULL) {
+ *eContentType = NULL;
+ }
+ else {
+ CSSM_OID *ecOid = cmsDecoder->eContentType;
+ *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Obtain an array of all of the certificates in a message. Elements of the
+ * returned array are SecCertificateRefs. The caller must CFRelease the returned
+ * array.
+ * This cannot be called until after CMSDecoderFinalizeMessage() is called.
+ */
+OSStatus CMSDecoderCopyAllCerts(
+ CMSDecoderRef cmsDecoder,
+ CFArrayRef *certs) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (certs == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->decState != DS_Final) {
+ return errSecParam;
+ }
+ if(cmsDecoder->signedData == NULL) {
+ /* message wasn't signed */
+ *certs = NULL;
+ return errSecSuccess;
+ }
+
+ /* NULL_terminated array of CSSM_DATA ptrs */
+ CSSM_DATA_PTR *cssmCerts = SecCmsSignedDataGetCertificateList(cmsDecoder->signedData);
+ if((cssmCerts == NULL) || (*cssmCerts == NULL)) {
+ *certs = NULL;
+ return errSecSuccess;
+ }
+
+ CFMutableArrayRef allCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ CSSM_DATA_PTR *cssmCert;
+ for(cssmCert=cssmCerts; *cssmCert!=NULL; cssmCert++) {
+ OSStatus ortn;
+ SecCertificateRef cfCert;
+ ortn = SecCertificateCreateFromData(*cssmCert,
+ CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
+ &cfCert);
+ if(ortn) {
+ CFRelease(allCerts);
+ return ortn;
+ }
+ CFArrayAppendValue(allCerts, cfCert);
+ /* the array holds the only needed refcount */
+ CFRelease(cfCert);
+ }
+ *certs = allCerts;
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the actual message content (payload), if any. If the message was
+ * signed with detached content this will return NULL.
+ * Caller must CFRelease the result.
+ */
+OSStatus CMSDecoderCopyContent(
+ CMSDecoderRef cmsDecoder,
+ CFDataRef *content) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (content == NULL)) {
+ return errSecParam;
+ }
+ if(cmsDecoder->decState != DS_Final) {
+ return errSecParam;
+ }
+ if(cmsDecoder->cmsMsg == NULL) {
+ /* Hmmm....looks like the finalize call failed */
+ return errSecParam;
+ }
+ CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsDecoder->cmsMsg);
+ if((odata == NULL) || (odata->Length == 0)) {
+ /* i.e., detached content */
+ *content = NULL;
+ return errSecSuccess;
+ }
+ *content = CFDataCreate(NULL, (const UInt8 *)odata->Data, odata->Length);
+ return errSecSuccess;
+}
+
+#pragma mark --- SPI declared in CMSPrivate.h ---
+
+/*
+ * Obtain the SecCmsMessageRef associated with a CMSDecoderRef. Intended
+ * to be called after decoding the message (i.e., after
+ * CMSDecoderFinalizeMessage() to gain finer access to the contents of the
+ * SecCmsMessageRef than is otherwise available via the CMSDecoder interface.
+ * Returns a NULL SecCmsMessageRef if CMSDecoderFinalizeMessage() has not been
+ * called.
+ *
+ * The CMSDecoder retains ownership of the returned SecCmsMessageRef.
+ */
+OSStatus CMSDecoderGetCmsMessage(
+ CMSDecoderRef cmsDecoder,
+ SecCmsMessageRef *cmsMessage) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (cmsMessage == NULL)) {
+ return errSecParam;
+ }
+ /* any state, whether we have a msg or not is OK */
+ *cmsMessage = cmsDecoder->cmsMsg;
+ return errSecSuccess;
+}
+
+/*
+ * Optionally specify a SecCmsDecoderRef to use with a CMSDecoderRef.
+ * If this is called, it must be called before the first call to
+ * CMSDecoderUpdateMessage(). The CMSDecoderRef takes ownership of the
+ * incoming SecCmsDecoderRef.
+ */
+OSStatus CMSDecoderSetDecoder(
+ CMSDecoderRef cmsDecoder,
+ SecCmsDecoderRef decoder)
+{
+ if((cmsDecoder == NULL) || (decoder == NULL)) {
+ return errSecParam;
+ }
+ switch(cmsDecoder->decState) {
+ case DS_Init:
+ ASSERT(cmsDecoder->decoder == NULL);
+ cmsDecoder->decoder = decoder;
+ cmsDecoder->decState = DS_Updating;
+ return errSecSuccess;
+ case DS_Updating:
+ case DS_Final:
+ return errSecParam;
+ }
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the SecCmsDecoderRef associated with a CMSDecoderRef.
+ * Returns a NULL SecCmsDecoderRef if neither CMSDecoderSetDecoder() nor
+ * CMSDecoderUpdateMessage() has been called.
+ * The CMSDecoderRef retains ownership of the SecCmsDecoderRef.
+ */
+OSStatus CMSDecoderGetDecoder(
+ CMSDecoderRef cmsDecoder,
+ SecCmsDecoderRef *decoder) /* RETURNED */
+{
+ if((cmsDecoder == NULL) || (decoder == NULL)) {
+ return errSecParam;
+ }
+ /* any state, whether we have a decoder or not is OK */
+ *decoder = cmsDecoder->decoder;
+ return errSecSuccess;
+}
+
+/*
+ * Obtain the signing time of signer 'signerIndex' of a CMS message, if
+ * present. This is an unauthenticate time, although it is part of the
+ * signed attributes of the message.
+ *
+ * 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 CMSDecoderCopySignerSigningTime(
+ CMSDecoderRef cmsDecoder,
+ size_t signerIndex, /* usually 0 */
+ CFAbsoluteTime *signingTime) /* RETURNED */
+{
+ OSStatus status = errSecParam;
+ SecCmsMessageRef cmsg;
+ SecCmsSignedDataRef signedData = NULL;
+ int numContentInfos = 0;
+
+ require(cmsDecoder && signingTime, 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 = SecCmsSignerInfoGetSigningTime(signerInfo, signingTime);
+ break;
+ }
+ }
+xit:
+ return status;
+}
+
+/*
+ * 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 */
+{
+ return CMSDecoderCopySignerTimestampWithPolicy(cmsDecoder, NULL, signerIndex, timestamp);
+}
+
+OSStatus CMSDecoderCopySignerTimestampWithPolicy(
+ CMSDecoderRef cmsDecoder,
+ CFTypeRef timeStampPolicy,
+ 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 = SecCmsSignerInfoGetTimestampTimeWithPolicy(signerInfo, timeStampPolicy, timestamp);
+ break;
+ }
+ }
+
+xit:
+ return status;
+}
+
+/*
+ * 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;
+ }