]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Changes from Dave Mills
authorHarlan Stenn <stenn@ntp.org>
Fri, 1 Apr 2005 04:03:22 +0000 (23:03 -0500)
committerHarlan Stenn <stenn@ntp.org>
Fri, 1 Apr 2005 04:03:22 +0000 (23:03 -0500)
bk: 424cc80aoKbmDXsXsGg0DM8mViTuHw

include/ntpd.h
ntpd/ntp_peer.c
ntpd/ntp_proto.c

index c01863fd8f152b24f13fb7b5310a0ed6ecace511..8624af7b8c38dd51900ea5a31bcf3c0437673dd4 100644 (file)
@@ -120,7 +120,7 @@ extern      void    ntp_monitor P((struct recvbuf *));
 /* ntp_peer.c */
 extern void    init_peer       P((void));
 extern struct peer *findexistingpeer P((struct sockaddr_storage *, struct peer *, int));
-extern struct peer *findpeer   P((struct sockaddr_storage *, struct interface *, int, int, int *));
+extern struct peer *findpeer   P((struct sockaddr_storage *, struct interface *, int, int, int, int *));
 extern struct peer *findpeerbyassoc P((u_int));
 extern struct peer *newpeer    P((struct sockaddr_storage *, struct interface *, int, int, int, int, u_int, u_char, int, keyid_t));
 extern void    peer_all_reset  P((void));
index 3a37a7c19821bb024b365cd23b6fba3628986bda..bc9e0a13118dd101218b4f26ae7be773aad9064c 100644 (file)
@@ -214,9 +214,10 @@ struct peer *
 findpeer(
        struct sockaddr_storage *srcadr,
        struct interface *dstadr,
-       int fd,
-       int pkt_mode,
-       int *action
+       int     fd,
+       int     pkt_version,
+       int     pkt_mode,
+       int     *action
        )
 {
        register struct peer *peer;
@@ -225,8 +226,9 @@ findpeer(
        findpeer_calls++;
        hash = NTP_HASH_ADDR(srcadr);
        for (peer = peer_hash[hash]; peer != NULL; peer = peer->next) {
-               if (SOCKCMP(srcadr, &peer->srcadr)
-                   && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
+               if (SOCKCMP(srcadr, &peer->srcadr) &&
+                   NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr) &&
+                   peer->version == pkt_version) {
 
                        /*
                         * if the association matching rules determine
index 521c370adbc7e70148fbadb6db7daa4d861a2c5b..e09fb5555cb7d0836ed58d57d8f2cd7d4052acb9 100644 (file)
 #include <sys/sysctl.h>
 #endif
 
+/*
+ * This macro defines the authentication state. If x is 1 authentication
+ * is required; othewise it is optional.
+ */
+#define        AUTH(x, y)      ((x) ? (y) == AUTH_OK : (y) == AUTH_OK || \
+                           (y) == AUTH_NONE)
+
 /*
  * System variables are declared here. See Section 3.2 of the
  * specification.
@@ -268,6 +275,7 @@ receive(
 {
        register struct peer *peer;     /* peer structure pointer */
        register struct pkt *pkt;       /* receive packet pointer */
+       int     hisversion;             /* packet version */
        int     hismode;                /* packet mode */
        int     restrict_mask;          /* restrict bits */
        int     has_mac;                /* length of MAC field */
@@ -320,6 +328,7 @@ receive(
                return;                         /* no anything */
        }
        pkt = &rbufp->recv_pkt;
+       hisversion = PKT_VERSION(pkt->li_vn_mode);
        hismode = (int)PKT_MODE(pkt->li_vn_mode);
        if (hismode == MODE_PRIVATE) {
                if (restrict_mask & RES_NOQUERY) {
@@ -351,10 +360,10 @@ receive(
         * Version check must be after the query packets, since they
         * intentionally use early version.
         */
-       if (PKT_VERSION(pkt->li_vn_mode) == NTP_VERSION) {
+       if (hisversion == NTP_VERSION) {
                sys_newversionpkt++;            /* new version */
-       } else if (!(restrict_mask & RES_VERSION) &&
-           PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION) {
+       } else if (!(restrict_mask & RES_VERSION) && hisversion >=
+           NTP_OLDVERSION) {
                sys_oldversionpkt++;            /* previous version */
        } else {
                sys_unknownversion++;
@@ -368,7 +377,7 @@ receive(
         * would interpret as client mode.
         */
        if (hismode == MODE_UNSPEC) {
-               if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION) {
+               if (hisversion == NTP_OLDVERSION) {
                        hismode = MODE_CLIENT;
                } else {
                        sys_badlength++;
@@ -376,16 +385,6 @@ receive(
                }
        }
 
-       /*
-        * Discard broadcast if not enabled as broadcast client.
-        */
-       if (hismode == MODE_BROADCAST) {
-               if (!sys_bclient || restrict_mask & RES_NOPEER) {
-                       sys_restricted++;
-                       return;                 /* no client */
-               }
-       }
-
        /*
         * Parse the extension field if present. We figure out whether
         * an extension field is present by measuring the MAC size. If
@@ -426,16 +425,6 @@ receive(
                        return;                 /* bad MAC length */
                }
        }
-
-       /*
-        * If specified, access control rejects packets which have no
-        * MAC.
-        */
-       if (restrict_mask & RES_DONTTRUST && !has_mac) {
-               sys_restricted++;
-               return;
-       }
-       restrict_mask &= ~RES_DONTTRUST;
 #ifdef OPENSSL
        pkeyid = tkeyid = 0;
 #endif /* OPENSSL */
@@ -460,7 +449,7 @@ receive(
         * unicast address anyway. Don't ask.
         */
        peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd,
-           hismode, &retcode);
+           hisversion, hismode, &retcode);
        dstadr_sin = &rbufp->dstadr->sin;
        NTOHL_FP(&pkt->org, &p_org);
        NTOHL_FP(&pkt->rec, &p_rec);
@@ -479,6 +468,9 @@ receive(
         * is nonzero, the authentication code must not be NONE.
         */
        if (has_mac == 0) {
+               if (restrict_mask & RES_DONTTRUST)
+                       is_authentic = AUTH_ERROR; /* required */
+               else
                        is_authentic = AUTH_NONE; /* not required */
 #ifdef DEBUG
                if (debug)
@@ -601,8 +593,7 @@ receive(
                            "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n",
                            current_time, stoa(dstadr_sin),
                            stoa(&rbufp->recv_srcadr), hismode, retcode,
-                           skeyid, authlen, has_mac,
-                           is_authentic);
+                           skeyid, authlen, has_mac, is_authentic);
 #endif
        }
 
@@ -614,53 +605,60 @@ receive(
         * is mobilized: a broadcast packet mobilizes a broadcast client
         * aassociation; a manycast server packet mobilizes a manycast
         * client association; a symmetric active packet mobilizes a
-        * symmetric passive association. And, the adventure
-        * continues...
+        * symmetric passive association.
         */
        switch (retcode) {
 
        /*
-        * This is a client mode packet not matching any association, so
-        * it must be from a manycast client. If there are no errors and
-        * we (as manycast server) toss a server mode packet back to the
-        * rascal.
+        * This is a client mode packet not matching any association. If
+        * an ordinary client, simply toss a server mode packet back
+        * over the fence. If a manycast client, we have to work a
+        * little harder.
         */
        case AM_FXMIT:
-               if (rbufp->dstadr->flags & INT_MCASTOPEN) {
 
-                       /*
-                        * Do not respond to multicast if not configured
-                        * as a manycast server.
-                        */
-                       if (hismode == MODE_CLIENT &&
-                           !sys_manycastserver)
-                               return;
+               /*
+                * The vanilla case is when this is not a multicast
+                * interface. If authentication succeeds, return a
+                * server mode packet; if not, return a crypto-NAK.
+                */
+               if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) {
+                       if (AUTH(0, is_authentic))
+                               fast_xmit(rbufp, MODE_SERVER, skeyid,
+                                   restrict_mask);
+                       else
+                               fast_xmit(rbufp, MODE_SERVER, 0,
+                                   restrict_mask);
+                       return;                 /* hooray */
 
-                       /*
-                        * There is no reason to respond to a request if
-                        * our time is worse than the manycaster or it
-                        * has already synchronized to us.
-                        */
-                       if (sys_peer == NULL ||
-                           PKT_TO_STRATUM(pkt->stratum) <
-                           sys_stratum || (sys_cohort &&
-                           PKT_TO_STRATUM(pkt->stratum) ==
-                           sys_stratum) || rbufp->dstadr->addr_refid ==
-                           pkt->refid)
-                               return;         /* manycast dropped */
-               }
+               /*
+                * This must be manycast. Do not respond if not
+                * configured as a manycast server.
+                */
+               } else if (hismode == MODE_CLIENT &&
+                   !sys_manycastserver) {
+                       return;                 /* not enabled */
 
                /*
-                * Note that we don't require authentication here, since
-                * we can't set the system clock; but, we do send a
-                * crypto-NAK (kiss the frog) if authentication failed.
+                * Do not respond if our time is worse than the
+                * manycaster or it has already synchronized to us.
                 */
-               if (is_authentic == AUTH_ERROR)
-                       fast_xmit(rbufp, MODE_SERVER, 0, restrict_mask);
-               else
+               } else if (sys_peer == NULL ||
+                   PKT_TO_STRATUM(pkt->stratum) < sys_stratum ||
+                   (sys_cohort && PKT_TO_STRATUM(pkt->stratum) ==
+                   sys_stratum) || rbufp->dstadr->addr_refid ==
+                   pkt->refid) {
+                       return;                 /* no help */
+
+               /*
+                * Respond only if authentication succeeds. Don't do a
+                * crypto-NAK, as that would not be useful.
+                */
+               } else if (AUTH(sys_authenticate, is_authentic)) {
                        fast_xmit(rbufp, MODE_SERVER, skeyid,
                            restrict_mask);
-               return;
+               }
+               return;                         /* hooray */
 
        /*
         * This is a server mode packet returned in response to a client
@@ -680,19 +678,22 @@ receive(
         * the guy is already here, don't fire up a duplicate.
         */
        case AM_MANYCAST:
-               if (!(is_authentic == AUTH_NONE || is_authentic ==
-                   AUTH_OK))
+               if (restrict_mask & RES_NOPEER) {
+                       sys_restricted++;
+                       return;                 /* not enabled */
+
+               } else if (!AUTH(sys_authenticate, is_authentic)) {
                        return;                 /* bad auth */
 
-               if ((peer2 = findmanycastpeer(rbufp)) == NULL)
+               } else if ((peer2 = findmanycastpeer(rbufp)) == NULL) {
                        return;                 /* no assoc match */
 
-               peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
-                   MODE_CLIENT, PKT_VERSION(pkt->li_vn_mode),
-                   NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_IBURST, MDF_UCAST |
-                   MDF_ACLNT, 0, skeyid);
-               if (peer == NULL)
+               } else if ((peer = newpeer(&rbufp->recv_srcadr,
+                   rbufp->dstadr, MODE_CLIENT,
+                   hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_IBURST,
+                   MDF_UCAST | MDF_ACLNT, 0, skeyid)) == NULL) {
                        return;                 /* system error */
+               }
 
                /*
                 * We don't need these, but it warms the billboards.
@@ -726,8 +727,11 @@ receive(
         * kiss any frogs here.
         */
        case AM_NEWBCL:
-               if (!(is_authentic == AUTH_NONE || is_authentic ==
-                   AUTH_OK))
+               if (restrict_mask & RES_NOPEER || sys_bclient == 0) {
+                       sys_restricted++;
+                       return;                 /* not enabled */
+
+               } else if (!AUTH(sys_authenticate, is_authentic)) {
                        return;                 /* bad auth */
 
                /*
@@ -736,43 +740,41 @@ receive(
                 * In the latter, autokey will not work, as it requires
                 * a cryptographic data exchange.
                 */
-               if (sys_bclient == 1) {
-                       peer = newpeer(&rbufp->recv_srcadr,
-                           rbufp->dstadr, MODE_CLIENT,
-                           PKT_VERSION(pkt->li_vn_mode), NTP_MINDPOLL,
-                           NTP_MAXDPOLL, FLAG_MCAST | FLAG_IBURST,
-                           MDF_BCLNT, 0, skeyid);
-#ifdef OPENSSL
-                       if (peer == NULL)
+               } else if (sys_bclient == 1) {
+                       if ((peer = newpeer(&rbufp->recv_srcadr,
+                           rbufp->dstadr, MODE_CLIENT, hisversion,
+                           NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_MCAST |
+                           FLAG_IBURST, MDF_BCLNT, 0, skeyid)) == NULL)
+                           {
                                return;         /* system error */
-                       if (!(peer->flags & FLAG_SKEY))
+#ifdef OPENSSL
+                       if (!(peer->flags & FLAG_SKEY)) {
                                return;
 
-                       rval = crypto_recv(peer, rbufp);
-                       if (rval != XEVNT_OK) { /* crypto error */
+                       } else if (crypto_recv(peer, rbufp) != XEVNT_OK)
+                           {
                                peer_clear(peer, "CRYP");
                                unpeer(peer);
                        }
-               }
 #endif /* OPENSSL */
                        return;
 
+
+#ifdef OPENSSL
                /*
                 * Autokey cryptography requires a two-way exchange
                 * between the client and server. If a two-way exchange
                 * is not possible, neither is Autokey.
                 */
-#ifdef OPENSSL
-               if (!(peer->flags & FLAG_SKEY))
-                       return;
+               } else if (peer->flags & FLAG_SKEY)
+                       return;                 /* no autokey */
 #endif /* OPENSSL */
-               peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
-                   MODE_BCLIENT, PKT_VERSION(pkt->li_vn_mode),
+               } else if ((peer = newpeer(&rbufp->recv_srcadr,
+                   rbufp->dstadr, MODE_BCLIENT, hisversion,
                    NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_BCLNT, 0,
-                   skeyid);
-               if (peer == NULL)
+                   skeyid)) == NULL) {
                        return;                 /* system error */
-
+               }
                /* fall through */
 
        /*
@@ -788,19 +790,36 @@ receive(
         * mobilize a passive association. If not, kiss the frog.
         */
        case AM_NEWPASS:
-               if (!((is_authentic == AUTH_NONE || is_authentic ==
-                   AUTH_OK) && L_ISZERO(&p_org))) {
+               if (restrict_mask & RES_NOPEER) {
+                       sys_restricted++;
+                       return;                 /* not enabled */
+
+               /*
+                * Pay close attention, as the following test has
+                * interesting consequences. If the inbound packet has
+                * no MAC and authentication is enabled, this mode works
+                * just as in client/server mode. If no MAC and
+                * authentiation is disabled, a symmetric passive
+                * association is mobilized instead.
+                *
+                * If the inbound packet has a MAC and is correctly
+                * authenticated, a symmetric passive association is
+                * mobilized. A crypto-NAK is returned if authentication
+                * fails. The reason this works is that a crypto-NAK
+                * without a MAC looks like an ordinary packet.
+                */
+               } else if (!AUTH(sys_authenticate, is_authentic) ||
+                    (sys_authenticate && !L_ISZERO(&p_org))) {
                        fast_xmit(rbufp, MODE_PASSIVE, 0,
                            restrict_mask);
                        return;                 /* bad auth */
-               }
-               peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr,
-                   MODE_PASSIVE, PKT_VERSION(pkt->li_vn_mode),
+
+               } else if ((peer = newpeer(&rbufp->recv_srcadr,
+                   rbufp->dstadr, MODE_PASSIVE, hisversion,
                    NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_UCAST, 0,
-                   skeyid);
-               if (peer == NULL)
+                   skeyid)) == NULL) {
                        return;                 /* system error */
-
+               }
                /* fall through */
 
        /*
@@ -839,33 +858,12 @@ receive(
                return;
        }
 
-       /* 
-        * If the association is authenticated, each packet carries a
-        * MAC with nonzero key ID. Subsequently, if a packet arrives
-        * with no MAC, it is dropped. This is designed to avoid a bait-
-        * and-switch attack.
-        */
-       if (is_authentic == AUTH_ERROR || (peer->keyid != 0 &&
-           is_authentic == AUTH_NONE)) {
-               peer->flash |= TEST5;   /* bad auth */
-               return;
-       }
-
-       /*
-        * The packet matches association p. Verify the timestamps are
-        * valid and the packet is not a replay or bogus. Note the only
-        * way we can get here is either with an newlly-mobilized
-        * association or via PROCPK, so the flashers are properly
-        * illuminated.
-        */
-       peer->received++;
-       peer->timereceived = current_time;
-
        /*
-        * If the transmit timestamp is zero, the server is broken.
+        * Comes now an exhaustive set of sanity checks. If the transmit
+        * timestamp is zero, the server is broken.
         */
        if (L_ISZERO(&p_xmt)) {
-               return;
+               return;                         /* read rfc1305 */
 
        /*
         * If the transmit timestamp duplicates a previous one, the
@@ -873,9 +871,9 @@ receive(
         * the most recent packet, authenticated or not.
         */
        } else if (L_ISEQU(&peer->org, &p_xmt)) {
-               peer->flash |= TEST1;           /* dupe */
+               peer->flash |= TEST1;
                peer->oldpkt++;
-               return;
+               return;                         /* dupe */
 
        /*
         * The timestamps are valid and the receive packet matches the
@@ -887,16 +885,25 @@ receive(
                peer_clear(peer, "AUTH");
                if (!(peer->flags & FLAG_CONFIG))
                        unpeer(peer);
-               return;
+               return;                         /* crypto-NAK */
+
+       /* 
+        * If the association is authenticated, the key ID is nonzero
+        * and received packets must be authenticated. This is designed
+        * to avoid a bait-and-switch attack, which was possible in past
+        * versions.
+        */
+       } else if (!AUTH(peer->keyid, is_authentic)) {
+               peer->flash |= TEST5;
+               return;                         /* bad auth */
        }
 
        /*
-        * The flashers that can survive here include TEST2 (bogus),
-        * TEST3 (synch) and TEST5 (auth). Let the first two pass, since
-        * they might contain useful extension fields.
+        * That was hard and I am sweaty, but the packet is squeaky
+        * clean. Get on with real work.
         */
-       if (peer->flash & ~(TEST2 | TEST3))
-               return;
+       peer->received++;
+       peer->timereceived = current_time;
 
 #ifdef OPENSSL
        /*
@@ -1004,9 +1011,8 @@ receive(
                peer_clear(peer, "AUTO");
                if (!(peer->flags & FLAG_CONFIG))
                        unpeer(peer);
-#endif /* OPENSSL */
-
        }
+#endif /* OPENSSL */
 }
 
 
@@ -3070,10 +3076,9 @@ proto_config(
                break;
 
        /*
-        * Add multicast group address.
+        * Add muliticast group address.
         */
        case PROTO_MULTICAST_ADD:
-               sys_bclient = (int)value;
                if (svalue)
                    io_multicast_add(*svalue);
                sys_bclient = 1;