]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_smime/lib/cmsdecode.c
Security-59306.41.2.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmsdecode.c
index f9ce1257a244860326c7fedb665495a4b9683f90..c3c1e2e14d3ca8ab2610a31d62977a1b085a44fc 100644 (file)
 
 #include "cmslocal.h"
 
-#include "secitem.h"
+#include "SecAsn1Item.h"
 #include "secoid.h"
+
 #include <security_asn1/secasn1.h>
 #include <security_asn1/secerr.h>
+#include <security_asn1/secport.h>
+
+#include <limits.h>
 
 struct SecCmsDecoderStr {
     SEC_ASN1DecoderContext *           dcx;            /* ASN.1 decoder context */
@@ -59,6 +63,8 @@ struct SecCmsDecoderStr {
     void *                             cb_arg;
 };
 
+/* We use size_t for len in this function because the SEC_ASN1Decoder* layer
+   uses that for callback function */
 static void nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
                           int depth, SEC_ASN1EncodingPart data_kind);
 static OSStatus nss_cms_before_data(SecCmsDecoderRef p7dcx);
@@ -89,7 +95,7 @@ nss_cms_decoder_notify(void *arg, Boolean before, void *dest, int depth)
     /* XXX error handling: need to set p7dcx->error */
 
 #ifdef CMSDEBUG 
-    fprintf(stderr, "%6.6s, dest = %p, depth = %d\n", before ? "before" : "after", dest, depth);
+    fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
 #endif
 
     /* so what are we working on right now? */
@@ -110,7 +116,6 @@ nss_cms_decoder_notify(void *arg, Boolean before, void *dest, int depth)
        }
        break;
     case SEC_OID_PKCS7_DATA:
-    case SEC_OID_OTHER:
        /* this can only happen if the outermost cinfo has DATA in it */
        /* otherwise, we handle this type implicitely in the inner decoders */
 
@@ -150,16 +155,16 @@ nss_cms_decoder_notify(void *arg, Boolean before, void *dest, int depth)
            /* please give me C++ */
            switch (p7dcx->type) {
            case SEC_OID_PKCS7_SIGNED_DATA:
-               p7dcx->content.signedData->cmsg = p7dcx->cmsg;
+               p7dcx->content.signedData->contentInfo.cmsg = p7dcx->cmsg;
                break;
            case SEC_OID_PKCS7_DIGESTED_DATA:
-               p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
+               p7dcx->content.digestedData->contentInfo.cmsg = p7dcx->cmsg;
                break;
            case SEC_OID_PKCS7_ENVELOPED_DATA:
-               p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
+               p7dcx->content.envelopedData->contentInfo.cmsg = p7dcx->cmsg;
                break;
            case SEC_OID_PKCS7_ENCRYPTED_DATA:
-               p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
+               p7dcx->content.encryptedData->contentInfo.cmsg = p7dcx->cmsg;
                break;
            default:
                PORT_Assert(0);
@@ -177,12 +182,15 @@ nss_cms_decoder_notify(void *arg, Boolean before, void *dest, int depth)
            if (nss_cms_before_data(p7dcx) != SECSuccess) {
                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);     /* stop all processing */
                p7dcx->error = PORT_GetError();
+                PORT_SetError(0);
            }
        }
        if (after && dest == &(cinfo->rawContent)) {
            /* we're right after of the data */
-           if (nss_cms_after_data(p7dcx) != SECSuccess)
+            if (nss_cms_after_data(p7dcx) != SECSuccess) {
                p7dcx->error = PORT_GetError();
+                PORT_SetError(0);
+            }
 
            /* we don't need to see the contents anymore */
            SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
@@ -249,13 +257,8 @@ nss_cms_before_data(SecCmsDecoderRef p7dcx)
     
     cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
     childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
-    
-    /* special case for SignedData: "unknown" child type maps to SEC_OID_OTHER */
-    if((childtype == SEC_OID_UNKNOWN) && (p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA)) {
-       childtype = SEC_OID_OTHER;
-    }
-    
-    if ((childtype == SEC_OID_PKCS7_DATA) || (childtype == SEC_OID_OTHER)){
+
+    if (childtype == SEC_OID_PKCS7_DATA) {
        cinfo->content.data = SECITEM_AllocItem(poolp, NULL, 0);
        if (cinfo->content.data == NULL)
            /* set memory error */
@@ -282,11 +285,8 @@ nss_cms_before_data(SecCmsDecoderRef p7dcx)
     if (childp7dcx->content.pointer == NULL)
        goto loser;
 
-    /* Apple: link the new content to parent ContentInfo */
-    cinfo->content.pointer = childp7dcx->content.pointer;
-    
     /* start the child decoder */
-    childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL);
+    childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL, 0);
     if (childp7dcx->dcx == NULL)
        goto loser;
 
@@ -324,12 +324,9 @@ loser:
 static OSStatus
 nss_cms_after_data(SecCmsDecoderRef p7dcx)
 {
-    PLArenaPool *poolp;
     SecCmsDecoderRef childp7dcx;
     OSStatus rv = SECFailure;
 
-    poolp = p7dcx->cmsg->poolp;
-
     /* Handle last block. This is necessary to flush out the last bytes
      * of a possibly incomplete block */
     nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
@@ -380,9 +377,6 @@ static OSStatus
 nss_cms_after_end(SecCmsDecoderRef p7dcx)
 {
     OSStatus rv;
-    PLArenaPool *poolp;
-
-    poolp = p7dcx->cmsg->poolp;
 
     switch (p7dcx->type) {
     case SEC_OID_PKCS7_SIGNED_DATA:
@@ -421,10 +415,10 @@ nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
     SecCmsContentInfoRef cinfo;
     unsigned char *buf = NULL;
     unsigned char *dest;
-    CSSM_SIZE offset;
+    size_t offset;
     OSStatus rv;
-    CSSM_DATA_PTR storage;
-    
+    SecAsn1Item * storage;
+
     /*
      * We should really have data to process, or we should be trying
      * to finish/flush the last block.  (This is an overly paranoid
@@ -432,80 +426,84 @@ nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
      * proves they do it right.  But it could find a bug in future
      * modifications/development, that is why it is here.)
      */
-    PORT_Assert ((data != NULL && len) || final);
+    if (((data == NULL || len == 0) && !final) ||
+        len > UINT_MAX) {
+        goto loser;
+    }
 
     if (!p7dcx->content.pointer)       // might be ExContent??
         return;
-        
+
     cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
 
     if (cinfo->ciphcx != NULL) {
-       /*
-        * we are decrypting.
-        * 
-        * XXX If we get an error, we do not want to do the digest or callback,
-        * but we want to keep decoding.  Or maybe we want to stop decoding
-        * altogether if there is a callback, because obviously we are not
-        * sending the data back and they want to know that.
-        */
-
-       CSSM_SIZE outlen = 0;   /* length of decrypted data */
-       CSSM_SIZE buflen;               /* length available for decrypted data */
-
-       /* find out about the length of decrypted data */
-       buflen = SecCmsCipherContextDecryptLength(cinfo->ciphcx, len, final);
-
-       /*
-        * it might happen that we did not provide enough data for a full
-        * block (decryption unit), and that there is no output available
-        */
-
-       /* no output available, AND no input? */
-       if (buflen == 0 && len == 0)
-           goto loser; /* bail out */
-
-       /*
-        * have inner decoder: pass the data on (means inner content type is NOT data)
-        * no inner decoder: we have DATA in here: either call callback or store
-        */
-       if (buflen != 0) {
-           /* there will be some output - need to make room for it */
-           /* allocate buffer from the heap */
-           buf = (unsigned char *)PORT_Alloc(buflen);
-           if (buf == NULL) {
-               p7dcx->error = SEC_ERROR_NO_MEMORY;
-               goto loser;
-           }
-       }
-
-       /*
-        * decrypt incoming data
-        * buf can still be NULL here (and buflen == 0) here if we don't expect
-        * any output (see above), but we still need to call SecCmsCipherContextDecrypt to
-        * keep track of incoming data
-        */
-       rv = SecCmsCipherContextDecrypt(cinfo->ciphcx, buf, &outlen, buflen,
-                              data, len, final);
-       if (rv != SECSuccess) {
-           p7dcx->error = PORT_GetError();
-           goto loser;
-       }
-
-       PORT_Assert (final || outlen == buflen);
-       
-       /* swap decrypted data in */
-       data = buf;
-       len = outlen;
+        /*
+         * we are decrypting.
+         *
+         * XXX If we get an error, we do not want to do the digest or callback,
+         * but we want to keep decoding.  Or maybe we want to stop decoding
+         * altogether if there is a callback, because obviously we are not
+         * sending the data back and they want to know that.
+         */
+
+        unsigned int outlen = 0;       /* length of decrypted data */
+        unsigned int buflen;           /* length available for decrypted data */
+
+        /* find out about the length of decrypted data */
+        /* 64 bits cast: Worst case here is we may not decrypt the full CMS blob, if the blob is bigger than 4GB */
+        buflen = SecCmsCipherContextDecryptLength(cinfo->ciphcx, (unsigned int)len, final);
+
+        /*
+         * it might happen that we did not provide enough data for a full
+         * block (decryption unit), and that there is no output available
+         */
+
+        /* no output available, AND no input? */
+        if (buflen == 0 && len == 0)
+            goto loser;        /* bail out */
+
+        /*
+         * have inner decoder: pass the data on (means inner content type is NOT data)
+         * no inner decoder: we have DATA in here: either call callback or store
+         */
+        if (buflen != 0) {
+            /* there will be some output - need to make room for it */
+            /* allocate buffer from the heap */
+            buf = (unsigned char *)PORT_Alloc(buflen);
+            if (buf == NULL) {
+                p7dcx->error = SEC_ERROR_NO_MEMORY;
+                goto loser;
+            }
+        }
+
+        /*
+         * decrypt incoming data
+         * buf can still be NULL here (and buflen == 0) here if we don't expect
+         * any output (see above), but we still need to call SecCmsCipherContextDecrypt to
+         * keep track of incoming data
+         */
+        rv = SecCmsCipherContextDecrypt(cinfo->ciphcx, buf, &outlen, buflen,
+                                        data, (unsigned int)len, final);
+        if (rv != SECSuccess) {
+            p7dcx->error = PORT_GetError();
+            goto loser;
+        }
+
+        //PORT_Assert (final || outlen == buflen);
+
+        /* swap decrypted data in */
+        data = buf;
+        len = outlen;
     }
 
     if (len == 0)
-       goto done;              /* nothing more to do */
+        goto done;             /* nothing more to do */
 
     /*
      * Update the running digests with plaintext bytes (if we need to).
      */
     if (cinfo->digcx)
-       SecCmsDigestContextUpdate(cinfo->digcx, data, len);
+        SecCmsDigestContextUpdate(cinfo->digcx, data, len);
 
     /* at this point, we have the plain decoded & decrypted data */
     /* which is either more encoded DER which we need to hand to the child decoder */
@@ -514,45 +512,46 @@ nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
     /* pass the content back to our caller or */
     /* feed our freshly decrypted and decoded data into child decoder */
     if (p7dcx->cb != NULL) {
-       (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
-    }
-#if 1
-    else
-#endif
-    switch(SecCmsContentInfoGetContentTypeTag(cinfo)) {
-       default:
-           break;
-       case SEC_OID_PKCS7_DATA:
-       case SEC_OID_OTHER:
-       /* store it in "inner" data item as well */
-       /* find the DATA item in the encapsulated cinfo and store it there */
-       storage = cinfo->content.data;
-
-       offset = storage->Length;
-       if (storage->Length == 0) {
-           dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
-       } else {
-           dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp, 
-                                 storage->Data,
-                                 storage->Length,
-                                 storage->Length + len);
-       }
-       if (dest == NULL) {
-           p7dcx->error = SEC_ERROR_NO_MEMORY;
-           goto loser;
-       }
-
-       storage->Data = dest;
-       storage->Length += len;
-
-       /* copy it in */
-       PORT_Memcpy(storage->Data + offset, data, len);
+        (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
+    } else if (SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
+        /* store it in "inner" data item as well */
+        /* find the DATA item in the encapsulated cinfo and store it there */
+        storage = cinfo->content.data;
+
+        offset = storage->Length;
+
+        /* check for potential overflow */
+        if (len >= (size_t)(INT_MAX - storage->Length)) {
+            p7dcx->error = SEC_ERROR_NO_MEMORY;
+            goto loser;
+        }
+
+        if (storage->Length == 0) {
+            dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
+        } else {
+            dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp,
+                                                   storage->Data,
+                                                   storage->Length,
+                                                   storage->Length + len);
+        }
+        if (dest == NULL) {
+            p7dcx->error = SEC_ERROR_NO_MEMORY;
+            goto loser;
+        }
+
+        storage->Data = dest;
+        storage->Length += len;
+
+        /* copy it in */
+        if (data != NULL) {
+            PORT_Memcpy(storage->Data + offset, data, len);
+        }
     }
 
 done:
 loser:
     if (buf)
-       PORT_Free (buf);
+        PORT_Free (buf);
 }
 
 /*
@@ -562,6 +561,9 @@ loser:
  * all data processed by the ASN.1 decoder is also passed through here.
  * we pass the content bytes (as opposed to length and tag bytes) on to
  * nss_cms_decoder_work_data().
+ *
+ * len has to be of type size_t because it is a callback to the
+ * SEC_ASN1Decoder layer
  */
 static void
 nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
@@ -586,8 +588,7 @@ nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
  * SecCmsDecoderCreate - set up decoding of a BER-encoded CMS message
  */
 OSStatus
-SecCmsDecoderCreate(SecArenaPoolRef pool,
-                    SecCmsContentCallback cb, void *cb_arg,
+SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg,
                     PK11PasswordFunc pwfn, void *pwfn_arg,
                     SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
                     SecCmsDecoderRef *outDecoder)
@@ -596,12 +597,14 @@ SecCmsDecoderCreate(SecArenaPoolRef pool,
     SecCmsMessageRef cmsg;
     OSStatus result;
 
-    cmsg = SecCmsMessageCreate(pool);
+    /* Clear the thread error to clean up dirty threads */
+    PORT_SetError(0);
+
+    cmsg = SecCmsMessageCreate();
     if (cmsg == NULL)
         goto loser;
 
-    SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
-                                       NULL, NULL);
+    SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg);
 
     p7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr));
     if (p7dcx == NULL) {
@@ -609,7 +612,7 @@ SecCmsDecoderCreate(SecArenaPoolRef pool,
        goto loser;
     }
 
-    p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL);
+    p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL, 0);
     if (p7dcx->dcx == NULL) {
        PORT_Free (p7dcx);
        SecCmsMessageDestroy(cmsg);
@@ -625,10 +628,11 @@ SecCmsDecoderCreate(SecArenaPoolRef pool,
     p7dcx->cb_arg = cb_arg;
 
     *outDecoder = p7dcx;
-    return noErr;
+    return errSecSuccess;
 
 loser:
     result = PORT_GetError();
+    PORT_SetError(0); // Clean the thread error since we've returned the error
     return result;
 }
 
@@ -638,6 +642,10 @@ loser:
 OSStatus
 SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len)
 {
+    if (!p7dcx) {
+        return errSecParam;
+    }
+
     if (p7dcx->dcx != NULL && p7dcx->error == 0) {     /* if error is set already, don't bother */
        if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
            p7dcx->error = PORT_GetError();
@@ -656,7 +664,8 @@ SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len)
        (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
        p7dcx->dcx = NULL;
     }
-    PORT_SetError (p7dcx->error);
+
+    PORT_SetError (0); // Clean the thread error since we've returned the error
 
     return p7dcx->error;
 }
@@ -667,11 +676,15 @@ SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len)
 void
 SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx)
 {
-    /* XXXX what about inner decoders? running digests? decryption? */
-    /* XXXX there's a leak here! */
+    /* SecCmsMessageDestroy frees inner decoders and digests. */
     SecCmsMessageDestroy(p7dcx->cmsg);
+    p7dcx->cmsg = NULL;
     if (p7dcx->dcx)
         (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
+    /* Clear out references */
+    p7dcx->cmsg = NULL;
+    p7dcx->dcx = NULL;
+    p7dcx->childp7dcx = NULL;
     PORT_Free(p7dcx);
 }
 
@@ -689,30 +702,37 @@ SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
     if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
        nss_cms_after_end(p7dcx) != SECSuccess)
     {
-       SecCmsMessageDestroy(cmsg);     /* needs to get rid of pool if it's ours */
+        if (p7dcx->cmsg) {
+            SecCmsMessageDestroy(cmsg);
+        }
         result = PORT_GetError();
         goto loser;
     }
 
     *outMessage = cmsg;
-    result = noErr;
+    result = errSecSuccess;
 
 loser:
+    /* Clear out references */
+    p7dcx->cmsg = NULL;
+    p7dcx->dcx = NULL;
+    p7dcx->childp7dcx = NULL;
     PORT_Free(p7dcx);
+    PORT_SetError(0); // Clean the thread error since we've returned the error
     return result;
 }
 
 OSStatus
-SecCmsMessageDecode(const CSSM_DATA *encodedMessage,
+SecCmsMessageDecode(const SecAsn1Item *encodedMessage,
                     SecCmsContentCallback cb, void *cb_arg,
                     PK11PasswordFunc pwfn, void *pwfn_arg,
                     SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
                     SecCmsMessageRef *outMessage)
 {
     OSStatus result;
-    SecCmsDecoderRef decoder;
+    SecCmsDecoderRef decoder = NULL;
 
-    result = SecCmsDecoderCreate(NULL, cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder);
+    result = SecCmsDecoderCreate(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder);
     if (result)
         goto loser;
     result = SecCmsDecoderUpdate(decoder, encodedMessage->Data, encodedMessage->Length);
@@ -725,4 +745,3 @@ SecCmsMessageDecode(const CSSM_DATA *encodedMessage,
 loser:
     return result;
 }
-