xref: /openbsd/usr.sbin/rdate/ntp.c (revision 2d44acdf)
1 /*	$OpenBSD: ntp.c,v 1.37 2023/11/12 18:53:22 otto Exp $	*/
2 
3 /*
4  * Copyright (c) 1996, 1997 by N.M. Maclaren. All rights reserved.
5  * Copyright (c) 1996, 1997 by University of Cambridge. All rights reserved.
6  * Copyright (c) 2002 by Thorsten "mirabile" Glaser.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the author nor the university may be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <float.h>
42 #include <limits.h>
43 #include <math.h>
44 #include <netdb.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <poll.h>
51 #include <unistd.h>
52 
53 #include "ntpleaps.h"
54 
55 /*
56  * NTP definitions.  Note that these assume 8-bit bytes - sigh.  There
57  * is little point in parameterising everything, as it is neither
58  * feasible nor useful.  It would be very useful if more fields could
59  * be defined as unspecified.  The NTP packet-handling routines
60  * contain a lot of extra assumptions.
61  */
62 
63 #define JAN_1970   2208988800.0		/* 1970 - 1900 in seconds */
64 #define NTP_SCALE  4294967296.0		/* 2^32, of course! */
65 
66 #define NTP_MODE_CLIENT       3		/* NTP client mode */
67 #define NTP_MODE_SERVER       4		/* NTP server mode */
68 #define NTP_VERSION           4		/* The current version */
69 #define NTP_VERSION_MIN       1		/* The minimum valid version */
70 #define NTP_VERSION_MAX       4		/* The maximum valid version */
71 #define NTP_STRATUM_MAX      14		/* The maximum valid stratum */
72 #define NTP_INSANITY     3600.0		/* Errors beyond this are hopeless */
73 
74 #define NTP_PACKET_MIN       48		/* Without authentication */
75 #define NTP_PACKET_MAX       68		/* With authentication (ignored) */
76 
77 #define NTP_DISP_FIELD        8		/* Offset of dispersion field */
78 #define NTP_REFERENCE        16		/* Offset of reference timestamp */
79 #define NTP_ORIGINATE        24		/* Offset of originate timestamp */
80 #define NTP_RECEIVE          32		/* Offset of receive timestamp */
81 #define NTP_TRANSMIT         40		/* Offset of transmit timestamp */
82 
83 #define STATUS_NOWARNING      0		/* No Leap Indicator */
84 #define STATUS_LEAPHIGH       1		/* Last Minute Has 61 Seconds */
85 #define STATUS_LEAPLOW        2		/* Last Minute Has 59 Seconds */
86 #define STATUS_ALARM          3		/* Server Clock Not Synchronized */
87 
88 #define MAX_QUERIES         25
89 #define MAX_DELAY           15
90 
91 #define MILLION_L    1000000l		/* For conversion to/from timeval */
92 #define MILLION_D       1.0e6		/* Must be equal to MILLION_L */
93 
94 /*
95  * The era we're in if we have no reason to assume otherwise.
96  * If unpack_ntp() sees a small offset the era is is assumed to be
97  * NTP_ERA + 1.
98  * Once the actual year is well into era 1, (after 2036) define NTP_ERA to 1
99  * and adapt (disable) the increments in unpack_ntp().
100  * Once more than half of era 1 has elapsed (after 2104), re-inroduce the test
101  * to move to era 2 if offset is small, repeat for each half era.
102  */
103 #define NTP_ERA         0
104 
105 #define SECS_IN_ERA     (UINT32_MAX + 1ULL)
106 
107 
108 struct ntp_data {
109 	u_char		status;
110 	u_char		version;
111 	u_char		mode;
112 	u_char		stratum;
113 	double		receive;
114 	double		transmit;
115 	double		current;
116 	u_int64_t	recvck;
117 
118 	/* Local State */
119 	double		originate;
120 	u_int64_t	xmitck;
121 };
122 
123 void	ntp_client(const char *, int, struct timeval *, struct timeval *, int);
124 int	sync_ntp(int, const struct sockaddr *, double *, double *);
125 int	write_packet(int, struct ntp_data *);
126 int	read_packet(int, struct ntp_data *, double *, double *);
127 void	unpack_ntp(struct ntp_data *, u_char *);
128 double	current_time(double);
129 void	create_timeval(double, struct timeval *, struct timeval *);
130 
131 #ifdef DEBUG
132 void	print_packet(const struct ntp_data *);
133 #endif
134 
135 int	corrleaps;
136 
137 void
ntp_client(const char * hostname,int family,struct timeval * new,struct timeval * adjust,int leapflag)138 ntp_client(const char *hostname, int family, struct timeval *new,
139     struct timeval *adjust, int leapflag)
140 {
141 	struct addrinfo hints, *res0, *res;
142 	double offset, error;
143 	int accept = 0, ret, s, ierror;
144 
145 	memset(&hints, 0, sizeof(hints));
146 	hints.ai_family = family;
147 	hints.ai_socktype = SOCK_DGRAM;
148 	ierror = getaddrinfo(hostname, "ntp", &hints, &res0);
149 	if (ierror) {
150 		errx(1, "%s: %s", hostname, gai_strerror(ierror));
151 		/*NOTREACHED*/
152 	}
153 
154 	if (pledge("stdio inet", NULL) == -1)
155 		err(1, "pledge");
156 
157 	corrleaps = leapflag;
158 	if (corrleaps)
159 		ntpleaps_init();
160 
161 	s = -1;
162 	for (res = res0; res; res = res->ai_next) {
163 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
164 		if (s == -1)
165 			continue;
166 
167 		ret = sync_ntp(s, res->ai_addr, &offset, &error);
168 		if (ret < 0) {
169 #ifdef DEBUG
170 			fprintf(stderr, "try the next address\n");
171 #endif
172 			close(s);
173 			s = -1;
174 			continue;
175 		}
176 
177 		accept++;
178 		break;
179 	}
180 	freeaddrinfo(res0);
181 
182 #ifdef DEBUG
183 	fprintf(stderr, "Correction: %.6f +/- %.6f\n", offset, error);
184 #endif
185 
186 	if (accept < 1)
187 		errx(1, "Unable to get a reasonable time estimate");
188 
189 	create_timeval(offset, new, adjust);
190 }
191 
192 int
sync_ntp(int fd,const struct sockaddr * peer,double * offset,double * error)193 sync_ntp(int fd, const struct sockaddr *peer, double *offset, double *error)
194 {
195 	int accepts = 0, rejects = 0;
196 	int delay = MAX_DELAY, ret;
197 	double deadline;
198 	double a, b, x, y;
199 	double minerr = 0.1;		/* Maximum ignorable variation */
200 	struct ntp_data data;
201 
202 	deadline = current_time(JAN_1970) + delay;
203 	*offset = 0.0;
204 	*error = NTP_INSANITY;
205 
206 	if (connect(fd, peer, SA_LEN(peer)) == -1) {
207 		warn("Failed to connect to server");
208 		return (-1);
209 	}
210 
211 	while (accepts < MAX_QUERIES) {
212 		memset(&data, 0, sizeof(data));
213 
214 		if (current_time(JAN_1970) > deadline) {
215 			warnx("Not enough valid responses received in time");
216 			return (-1);
217 		}
218 
219 		if (write_packet(fd, &data) < 0)
220 			return (-1);
221 
222 		ret = read_packet(fd, &data, &x, &y);
223 
224 		if (ret < 0)
225 			return (-1);
226 		else if (ret > 0) {
227 #ifdef DEBUG
228 			print_packet(&data);
229 #endif
230 
231 			if (++rejects > MAX_QUERIES) {
232 				warnx("Too many bad or lost packets");
233 				return (-1);
234 			} else
235 				continue;
236 		} else
237 			++accepts;
238 
239 #ifdef DEBUG
240 		fprintf(stderr, "Offset: %.6f +/- %.6f\n", x, y);
241 #endif
242 
243 		if ((a = x - *offset) < 0.0)
244 			a = -a;
245 		if (accepts <= 1)
246 			a = 0.0;
247 		b = *error + y;
248 		if (y < *error) {
249 			*offset = x;
250 			*error = y;
251 		}
252 
253 #ifdef DEBUG
254 		fprintf(stderr, "Best: %.6f +/- %.6f\n", *offset, *error);
255 #endif
256 
257 		if (a > b) {
258 			warnx("Inconsistent times received from NTP server");
259 			return (-1);
260 		}
261 
262 		if ((data.status & STATUS_ALARM) == STATUS_ALARM) {
263 			warnx("Ignoring NTP server with alarm flag set");
264 			return (-1);
265 		}
266 
267 		if (*error <= minerr)
268 			break;
269 	}
270 
271 	return (accepts);
272 }
273 
274 /* Send out NTP packet. */
275 int
write_packet(int fd,struct ntp_data * data)276 write_packet(int fd, struct ntp_data *data)
277 {
278 	u_char	packet[NTP_PACKET_MIN];
279 	ssize_t	length;
280 
281 	memset(packet, 0, sizeof(packet));
282 
283 	packet[0] = (NTP_VERSION << 3) | (NTP_MODE_CLIENT);
284 
285 	arc4random_buf(&data->xmitck, sizeof(data->xmitck));
286 
287 	/*
288 	 * Send out a random 64-bit number as our transmit time.  The NTP
289 	 * server will copy said number into the originate field on the
290 	 * response that it sends us.  This is totally legal per the SNTP spec.
291 	 *
292 	 * The impact of this is two fold: we no longer send out the current
293 	 * system time for the world to see (which may aid an attacker), and
294 	 * it gives us a (not very secure) way of knowing that we're not
295 	 * getting spoofed by an attacker that can't capture our traffic
296 	 * but can spoof packets from the NTP server we're communicating with.
297 	 *
298 	 * No endian concerns here.  Since we're running as a strict
299 	 * unicast client, we don't have to worry about anyone else finding
300 	 * the transmit field intelligible.
301 	 */
302 
303 	bcopy(&data->xmitck, (packet + NTP_TRANSMIT), sizeof(data->xmitck));
304 
305 	data->originate = current_time(JAN_1970);
306 
307 	length = write(fd, packet, sizeof(packet));
308 
309 	if (length != sizeof(packet)) {
310 		warn("Unable to send NTP packet to server");
311 		return (-1);
312 	}
313 
314 	return (0);
315 }
316 
317 /*
318  * Check the packet and work out the offset and optionally the error.
319  * Note that this contains more checking than xntp does. Return 0 for
320  * success, 1 for failure. Note that it must not change its arguments
321  * if it fails.
322  */
323 int
read_packet(int fd,struct ntp_data * data,double * off,double * error)324 read_packet(int fd, struct ntp_data *data, double *off, double *error)
325 {
326 	u_char	receive[NTP_PACKET_MAX];
327 	struct	pollfd pfd[1];
328 	double	x, y;
329 	int	length, r;
330 
331 	pfd[0].fd = fd;
332 	pfd[0].events = POLLIN;
333 
334 retry:
335 	r = poll(pfd, 1, 1000 * MAX_DELAY / MAX_QUERIES);
336 	if (r == -1) {
337 		if (errno == EINTR)
338 			goto retry;
339 		warn("select");
340 		return (r);
341 	}
342 
343 	if (r != 1)
344 		return (1);
345 	if ((pfd[0].revents & POLLIN) == 0)
346 		return (1);
347 
348 	length = read(fd, receive, NTP_PACKET_MAX);
349 	if (length == -1) {
350 		warn("Unable to receive NTP packet from server");
351 		return (-1);
352 	}
353 
354 	if (length < NTP_PACKET_MIN || length > NTP_PACKET_MAX) {
355 		warnx("Invalid NTP packet size, packet rejected");
356 		return (1);
357 	}
358 
359 	unpack_ntp(data, receive);
360 
361 	if (data->recvck != data->xmitck) {
362 		warnx("Invalid cookie received, packet rejected");
363 		return (1);
364 	}
365 
366 	if (data->version < NTP_VERSION_MIN ||
367 	    data->version > NTP_VERSION_MAX) {
368 		warnx("Received NTP version %u, need %u or lower",
369 		    data->version, NTP_VERSION);
370 		return (1);
371 	}
372 
373 	if (data->mode != NTP_MODE_SERVER) {
374 		warnx("Invalid NTP server mode, packet rejected");
375 		return (1);
376 	}
377 
378 	if (data->stratum > NTP_STRATUM_MAX) {
379 		warnx("Invalid stratum received, packet rejected");
380 		return (1);
381 	}
382 
383 	if (data->transmit == 0.0) {
384 		warnx("Server clock invalid, packet rejected");
385 		return (1);
386 	}
387 
388 	x = data->receive - data->originate;
389 	y = data->transmit - data->current;
390 
391 	*off = (x + y) / 2;
392 	*error = x - y;
393 
394 	x = (data->current - data->originate) / 2;
395 
396 	if (x > *error)
397 		*error = x;
398 
399 	return (0);
400 }
401 
402 /*
403  * Unpack the essential data from an NTP packet, bypassing struct
404  * layout and endian problems.  Note that it ignores fields irrelevant
405  * to SNTP.
406  */
407 void
unpack_ntp(struct ntp_data * data,u_char * packet)408 unpack_ntp(struct ntp_data *data, u_char *packet)
409 {
410 	int i, era;
411 	double d;
412 
413 	data->current = current_time(JAN_1970);
414 
415 	data->status = (packet[0] >> 6);
416 	data->version = (packet[0] >> 3) & 0x07;
417 	data->mode = packet[0] & 0x07;
418 	data->stratum = packet[1];
419 
420 	for (i = 0, d = 0.0; i < 8; ++i)
421 		d = 256.0*d+packet[NTP_RECEIVE+i];
422 
423 	era = NTP_ERA;
424 	if (packet[NTP_RECEIVE] <= 127)
425 		era++;
426 	data->receive = d / NTP_SCALE;
427 	data->receive += era * SECS_IN_ERA;
428 
429 	for (i = 0, d = 0.0; i < 8; ++i)
430 		d = 256.0*d+packet[NTP_TRANSMIT+i];
431 
432 	era = NTP_ERA;
433 	if (packet[NTP_TRANSMIT] <= 127)
434 		era++;
435 	data->transmit = d / NTP_SCALE;
436 	data->transmit += era * SECS_IN_ERA;
437 
438 	/* See write_packet for why this isn't an endian problem. */
439 	bcopy((packet + NTP_ORIGINATE), &data->recvck, sizeof(data->recvck));
440 }
441 
442 /*
443  * Get the current UTC time in seconds since the Epoch plus an offset
444  * (usually the time from the beginning of the century to the Epoch)
445  */
446 double
current_time(double offset)447 current_time(double offset)
448 {
449 	struct timeval current;
450 	u_int64_t t;
451 
452 	if (gettimeofday(&current, NULL))
453 		err(1, "Could not get local time of day");
454 
455 	/*
456 	 * At this point, current has the current TAI time.
457 	 * Now subtract leap seconds to set the posix tick.
458 	 */
459 
460 	t = SEC_TO_TAI64(current.tv_sec);
461 	if (corrleaps)
462 		ntpleaps_sub(&t);
463 
464 	return (offset + TAI64_TO_SEC(t) + 1.0e-6 * current.tv_usec);
465 }
466 
467 /*
468  * Change offset into current UTC time. This is portable, even if
469  * struct timeval uses an unsigned long for tv_sec.
470  */
471 void
create_timeval(double difference,struct timeval * new,struct timeval * adjust)472 create_timeval(double difference, struct timeval *new, struct timeval *adjust)
473 {
474 	struct timeval old;
475 	long long n;
476 
477 	/* Start by converting to timeval format. Note that we have to
478 	 * cater for negative, unsigned values. */
479 	if ((n = (long long) difference) > difference)
480 		--n;
481 	adjust->tv_sec = n;
482 	adjust->tv_usec = (long) (MILLION_D * (difference-n));
483 	errno = 0;
484 	if (gettimeofday(&old, NULL))
485 		err(1, "Could not get local time of day");
486 	new->tv_sec = old.tv_sec + adjust->tv_sec;
487 	new->tv_usec = (n = (long) old.tv_usec + (long) adjust->tv_usec);
488 
489 	if (n < 0) {
490 		new->tv_usec += MILLION_L;
491 		--new->tv_sec;
492 	} else if (n >= MILLION_L) {
493 		new->tv_usec -= MILLION_L;
494 		++new->tv_sec;
495 	}
496 }
497 
498 #ifdef DEBUG
499 void
print_packet(const struct ntp_data * data)500 print_packet(const struct ntp_data *data)
501 {
502 	printf("status:      %u\n", data->status);
503 	printf("version:     %u\n", data->version);
504 	printf("mode:        %u\n", data->mode);
505 	printf("stratum:     %u\n", data->stratum);
506 	printf("originate:   %f\n", data->originate);
507 	printf("receive:     %f\n", data->receive);
508 	printf("transmit:    %f\n", data->transmit);
509 	printf("current:     %f\n", data->current);
510 	printf("xmitck:      0x%0llX\n", data->xmitck);
511 	printf("recvck:      0x%0llX\n", data->recvck);
512 };
513 #endif
514