Commit message: Fix #7152 - Null deref in cms
CVE: CVE-2017-7274
CWE: CWE-476
Vulnerability description: The r_pkcs7_parse_cms function in libr/util/r_pkcs7.c in radare2 1.3.0 allows remote attackers to cause a denial of service (NULL pointer dereference and application crash) via a crafted PE file.

Project: radare2
File: r_pkcs7.c

char *r_pkcs7_cms_dump (RCMS* container) {
	RPKCS7SignedData *sd;
	ut32 i, length, p = 0;
	int r;
	char *buffer = NULL, *tmp = NULL;
	if (!container) {
		return NULL;
	}
	sd = &container->signedData;
	length = 2048 + (container->signedData.certificates.length * 1024);
	if(!length) {
		return NULL;
	}
	buffer = (char*) calloc (1, length);
	if (!buffer) {
		return NULL;
	}
	r = snprintf (buffer, length, "signedData\n  Version: %u\n  Digest Algorithms:\n", sd->version);
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (buffer);
		return NULL;
	}
	if (container->signedData.digestAlgorithms.elements) {
		for (i = 0; i < container->signedData.digestAlgorithms.length; ++i) {
			if (container->signedData.digestAlgorithms.elements[i]) {
				RASN1String *s = container->signedData.digestAlgorithms.elements[i]->algorithm;
				r = snprintf (buffer + p, length - p, "    %s\n", s ? s->string : "Missing");
				p += (ut32) r;
				if (r < 0 || length <= p) {
					free (buffer);
					return NULL;
				}
			}
		}
	}
	r = snprintf (buffer + p, length - p, "  Certificates: %u\n", container->signedData.certificates.length);
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (buffer);
		return NULL;
	}
	for (i = 0; i < container->signedData.certificates.length; ++i) {
		if (length <= p || !(tmp = r_x509_certificate_dump (container->signedData.certificates.elements[i], buffer + p, length - p, "    "))) {
			free (buffer);
			return NULL;
		}
		p = tmp - buffer;
	}

	for (i = 0; i < container->signedData.crls.length; ++i) {
		if (length <= p || !(tmp = r_x509_crl_dump (container->signedData.crls.elements[i], buffer + p, length - p, "    "))) {
			free (buffer);
			return NULL;
		}
		p = tmp - buffer;
	}
	p = tmp - buffer;
	if (length <= p) {
		free (buffer);
		return NULL;
	}	
	r = snprintf (buffer + p, length - p, "  SignerInfos:\n");
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (buffer);
		return NULL;
	}
	if (container->signedData.signerinfos.elements) {
		for (i = 0; i < container->signedData.signerinfos.length; ++i) {
			if (length <= p || !(tmp = r_x509_signedinfo_dump (container->signedData.signerinfos.elements[i], buffer + p, length - p, "    "))) {
				free (buffer);
				return NULL;
			}
			p = tmp - buffer;
		}
	}
	return buffer;
}

Project: radare2
File: r_pkcs7.c

void r_pkcs7_free_attribute (RPKCS7Attribute* attribute) {
	if (attribute) {
		r_asn1_free_object (attribute->data);
		r_asn1_free_string (attribute->oid);
		free (attribute);
	}
}

Project: radare2

void r_pkcs7_free_attributes (RPKCS7Attributes* attributes) {
	ut32 i;
	if (attributes) {
		for (i = 0; i < attributes->length; ++i) {
			r_pkcs7_free_attribute (attributes->elements[i]);
		}
		R_FREE (attributes->elements);
	}
}

Project: radare2

void r_pkcs7_free_certificaterevocationlists (RPKCS7CertificateRevocationLists *crls) {
	ut32 i;
	if (crls) {
		for (i = 0; i < crls->length; ++i) {
			r_x509_free_crl (crls->elements[i]);
			crls->elements[i] = NULL;
		}
		R_FREE (crls->elements);
	}
}

Project: radare2
File: r_pkcs7.c

void r_pkcs7_free_cms (RCMS* container) {
	if (container) {
		r_asn1_free_string (container->contentType);
		r_pkcs7_free_signeddata (&container->signedData);
		free (container);
	}
}

Project: radare2

void r_pkcs7_free_contentinfo (RPKCS7ContentInfo* ci) {
	if (ci) {
		r_asn1_free_object (ci->content);
		r_asn1_free_string (ci->contentType);
	}
}

Project: radare2

void r_pkcs7_free_digestalgorithmidentifier (RPKCS7DigestAlgorithmIdentifiers *dai) {
	ut32 i;
	if (dai) {
		for (i = 0; i < dai->length; ++i) {
			if (dai->elements[i]) {
				r_x509_free_algorithmidentifier (dai->elements[i]);
				R_FREE (dai->elements[i]);
			}
		}
		R_FREE (dai->elements);
	}
}

Project: radare2

void r_pkcs7_free_extendedcertificatesandcertificates (RPKCS7ExtendedCertificatesAndCertificates *ecac) {
	ut32 i;
	if (ecac) {
		for (i = 0; i < ecac->length; ++i) {
			r_x509_free_certificate (ecac->elements[i]);
			ecac->elements[i] = NULL;
		}
		R_FREE (ecac->elements);
	}
}

Project: radare2

void r_pkcs7_free_issuerandserialnumber (RPKCS7IssuerAndSerialNumber* iasu) {
	if (iasu) {
		r_x509_free_name (&iasu->issuer);
		r_asn1_free_object (iasu->serialNumber);
	}
}

Project: radare2

void r_pkcs7_free_signeddata (RPKCS7SignedData* sd) {
	if (sd) {
		r_pkcs7_free_digestalgorithmidentifier (&sd->digestAlgorithms);
		r_pkcs7_free_contentinfo (&sd->contentInfo);
		r_pkcs7_free_extendedcertificatesandcertificates (&sd->certificates);
		r_pkcs7_free_certificaterevocationlists (&sd->crls);
		r_pkcs7_free_signerinfos (&sd->signerinfos);
	}
}

Project: radare2
File: r_pkcs7.c

void r_pkcs7_free_signerinfo (RPKCS7SignerInfo* si) {
	if (si) {
		r_pkcs7_free_issuerandserialnumber (&si->issuerAndSerialNumber);
		r_x509_free_algorithmidentifier (&si->digestAlgorithm);
		r_pkcs7_free_attributes (&si->authenticatedAttributes);
		r_x509_free_algorithmidentifier (&si->digestEncryptionAlgorithm);
		r_asn1_free_object (si->encryptedDigest);
		r_pkcs7_free_attributes (&si->unauthenticatedAttributes);
		free (si);
	}
}

Project: radare2

void r_pkcs7_free_signerinfos (RPKCS7SignerInfos *ss) {
	ut32 i;
	if (ss) {
		for (i = 0; i < ss->length; i++) {
			r_pkcs7_free_signerinfo (ss->elements[i]);
			ss->elements[i] = NULL;
		}
		R_FREE (ss->elements);
	}
}

Project: radare2
File: r_pkcs7.c

RPKCS7Attribute* r_pkcs7_parse_attribute (RASN1Object *object) {
	RPKCS7Attribute* attribute;
	if (!object || object->list.length < 1) {
		return NULL;
	}
	attribute = R_NEW0 (RPKCS7Attribute);
	if (!attribute) {
		return NULL;
	}
	if (object->list.objects[0]) {
		attribute->oid = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length);
	}
	if (object->list.length == 2) {
		R_PTR_MOVE (attribute->data, object->list.objects[1]);
	}
	return attribute;
}

Project: radare2
File: r_pkcs7.c

bool r_pkcs7_parse_attributes (RPKCS7Attributes* attributes, RASN1Object *object) {
	ut32 i;
	if (!attributes || !object || !object->list.length) {
		return false;
	}

	attributes->length = object->list.length;
	if (attributes->length > 0) {
		attributes->elements = R_NEWS0 (RPKCS7Attribute*, attributes->length);
		if (!attributes->elements) {
			attributes->length = 0;
			return false;
		}
		for (i = 0; i < object->list.length; ++i) {
			attributes->elements[i] = r_pkcs7_parse_attribute (object->list.objects[i]);
		}
	}
	return true;
}

Project: radare2
File: r_pkcs7.c

bool r_pkcs7_parse_certificaterevocationlists (RPKCS7CertificateRevocationLists *crls, RASN1Object *object) {
	ut32 i;
	if (!crls && !object) {
		return false;
	}
	if (object->list.length > 0) {
		crls->elements = (RX509CertificateRevocationList **) calloc (object->list.length, sizeof (RX509CertificateRevocationList*));
		if (!crls->elements) {
			return false;
		}
		crls->length = object->list.length;
		for (i = 0; i < crls->length; ++i) {
			crls->elements[i] = r_x509_parse_crl (object->list.objects[i]);
		}
	}
	return true;
}

Project: radare2
File: r_pkcs7.c

bool r_pkcs7_parse_contentinfo (RPKCS7ContentInfo* ci, RASN1Object *object) {
	if (!ci || !object || object->list.length < 1 || !object->list.objects[0]) {
		return false;
	}

	ci->contentType = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length);
	if (object->list.length > 1) {
		R_PTR_MOVE (ci->content, object->list.objects[1]);
	}

	return true;
}

Project: radare2

bool r_pkcs7_parse_digestalgorithmidentifier (RPKCS7DigestAlgorithmIdentifiers *dai, RASN1Object *object) {
	ut32 i;
	if (!dai && !object) {
		return false;
	}
	if (object->list.length > 0) {
		dai->elements = (RX509AlgorithmIdentifier **) calloc (object->list.length, sizeof (RX509AlgorithmIdentifier*));
		if (!dai->elements) {
			return false;
		}
		dai->length = object->list.length;
		for (i = 0; i < dai->length; ++i) {
			dai->elements[i] = (RX509AlgorithmIdentifier *) malloc (sizeof (RX509AlgorithmIdentifier));
			if (dai->elements[i]) {
				memset (dai->elements[i], 0, sizeof (RX509AlgorithmIdentifier));
				r_x509_parse_algorithmidentifier (dai->elements[i], object->list.objects[i]);
			}
		}
	}
	return true;
}

Project: radare2
File: r_pkcs7.c

bool r_pkcs7_parse_extendedcertificatesandcertificates (RPKCS7ExtendedCertificatesAndCertificates *ecac, RASN1Object *object) {
	ut32 i;
	if (!ecac && !object) {
		return false;
	}
	if (object->list.length > 0) {
		ecac->elements = (RX509Certificate **) calloc (object->list.length, sizeof (RX509Certificate*));
		if (!ecac->elements) {
			return false;
		}
		ecac->length = object->list.length;
		for (i = 0; i < ecac->length; ++i) {
			ecac->elements[i] = r_x509_parse_certificate (object->list.objects[i]);
			object->list.objects[i] = NULL;
		}
	}
	return true;
}

Project: radare2
File: r_pkcs7.c

bool r_pkcs7_parse_issuerandserialnumber (RPKCS7IssuerAndSerialNumber* iasu, RASN1Object *object) {
	if (!iasu || !object || object->list.length != 2) {
		return false;
	}

	r_x509_parse_name (&iasu->issuer, object->list.objects[0]);
	R_PTR_MOVE (iasu->serialNumber, object->list.objects[1]);

	return true;
}

Project: radare2

bool r_pkcs7_parse_signeddata (RPKCS7SignedData *sd, RASN1Object *object) {
	RASN1Object **elems;
	ut32 shift = 3;
	if (!sd || !object || object->list.length < 4) {
		return false;
	}
	memset (sd, 0, sizeof (RPKCS7SignedData));
	elems = object->list.objects;
	sd->version = (ut32) elems[0]->sector[0]; 
	r_pkcs7_parse_digestalgorithmidentifier (&sd->digestAlgorithms, elems[1]);
	r_pkcs7_parse_contentinfo (&sd->contentInfo, elems[2]);
	if (shift < object->list.length && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 0) {
		r_pkcs7_parse_extendedcertificatesandcertificates (&sd->certificates, elems[shift]);
		shift++;
	}
	if (shift < object->list.length && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 1) {
		r_pkcs7_parse_certificaterevocationlists (&sd->crls, elems[shift]);
		shift++;
	}
	if (shift < object->list.length) {
		r_pkcs7_parse_signerinfos (&sd->signerinfos, elems[shift]);
	}
	return true;
}

Project: radare2

bool r_pkcs7_parse_signerinfo (RPKCS7SignerInfo* si, RASN1Object *object) {
	RASN1Object **elems;
	ut32 shift = 3;
	if (!si || !object || object->list.length < 5) {
		return false;
	}
	elems = object->list.objects;
	si->version = (ut32) elems[0]->sector[0];
	r_pkcs7_parse_issuerandserialnumber (&si->issuerAndSerialNumber, elems[1]);
	r_x509_parse_algorithmidentifier (&si->digestAlgorithm, elems[2]);
	if (shift < object->list.length && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 0) {
		r_pkcs7_parse_attributes (&si->authenticatedAttributes, elems[shift]);
		shift++;
	}
	if (shift < object->list.length) {
		r_x509_parse_algorithmidentifier (&si->digestEncryptionAlgorithm, elems[shift]);
		shift++;
	}
	if (shift < object->list.length) {
		R_PTR_MOVE (si->encryptedDigest, object->list.objects[shift]);
		shift++;
	}
	if (shift < object->list.length && elems[shift]->klass == CLASS_CONTEXT && elems[shift]->tag == 1) {
		r_pkcs7_parse_attributes (&si->unauthenticatedAttributes, elems[shift]);
	}
	return true;
}

Project: radare2

char* r_x509_signedinfo_dump (RPKCS7SignerInfo *si, char* buffer, ut32 length, const char* pad) {
	RASN1String *s = NULL;
	RASN1Object *o = NULL;
	ut32 i, p;
	int r;
	char *tmp, *pad2, *pad3;
	if (!si || !buffer || !length) {
		return NULL;
	}
	if (!pad) {
		pad = "";
	}
	pad3 = r_str_newf ("%s    ", pad);
	if (!pad3) {
		return NULL;
	}
	pad2 = pad3 + 2;


	r = snprintf (buffer, length, "%sSignerInfo:\n%sVersion: v%u\n%sIssuer\n", pad, pad2, si->version + 1, pad2);
	p = (ut32) r;
	if (r < 0) {
		free (pad3);
		return NULL;
	}

	if (length <= p || !(tmp = r_x509_name_dump (&si->issuerAndSerialNumber.issuer, buffer + p, length - p, pad3))) {
		free (pad3);
		return NULL;
	}
	p = tmp - buffer;
	if ((o = si->issuerAndSerialNumber.serialNumber)) {
		s = r_asn1_stringify_integer (o->sector, o->length);
	} else {
		s = NULL;
	}
	if (length <= p) {
		free (pad3);
		return NULL;
	}
	r = snprintf (buffer + p, length - p, "%sSerial Number:\n%s%s\n", pad2, pad3, s ? s->string : "Missing");
	p += (ut32) r;
	r_asn1_free_string (s);
	if (r < 0 || length <= p) {
		free (pad3);
		return NULL;
	}
	s = si->digestAlgorithm.algorithm;
	r = snprintf (buffer + p, length - p, "%sDigest Algorithm:\n%s%s\n%sAuthenticated Attributes:\n",
				pad2, pad3, s ? s->string : "Missing", pad2);
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (pad3);
		return NULL;
	}
	for (i = 0; i < si->authenticatedAttributes.length; ++i) {
		RPKCS7Attribute* attr = si->authenticatedAttributes.elements[i];
		if (!attr) continue;
		r = snprintf (buffer + p, length - p, "%s%s: %u bytes\n",
					pad3, attr->oid ? attr->oid->string : "Missing", attr->data ? attr->data->length : 0);
		p += (ut32) r;
		if (r < 0 || length <= p) {
			free (pad3);
			return NULL;
		}
	}
	s = si->digestEncryptionAlgorithm.algorithm;
	if (length <= p) {
		free (pad3);
		return NULL;
	}
	r = snprintf (buffer + p, length - p, "%sDigest Encryption Algorithm\n%s%s\n",
				pad2, pad3, s ? s->string : "Missing");
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (pad3);
		return NULL;
	}

	r = snprintf (buffer + p, length - p, "%sEncrypted Digest: %u bytes\n", pad2, o ? o->length : 0);
	if (r < 0 || length <= p) {
		free (pad3);
		return NULL;
	}
	r = snprintf (buffer + p, length - p, "%sUnauthenticated Attributes:\n", pad2);
	p += (ut32) r;
	if (r < 0 || length <= p) {
		free (pad3);
		return NULL;
	}
	for (i = 0; i < si->unauthenticatedAttributes.length; ++i) {
		RPKCS7Attribute* attr = si->unauthenticatedAttributes.elements[i];
		if (!attr) {
			continue;
		}
		o = attr->data;
		r = snprintf (buffer + p, length - p, "%s%s: %u bytes\n",
					pad3, attr->oid ? attr->oid->string : "Missing", o ? o->length : 0);
		p += (ut32) r;
		if (r < 0 || length <= p) {
			free (pad3);
			return NULL;
		}
	}
	free (pad3);
	return buffer + p;
}