CVE-2015-1792: Resource Management Errors (CWE-399)
value: The do_free_upto function in crypto/cms/cms_smime.c in OpenSSL before 0.9.8zg, 1.0.0 before 1.0.0s, 1.0.1 before 1.0.1n, and 1.0.2 before 1.0.2b allows remote attackers to cause a denial of service (infinite loop) via vectors that trigger a NULL value of a BIO data structure, as demonstrated by an unrecognized X.660 OID for a hash function.
CWE description: Weaknesses in this category are related to improper management of system resources.
Published: 2015-06-12T19:59Z
Repository: https://github.com/openssl/openssl
Language: C
Files changed: crypto/cms/cms_smime.c
Commit date: 2013-12-22 00:35:29 +0000
Commit message: Canonicalise input in CMS_verify.

If content is detached and not binary mode translate the input to
CRLF format. Before this change the input was verified verbatim
which lead to a discrepancy between sign and verify.

Diff: diff --git a/crypto/cms/cms_smime.c b/crypto/cms/cms_smime.c
index 204595b908..8f1aa38ccc 100644
--- a/crypto/cms/cms_smime.c
+++ b/crypto/cms/cms_smime.c
@@ -58,25 +58,32 @@
 #include <openssl/err.h>
 #include <openssl/cms.h>
 #include "cms_lcl.h"
 #include "asn1_locl.h"
 
-static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
+static BIO *cms_get_text_bio(BIO *out, unsigned int flags)
 	{
-	unsigned char buf[4096];
-	int r = 0, i;
-	BIO *tmpout = NULL;
-
+	BIO *rbio;
 	if (out == NULL)
-		tmpout = BIO_new(BIO_s_null());
+		rbio = BIO_new(BIO_s_null());
 	else if (flags & CMS_TEXT)
 		{
-		tmpout = BIO_new(BIO_s_mem());
-		BIO_set_mem_eof_return(tmpout, 0);
+		rbio = BIO_new(BIO_s_mem());
+		BIO_set_mem_eof_return(rbio, 0);
 		}
 	else
-		tmpout = out;
+		rbio = out;
+	return rbio;
+	}
+
+static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
+	{
+	unsigned char buf[4096];
+	int r = 0, i;
+	BIO *tmpout;
+
+	tmpout = cms_get_text_bio(out, flags);
 
 	if(!tmpout)
 		{
 		CMSerr(CMS_F_CMS_COPY_CONTENT,ERR_R_MALLOC_FAILURE);
 		goto err;
@@ -140,11 +147,11 @@ static void do_free_upto(BIO *f, BIO *upto)
 			{
 			tbio = BIO_pop(f);
 			BIO_free(f);
 			f = tbio;
 			}
-		while (f != upto);
+		while (f && f != upto);
 		}
 	else
 		BIO_free_all(f);
 	}
 
@@ -321,11 +328,11 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
 	STACK_OF(CMS_SignerInfo) *sinfos;
 	STACK_OF(X509) *cms_certs = NULL;
 	STACK_OF(X509_CRL) *crls = NULL;
 	X509 *signer;
 	int i, scount = 0, ret = 0;
-	BIO *cmsbio = NULL, *tmpin = NULL;
+	BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL;
 
 	if (!dcont && !check_content(cms))
 		return 0;
 
 	/* Attempt to find all signer certificates */
@@ -404,19 +411,52 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
 			return 0;
 			}
 		}
 	else
 		tmpin = dcont;
-		
+	/* If not binary mode and detached generate digests by *writing*
+	 * through the BIO. That makes it possible to canonicalise the
+	 * input.
+	 */
+	if (!(flags & SMIME_BINARY) && dcont)
+		{
+		/* Create output BIO so we can either handle text or to
+		 * ensure included content doesn't override detached content.
+		 */
+		tmpout = cms_get_text_bio(out, flags);
+		if(!tmpout)
+			{
+			CMSerr(CMS_F_CMS_VERIFY,ERR_R_MALLOC_FAILURE);
+			goto err;
+			}
+		cmsbio = CMS_dataInit(cms, tmpout);
+		if (!cmsbio)
+			goto err;
+		/* Don't use SMIME_TEXT for verify: it adds headers and
+		 * we want to remove them.
+		 */
+		SMIME_crlf_copy(dcont, cmsbio, flags & ~SMIME_TEXT);
 
-	cmsbio=CMS_dataInit(cms, tmpin);
-	if (!cmsbio)
-		goto err;
+		if(flags & CMS_TEXT)
+			{
+			if (!SMIME_text(tmpout, out))
+				{
+				CMSerr(CMS_F_CMS_VERIFY,CMS_R_SMIME_TEXT_ERROR);
+				goto err;
+				}
+			}
+		}
+	else
+		{
+		cmsbio=CMS_dataInit(cms, tmpin);
+		if (!cmsbio)
+			goto err;
 
-	if (!cms_copy_content(out, cmsbio, flags))
-		goto err;
+		if (!cms_copy_content(out, cmsbio, flags))
+			goto err;
 
+		}
 	if (!(flags & CMS_NO_CONTENT_VERIFY))
 		{
 		for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++)
 			{
 			si = sk_CMS_SignerInfo_value(sinfos, i);
@@ -430,15 +470,27 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
 		}
 
 	ret = 1;
 
 	err:
-	
-	if (dcont && (tmpin == dcont))
-		do_free_upto(cmsbio, dcont);
+	if (!(flags & SMIME_BINARY) && dcont)
+		{
+		do_free_upto(cmsbio, tmpout);
+		if (tmpin != dcont)
+			BIO_free(tmpin);
+		}
 	else
-		BIO_free_all(cmsbio);
+		{
+
+		if (dcont && (tmpin == dcont))
+			do_free_upto(cmsbio, dcont);
+		else
+			BIO_free_all(cmsbio);
+		}
+
+	if (tmpout && out != tmpout)
+		BIO_free_all(tmpout);
 
 	if (cms_certs)
 		sk_X509_pop_free(cms_certs, X509_free);
 	if (crls)
 		sk_X509_CRL_pop_free(crls, X509_CRL_free);

Vulnerable code: static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
	unsigned char buf[4096];
	int r = 0, i;
	BIO *tmpout = NULL;

		tmpout = BIO_new(BIO_s_null());
		tmpout = BIO_new(BIO_s_mem());
		BIO_set_mem_eof_return(tmpout, 0);
		tmpout = out;
		while (f != upto);
	BIO *cmsbio = NULL, *tmpin = NULL;
		
	cmsbio=CMS_dataInit(cms, tmpin);
	if (!cmsbio)
		goto err;
	if (!cms_copy_content(out, cmsbio, flags))
		goto err;
	
	if (dcont && (tmpin == dcont))
		do_free_upto(cmsbio, dcont);
		BIO_free_all(cmsbio);
Fixed code: static BIO *cms_get_text_bio(BIO *out, unsigned int flags)
	BIO *rbio;
		rbio = BIO_new(BIO_s_null());
		rbio = BIO_new(BIO_s_mem());
		BIO_set_mem_eof_return(rbio, 0);
		rbio = out;
	return rbio;
	}

static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
	{
	unsigned char buf[4096];
	int r = 0, i;
	BIO *tmpout;

	tmpout = cms_get_text_bio(out, flags);
		while (f && f != upto);
	BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL;
	/* If not binary mode and detached generate digests by *writing*
	 * through the BIO. That makes it possible to canonicalise the
	 * input.
	 */
	if (!(flags & SMIME_BINARY) && dcont)
		{
		/* Create output BIO so we can either handle text or to
		 * ensure included content doesn't override detached content.
		 */
		tmpout = cms_get_text_bio(out, flags);
		if(!tmpout)
			{
			CMSerr(CMS_F_CMS_VERIFY,ERR_R_MALLOC_FAILURE);
			goto err;
			}
		cmsbio = CMS_dataInit(cms, tmpout);
		if (!cmsbio)
			goto err;
		/* Don't use SMIME_TEXT for verify: it adds headers and
		 * we want to remove them.
		 */
		SMIME_crlf_copy(dcont, cmsbio, flags & ~SMIME_TEXT);
		if(flags & CMS_TEXT)
			{
			if (!SMIME_text(tmpout, out))
				{
				CMSerr(CMS_F_CMS_VERIFY,CMS_R_SMIME_TEXT_ERROR);
				goto err;
				}
			}
		}
	else
		{
		cmsbio=CMS_dataInit(cms, tmpin);
		if (!cmsbio)
			goto err;
		if (!cms_copy_content(out, cmsbio, flags))
			goto err;
		}
	if (!(flags & SMIME_BINARY) && dcont)
		{
		do_free_upto(cmsbio, tmpout);
		if (tmpin != dcont)
			BIO_free(tmpin);
		}
		{

		if (dcont && (tmpin == dcont))
			do_free_upto(cmsbio, dcont);
		else
			BIO_free_all(cmsbio);
		}

	if (tmpout && out != tmpout)
		BIO_free_all(tmpout);