Blob Blame History Raw
diff -up ntp-4.2.6p5/ntpd/ntp_crypto.c.cve-2015-7691_7692_7702 ntp-4.2.6p5/ntpd/ntp_crypto.c
--- ntp-4.2.6p5/ntpd/ntp_crypto.c.cve-2015-7691_7692_7702	2015-10-22 13:05:34.696482937 +0200
+++ ntp-4.2.6p5/ntpd/ntp_crypto.c	2015-10-22 13:14:12.473848919 +0200
@@ -170,6 +170,7 @@ static	void	cert_free	(struct cert_info
 static	struct pkey_info *crypto_key (char *, char *, sockaddr_u *);
 static	void	bighash		(BIGNUM *, BIGNUM *);
 static	struct cert_info *crypto_cert (char *);
+static	u_int	exten_payload_size(const struct exten *);
 
 #ifdef SYS_WINNT
 int
@@ -389,7 +390,7 @@ crypto_recv(
 	struct autokey *ap, *bp; /* autokey pointer */
 	struct exten *ep, *fp;	/* extension pointers */
 	struct cert_info *xinfo; /* certificate info pointer */
-	int	has_mac;	/* length of MAC field */
+	int	macbytes;	/* length of MAC field, signed by intention */
 	int	authlen;	/* offset of MAC field */
 	associd_t associd;	/* association ID */
 	tstamp_t tstamp = 0;	/* timestamp */
@@ -417,7 +418,11 @@ crypto_recv(
 	 */
 	authlen = LEN_PKT_NOMAC;
 	hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode);
-	while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
+	while ((macbytes = rbufp->recv_length - authlen) > (int)MAX_MAC_LEN) {
+		/* We can be reasonably sure that we can read at least
+		 * the opcode and the size field here. More stringent
+		 * checks follow up shortly.
+		 */
 		pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
 		ep = (struct exten *)pkt;
 		code = ntohl(ep->opcode) & 0xffff0000;
@@ -441,6 +446,18 @@ crypto_recv(
 			code |= CRYPTO_ERROR;
 		}
 
+		/* Check if the declared size fits into the remaining
+		 * buffer.
+		 */
+		if (len > macbytes) {
+			DPRINTF(1, ("crypto_recv: possible attack detected, associd %d\n",
+				    associd));
+			return XEVNT_LEN;
+		}
+
+		/* Check if the paylod of the extension fits into the
+		 * declared frame.
+		 */
 		if (len >= VALUE_LEN) {
 			tstamp = ntohl(ep->tstamp);
 			fstamp = ntohl(ep->fstamp);
@@ -1170,9 +1187,8 @@ crypto_xmit(
 	 * choice. 
 	 */
 	case CRYPTO_CERT | CRYPTO_RESP:
-		vallen = ntohl(ep->vallen);	/* Must be <64k */
-		if (vallen == 0 || vallen > MAXHOSTNAME ||
-		    len - VALUE_LEN < vallen) {
+		vallen = exten_payload_size(ep); /* Must be <64k */
+		if (vallen == 0 || vallen >= sizeof(certname) ) {
 			rval = XEVNT_LEN;
 			break;
 		} else {
@@ -2134,8 +2150,7 @@ crypto_bob(
 	tstamp_t tstamp;	/* NTP timestamp */
 	BIGNUM	*bn, *bk, *r;
 	u_char	*ptr;
-	u_int	len;		/* extension field length */
-	u_int	vallen = 0;	/* value length */
+	u_int	len;		/* extension field value length */
 
 	/*
 	 * If the IFF parameters are not valid, something awful
@@ -2150,11 +2165,10 @@ crypto_bob(
 	/*
 	 * Extract r from the challenge.
 	 */
-	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) {
+	len = exten_payload_size(ep);
+	if (len == 0 || len > MAX_VALLEN)
+		return (XEVNT_LEN);
+	if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
 		msyslog(LOG_ERR, "crypto_bob: %s",
 		    ERR_error_string(ERR_get_error(), NULL));
 		return (XEVNT_ERR);
@@ -2166,7 +2180,7 @@ crypto_bob(
 	 */
 	bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new();
 	sdsa = DSA_SIG_new();
-	BN_rand(bk, vallen * 8, -1, 1);		/* k */
+	BN_rand(bk, len * 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 */
@@ -2185,16 +2199,16 @@ crypto_bob(
 	 * Encode the values in ASN.1 and sign. The filestamp is from
 	 * the local file.
 	 */
-	vallen = i2d_DSA_SIG(sdsa, NULL);
-	if (vallen == 0) {
+	len = i2d_DSA_SIG(sdsa, NULL);
+	if (len == 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);
+	if (len > MAX_VALLEN) {
+		msyslog(LOG_ERR, "crypto_bob: signature is too big: %u",
+		    len);
 		DSA_SIG_free(sdsa);
 		return (XEVNT_LEN);
 	}
@@ -2202,8 +2216,8 @@ crypto_bob(
 	tstamp = crypto_time();
 	vp->tstamp = htonl(tstamp);
 	vp->fstamp = htonl(iffkey_info->fstamp);
-	vp->vallen = htonl(vallen);
-	ptr = emalloc(vallen);
+	vp->vallen = htonl(len);
+	ptr = emalloc(len);
 	vp->ptr = ptr;
 	i2d_DSA_SIG(sdsa, &ptr);
 	DSA_SIG_free(sdsa);
@@ -2214,9 +2228,9 @@ crypto_bob(
 	vp->sig = emalloc(sign_siglen);
 	EVP_SignInit(&ctx, sign_digest);
 	EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
-	EVP_SignUpdate(&ctx, vp->ptr, vallen);
-	if (EVP_SignFinal(&ctx, vp->sig, &vallen, sign_pkey))
-		vp->siglen = htonl(sign_siglen);
+	EVP_SignUpdate(&ctx, vp->ptr, len);
+	if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+		vp->siglen = htonl(len);
 	return (XEVNT_OK);
 }
 
@@ -2462,7 +2476,9 @@ crypto_bob2(
 	/*
 	 * Extract r from the challenge.
 	 */
-	len = ntohl(ep->vallen);
+	len = exten_payload_size(ep);
+	if (len == 0 || len > MAX_VALLEN)
+		return (XEVNT_LEN);
 	if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
 		msyslog(LOG_ERR, "crypto_bob2: %s",
 		    ERR_error_string(ERR_get_error(), NULL));
@@ -2787,7 +2803,9 @@ crypto_bob3(
 	/*
 	 * Extract r from the challenge.
 	 */
-	len = ntohl(ep->vallen);
+	len = exten_payload_size(ep);
+	if (len == 0 || len > MAX_VALLEN)
+		return (XEVNT_LEN);
 	if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
 		msyslog(LOG_ERR, "crypto_bob3: %s",
 		    ERR_error_string(ERR_get_error(), NULL));
@@ -3002,8 +3020,11 @@ cert_sign(
 	if (tstamp == 0)
 		return (XEVNT_TSP);
 
+	len = exten_payload_size(ep);
+	if (len == 0 || len > MAX_VALLEN)
+		return (XEVNT_LEN);
 	ptr = (u_char *)ep->pkt;
-	if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) {
+	if ((req = d2i_X509(NULL, &ptr, len)) == NULL) {
 		msyslog(LOG_ERR, "cert_sign: %s",
 		    ERR_error_string(ERR_get_error(), NULL));
 		return (XEVNT_CRT);
@@ -3968,6 +3989,36 @@ crypto_config(
 		break;
 	}
 }
+
+/*
+ * Get payload size (internal value length) of an extension packet. If
+ * the inner value length does not match the outer packet length (that
+ * is, the value would end behind the frame given by the opcode/size
+ * field) the function will efectively return UINT_MAX. If the frame is
+ * too short to holda variable-sized value, the return value is zero.
+ */
+static u_int
+exten_payload_size(
+	const struct exten * ep)
+{
+	typedef const u_char *BPTR;
+	
+	size_t extn_size;
+	size_t data_size;
+	size_t head_size;
+
+	data_size = 0;
+	if (NULL != ep) {
+		head_size = (BPTR)(&ep->vallen + 1) - (BPTR)ep;
+		extn_size = (uint16_t)(ntohl(ep->opcode) & 0x0000ffff);
+		if (extn_size >= head_size) {
+			data_size = (uint32_t)ntohl(ep->vallen);
+			if (data_size > extn_size - head_size)
+				data_size = ~(size_t)0u;
+		}
+	}
+	return (u_int)data_size;
+}
 # else
 int ntp_crypto_bs_pubkey;
 # endif /* OPENSSL */