]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
CVE-2014-9297
authorJuergen Perlinger <perlinger@ntp.org>
Mon, 17 Aug 2015 05:54:01 +0000 (07:54 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Mon, 17 Aug 2015 05:54:01 +0000 (07:54 +0200)
bk: 55d176f9Tib3Qeh9R5EV-hSWzl2C0g

ChangeLog
ntpd/ntp_crypto.c

index aa4ef6999ec27622ba6d466ce8fc61b4c671e19a..c6d3609919ce33bd13d05307240cc9d3466c227e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,5 @@
 ---
+* CVE-2014-9297  perlinger@ntp.org
 * [Bug 2595] ntpdate man page quirks.  Hal Murray, Harlan Stenn.
 * [Bug 2625] Deprecate flag1 in local refclock.  Hal Murray, Harlan Stenn.
 * [Bug 2817] Stop locking ntpd into memory by default under Linux.  H.Stenn.
index e9cd6c2036f323c9abba43128bd1d423a7c335e4..a09d6eef6d888de5045259188acfa2c57e954231 100644 (file)
@@ -202,6 +202,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
@@ -419,7 +420,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 fstamp = 0;    /* filestamp */
@@ -446,7 +447,11 @@ crypto_recv(
         */
        authlen = LEN_PKT_NOMAC;
        hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode);
-       while ((has_mac = rbufp->recv_length - authlen) > (int)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;
@@ -467,6 +472,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) {
                        fstamp = ntohl(ep->fstamp);
                        vallen = ntohl(ep->vallen);
@@ -1153,9 +1170,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;
                }
@@ -2197,8 +2213,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
@@ -2213,11 +2228,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);
@@ -2229,7 +2243,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 */
@@ -2248,16 +2262,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);
        }
@@ -2265,8 +2279,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);
@@ -2277,10 +2291,10 @@ 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)) {
-               INSIST(vallen <= sign_siglen);
-               vp->siglen = htonl(vallen);
+       EVP_SignUpdate(&ctx, vp->ptr, len);
+       if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) {
+               INSIST(len <= sign_siglen);
+               vp->siglen = htonl(len);
        }
        return (XEVNT_OK);
 }
@@ -2530,7 +2544,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));
@@ -2859,7 +2875,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));
@@ -3078,8 +3096,11 @@ cert_sign(
        if (tstamp == 0)
                return (XEVNT_TSP);
 
+       len = exten_payload_size(ep);
+       if (len == 0 || len > MAX_VALLEN)
+               return (XEVNT_LEN);
        cptr = (void *)ep->pkt;
-       if ((req = d2i_X509(NULL, &cptr, ntohl(ep->vallen))) == NULL) {
+       if ((req = d2i_X509(NULL, &cptr, len)) == NULL) {
                msyslog(LOG_ERR, "cert_sign: %s",
                    ERR_error_string(ERR_get_error(), NULL));
                return (XEVNT_CRT);
@@ -4028,6 +4049,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 /* !AUTOKEY follows */
 int ntp_crypto_bs_pubkey;
 # endif        /* !AUTOKEY */