]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_cms/lib/CMSDecoder.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_cms / lib / CMSDecoder.cpp
diff --git a/Security/libsecurity_cms/lib/CMSDecoder.cpp b/Security/libsecurity_cms/lib/CMSDecoder.cpp
new file mode 100644 (file)
index 0000000..e4ed76a
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ * 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;
+    }