]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_smime/lib/cmsdecode.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmsdecode.c
index 765a1685f670dde02abe16f7a165438b961ce6ea..e8c9b28c2d2dae2320fcae117a0f986679f6d7ce 100644 (file)
@@ -177,12 +177,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); // Clean the thread error since we've returned the error
            }
        }
        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); // Clean the thread error since we've returned the error
+            }
 
            /* we don't need to see the contents anymore */
            SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
@@ -380,9 +383,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:
@@ -436,76 +436,77 @@ nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
 
     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.
+         */
+
+        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();
+            PORT_SetError(0); // Clean the thread error since we've returned the error
+            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,52 +515,54 @@ 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);
+        (*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;
-
-       /* 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);
-    }
+        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;
+
+                /* 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);
 }
 
 /*
@@ -603,6 +606,9 @@ SecCmsDecoderCreate(SecArenaPoolRef pool,
     SecCmsMessageRef cmsg;
     OSStatus result;
 
+    /* Clear the thread error to clean up dirty threads */
+    PORT_SetError(0);
+
     cmsg = SecCmsMessageCreate(pool);
     if (cmsg == NULL)
         goto loser;
@@ -636,6 +642,7 @@ SecCmsDecoderCreate(SecArenaPoolRef pool,
 
 loser:
     result = PORT_GetError();
+    PORT_SetError(0); // Clean the thread error since we've returned the error
     return result;
 }
 
@@ -645,6 +652,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();
@@ -663,7 +674,7 @@ 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;
 }
@@ -674,11 +685,17 @@ 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(p7dcx->cmsg);
-    if (p7dcx->dcx)
+    /* SecCmsMessageDestroy frees inner decoders and digests. */
+    if (p7dcx->cmsg) {
+        SecCmsMessageDestroy(p7dcx->cmsg);
+    }
+    if (p7dcx->dcx) {
         (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
+    }
+    /* Clear out references */
+    p7dcx->cmsg = NULL;
+    p7dcx->dcx = NULL;
+    p7dcx->childp7dcx = NULL;
     PORT_Free(p7dcx);
 }
 
@@ -696,7 +713,9 @@ 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);        /* needs to get rid of pool if it's ours */
+        }
         result = PORT_GetError();
         goto loser;
     }
@@ -705,7 +724,12 @@ SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
     result = noErr;
 
 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;
 }