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