diff --git a/src/usr.sbin/ntpd/Makefile b/src/usr.sbin/ntpd/Makefile index 96664576..0fedc671 100644 --- a/src/usr.sbin/ntpd/Makefile +++ b/src/usr.sbin/ntpd/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.4 2004/06/02 10:08:59 henning Exp $ +# $OpenBSD: Makefile,v 1.5 2004/06/17 19:17:48 henning Exp $ .PATH: ${.CURDIR}/.. PROG= ntpd SRCS= ntpd.c buffer.c log.c imsg.c ntp.c ntp_msg.c parse.y config.c \ - server.c + server.c client.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/src/usr.sbin/ntpd/client.c b/src/usr.sbin/ntpd/client.c new file mode 100644 index 00000000..c742d1d1 --- /dev/null +++ b/src/usr.sbin/ntpd/client.c @@ -0,0 +1,129 @@ +/* $OpenBSD: client.c,v 1.1 2004/06/17 19:17:48 henning Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * Copyright (c) 2004 Alexander Guy + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "ntpd.h" + +int +client_peer_init(struct ntp_peer *p) +{ + struct sockaddr_in *sa_in; + struct sockaddr_in6 *sa_in6; + + if ((p->query = calloc(1, sizeof(struct ntp_msg))) == NULL) + fatal("client_query calloc"); + + switch (p->ss.ss_family) { + case AF_INET: + sa_in = (struct sockaddr_in *)&p->ss; + if (ntohs(sa_in->sin_port) == 0) + sa_in->sin_port = htons(123); + break; + case AF_INET6: + sa_in6 = (struct sockaddr_in6 *)&p->ss; + if (ntohs(sa_in6->sin6_port) == 0) + sa_in6->sin6_port = htons(123); + break; + default: + fatal("king bula sez: wrong AF in client_peer_init"); + /* not reached */ + } + + if ((p->query->fd = socket(p->ss.ss_family, SOCK_DGRAM, 0)) == -1) + fatal("client_query socket"); + + p->query->msg.status = MODE_CLIENT | (NTP_VERSION << 3); + p->state = STATE_NONE; + p->next = time(NULL); + + return (0); +} + +int +client_query(struct ntp_peer *p) +{ + /* + * Send out a random 64-bit number as our transmit time. The NTP + * server will copy said number into the originate field on the + * response that it sends us. This is totally legal per the SNTP spec. + * + * The impact of this is two fold: we no longer send out the current + * system time for the world to see (which may aid an attacker), and + * it gives us a (not very secure) way of knowing that we're not + * getting spoofed by an attacker that can't capture our traffic + * but can spoof packets from the NTP server we're communicating with. + * + * Save the real transmit timestamp locally. + */ + + p->query->msg.xmttime.int_part = arc4random(); + p->query->msg.xmttime.fraction = arc4random(); + get_ts(&p->query->xmttime); + + ntp_sendmsg(p->query->fd, (struct sockaddr *)&p->ss, &p->query->msg, + NTP_MSGSIZE_NOAUTH, 0); + p->state = STATE_QUERY_SENT; + p->next = 0; + p->deadline = time(NULL) + MAX_QUERYTIME; + + return (0); +} + +int +client_dispatch(struct ntp_peer *p) +{ + struct sockaddr_storage fsa; + socklen_t fsa_len; + char buf[NTP_MSGSIZE]; + ssize_t size; + struct ntp_msg msg; + struct l_fixedpt rtt, t; + + fsa_len = sizeof(fsa); + if ((size = recvfrom(p->query->fd, &buf, sizeof(buf), 0, + (struct sockaddr *)&fsa, &fsa_len)) == -1) + fatal("recvfrom"); + + ntp_getmsg(buf, size, &msg); + + if (msg.orgtime.int_part != p->query->msg.xmttime.int_part || + msg.orgtime.fraction != p->query->msg.xmttime.fraction) { + log_warn("received packet without correct cookie, discarding"); + return (0); + } + +log_debug("reply received"); + + /* XXX parse */ + get_ts(&t); + rtt.int_part = (t.int_part - p->query->xmttime.int_part) - + (msg.rectime.int_part - msg.xmttime.int_part); + + p->state = STATE_REPLY_RECEIVED; + p->next = time(NULL) + QUERY_INTERVAL; + p->deadline = 0; + + return (0); +} diff --git a/src/usr.sbin/ntpd/ntp.c b/src/usr.sbin/ntpd/ntp.c index 55538d58..79233742 100644 --- a/src/usr.sbin/ntpd/ntp.c +++ b/src/usr.sbin/ntpd/ntp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ntp.c,v 1.6 2004/06/02 10:08:59 henning Exp $ */ +/* $OpenBSD: ntp.c,v 1.7 2004/06/17 19:17:48 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -52,12 +52,15 @@ ntp_sighdlr(int sig) pid_t ntp_main(int pipe_prnt[2], struct ntpd_conf *conf) { - int nfds, i, j; + int nfds, i, j, idx_peers, timeout; pid_t pid; struct pollfd pfd[OPEN_MAX]; struct passwd *pw; struct servent *se; struct listen_addr *la; + struct ntp_peer *p; + struct ntp_peer *idx2peer[OPEN_MAX]; + time_t nextaction; switch (pid = fork()) { case -1: @@ -98,10 +101,15 @@ ntp_main(int pipe_prnt[2], struct ntpd_conf *conf) close(pipe_prnt[0]); imsg_init(&ibuf_main, pipe_prnt[1]); + TAILQ_FOREACH(p, &conf->ntp_peers, entry) + client_peer_init(p); + log_info("ntp engine ready"); while (ntp_quit == 0) { bzero(&pfd, sizeof(pfd)); + bzero(idx2peer, sizeof(idx2peer)); + nextaction = time(NULL) + 240; pfd[PFD_PIPE_MAIN].fd = ibuf_main.fd; pfd[PFD_PIPE_MAIN].events = POLLIN; if (ibuf_main.w.queued > 0) @@ -112,14 +120,38 @@ ntp_main(int pipe_prnt[2], struct ntpd_conf *conf) pfd[i].fd = la->fd; pfd[i].events = POLLIN; i++; + if (i > OPEN_MAX) + fatal("i > OPEN_MAX"); + } + + + idx_peers = i; + TAILQ_FOREACH(p, &conf->ntp_peers, entry) { + if (p->next > 0 && p->next < nextaction) + nextaction = p->next; + if ((p->next > 0 && p->next <= time(NULL)) || + (p->deadline > 0 && p->deadline <= time(NULL))) + client_query(p); + + if (p->state == STATE_QUERY_SENT) { + pfd[i].fd = p->query->fd; + pfd[i].events = POLLIN; + idx2peer[i - idx_peers] = p; + i++; + if (i > OPEN_MAX) + fatal("i > OPEN_MAX"); + } } - if ((nfds = poll(pfd, i, INFTIM)) == -1) + timeout = nextaction - time(NULL); + if (timeout < 0) + timeout = 0; + + if ((nfds = poll(pfd, i, timeout * 1000)) == -1) if (errno != EINTR) { log_warn("poll error"); ntp_quit = 1; } - if (nfds > 0 && (pfd[PFD_PIPE_MAIN].revents & POLLOUT)) if (msgbuf_write(&ibuf_main.w) < 0) { log_warn("pipe write error (to parent)"); @@ -132,12 +164,20 @@ ntp_main(int pipe_prnt[2], struct ntpd_conf *conf) ntp_quit = 1; } - for (j = 1; nfds > 0 && j < i; j++) + for (j = 1; nfds > 0 && j < idx_peers; j++) if (pfd[j].revents & POLLIN) { nfds--; if (ntp_dispatch(pfd[j].fd) == -1) ntp_quit = 1; } + + for (; nfds > 0 && j < i; j++) + if (pfd[j].revents & POLLIN) { + nfds--; + if (client_dispatch(idx2peer[j - idx_peers]) == + -1) + ntp_quit = 1; + } } msgbuf_write(&ibuf_main.w); diff --git a/src/usr.sbin/ntpd/ntp.h b/src/usr.sbin/ntpd/ntp.h index 461de9d8..8a63c006 100644 --- a/src/usr.sbin/ntpd/ntp.h +++ b/src/usr.sbin/ntpd/ntp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ntp.h,v 1.4 2004/06/05 12:29:15 alexander Exp $ */ +/* $OpenBSD: ntp.h,v 1.5 2004/06/17 19:17:48 henning Exp $ */ /* * Copyright (c) 2004 Henning Brauer @@ -109,6 +109,12 @@ struct ntp_msg { u_int8_t digest[NTP_DIGESTSIZE]; }; +struct ntp_query { + int fd; + struct ntp_msg msg; + struct l_fixedpt xmttime; +}; + /* * Leap Second Codes (high order two bits) */ @@ -138,4 +144,8 @@ struct ntp_msg { #define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ -#endif /* _NTP_H_ */ +#define NTP_VERSION 4 +#define MAX_QUERYTIME 30 /* max seconds a single query might take */ +#define QUERY_INTERVAL 60 /* sync with peers every n seconds */ + +#endif /* _NTP_H_ */ diff --git a/src/usr.sbin/ntpd/ntpd.c b/src/usr.sbin/ntpd/ntpd.c index a60cb153..7d6eadac 100644 --- a/src/usr.sbin/ntpd/ntpd.c +++ b/src/usr.sbin/ntpd/ntpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ntpd.c,v 1.4 2004/06/01 21:58:08 henning Exp $ */ +/* $OpenBSD: ntpd.c,v 1.5 2004/06/17 19:17:48 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -91,6 +91,7 @@ main(int argc, char *argv[]) bzero(&conf, sizeof(conf)); TAILQ_INIT(&conf.listen_addrs); + TAILQ_INIT(&conf.ntp_peers); log_init(1); /* log to stderr until daemonized */ diff --git a/src/usr.sbin/ntpd/ntpd.h b/src/usr.sbin/ntpd/ntpd.h index 44e75c1f..71d309cb 100644 --- a/src/usr.sbin/ntpd/ntpd.h +++ b/src/usr.sbin/ntpd/ntpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ntpd.h,v 1.5 2004/06/05 12:29:15 alexander Exp $ */ +/* $OpenBSD: ntpd.h,v 1.6 2004/06/17 19:17:48 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -34,14 +34,30 @@ #define NTPD_OPT_VERBOSE 0x0001 #define NTPD_OPT_VERBOSE2 0x0002 +enum client_state { + STATE_NONE, + STATE_QUERY_SENT, + STATE_REPLY_RECEIVED +}; + struct listen_addr { TAILQ_ENTRY(listen_addr) entry; struct sockaddr_storage sa; int fd; }; +struct ntp_peer { + TAILQ_ENTRY(ntp_peer) entry; + struct sockaddr_storage ss; + struct ntp_query *query; + enum client_state state; + time_t next; + time_t deadline; +}; + struct ntpd_conf { TAILQ_HEAD(listen_addrs, listen_addr) listen_addrs; + TAILQ_HEAD(ntp_peers, ntp_peer) ntp_peers; u_int8_t opts; }; @@ -103,6 +119,7 @@ void log_info(const char *, ...); void log_debug(const char *, ...); void fatal(const char *); void fatalx(const char *); +const char * log_sockaddr(struct sockaddr *); /* buffer.c */ struct buf *buf_open(ssize_t); @@ -144,3 +161,8 @@ int ntp_sendmsg(int, struct sockaddr *, struct ntp_msg *, ssize_t, int); /* server.c */ int setup_listeners(struct servent *, struct ntpd_conf *); int ntp_reply(int, struct sockaddr *, struct ntp_msg *, int); + +/* client.c */ +int client_peer_init(struct ntp_peer *); +int client_query(struct ntp_peer *); +int client_dispatch(struct ntp_peer *); diff --git a/src/usr.sbin/ntpd/parse.y b/src/usr.sbin/ntpd/parse.y index 18e3ab37..da13af04 100644 --- a/src/usr.sbin/ntpd/parse.y +++ b/src/usr.sbin/ntpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.1 2004/06/01 21:58:09 henning Exp $ */ +/* $OpenBSD: parse.y,v 1.2 2004/06/17 19:17:48 henning Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -77,6 +77,7 @@ typedef struct { %} %token LISTEN ON +%token SERVER %token ERROR %token STRING %type number @@ -130,9 +131,23 @@ conf_main : LISTEN ON address { NULL) fatal("parse conf_main listen on calloc"); + la->fd = -1; memcpy(&la->sa, &$3, sizeof(struct sockaddr_storage)); TAILQ_INSERT_TAIL(&conf->listen_addrs, la, entry); } + | SERVER address { + struct ntp_peer *p; + + if (!TAILQ_EMPTY(&conf->ntp_peers)) { + yyerror("king bula sez: only on remote server " + "supported for now"); + YYERROR; + } + if ((p = calloc(1, sizeof(struct listen_addr))) == NULL) + fatal("parse conf_mail server on calloc"); + memcpy(&p->ss, &$2, sizeof(struct sockaddr_storage)); + TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry); + } ; address : STRING { @@ -192,7 +207,8 @@ lookup(char *s) /* this has to be sorted always */ static const struct keywords keywords[] = { { "listen", LISTEN}, - { "on", ON} + { "on", ON}, + { "server", SERVER} }; const struct keywords *p; @@ -402,12 +418,14 @@ parse_config(char *filename, struct ntpd_conf *xconf) { struct sym *sym, *next; struct listen_addr *la; + struct ntp_peer *p; if ((conf = calloc(1, sizeof(struct ntpd_conf))) == NULL) fatal(NULL); lineno = 1; errors = 0; TAILQ_INIT(&conf->listen_addrs); + TAILQ_INIT(&conf->ntp_peers); if ((fin = fopen(filename, "r")) == NULL) { log_warn("%s", filename); @@ -444,6 +462,16 @@ parse_config(char *filename, struct ntpd_conf *xconf) TAILQ_INSERT_TAIL(&xconf->listen_addrs, la, entry); } + while ((p = TAILQ_FIRST(&xconf->ntp_peers)) != NULL) { + TAILQ_REMOVE(&xconf->ntp_peers, p, entry); + free(p); + } + + while ((p = TAILQ_FIRST(&conf->ntp_peers)) != NULL) { + TAILQ_REMOVE(&conf->ntp_peers, p, entry); + TAILQ_INSERT_TAIL(&xconf->ntp_peers, p, entry); + } + free(conf); return (errors ? -1 : 0);