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 */