Vulnerability: 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.
Vulnerability ID: CVE-2015-1792
CWE: CWE-399
Severity score: 2.9
Project: https://github.com/openssl/openssl
Published: 2015-06-12
File: crypto/cms/cms_smime.c
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.
Commit author: Dr. Stephen Henson

Vulnerable version (before fix):
 #include "cms_lcl.h" #include "asn1_locl.h"  static int cms_copy_content(BIO *out, BIO *in, unsigned int flags) 	{ 	unsigned char buf[4096]; 	int r = 0, i; 	BIO *tmpout = NULL;  	if (out == NULL) 		tmpout = BIO_new(BIO_s_null()); 	else if (flags & CMS_TEXT) 		{ 		tmpout = BIO_new(BIO_s_mem()); 		BIO_set_mem_eof_return(tmpout, 0); 		} 	else 		tmpout = out;  	if(!tmpout) 		{ static void do_free_upto(BIO *f, BIO *upto) 			BIO_free(f); 			f = tbio; 			} 		while (f != upto); 		} 	else 		BIO_free_all(f); int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 	STACK_OF(X509_CRL) *crls = NULL; 	X509 *signer; 	int i, scount = 0, ret = 0; 	BIO *cmsbio = NULL, *tmpin = NULL;  	if (!dcont && !check_content(cms)) 		return 0; int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 		} 	else 		tmpin = dcont; 		  	cmsbio=CMS_dataInit(cms, tmpin); 	if (!cmsbio) 		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++) int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 	ret = 1;  	err: 	 	if (dcont && (tmpin == dcont)) 		do_free_upto(cmsbio, dcont); 	else 		BIO_free_all(cmsbio);  	if (cms_certs) 		sk_X509_pop_free(cms_certs, X509_free);

Fixed version (after fix):
 #include "cms_lcl.h" #include "asn1_locl.h"  static BIO *cms_get_text_bio(BIO *out, unsigned int flags) 	{ 	BIO *rbio; 	if (out == NULL) 		rbio = BIO_new(BIO_s_null()); 	else if (flags & CMS_TEXT) 		{ 		rbio = BIO_new(BIO_s_mem()); 		BIO_set_mem_eof_return(rbio, 0); 		} 	else 		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) 		{ static void do_free_upto(BIO *f, BIO *upto) 			BIO_free(f); 			f = tbio; 			} 		while (f && f != upto); 		} 	else 		BIO_free_all(f); int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 	STACK_OF(X509_CRL) *crls = NULL; 	X509 *signer; 	int i, scount = 0, ret = 0; 	BIO *cmsbio = NULL, *tmpin = NULL, *tmpout = NULL;  	if (!dcont && !check_content(cms)) 		return 0; int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 		} 	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);  		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 & CMS_NO_CONTENT_VERIFY)) 		{ 		for (i = 0; i < sk_CMS_SignerInfo_num(sinfos); i++) int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs, 	ret = 1;  	err: 	if (!(flags & SMIME_BINARY) && dcont) 		{ 		do_free_upto(cmsbio, tmpout); 		if (tmpin != dcont) 			BIO_free(tmpin); 		} 	else 		{  		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);

Patch:
patch: @@ -60,21 +60,28 @@
 #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)
 		{
@@ -142,7 +149,7 @@ static void do_free_upto(BIO *f, BIO *upto)
 			BIO_free(f);
 			f = tbio;
 			}
-		while (f != upto);
+		while (f && f != upto);
 		}
 	else
 		BIO_free_all(f);
@@ -323,7 +330,7 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
 	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;
@@ -406,15 +413,48 @@ int CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
 		}
 	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++)
@@ -432,11 +472,23 @@ 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);
path: crypto/cms/cms_smime.c