From: Dave Hart Date: Thu, 22 Apr 2010 10:06:01 +0000 (+0000) Subject: [Bug 1531] Require nonce with mrulist requests. X-Git-Tag: NTP_4_2_7P26~2^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7e31af68411568605887c2ef556ad3db4547a26;p=thirdparty%2Fntp.git [Bug 1531] Require nonce with mrulist requests. [Bug 1532] Remove ntpd support for ntpdc's monlist in favor of ntpq's mrulist. bk: 4bd01f892ejm36_aylOSZXisaW9PYw --- diff --git a/ChangeLog b/ChangeLog index 793e83a0c..d769c8c74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +* [Bug 1531] Require nonce with mrulist requests. +* [Bug 1532] Remove ntpd support for ntpdc's monlist in favor of ntpq's + mrulist. * Include (4.2.6p2-RC1) - [Bug 1503] [Bug 1504] [Bug 1518] [Bug 1522], all of which were fixed in 4.2.7 previously. (4.2.7p24) 2010/04/13 Released by Harlan Stenn diff --git a/include/ntp_control.h b/include/ntp_control.h index 065be7b9d..3b0156b30 100644 --- a/include/ntp_control.h +++ b/include/ntp_control.h @@ -57,6 +57,7 @@ struct ntp_control { #define CTL_OP_SAVECONFIG 9 /* save config to file */ #define CTL_OP_READ_MRU 10 /* retrieve MRU (mrulist) */ #define CTL_OP_READ_IFSTATS 11 /* interface stats (ifstats) */ +#define CTL_OP_REQ_NONCE 12 /* request a client nonce */ #define CTL_OP_UNSETTRAP 31 /* unset trap */ /* diff --git a/include/ntp_md5.h b/include/ntp_md5.h index 5e2206b00..458962fda 100644 --- a/include/ntp_md5.h +++ b/include/ntp_md5.h @@ -1,13 +1,29 @@ /* * ntp_md5.h: deal with md5.h headers + * + * Use the system MD5 if available, otherwise libisc's. */ - #if defined HAVE_MD5_H && defined HAVE_MD5INIT # include #else # include "isc/md5.h" -# define MD5_CTX isc_md5_t -# define MD5Init isc_md5_init -# define MD5Update isc_md5_update -# define MD5Final(d,c) isc_md5_final(c,d) + typedef isc_md5_t MD5_CTX; +# define MD5Init(c) isc_md5_init(c) +# define MD5Update(c, p, s) isc_md5_update(c, p, s) +# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */ +#endif + +/* + * Provide OpenSSL-alike MD5 API if we're not using OpenSSL + */ +#ifndef OPENSSL + typedef MD5_CTX EVP_MD_CTX; +# define EVP_get_digestbynid(t) NULL +# define EVP_DigestInit(c, dt) MD5Init(c) +# define EVP_DigestUpdate(c, p, s) MD5Update(c, p, s) +# define EVP_DigestFinal(c, d, pdl) \ + do { \ + MD5Final((d), (c)); \ + *(pdl) = 16; \ + } while (0) #endif diff --git a/include/ntpd.h b/include/ntpd.h index ed5776d79..01a4178f6 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -527,6 +527,9 @@ extern restrict_u * restrictlist6; /* IPv6 restriction list */ extern int ntp_minpkt; extern u_char ntp_minpoll; +/* ntp_scanner.c */ +extern u_int32 conf_file_sum; /* Simple sum of characters */ + /* ntp_signd.c */ #ifdef HAVE_NTP_SIGND extern void send_via_ntp_signd(struct recvbuf, int, keyid_t, int, diff --git a/libntp/a_md5encrypt.c b/libntp/a_md5encrypt.c index bd30ba2c6..05c109a9f 100644 --- a/libntp/a_md5encrypt.c +++ b/libntp/a_md5encrypt.c @@ -10,10 +10,10 @@ #include "ntp_stdlib.h" #include "ntp.h" #ifdef OPENSSL -#include "openssl/evp.h" +# include "openssl/evp.h" #else -#include "ntp_md5.h" -#endif /* OPENSSSL */ +# include "ntp_md5.h" /* provides clone of OpenSSL MD5 API */ +#endif /* * MD5authencrypt - generate message digest @@ -30,30 +30,18 @@ MD5authencrypt( { u_char digest[EVP_MAX_MD_SIZE]; u_int len; -#ifdef OPENSSL EVP_MD_CTX ctx; -#else - MD5_CTX md5; -#endif /* OPENSSL */ /* * Compute digest of key concatenated with packet. Note: the * key type and digest type have been verified when the key * was creaded. */ -#ifdef OPENSSL INIT_SSL(); EVP_DigestInit(&ctx, EVP_get_digestbynid(type)); EVP_DigestUpdate(&ctx, key, (u_int)cache_keylen); EVP_DigestUpdate(&ctx, (u_char *)pkt, (u_int)length); EVP_DigestFinal(&ctx, digest, &len); -#else /* OPENSSL */ - MD5Init(&md5); - MD5Update(&md5, key, (u_int)cache_keylen); - MD5Update(&md5, (u_char *)pkt, (u_int)length); - MD5Final(digest, &md5); - len = 16; -#endif /* OPENSSL */ memmove((u_char *)pkt + length + 4, digest, len); return (len + 4); } @@ -75,30 +63,18 @@ MD5authdecrypt( { u_char digest[EVP_MAX_MD_SIZE]; u_int len; -#ifdef OPENSSL EVP_MD_CTX ctx; -#else - MD5_CTX md5; -#endif /* OPENSSL */ /* * Compute digest of key concatenated with packet. Note: the * key type and digest type have been verified when the key * was created. */ -#ifdef OPENSSL INIT_SSL(); EVP_DigestInit(&ctx, EVP_get_digestbynid(type)); EVP_DigestUpdate(&ctx, key, (u_int)cache_keylen); EVP_DigestUpdate(&ctx, (u_char *)pkt, (u_int)length); EVP_DigestFinal(&ctx, digest, &len); -#else /* OPENSSL */ - MD5Init(&md5); - MD5Update(&md5, key, (u_int)cache_keylen); - MD5Update(&md5, (u_char *)pkt, (u_int)length); - MD5Final(digest, &md5); - len = 16; -#endif /* OPENSSL */ if ((u_int)size != len + 4) { msyslog(LOG_ERR, "MAC decrypt: MAC length error"); @@ -117,28 +93,17 @@ addr2refid(sockaddr_u *addr) { u_char digest[20]; u_int32 addr_refid; -#ifdef OPENSSL EVP_MD_CTX ctx; u_int len; -#else - MD5_CTX md5; -#endif /* OPENSSL */ if (IS_IPV4(addr)) return (NSRCADR(addr)); -#ifdef OPENSSL INIT_SSL(); EVP_DigestInit(&ctx, EVP_get_digestbynid(NID_md5)); EVP_DigestUpdate(&ctx, (u_char *)PSOCK_ADDR6(addr), sizeof(struct in6_addr)); EVP_DigestFinal(&ctx, digest, &len); -#else - MD5Init(&md5); - MD5Update(&md5, (u_char *)PSOCK_ADDR6(addr), - sizeof(struct in6_addr)); - MD5Final(digest, &md5); -#endif /* OPENSSL */ memcpy(&addr_refid, digest, 4); return (addr_refid); } diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c index 14baee287..fec962525 100644 --- a/ntpd/ntp_control.c +++ b/ntpd/ntp_control.c @@ -26,6 +26,13 @@ #endif #include +#ifdef OPENSSL +# include "openssl/evp.h" +#else +# include "ntp_md5.h" /* provides clone of OpenSSL MD5 API */ +#endif + + /* * Structure to hold request procedure information */ @@ -82,8 +89,12 @@ static void configure (struct recvbuf *, int); static void send_mru_entry (mon_entry *, int); static void send_random_tag_value(int); static void read_mru_list (struct recvbuf *, int); -static void send_ifstats_entry(struct interface *, u_int); +static void send_ifstats_entry(struct interface *, u_int); static void read_ifstats (struct recvbuf *, int); +static u_int32 derive_nonce (sockaddr_u *, u_int32, u_int32); +static void generate_nonce (struct recvbuf *, char *, size_t); +static int validate_nonce (const char *, struct recvbuf *); +static void req_nonce (struct recvbuf *, int); static void unset_trap (struct recvbuf *, int); static struct ctl_trap *ctlfindtrap(sockaddr_u *, struct interface *); @@ -100,6 +111,7 @@ static struct ctl_proc control_codes[] = { { CTL_OP_CONFIGURE, AUTH, configure }, { CTL_OP_READ_MRU, NOAUTH, read_mru_list }, { CTL_OP_READ_IFSTATS, AUTH, read_ifstats }, + { CTL_OP_REQ_NONCE, NOAUTH, req_nonce }, { CTL_OP_UNSETTRAP, NOAUTH, unset_trap }, { NO_REQUEST, 0 } }; @@ -2676,6 +2688,96 @@ static void configure( } +/* + * derive_nonce - generate client-address-specific nonce value + * associated with a given timestamp. + */ +static u_int32 derive_nonce( + sockaddr_u * addr, + u_int32 ts_i, + u_int32 ts_f + ) +{ + static u_int32 salt[2]; + union d_tag { + u_char digest[EVP_MAX_MD_SIZE]; + u_int32 extract; + } d; + EVP_MD_CTX ctx; + u_int len; + + while (!salt[0]) + salt[0] = ntp_random(); + salt[1] = conf_file_sum; + + EVP_DigestInit(&ctx, EVP_get_digestbynid(NID_md5)); + EVP_DigestUpdate(&ctx, salt, sizeof(salt)); + EVP_DigestUpdate(&ctx, &ts_i, sizeof(ts_i)); + EVP_DigestUpdate(&ctx, &ts_f, sizeof(ts_f)); + if (IS_IPV4(addr)) + EVP_DigestUpdate(&ctx, &SOCK_ADDR4(addr), + sizeof(SOCK_ADDR4(addr))); + else + EVP_DigestUpdate(&ctx, &SOCK_ADDR6(addr), + sizeof(SOCK_ADDR6(addr))); + EVP_DigestUpdate(&ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr))); + EVP_DigestUpdate(&ctx, salt, sizeof(salt)); + EVP_DigestFinal(&ctx, d.digest, &len); + + return d.extract; +} + + +/* + * generate_nonce - generate client-address-specific nonce string. + */ +static void generate_nonce( + struct recvbuf * rbufp, + char * nonce, + size_t nonce_octets + ) +{ + u_int32 derived; + + derived = derive_nonce(&rbufp->recv_srcadr, + rbufp->recv_time.l_ui, + rbufp->recv_time.l_uf); + snprintf(nonce, nonce_octets, "%08x%08x%08x", + rbufp->recv_time.l_ui, rbufp->recv_time.l_uf, derived); +} + + +/* + * validate_nonce - validate client-address-specific nonce string. + * + * Returns TRUE if the local calculation of the nonce matches the + * client-provided value and the timestamp is recent enough. + */ +static int validate_nonce( + const char * pnonce, + struct recvbuf * rbufp + ) +{ + u_int ts_i; + u_int ts_f; + l_fp ts; + l_fp now_delta; + u_int supposed; + u_int derived; + + if (3 != sscanf(pnonce, "%08x%08x%08x", &ts_i, &ts_f, &supposed)) + return FALSE; + + ts.l_ui = (u_int32)ts_i; + ts.l_uf = (u_int32)ts_f; + derived = derive_nonce(&rbufp->recv_srcadr, ts.l_ui, ts.l_uf); + get_systime(&now_delta); + L_SUB(&now_delta, &ts); + + return (supposed == derived && now_delta.l_ui < 16); +} + + /* * Send a MRU list entry in response to a "ntpq -c mrulist" operation. * @@ -2755,7 +2857,7 @@ send_mru_entry( * random integer value. * * To try to force clients to ignore unrecognized tags in mrulist - * and iflist responses, the first and last rows are spiced with + * and ifstats responses, the first and last rows are spiced with * randomly-generated tag names with correct .# index. * Make it three characters knowing that none of the currently-used * tags have that length, avoiding the need to test for tag collision. @@ -2813,6 +2915,9 @@ send_random_tag_value( * backing up all the way to the starting point. * * input parameters: + * nonce= Regurgitated nonce retrieved by the client + * previously using CTL_OP_REQ_NONCE, demonstrating + * ability to receive traffic sent to its address. * limit= Limit on MRU entries returned. This is the sole * required input parameter. [1...256] * limit=1 is a special case: Instead of fetching @@ -2842,12 +2947,13 @@ send_random_tag_value( * ntpq provides as many last/addr pairs as will fit in a single request * packet, except for the first request in a MRU fetch operation. * - * The response begins with the next newer entry than referred to by - * last.0 and addr.0, if the "0" entry has not been bumped to the front. - * Otherwise, it will begin with the next entry newer than referred to - * by last.1 and addr.1, and so on. If none of the referenced entries - * remain unchanged, the request fails and ntpq backs up to the next - * earlier set of entries to resync. + * The response begins with a new nonce value to be used for any + * followup request. Following the nonce is the next newer entry than + * referred to by last.0 and addr.0, if the "0" entry has not been + * bumped to the front. If it has, the first entry returned will be the + * next entry newer than referred to by last.1 and addr.1, and so on. + * If none of the referenced entries remain unchanged, the request fails + * and ntpq backs up to the next earlier set of entries to resync. * * Except for the first response, the response begins with confirmation * of the entry that precedes the first additional entry provided: @@ -2893,6 +2999,7 @@ static void read_mru_list( int restrict_mask ) { + const char nonce_text[] = "nonce"; const char limit_text[] = "limit"; const char mincount_text[] = "mincount"; const char resall_text[] = "resall"; @@ -2917,6 +3024,8 @@ static void read_mru_list( struct ctl_var * v; char * val; char * pch; + char * pnonce; + int nonce_valid; int i; int priors; u_short hash; @@ -2928,6 +3037,7 @@ static void read_mru_list( * fill in_parms var list with all possible input parameters. */ in_parms = NULL; + set_var(&in_parms, nonce_text, sizeof(nonce_text), 0); set_var(&in_parms, limit_text, sizeof(limit_text), 0); set_var(&in_parms, mincount_text, sizeof(mincount_text), 0); set_var(&in_parms, resall_text, sizeof(resall_text), 0); @@ -2942,6 +3052,7 @@ static void read_mru_list( } /* decode input parms */ + pnonce = NULL; limit = 0; mincount = 0; resall = 0; @@ -2955,7 +3066,9 @@ static void read_mru_list( while (NULL != (v = ctl_getitem(in_parms, &val)) && !(EOV & v->flags)) { - if (!strcmp(limit_text, v->text)) + if (!strcmp(nonce_text, v->text)) + pnonce = estrdup(val); + else if (!strcmp(limit_text, v->text)) sscanf(val, "%u", &limit); else if (!strcmp(mincount_text, v->text)) { if (1 != sscanf(val, "%d", &mincount) || @@ -2989,6 +3102,16 @@ static void read_mru_list( } free_varlist(in_parms); in_parms = NULL; + + /* return no responses until the nonce is validated */ + if (NULL == pnonce) + return; + + nonce_valid = validate_nonce(pnonce, rbufp); + free(pnonce); + if (!nonce_valid) + return; + if (!(0 < limit && limit <= MRU_ROW_LIMIT)) { ctl_error(CERR_BADVALUE); return; @@ -3036,6 +3159,8 @@ static void read_mru_list( * send up to limit= entries */ get_systime(&now); + generate_nonce(rbufp, buf, sizeof(buf)); + ctl_putunqstr("nonce", buf, strlen(buf)); prior_mon = NULL; for (count = 0; count < limit && mon != NULL; @@ -3229,6 +3354,22 @@ static void read_ifstats( } +/* + * req_nonce - CTL_OP_REQ_NONCE for ntpq -c mrulist prerequisite. + */ +static void req_nonce( + struct recvbuf * rbufp, + int restrict_mask + ) +{ + char buf[64]; + + generate_nonce(rbufp, buf, sizeof(buf)); + ctl_putunqstr("nonce", buf, strlen(buf)); + ctl_flushpkt(0); +} + + /* * read_clockstatus - return clock radio status */ diff --git a/ntpd/ntp_request.c b/ntpd/ntp_request.c index 419d417e2..27a4c56cb 100644 --- a/ntpd/ntp_request.c +++ b/ntpd/ntp_request.c @@ -88,8 +88,7 @@ static void do_resaddflags (sockaddr_u *, struct interface *, struct req_pkt *); static void do_ressubflags (sockaddr_u *, struct interface *, struct req_pkt *); static void do_unrestrict (sockaddr_u *, struct interface *, struct req_pkt *); static void do_restrict (sockaddr_u *, struct interface *, struct req_pkt *, int); -static void mon_getlist_0 (sockaddr_u *, struct interface *, struct req_pkt *); -static void mon_getlist_1 (sockaddr_u *, struct interface *, struct req_pkt *); +static void mon_getlist (sockaddr_u *, struct interface *, struct req_pkt *); static void reset_stats (sockaddr_u *, struct interface *, struct req_pkt *); static void reset_peer (sockaddr_u *, struct interface *, struct req_pkt *); static void do_key_reread (sockaddr_u *, struct interface *, struct req_pkt *); @@ -149,8 +148,8 @@ static struct req_proc ntp_codes[] = { sizeof(struct conf_restrict), do_ressubflags }, { REQ_UNRESTRICT, AUTH, v4sizeof(struct conf_restrict), sizeof(struct conf_restrict), do_unrestrict }, - { REQ_MON_GETLIST, NOAUTH, 0, 0, mon_getlist_0 }, - { REQ_MON_GETLIST_1, NOAUTH, 0, 0, mon_getlist_1 }, + { REQ_MON_GETLIST, NOAUTH, 0, 0, mon_getlist }, + { REQ_MON_GETLIST_1, NOAUTH, 0, 0, mon_getlist }, { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), 0, reset_stats }, { REQ_RESET_PEER, AUTH, v4sizeof(struct conf_unpeer), sizeof(struct conf_unpeer), reset_peer }, @@ -1874,125 +1873,15 @@ do_restrict( * mon_getlist - return monitor data */ static void -mon_getlist_0( +mon_getlist( sockaddr_u *srcadr, struct interface *inter, struct req_pkt *inpkt ) { - register struct info_monitor *im; - register mon_entry *md; - l_fp now; - l_fp diff; - size_t count; - - DPRINTF(3, ("wants monitor 0 list\n")); - - if (!mon_enabled) { - req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); - return; - } - get_systime(&now); - im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt, - v6sizeof(struct info_monitor)); - count = 0; - - ITER_DLIST_BEGIN(mon_mru_list, md, mru, mon_entry) - diff = now; - L_SUB(&diff, &md->first); - im->avg_int = htonl((u_int32)(diff.l_ui / md->count)); - diff = now; - L_SUB(&diff, &md->last); - im->last_int = htonl(diff.l_ui); - im->restr = htonl(md->flags); - im->count = htonl((u_int32)(md->count)); - if (IS_IPV6(&md->rmtadr)) { - if (!client_v6_capable) - continue; - im->addr6 = SOCK_ADDR6(&md->rmtadr); - im->v6_flag = 1; - } else { - im->addr = NSRCADR(&md->rmtadr); - if (client_v6_capable) - im->v6_flag = 0; - } - im->port = NSRCPORT(&md->rmtadr); - im->mode = PKT_MODE(md->vn_mode); - im->version = PKT_VERSION(md->vn_mode); - im = (struct info_monitor *)more_pkt(); - count++; - if (NULL == im || count >= 600) - break; - ITER_DLIST_END() - - flush_pkt(); + req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); } -/* - * mon_getlist - return monitor data - */ -static void -mon_getlist_1( - sockaddr_u *srcadr, - struct interface *inter, - struct req_pkt *inpkt - ) -{ - register struct info_monitor_1 *im; - register mon_entry *md; - l_fp now; - l_fp diff; - size_t count; - - if (!mon_enabled) { - req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA); - return; - } - get_systime(&now); - im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt, - v6sizeof(struct info_monitor_1)); - count = 0; - - ITER_DLIST_BEGIN(mon_mru_list, md, mru, mon_entry) - diff = now; - L_SUB(&diff, &md->first); - im->avg_int = htonl((u_int32)(diff.l_ui / md->count)); - diff = now; - L_SUB(&diff, &md->last); - im->last_int = htonl(diff.l_ui); - im->restr = htonl((u_int32)md->flags); - im->count = htonl((u_int32)md->count); - if (IS_IPV6(&md->rmtadr)) { - if (!client_v6_capable) - continue; - im->addr6 = SOCK_ADDR6(&md->rmtadr); - im->v6_flag = 1; - im->daddr6 = SOCK_ADDR6(&md->lcladr->sin); - } else { - im->addr = NSRCADR(&md->rmtadr); - if (client_v6_capable) - im->v6_flag = 0; - if (MDF_BCAST == md->cast_flags) - im->daddr = NSRCADR(&md->lcladr->bcast); - else if (md->cast_flags) { - im->daddr = NSRCADR(&md->lcladr->sin); - if (!im->daddr) - im->daddr = NSRCADR(&md->lcladr->bcast); - } else - im->daddr = 4; - } - im->flags = htonl(md->cast_flags); - im->port = NSRCPORT(&md->rmtadr); - im->mode = PKT_MODE(md->vn_mode); - im->version = PKT_VERSION(md->vn_mode); - im = (struct info_monitor_1 *)more_pkt(); - count++; - if (NULL == im || count >= 600) - break; - ITER_DLIST_END() - - flush_pkt(); -} /* * Module entry points and the flags they correspond with diff --git a/ntpd/ntp_scanner.c b/ntpd/ntp_scanner.c index 9f1752399..5a8827886 100644 --- a/ntpd/ntp_scanner.c +++ b/ntpd/ntp_scanner.c @@ -36,6 +36,7 @@ #define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */ char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */ +u_int32 conf_file_sum; /* Simple sum of characters read */ extern int input_from_file; @@ -127,6 +128,8 @@ FGETC( while (EOF != ch && (CHAR_MIN > ch || ch > CHAR_MAX)); if (EOF != ch) { + if (input_from_file) + conf_file_sum += (u_char)ch; ++stream->col_no; if (ch == '\n') { stream->prev_line_col_no = stream->col_no; @@ -148,6 +151,8 @@ UNGETC( struct FILE_INFO *stream ) { + if (input_from_file) + conf_file_sum -= (u_char)ch; if (ch == '\n') { stream->col_no = stream->prev_line_col_no; stream->prev_line_col_no = -1; diff --git a/ntpq/ntpq-subs.c b/ntpq/ntpq-subs.c index 2df0e4f94..21876635f 100644 --- a/ntpq/ntpq-subs.c +++ b/ntpq/ntpq-subs.c @@ -2256,11 +2256,13 @@ collect_mru_list( int c_mru_l_rc; /* this function's return code */ u_char got; /* MRU_GOT_* bits */ const char ts_fmt[] = "0x%08x.%08x"; + const char nonce_eq[] = "nonce="; size_t cb; mru *mon; mru *head; mru *recent; int list_complete; + char nonce[128]; char buf[128]; char req_buf[CTL_MAX_DATA_LEN]; char *req; @@ -2286,6 +2288,31 @@ collect_mru_list( u_short hash; mru *unlinked; + /* + * Retrieve a nonce specific to this client to demonstrate to + * ntpd that we're capable of receiving responses to our source + * IP address, and thereby unlikely to be forging the source. + */ + qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus, + &rsize, &rdata); + if (qres) { + fprintf(stderr, "nonce request failed\n"); + return FALSE; + } + + if (strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) { + fprintf(stderr, "unexpected nonce response format: %.*s\n", + rsize, rdata); + return FALSE; + } + strncpy(nonce, rdata + sizeof(nonce_eq) - 1, sizeof(nonce)); + chars = strlen(nonce); + while (chars > 0 && + ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) { + chars--; + nonce[chars] = '\0'; + } + mru_count = 0; INIT_DLIST(mru_list, mlink); cb = NTP_HASH_SIZE * sizeof(*hash_table); @@ -2304,11 +2331,12 @@ collect_mru_list( memset(&last_older, 0, sizeof(last_older)); limit = min(3 * MAXFRAGS, ntpd_row_limit); - snprintf(req_buf, sizeof(req_buf), "limit=%d%s", limit, parms); + snprintf(req_buf, sizeof(req_buf), "nonce=%s, limit=%d%s", + nonce, limit, parms); while (TRUE) { if (debug) - fprintf(stderr, "READ_MRU: %s\n", req_buf); + fprintf(stderr, "READ_MRU parms: %s\n", req_buf); qres = doqueryex(CTL_OP_READ_MRU, 0, 0, strlen(req_buf), req_buf, &rstatus, &rsize, &rdata, TRUE); @@ -2334,7 +2362,7 @@ collect_mru_list( NTP_INSIST(unlinked == recent); free(recent); } - } else if (CERR_BADVALUE == qres /* temp -> */ || CERR_BADFMT == qres /* <- temp */) { + } else if (CERR_BADVALUE == qres) { /* ntpd has lower cap on row limit */ ntpd_row_limit--; limit = min(limit, ntpd_row_limit); @@ -2474,7 +2502,11 @@ collect_mru_list( break; case 'n': - if (strcmp(tag, "now") || + if (!strcmp(tag, "nonce")) { + strncpy(nonce, val, + sizeof(nonce)); + break; /* case */ + } else if (strcmp(tag, "now") || 2 != sscanf(val, ts_fmt, &pnow->l_ui, &pnow->l_uf)) @@ -2552,7 +2584,8 @@ collect_mru_list( req = req_buf; req_end = req_buf + sizeof(req_buf); #define REQ_ROOM (req_end - req) - snprintf(req, REQ_ROOM, "limit=%d%s", limit, parms); + snprintf(req, REQ_ROOM, "nonce=%s, limit=%d%s", nonce, + limit, parms); req += strlen(req); for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);