diff options
author | delphij <delphij@FreeBSD.org> | 2016-04-29 08:02:31 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2016-04-29 08:02:31 +0000 |
commit | 8738d3374d360bdb231ac07c863e462cd62f83c6 (patch) | |
tree | 88283a221508ca2b5a5ccce12c44af1f57c0e909 /contrib/ntp/ntpd/ntp_proto.c | |
parent | e021bee65027979bc179c9148040d65d33c528a1 (diff) | |
download | FreeBSD-src-8738d3374d360bdb231ac07c863e462cd62f83c6.zip FreeBSD-src-8738d3374d360bdb231ac07c863e462cd62f83c6.tar.gz |
Fix ntp multiple vulnerabilities.
Approved by: so
Diffstat (limited to 'contrib/ntp/ntpd/ntp_proto.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_proto.c | 259 |
1 files changed, 226 insertions, 33 deletions
diff --git a/contrib/ntp/ntpd/ntp_proto.c b/contrib/ntp/ntpd/ntp_proto.c index ad45409..713a0c2 100644 --- a/contrib/ntp/ntpd/ntp_proto.c +++ b/contrib/ntp/ntpd/ntp_proto.c @@ -25,6 +25,11 @@ #include <unistd.h> #endif +/* [Bug 3031] define automatic broadcastdelay cutoff preset */ +#ifndef BDELAY_DEFAULT +# define BDELAY_DEFAULT (-0.050) +#endif + /* * This macro defines the authentication state. If x is 1 authentication * is required; othewise it is optional. @@ -50,6 +55,12 @@ enum kiss_codes { UNKNOWNKISS /* Unknown Kiss Code */ }; +enum nak_error_codes { + NONAK, /* No NAK seen */ + INVALIDNAK, /* NAK cannot be used */ + VALIDNAK /* NAK is valid */ +}; + /* * traffic shaping parameters */ @@ -166,7 +177,10 @@ int unpeer_crypto_early = 1; /* bad crypto (TEST9) */ int unpeer_crypto_nak_early = 1; /* crypto_NAK (TEST5) */ int unpeer_digest_early = 1; /* bad digest (TEST5) */ -static int kiss_code_check(u_char hisleap, u_char hisstratum, u_char hismode, u_int32 refid); +int dynamic_interleave = DYNAMIC_INTERLEAVE; /* Bug 2978 mitigation */ + +int kiss_code_check(u_char hisleap, u_char hisstratum, u_char hismode, u_int32 refid); +enum nak_error_codes valid_NAK(struct peer *peer, struct recvbuf *rbufp, u_char hismode); static double root_distance (struct peer *); static void clock_combine (peer_select *, int, int); static void peer_xmit (struct peer *); @@ -253,6 +267,68 @@ kiss_code_check( } +/* + * Check that NAK is valid + */ +enum nak_error_codes +valid_NAK( + struct peer *peer, + struct recvbuf *rbufp, + u_char hismode + ) +{ + int base_packet_length = MIN_V4_PKT_LEN; + int remainder_size; + struct pkt *rpkt; + int keyid; + + /* + * Check to see if there is something beyond the basic packet + */ + if (rbufp->recv_length == base_packet_length) { + return NONAK; + } + + remainder_size = rbufp->recv_length - base_packet_length; + /* + * Is this a potential NAK? + */ + if (remainder_size != 4) { + return NONAK; + } + + /* + * Only server responses can contain NAK's + */ + + if (hismode != MODE_SERVER && + hismode != MODE_ACTIVE && + hismode != MODE_PASSIVE + ) { + return (INVALIDNAK); + } + + /* + * Make sure that the extra field in the packet is all zeros + */ + rpkt = &rbufp->recv_pkt; + keyid = ntohl(((u_int32 *)rpkt)[base_packet_length / 4]); + if (keyid != 0) { + return (INVALIDNAK); + } + + /* + * Only valid if peer uses a key + */ + if (peer->keyid > 0 || peer->flags & FLAG_SKEY) { + return (VALIDNAK); + } + else { + return (INVALIDNAK); + } +} + + /* * transmit - transmit procedure called by poll timeout */ @@ -493,6 +569,7 @@ receive( int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ int is_authentic = 0; /* cryptosum ok */ + int crypto_nak_test; /* result of crypto-NAK check */ int retcode = AM_NOMATCH; /* match code */ keyid_t skeyid = 0; /* key IDs */ u_int32 opcode = 0; /* extension field opcode */ @@ -617,6 +694,7 @@ receive( * extension field is present, so we subtract the length of the * field and go around again. */ + authlen = LEN_PKT_NOMAC; has_mac = rbufp->recv_length - authlen; while (has_mac > 0) { @@ -767,6 +845,20 @@ receive( * is zero, acceptable outcomes of y are NONE and OK. If x is * one, the only acceptable outcome of y is OK. */ + crypto_nak_test = valid_NAK(peer, rbufp, hismode); + + /* + * Drop any invalid crypto-NAKs + */ + if (crypto_nak_test == INVALIDNAK) { + report_event(PEVNT_AUTH, peer, "Invalid_NAK"); + if (0 != peer) { + peer->badNAK++; + } + msyslog(LOG_ERR, "Invalid-NAK error at %ld %s<-%s", + current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr)); + return; + } if (has_mac == 0) { restrict_mask &= ~RES_MSSNTP; @@ -777,7 +869,7 @@ receive( authlen, ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf), ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf))); - } else if (has_mac == 4) { + } else if (crypto_nak_test == VALIDNAK) { restrict_mask &= ~RES_MSSNTP; is_authentic = AUTH_CRYPTO; /* crypto-NAK */ DPRINTF(2, ("receive: at %ld %s<-%s mode %d/%s:%s keyid %08x len %d auth %d org %#010x.%08x xmt %#010x.%08x MAC4\n", @@ -1144,7 +1236,7 @@ receive( /* * Determine whether to execute the initial volley. */ - if (sys_bdelay != 0) { + if (sys_bdelay > 0.0) { #ifdef AUTOKEY /* * If a two-way exchange is not possible, @@ -1303,9 +1395,9 @@ receive( #endif /* AUTOKEY */ if (MODE_BROADCAST == hismode) { - u_char poll; - int bail = 0; - l_fp tdiff; + int bail = 0; + l_fp tdiff; + u_long deadband; DPRINTF(2, ("receive: PROCPKT/BROADCAST: prev pkt %ld seconds ago, ppoll: %d, %d secs\n", (current_time - peer->timelastrec), @@ -1327,27 +1419,28 @@ receive( peer->ppoll, pkt->ppoll); } - poll = min(peer->maxpoll, - max(peer->minpoll, pkt->ppoll)); - /* This is error-worthy */ - if (pkt->ppoll != poll) { + if (pkt->ppoll < peer->minpoll || + pkt->ppoll > peer->maxpoll ) { msyslog(LOG_INFO, "receive: broadcast poll of %ud from %s is out-of-range (%d to %d)!", pkt->ppoll, stoa(&rbufp->recv_srcadr), peer->minpoll, peer->maxpoll); ++bail; } - if ( (current_time - peer->timelastrec) - < (1 << pkt->ppoll)) { - msyslog(LOG_INFO, "receive: broadcast packet from %s arrived after %ld, not %d seconds!", + /* too early? worth an error, too! */ + deadband = (1u << pkt->ppoll); + if (FLAG_BC_VOL & peer->flags) + deadband -= 3; /* allow greater fuzz after volley */ + if ((current_time - peer->timelastrec) < deadband) { + msyslog(LOG_INFO, "receive: broadcast packet from %s arrived after %lu, not %lu seconds!", stoa(&rbufp->recv_srcadr), (current_time - peer->timelastrec), - (1 << pkt->ppoll) - ); + deadband); ++bail; } + /* Alert if time from the server is non-monotonic */ tdiff = p_xmt; L_SUB(&tdiff, &peer->bxmt); if (tdiff.l_i < 0) { @@ -1401,6 +1494,7 @@ receive( return; } #endif /* AUTOKEY */ + peer->received++; peer->flash &= ~PKT_TEST_MASK; if (peer->flags & FLAG_XBOGUS) { @@ -1412,12 +1506,22 @@ receive( * Next comes a rigorous schedule of timestamp checking. If the * transmit timestamp is zero, the server has not initialized in * interleaved modes or is horribly broken. + * + * A KoD packet we pay attention to cannot have a 0 transmit + * timestamp. */ if (L_ISZERO(&p_xmt)) { peer->flash |= TEST3; /* unsynch */ + if (0 == hisstratum) { /* KoD packet */ + peer->bogusorg++; /* for TEST2 or TEST3 */ + msyslog(LOG_INFO, + "receive: Unexpected zero transmit timestamp in KoD from %s", + ntoa(&peer->srcadr)); + return; + } /* - * If the transmit timestamp duplicates a previous one, the + * If the transmit timestamp duplicates our previous one, the * packet is a replay. This prevents the bad guys from replaying * the most recent packet, authenticated or not. */ @@ -1442,14 +1546,66 @@ receive( } /* + * Basic KoD validation checking: + * + * KoD packets are a mixed-blessing. Forged KoD packets + * are DoS attacks. There are rare situations where we might + * get a valid KoD response, though. Since KoD packets are + * a special case that complicate the checks we do next, we + * handle the basic KoD checks here. + * + * Note that we expect the incoming KoD packet to have its + * (nonzero) org, rec, and xmt timestamps set to the xmt timestamp + * that we have previously sent out. Watch interleave mode. + */ + } else if (0 == hisstratum) { + DEBUG_INSIST(!L_ISZERO(&p_xmt)); + if ( L_ISZERO(&p_org) /* We checked p_xmt above */ + || L_ISZERO(&p_rec)) { + peer->bogusorg++; + msyslog(LOG_INFO, + "receive: KoD packet from %s has a zero org or rec timestamp. Ignoring.", + ntoa(&peer->srcadr)); + return; + } + + if ( !L_ISEQU(&p_xmt, &p_org) + || !L_ISEQU(&p_xmt, &p_rec)) { + peer->bogusorg++; + msyslog(LOG_INFO, + "receive: KoD packet from %s has inconsistent xmt/org/rec timestamps. Ignoring.", + ntoa(&peer->srcadr)); + return; + } + + /* Be conservative */ + if (peer->flip == 0 && !L_ISEQU(&p_org, &peer->aorg)) { + peer->bogusorg++; + msyslog(LOG_INFO, + "receive: flip 0 KoD origin timestamp %#010x.%08x from %s does not match %#010x.%08x - ignoring.", + p_org.l_ui, p_org.l_uf, + ntoa(&peer->srcadr), + peer->aorg.l_ui, peer->aorg.l_uf); + return; + } else if (peer->flip == 1 && !L_ISEQU(&p_org, &peer->borg)) { + peer->bogusorg++; + msyslog(LOG_INFO, + "receive: flip 1 KoD origin timestamp %#010x.%08x from %s does not match interleave %#010x.%08x - ignoring.", + p_org.l_ui, p_org.l_uf, + ntoa(&peer->srcadr), + peer->borg.l_ui, peer->borg.l_uf); + return; + } + + /* * Basic mode checks: * * If there is no origin timestamp, it's either an initial packet * or we've already received a response to our query. Of course, * should 'aorg' be all-zero because this really was the original - * transmit timestamp, we'll drop the reply. There is a window of - * one nanosecond once every 136 years' time where this is possible. - * We currently ignore this situation. + * transmit timestamp, we'll ignore this reply. There is a window + * of one nanosecond once every 136 years' time where this is + * possible. We currently ignore this situation. * * Otherwise, check for bogus packet in basic mode. * If it is bogus, switch to interleaved mode and resynchronize, @@ -1460,24 +1616,37 @@ receive( * be from us, attempting to cause our server to KoD us. */ } else if (peer->flip == 0) { - if (0 < hisstratum && L_ISZERO(&p_org)) { + INSIST(0 != hisstratum); + if (0) { + } else if (L_ISZERO(&p_org)) { + msyslog(LOG_INFO, + "receive: Got 0 origin timestamp from %s@%s xmt %#010x.%08x", + hm_str, ntoa(&peer->srcadr), + ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)); L_CLR(&peer->aorg); - } else if ( L_ISZERO(&peer->aorg) - || !L_ISEQU(&p_org, &peer->aorg)) { + } else if (!L_ISEQU(&p_org, &peer->aorg)) { + /* are there cases here where we should bail? */ + /* Should we set TEST2 if we decide to try xleave? */ peer->bogusorg++; peer->flash |= TEST2; /* bogus */ msyslog(LOG_INFO, - "receive: Unexpected origin timestamp %#010x.%08x from %s xmt %#010x.%08x", + "receive: Unexpected origin timestamp %#010x.%08x does not match aorg %#010x.%08x from %s@%s xmt %#010x.%08x", ntohl(pkt->org.l_ui), ntohl(pkt->org.l_uf), - ntoa(&peer->srcadr), + peer->aorg.l_ui, peer->aorg.l_uf, + hm_str, ntoa(&peer->srcadr), ntohl(pkt->xmt.l_ui), ntohl(pkt->xmt.l_uf)); if ( !L_ISZERO(&peer->dst) && L_ISEQU(&p_org, &peer->dst)) { /* Might be the start of an interleave */ - peer->flip = 1; - report_event(PEVNT_XLEAVE, peer, NULL); + if (dynamic_interleave) { + peer->flip = 1; + report_event(PEVNT_XLEAVE, peer, NULL); + } else { + msyslog(LOG_INFO, + "receive: Dynamic interleave from %s@%s denied", + hm_str, ntoa(&peer->srcadr)); + } } - return; /* Bogus or possible interleave packet */ } else { L_CLR(&peer->aorg); } @@ -1507,7 +1676,7 @@ receive( * client packet. The server might have just changed keys. Clear * the association and restart the protocol. */ - if (is_authentic == AUTH_CRYPTO) { + if (crypto_nak_test == VALIDNAK) { report_event(PEVNT_AUTH, peer, "crypto_NAK"); peer->flash |= TEST5; /* bad auth */ peer->badauth++; @@ -1600,17 +1769,22 @@ receive( /* * If: * - this is a *cast (uni-, broad-, or m-) server packet - * - and it's authenticated + * - and it's symmetric-key authenticated * then see if the sender's IP is trusted for this keyid. * If it is, great - nothing special to do here. * Otherwise, we should report and bail. + * + * Autokey-authenticated packets are accepted. */ switch (hismode) { case MODE_SERVER: /* server mode */ case MODE_BROADCAST: /* broadcast mode */ case MODE_ACTIVE: /* symmetric active mode */ + case MODE_PASSIVE: /* symmetric passive mode */ if ( is_authentic == AUTH_OK + && skeyid + && skeyid <= NTP_MAXKEY && !authistrustedip(skeyid, &peer->srcadr)) { report_event(PEVNT_AUTH, peer, "authIP"); peer->badauth++; @@ -1618,8 +1792,6 @@ receive( } break; - case MODE_UNSPEC: /* unspecified (old version) */ - case MODE_PASSIVE: /* symmetric passive mode */ case MODE_CLIENT: /* client mode */ #if 0 /* At this point, MODE_CONTROL is overloaded by MODE_BCLIENT */ case MODE_CONTROL: /* control mode */ @@ -1627,7 +1799,12 @@ receive( case MODE_PRIVATE: /* private mode */ case MODE_BCLIENT: /* broadcast client mode */ break; + + case MODE_UNSPEC: /* unspecified (old version) */ default: + msyslog(LOG_INFO, + "receive: Unexpected mode (%d) in packet from %s", + hismode, ntoa(&peer->srcadr)); break; } @@ -1958,6 +2135,9 @@ process_packet( peer->aorg = p_xmt; peer->borg = peer->dst; if (t34 < 0 || t34 > 1.) { + /* drop all if in the initial volley */ + if (FLAG_BC_VOL & peer->flags) + goto bcc_init_volley_fail; snprintf(statstr, sizeof(statstr), "offset %.6f delay %.6f", t21, t34); report_event(PEVNT_XERR, peer, statstr); @@ -1983,11 +2163,23 @@ process_packet( * between the unicast timestamp and the broadcast * timestamp. This works for both basic and interleaved * modes. + * [Bug 3031] Don't keep this peer when the delay + * calculation gives reason to suspect clock steps. + * This is assumed for delays > 50ms. */ if (FLAG_BC_VOL & peer->flags) { peer->flags &= ~FLAG_BC_VOL; peer->delay = fabs(peer->offset - p_offset) * 2; + DPRINTF(2, ("broadcast volley: initial delay=%.6f\n", + peer->delay)); + if (peer->delay > fabs(sys_bdelay)) { + bcc_init_volley_fail: + DPRINTF(2, ("%s", "broadcast volley: initial delay exceeds limit\n")); + unpeer(peer); + return; + } } + peer->nextdate = current_time + (1u << peer->ppoll) - 2u; p_del = peer->delay; p_offset += p_del / 2; @@ -4018,6 +4210,7 @@ group_test( } #endif /* AUTOKEY */ + #ifdef WORKER void pool_name_resolved( @@ -4333,7 +4526,7 @@ init_proto(void) sys_survivors = 0; sys_manycastserver = 0; sys_bclient = 0; - sys_bdelay = 0; + sys_bdelay = BDELAY_DEFAULT; /*[Bug 3031] delay cutoff */ sys_authenticate = 1; sys_stattime = current_time; orphwait = current_time + sys_orphwait; @@ -4426,7 +4619,7 @@ proto_config( break; case PROTO_BROADDELAY: /* default broadcast delay (bdelay) */ - sys_bdelay = dvalue; + sys_bdelay = (dvalue ? dvalue : BDELAY_DEFAULT); break; case PROTO_CEILING: /* stratum ceiling (ceiling) */ |