[ntp/f20] validate lengths of values in extension fields (CVE-2014-9297)

Miroslav Lichvar mlichvar at fedoraproject.org
Thu Feb 5 14:52:39 UTC 2015


commit 5f1c8505ee34165a9f76a6985d25607aadb55d6c
Author: Miroslav Lichvar <mlichvar at redhat.com>
Date:   Thu Feb 5 15:30:25 2015 +0100

    validate lengths of values in extension fields (CVE-2014-9297)

 ntp-4.2.6p5-cve-2014-9297.patch |  375 +++++++++++++++++++++++++++++++++++++++
 ntp.spec                        |    3 +
 2 files changed, 378 insertions(+), 0 deletions(-)
---
diff --git a/ntp-4.2.6p5-cve-2014-9297.patch b/ntp-4.2.6p5-cve-2014-9297.patch
new file mode 100644
index 0000000..1364e8c
--- /dev/null
+++ b/ntp-4.2.6p5-cve-2014-9297.patch
@@ -0,0 +1,375 @@
+http://bk.ntp.org/ntp-stable/?PAGE=patch&REV=54abb266In81wLNAqIaovtP8f2UmUw
+http://bk.ntp.org/ntp-stable/?PAGE=patch&REV=54a7c595jlwS3KmAxBML75HFGLR_pQ
+http://bk.ntp.org/ntp-stable/?PAGE=patch&REV=5492d353ncauuWt_PONxaDhC5Qv_SA
+
+diff -up ntp-4.2.6p5/ntpd/ntp_crypto.c.cve-2014-9297 ntp-4.2.6p5/ntpd/ntp_crypto.c
+--- ntp-4.2.6p5/ntpd/ntp_crypto.c.cve-2014-9297	2015-02-04 11:37:44.488673076 +0100
++++ ntp-4.2.6p5/ntpd/ntp_crypto.c	2015-02-04 11:37:44.491673082 +0100
+@@ -109,6 +109,7 @@
+ #define TAI_1972	10	/* initial TAI offset (s) */
+ #define MAX_LEAP	100	/* max UTC leapseconds (s) */
+ #define VALUE_LEN	(6 * 4) /* min response field length */
++#define MAX_VALLEN	(65535 - VALUE_LEN)
+ #define YEAR		(60 * 60 * 24 * 365) /* seconds in year */
+ 
+ /*
+@@ -147,8 +148,8 @@ static char *rand_file = NULL;	/* random
+  */
+ static	int	crypto_verify	(struct exten *, struct value *,
+ 				    struct peer *);
+-static	int	crypto_encrypt	(struct exten *, struct value *,
+-				    keyid_t *);
++static	int	crypto_encrypt	(const u_char *, u_int, keyid_t *,
++				    struct value *);
+ static	int	crypto_alice	(struct peer *, struct value *);
+ static	int	crypto_alice2	(struct peer *, struct value *);
+ static	int	crypto_alice3	(struct peer *, struct value *);
+@@ -444,6 +445,12 @@ crypto_recv(
+ 			tstamp = ntohl(ep->tstamp);
+ 			fstamp = ntohl(ep->fstamp);
+ 			vallen = ntohl(ep->vallen);
++			/*
++			 * Bug 2761: I hope this isn't too early...
++			 */
++			if (   vallen == 0
++			    || len - VALUE_LEN < vallen)
++				return XEVNT_LEN;
+ 		}
+ 		switch (code) {
+ 
+@@ -494,8 +501,9 @@ crypto_recv(
+ 					rval = XEVNT_ERR;
+ 				break;
+ 			}
++			INSIST(len >= VALUE_LEN);
+ 			if (vallen == 0 || vallen > MAXHOSTNAME ||
+-			    len < VALUE_LEN + vallen) {
++			    len - VALUE_LEN < vallen) {
+ 				rval = XEVNT_LEN;
+ 				break;
+ 			}
+@@ -1162,11 +1170,11 @@ crypto_xmit(
+ 	 * choice. 
+ 	 */
+ 	case CRYPTO_CERT | CRYPTO_RESP:
+-		vallen = ntohl(ep->vallen);
+-		if (vallen == 0 || vallen > MAXHOSTNAME) {
++		vallen = ntohl(ep->vallen);	/* Must be <64k */
++		if (vallen == 0 || vallen > MAXHOSTNAME ||
++		    len - VALUE_LEN < vallen) {
+ 			rval = XEVNT_LEN;
+ 			break;
+-
+ 		} else {
+ 			memcpy(certname, ep->pkt, vallen);
+ 			certname[vallen] = '\0';
+@@ -1315,7 +1323,10 @@ crypto_xmit(
+ 	 * anything goes wrong.
+ 	 */
+ 	case CRYPTO_COOK | CRYPTO_RESP:
+-		if ((opcode & 0xffff) < VALUE_LEN) {
++		vallen = ntohl(ep->vallen);	/* Must be <64k */
++		if (   vallen == 0
++		    || (vallen >= MAX_VALLEN)
++		    || (opcode & 0x0000ffff)  < VALUE_LEN + vallen) {
+ 			rval = XEVNT_LEN;
+ 			break;
+ 		}
+@@ -1323,8 +1334,8 @@ crypto_xmit(
+ 			tcookie = cookie;
+ 		else
+ 			tcookie = peer->hcookie;
+-		if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) ==
+-		    XEVNT_OK) {
++		if ((rval = crypto_encrypt((const u_char *)ep->pkt, vallen, &tcookie, &vtemp))
++		    == XEVNT_OK) {
+ 			len = crypto_send(fp, &vtemp, start);
+ 			value_free(&vtemp);
+ 		}
+@@ -1464,13 +1475,16 @@ crypto_verify(
+ 	 * up to the next word (4 octets).
+ 	 */
+ 	vallen = ntohl(ep->vallen);
+-	if (vallen == 0)
++	if (   vallen == 0
++	    || vallen > MAX_VALLEN)
+ 		return (XEVNT_LEN);
+ 
+ 	i = (vallen + 3) / 4;
+ 	siglen = ntohl(ep->pkt[i++]);
+-	if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) /
+-	    4) * 4)
++	if (   siglen > MAX_VALLEN
++	    || len - VALUE_LEN < ((vallen + 3) / 4) * 4
++	    || len - VALUE_LEN - ((vallen + 3) / 4) * 4
++	      < ((siglen + 3) / 4) * 4)
+ 		return (XEVNT_LEN);
+ 
+ 	/*
+@@ -1528,6 +1542,7 @@ crypto_verify(
+ 	 * proventic bit. What a relief.
+ 	 */
+ 	EVP_VerifyInit(&ctx, peer->digest);
++	/* XXX: the "+ 12" needs to be at least documented... */
+ 	EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12);
+ 	if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen,
+ 	    pkey) <= 0)
+@@ -1540,34 +1555,32 @@ crypto_verify(
+ 
+ 
+ /*
+- * crypto_encrypt - construct encrypted cookie and signature from
+- * extension field and cookie
++ * crypto_encrypt - construct vp (encrypted cookie and signature) from
++ * the public key and cookie.
+  *
+- * Returns
++ * Returns:
+  * XEVNT_OK	success
+  * XEVNT_CKY	bad or missing cookie
+  * XEVNT_PUB	bad or missing public key
+  */
+ static int
+ crypto_encrypt(
+-	struct exten *ep,	/* extension pointer */
+-	struct value *vp,	/* value pointer */
+-	keyid_t	*cookie		/* server cookie */
++	const u_char *ptr,	/* Public Key */
++	u_int	vallen,		/* Length of Public Key */
++	keyid_t	*cookie,	/* server cookie */
++	struct value *vp	/* value pointer */
+ 	)
+ {
+ 	EVP_PKEY *pkey;		/* public key */
+ 	EVP_MD_CTX ctx;		/* signature context */
+ 	tstamp_t tstamp;	/* NTP timestamp */
+ 	u_int32	temp32;
+-	u_int	len;
+-	u_char	*ptr;
++	u_char *puch;
+ 
+ 	/*
+ 	 * Extract the public key from the request.
+ 	 */
+-	len = ntohl(ep->vallen);
+-	ptr = (u_char *)ep->pkt;
+-	pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len);
++	pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, vallen);
+ 	if (pkey == NULL) {
+ 		msyslog(LOG_ERR, "crypto_encrypt: %s",
+ 		    ERR_error_string(ERR_get_error(), NULL));
+@@ -1581,12 +1594,12 @@ crypto_encrypt(
+ 	tstamp = crypto_time();
+ 	vp->tstamp = htonl(tstamp);
+ 	vp->fstamp = hostval.tstamp;
+-	len = EVP_PKEY_size(pkey);
+-	vp->vallen = htonl(len);
+-	vp->ptr = emalloc(len);
+-	ptr = vp->ptr;
++	vallen = EVP_PKEY_size(pkey);
++	vp->vallen = htonl(vallen);
++	vp->ptr = emalloc(vallen);
++	puch = vp->ptr;
+ 	temp32 = htonl(*cookie);
+-	if (RSA_public_encrypt(4, (u_char *)&temp32, ptr,
++	if (RSA_public_encrypt(4, (u_char *)&temp32, puch,
+ 	    pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ 		msyslog(LOG_ERR, "crypto_encrypt: %s",
+ 		    ERR_error_string(ERR_get_error(), NULL));
+@@ -1601,8 +1614,8 @@ crypto_encrypt(
+ 	vp->sig = emalloc(sign_siglen);
+ 	EVP_SignInit(&ctx, sign_digest);
+ 	EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+-	EVP_SignUpdate(&ctx, vp->ptr, len);
+-	if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
++	EVP_SignUpdate(&ctx, vp->ptr, vallen);
++	if (EVP_SignFinal(&ctx, vp->sig, &vallen, sign_pkey))
+ 		vp->siglen = htonl(sign_siglen);
+ 	return (XEVNT_OK);
+ }
+@@ -1673,6 +1686,9 @@ crypto_ident(
+  * call in the protocol module.
+  *
+  * Returns extension field pointer (no errors)
++ *
++ * XXX: opcode and len should really be 32-bit quantities and
++ * we should make sure that str is not too big.
+  */
+ struct exten *
+ crypto_args(
+@@ -1685,24 +1701,31 @@ crypto_args(
+ 	tstamp_t tstamp;	/* NTP timestamp */
+ 	struct exten *ep;	/* extension field pointer */
+ 	u_int	len;		/* extension field length */
++	size_t	slen;
+ 
+ 	tstamp = crypto_time();
+ 	len = sizeof(struct exten);
+-	if (str != NULL)
+-		len += strlen(str);
++	if (str != NULL) {
++		slen = strlen(str);
++		INSIST(slen < MAX_VALLEN);
++		len += slen;
++	}
+ 	ep = emalloc(len);
+ 	memset(ep, 0, len);
+ 	if (opcode == 0)
+ 		return (ep);
+ 
++	REQUIRE(0 == (len    & ~0x0000ffff));
++	REQUIRE(0 == (opcode & ~0xffff0000));
++
+ 	ep->opcode = htonl(opcode + len);
+ 	ep->associd = htonl(associd);
+ 	ep->tstamp = htonl(tstamp);
+ 	ep->fstamp = hostval.tstamp;
+ 	ep->vallen = 0;
+ 	if (str != NULL) {
+-		ep->vallen = htonl(strlen(str));
+-		memcpy((char *)ep->pkt, str, strlen(str));
++		ep->vallen = htonl(slen);
++		memcpy((char *)ep->pkt, str, slen);
+ 	}
+ 	return (ep);
+ }
+@@ -1715,6 +1738,8 @@ crypto_args(
+  * Note: it is not polite to send a nonempty signature with zero
+  * timestamp or a nonzero timestamp with an empty signature, but those
+  * rules are not enforced here.
++ *
++ * XXX This code won't work on a box with 16-bit ints.
+  */
+ int
+ crypto_send(
+@@ -1730,8 +1755,9 @@ crypto_send(
+ 	 * Calculate extension field length and check for buffer
+ 	 * overflow. Leave room for the MAC.
+ 	 */
+-	len = 16;
++	len = 16;				/* XXX Document! */
+ 	vallen = ntohl(vp->vallen);
++	INSIST(vallen <= MAX_VALLEN);
+ 	len += ((vallen + 3) / 4 + 1) * 4; 
+ 	siglen = ntohl(vp->siglen);
+ 	len += ((siglen + 3) / 4 + 1) * 4; 
+@@ -1772,6 +1798,7 @@ crypto_send(
+ 	}
+ 	opcode = ntohl(ep->opcode);
+ 	ep->opcode = htonl((opcode & 0xffff0000) | len); 
++	ENSURE(len <= MAX_VALLEN);
+ 	return (len);
+ }
+ 
+@@ -1807,7 +1834,6 @@ crypto_update(void)
+ 	if (hostval.tstamp == 0)
+ 		return;
+ 
+-
+ 	/*
+ 	 * Sign public key and timestamps. The filestamp is derived from
+ 	 * the host key file extension from wherever the file was
+@@ -2108,7 +2134,8 @@ crypto_bob(
+ 	tstamp_t tstamp;	/* NTP timestamp */
+ 	BIGNUM	*bn, *bk, *r;
+ 	u_char	*ptr;
+-	u_int	len;
++	u_int	len;		/* extension field length */
++	u_int	vallen = 0;	/* value length */
+ 
+ 	/*
+ 	 * If the IFF parameters are not valid, something awful
+@@ -2123,8 +2150,11 @@ crypto_bob(
+ 	/*
+ 	 * Extract r from the challenge.
+ 	 */
+-	len = ntohl(ep->vallen);
+-	if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
++	vallen = ntohl(ep->vallen);
++	len = ntohl(ep->opcode) & 0x0000ffff;
++	if (vallen == 0 || len < VALUE_LEN || len - VALUE_LEN < vallen)
++		return XEVNT_LEN;
++	if ((r = BN_bin2bn((u_char *)ep->pkt, vallen, NULL)) == NULL) {
+ 		msyslog(LOG_ERR, "crypto_bob: %s",
+ 		    ERR_error_string(ERR_get_error(), NULL));
+ 		return (XEVNT_ERR);
+@@ -2136,7 +2166,7 @@ crypto_bob(
+ 	 */
+ 	bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
+ 	sdsa = DSA_SIG_new();
+-	BN_rand(bk, len * 8, -1, 1);		/* k */
++	BN_rand(bk, vallen * 8, -1, 1);		/* k */
+ 	BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */
+ 	BN_add(bn, bn, bk);
+ 	BN_mod(bn, bn, dsa->q, bctx);		/* k + b r mod q */
+@@ -2155,30 +2185,37 @@ crypto_bob(
+ 	 * Encode the values in ASN.1 and sign. The filestamp is from
+ 	 * the local file.
+ 	 */
+-	len = i2d_DSA_SIG(sdsa, NULL);
+-	if (len == 0) {
++	vallen = i2d_DSA_SIG(sdsa, NULL);
++	if (vallen == 0) {
+ 		msyslog(LOG_ERR, "crypto_bob: %s",
+ 		    ERR_error_string(ERR_get_error(), NULL));
+ 		DSA_SIG_free(sdsa);
+ 		return (XEVNT_ERR);
+ 	}
++	if (vallen > MAX_VALLEN) {
++		msyslog(LOG_ERR, "crypto_bob: signature is too big: %d",
++		    vallen);
++		DSA_SIG_free(sdsa);
++		return (XEVNT_LEN);
++	}
+ 	memset(vp, 0, sizeof(struct value));
+ 	tstamp = crypto_time();
+ 	vp->tstamp = htonl(tstamp);
+ 	vp->fstamp = htonl(iffkey_info->fstamp);
+-	vp->vallen = htonl(len);
+-	ptr = emalloc(len);
++	vp->vallen = htonl(vallen);
++	ptr = emalloc(vallen);
+ 	vp->ptr = ptr;
+ 	i2d_DSA_SIG(sdsa, &ptr);
+ 	DSA_SIG_free(sdsa);
+ 	if (tstamp == 0)
+ 		return (XEVNT_OK);
+ 
++	/* XXX: more validation to make sure the sign fits... */
+ 	vp->sig = emalloc(sign_siglen);
+ 	EVP_SignInit(&ctx, sign_digest);
+ 	EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+-	EVP_SignUpdate(&ctx, vp->ptr, len);
+-	if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
++	EVP_SignUpdate(&ctx, vp->ptr, vallen);
++	if (EVP_SignFinal(&ctx, vp->sig, &vallen, sign_pkey))
+ 		vp->siglen = htonl(sign_siglen);
+ 	return (XEVNT_OK);
+ }
+diff -up ntp-4.2.6p5/ntpd/ntp_proto.c.cve-2014-9297 ntp-4.2.6p5/ntpd/ntp_proto.c
+--- ntp-4.2.6p5/ntpd/ntp_proto.c.cve-2014-9297	2015-02-04 11:37:44.490673080 +0100
++++ ntp-4.2.6p5/ntpd/ntp_proto.c	2015-02-04 11:47:42.653868627 +0100
+@@ -431,7 +431,7 @@ receive(
+ 	 */
+ 	authlen = LEN_PKT_NOMAC;
+ 	has_mac = rbufp->recv_length - authlen;
+-	while (has_mac != 0) {
++	while (has_mac > 0) {
+ 		u_int32	len;
+ 
+ 		if (has_mac % 4 != 0 || has_mac < MIN_MAC_LEN) {
+@@ -456,6 +456,14 @@ receive(
+ 	}
+ 
+ 	/*
++	 * If has_mac is < 0 we had a malformed packet.
++	 */
++	if (has_mac < 0) {
++		sys_badlength++;
++		return;		/* bad length */
++	}
++
++	/*
+ 	 * If authentication required, a MAC must be present.
+ 	 */
+ 	if (restrict_mask & RES_DONTTRUST && has_mac == 0) {
diff --git a/ntp.spec b/ntp.spec
index 57ad0da..2096554 100644
--- a/ntp.spec
+++ b/ntp.spec
@@ -97,6 +97,8 @@ Patch25: ntp-4.2.6p5-cve-2014-9293.patch
 Patch26: ntp-4.2.6p5-cve-2014-9295.patch
 # ntpbz #2670
 Patch27: ntp-4.2.6p5-cve-2014-9296.patch
+# ntpbz #2671
+Patch28: ntp-4.2.6p5-cve-2014-9297.patch
 
 # handle unknown clock types
 Patch50: ntpstat-0.2-clksrc.patch
@@ -209,6 +211,7 @@ This package contains NTP documentation in HTML format.
 %patch25 -p1 -b .cve-2014-9293
 %patch26 -p1 -b .cve-2014-9295
 %patch27 -p1 -b .cve-2014-9296
+%patch28 -p1 -b .cve-2014-9297
 
 # ntpstat patches
 %patch50 -p1 -b .clksrc


More information about the scm-commits mailing list