#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 */
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);
/* 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? */
}
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 */
/* 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);
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);
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 */
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;
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);
nss_cms_after_end(SecCmsDecoderRef p7dcx)
{
OSStatus rv;
- PLArenaPool *poolp;
-
- poolp = p7dcx->cmsg->poolp;
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
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
* 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 */
/* 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);
}
/*
* 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,
* 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)
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) {
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);
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;
}
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();
(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;
}
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);
}
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);
loser:
return result;
}
-