]> 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 da49df65c9f42c3ca06f1c0481ef9d1298405c75..c3c1e2e14d3ca8ab2610a31d62977a1b085a44fc 100644 (file)
@@ -182,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);
@@ -374,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:
@@ -426,83 +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);
-    /* Debug check for 64 bits cast later */
-    PORT_Assert (len <= UINT_MAX);
+    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.
-        */
-
-       unsigned int outlen = 0;        /* length of decrypted data */
-       unsigned int buflen;            /* length available for decrypted data */
-
-       /* find out about the length of decrypted data */
+        /*
+         * 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;
+        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 */
@@ -511,48 +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
-    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 */
-       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);
 }
 
 /*
@@ -598,6 +597,9 @@ SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg,
     SecCmsMessageRef cmsg;
     OSStatus result;
 
+    /* Clear the thread error to clean up dirty threads */
+    PORT_SetError(0);
+
     cmsg = SecCmsMessageCreate();
     if (cmsg == NULL)
         goto loser;
@@ -630,6 +632,7 @@ SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg,
 
 loser:
     result = PORT_GetError();
+    PORT_SetError(0); // Clean the thread error since we've returned the error
     return result;
 }
 
@@ -639,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();
@@ -657,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;
 }
@@ -668,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);
 }
 
@@ -690,7 +702,9 @@ SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
     if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
        nss_cms_after_end(p7dcx) != SECSuccess)
     {
-       SecCmsMessageDestroy(cmsg);
+        if (p7dcx->cmsg) {
+            SecCmsMessageDestroy(cmsg);
+        }
         result = PORT_GetError();
         goto loser;
     }
@@ -699,7 +713,12 @@ SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
     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;
 }