aboutsummaryrefslogtreecommitdiff
path: root/networking/ntpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/ntpd.c')
-rw-r--r--networking/ntpd.c266
1 files changed, 241 insertions, 25 deletions
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 1ebdc34c3..354bff897 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -62,13 +62,19 @@
62//config: help 62//config: help
63//config: Make ntpd look in /etc/ntp.conf for peers. Only "server address" 63//config: Make ntpd look in /etc/ntp.conf for peers. Only "server address"
64//config: is supported. 64//config: is supported.
65//config:config FEATURE_NTP_AUTH
66//config: bool "Support md5/sha1 message authentication codes"
67//config: default n
68//config: depends on NTPD
65 69
66//applet:IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) 70//applet:IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
67 71
68//kbuild:lib-$(CONFIG_NTPD) += ntpd.o 72//kbuild:lib-$(CONFIG_NTPD) += ntpd.o
69 73
70//usage:#define ntpd_trivial_usage 74//usage:#define ntpd_trivial_usage
71//usage: "[-dnqNw"IF_FEATURE_NTPD_SERVER("l -I IFACE")"] [-S PROG] [-p PEER]..." 75//usage: "[-dnqNw"IF_FEATURE_NTPD_SERVER("l] [-I IFACE")"] [-S PROG]"
76//usage: IF_NOT_FEATURE_NTP_AUTH(" [-p PEER]...")
77//usage: IF_FEATURE_NTP_AUTH(" [-k KEYFILE] [-p [keyno:N:]PEER]...")
72//usage:#define ntpd_full_usage "\n\n" 78//usage:#define ntpd_full_usage "\n\n"
73//usage: "NTP client/server\n" 79//usage: "NTP client/server\n"
74//usage: "\n -d Verbose (may be repeated)" 80//usage: "\n -d Verbose (may be repeated)"
@@ -76,8 +82,16 @@
76//usage: "\n -q Quit after clock is set" 82//usage: "\n -q Quit after clock is set"
77//usage: "\n -N Run at high priority" 83//usage: "\n -N Run at high priority"
78//usage: "\n -w Do not set time (only query peers), implies -n" 84//usage: "\n -w Do not set time (only query peers), implies -n"
79//usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins" 85//usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 min"
86//usage: IF_NOT_FEATURE_NTP_AUTH(
80//usage: "\n -p PEER Obtain time from PEER (may be repeated)" 87//usage: "\n -p PEER Obtain time from PEER (may be repeated)"
88//usage: )
89//usage: IF_FEATURE_NTP_AUTH(
90//usage: "\n -k FILE Key file (ntp.keys compatible)"
91//usage: "\n -p [keyno:NUM:]PEER"
92//usage: "\n Obtain time from PEER (may be repeated)"
93//usage: "\n Use key NUM for authentication"
94//usage: )
81//usage: IF_FEATURE_NTPD_CONF( 95//usage: IF_FEATURE_NTPD_CONF(
82//usage: "\n If -p is not given, 'server HOST' lines" 96//usage: "\n If -p is not given, 'server HOST' lines"
83//usage: "\n from /etc/ntp.conf are used" 97//usage: "\n from /etc/ntp.conf are used"
@@ -228,14 +242,18 @@
228/* Parameter averaging constant */ 242/* Parameter averaging constant */
229#define AVG 4 243#define AVG 4
230 244
245#define MAX_KEY_NUMBER 65535
246#define KEYID_SIZE sizeof(uint32_t)
231 247
232enum { 248enum {
233 NTP_VERSION = 4, 249 NTP_VERSION = 4,
234 NTP_MAXSTRATUM = 15, 250 NTP_MAXSTRATUM = 15,
235 251
236 NTP_DIGESTSIZE = 16, 252 NTP_MD5_DIGESTSIZE = 16,
237 NTP_MSGSIZE_NOAUTH = 48, 253 NTP_MSGSIZE_NOAUTH = 48,
238 NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE), 254 NTP_MSGSIZE_MD5_AUTH = NTP_MSGSIZE_NOAUTH + KEYID_SIZE + NTP_MD5_DIGESTSIZE,
255 NTP_SHA1_DIGESTSIZE = 20,
256 NTP_MSGSIZE_SHA1_AUTH = NTP_MSGSIZE_NOAUTH + KEYID_SIZE + NTP_SHA1_DIGESTSIZE,
239 257
240 /* Status Masks */ 258 /* Status Masks */
241 MODE_MASK = (7 << 0), 259 MODE_MASK = (7 << 0),
@@ -288,7 +306,7 @@ typedef struct {
288 l_fixedpt_t m_rectime; 306 l_fixedpt_t m_rectime;
289 l_fixedpt_t m_xmttime; 307 l_fixedpt_t m_xmttime;
290 uint32_t m_keyid; 308 uint32_t m_keyid;
291 uint8_t m_digest[NTP_DIGESTSIZE]; 309 uint8_t m_digest[ENABLE_FEATURE_NTP_AUTH ? NTP_SHA1_DIGESTSIZE : NTP_MD5_DIGESTSIZE];
292} msg_t; 310} msg_t;
293 311
294typedef struct { 312typedef struct {
@@ -297,9 +315,26 @@ typedef struct {
297 double d_dispersion; 315 double d_dispersion;
298} datapoint_t; 316} datapoint_t;
299 317
318#if ENABLE_FEATURE_NTP_AUTH
319enum {
320 HASH_MD5,
321 HASH_SHA1,
322};
323typedef struct {
324 unsigned id; //try uint16_t?
325 smalluint type;
326 smalluint msg_size;
327 smalluint key_length;
328 char key[0];
329} key_entry_t;
330#endif
331
300typedef struct { 332typedef struct {
301 len_and_sockaddr *p_lsa; 333 len_and_sockaddr *p_lsa;
302 char *p_dotted; 334 char *p_dotted;
335#if ENABLE_FEATURE_NTP_AUTH
336 key_entry_t *key_entry;
337#endif
303 int p_fd; 338 int p_fd;
304 int datapoint_idx; 339 int datapoint_idx;
305 uint32_t lastpkt_refid; 340 uint32_t lastpkt_refid;
@@ -337,13 +372,14 @@ enum {
337 OPT_q = (1 << 1), 372 OPT_q = (1 << 1),
338 OPT_N = (1 << 2), 373 OPT_N = (1 << 2),
339 OPT_x = (1 << 3), 374 OPT_x = (1 << 3),
375 OPT_k = (1 << 4) * ENABLE_FEATURE_NTP_AUTH,
340 /* Insert new options above this line. */ 376 /* Insert new options above this line. */
341 /* Non-compat options: */ 377 /* Non-compat options: */
342 OPT_w = (1 << 4), 378 OPT_w = (1 << (4+ENABLE_FEATURE_NTP_AUTH)),
343 OPT_p = (1 << 5), 379 OPT_p = (1 << (5+ENABLE_FEATURE_NTP_AUTH)),
344 OPT_S = (1 << 6), 380 OPT_S = (1 << (6+ENABLE_FEATURE_NTP_AUTH)),
345 OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER, 381 OPT_l = (1 << (7+ENABLE_FEATURE_NTP_AUTH)) * ENABLE_FEATURE_NTPD_SERVER,
346 OPT_I = (1 << 8) * ENABLE_FEATURE_NTPD_SERVER, 382 OPT_I = (1 << (8+ENABLE_FEATURE_NTP_AUTH)) * ENABLE_FEATURE_NTPD_SERVER,
347 /* We hijack some bits for other purposes */ 383 /* We hijack some bits for other purposes */
348 OPT_qq = (1 << 31), 384 OPT_qq = (1 << 31),
349}; 385};
@@ -816,8 +852,12 @@ resolve_peer_hostname(peer_t *p)
816 return lsa; 852 return lsa;
817} 853}
818 854
855#if !ENABLE_FEATURE_NTP_AUTH
856#define add_peers(s, key_entry) \
857 add_peers(s)
858#endif
819static void 859static void
820add_peers(const char *s) 860add_peers(const char *s, key_entry_t *key_entry)
821{ 861{
822 llist_t *item; 862 llist_t *item;
823 peer_t *p; 863 peer_t *p;
@@ -846,6 +886,7 @@ add_peers(const char *s)
846 } 886 }
847 } 887 }
848 888
889 IF_FEATURE_NTP_AUTH(p->key_entry = key_entry;)
849 llist_add_to(&G.ntp_peers, p); 890 llist_add_to(&G.ntp_peers, p);
850 G.peer_cnt++; 891 G.peer_cnt++;
851} 892}
@@ -870,6 +911,48 @@ do_sendto(int fd,
870 return 0; 911 return 0;
871} 912}
872 913
914#if ENABLE_FEATURE_NTP_AUTH
915static void
916hash(key_entry_t *key_entry, const msg_t *msg, uint8_t *output)
917{
918 union {
919 md5_ctx_t m;
920 sha1_ctx_t s;
921 } ctx;
922 unsigned hash_size = sizeof(*msg) - sizeof(msg->m_keyid) - sizeof(msg->m_digest);
923
924 switch (key_entry->type) {
925 case HASH_MD5:
926 md5_begin(&ctx.m);
927 md5_hash(&ctx.m, key_entry->key, key_entry->key_length);
928 md5_hash(&ctx.m, msg, hash_size);
929 md5_end(&ctx.m, output);
930 break;
931 default: /* it's HASH_SHA1 */
932 sha1_begin(&ctx.s);
933 sha1_hash(&ctx.s, key_entry->key, key_entry->key_length);
934 sha1_hash(&ctx.s, msg, hash_size);
935 sha1_end(&ctx.s, output);
936 break;
937 }
938}
939
940static void
941hash_peer(peer_t *p)
942{
943 p->p_xmt_msg.m_keyid = htonl(p->key_entry->id);
944 hash(p->key_entry, &p->p_xmt_msg, p->p_xmt_msg.m_digest);
945}
946
947static int
948hashes_differ(peer_t *p, const msg_t *msg)
949{
950 uint8_t digest[NTP_SHA1_DIGESTSIZE];
951 hash(p->key_entry, msg, digest);
952 return memcmp(digest, msg->m_digest, p->key_entry->msg_size - NTP_MSGSIZE_NOAUTH - KEYID_SIZE);
953}
954#endif
955
873static void 956static void
874send_query_to_peer(peer_t *p) 957send_query_to_peer(peer_t *p)
875{ 958{
@@ -946,9 +1029,18 @@ send_query_to_peer(peer_t *p)
946 */ 1029 */
947 p->reachable_bits <<= 1; 1030 p->reachable_bits <<= 1;
948 1031
1032#if ENABLE_FEATURE_NTP_AUTH
1033 if (p->key_entry)
1034 hash_peer(p);
949 if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len, 1035 if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
950 &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1 1036 &p->p_xmt_msg, !p->key_entry ? NTP_MSGSIZE_NOAUTH : p->key_entry->msg_size) == -1
951 ) { 1037 )
1038#else
1039 if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
1040 &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1
1041 )
1042#endif
1043 {
952 close(p->p_fd); 1044 close(p->p_fd);
953 p->p_fd = -1; 1045 p->p_fd = -1;
954 /* 1046 /*
@@ -1924,10 +2016,21 @@ recv_and_process_peer_pkt(peer_t *p)
1924 bb_perror_msg_and_die("recv(%s) error", p->p_dotted); 2016 bb_perror_msg_and_die("recv(%s) error", p->p_dotted);
1925 } 2017 }
1926 2018
1927 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { 2019#if ENABLE_FEATURE_NTP_AUTH
2020 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_MD5_AUTH && size != NTP_MSGSIZE_SHA1_AUTH) {
2021 bb_error_msg("malformed packet received from %s", p->p_dotted);
2022 return;
2023 }
2024 if (p->key_entry && hashes_differ(p, &msg)) {
2025 bb_error_msg("invalid cryptographic hash received from %s", p->p_dotted);
2026 return;
2027 }
2028#else
2029 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_MD5_AUTH) {
1928 bb_error_msg("malformed packet received from %s", p->p_dotted); 2030 bb_error_msg("malformed packet received from %s", p->p_dotted);
1929 return; 2031 return;
1930 } 2032 }
2033#endif
1931 2034
1932 if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl 2035 if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
1933 || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl 2036 || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
@@ -2135,7 +2238,12 @@ recv_and_process_client_pkt(void /*int fd*/)
2135 from = xzalloc(to->len); 2238 from = xzalloc(to->len);
2136 2239
2137 size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len); 2240 size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
2138 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { 2241#if ENABLE_FEATURE_NTP_AUTH
2242 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_MD5_AUTH && size != NTP_MSGSIZE_SHA1_AUTH)
2243#else
2244 if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_MD5_AUTH)
2245#endif
2246 {
2139 char *addr; 2247 char *addr;
2140 if (size < 0) { 2248 if (size < 0) {
2141 if (errno == EAGAIN) 2249 if (errno == EAGAIN)
@@ -2278,6 +2386,19 @@ recv_and_process_client_pkt(void /*int fd*/)
2278 * with the -g and -q options. See the tinker command for other options. 2386 * with the -g and -q options. See the tinker command for other options.
2279 * Note: The kernel time discipline is disabled with this option. 2387 * Note: The kernel time discipline is disabled with this option.
2280 */ 2388 */
2389#if ENABLE_FEATURE_NTP_AUTH
2390static key_entry_t *
2391find_key_entry(llist_t *key_entries, unsigned id)
2392{
2393 while (key_entries) {
2394 key_entry_t *cur = (key_entry_t*) key_entries->data;
2395 if (cur->id == id)
2396 return cur;
2397 key_entries = key_entries->link;
2398 }
2399 bb_error_msg_and_die("key %u is not defined", id);
2400}
2401#endif
2281 2402
2282/* By doing init in a separate function we decrease stack usage 2403/* By doing init in a separate function we decrease stack usage
2283 * in main loop. 2404 * in main loop.
@@ -2286,6 +2407,10 @@ static NOINLINE void ntp_init(char **argv)
2286{ 2407{
2287 unsigned opts; 2408 unsigned opts;
2288 llist_t *peers; 2409 llist_t *peers;
2410#if ENABLE_FEATURE_NTP_AUTH
2411 llist_t *key_entries;
2412 char *key_file_path;
2413#endif
2289 2414
2290 srand(getpid()); 2415 srand(getpid());
2291 2416
@@ -2302,8 +2427,10 @@ static NOINLINE void ntp_init(char **argv)
2302 2427
2303 /* Parse options */ 2428 /* Parse options */
2304 peers = NULL; 2429 peers = NULL;
2430 IF_FEATURE_NTP_AUTH(key_entries = NULL;)
2305 opts = getopt32(argv, "^" 2431 opts = getopt32(argv, "^"
2306 "nqNx" /* compat */ 2432 "nqNx" /* compat */
2433 IF_FEATURE_NTP_AUTH("k:") /* compat */
2307 "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */ 2434 "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
2308 IF_FEATURE_NTPD_SERVER("I:") /* compat */ 2435 IF_FEATURE_NTPD_SERVER("I:") /* compat */
2309 "d" /* compat */ 2436 "d" /* compat */
@@ -2311,11 +2438,11 @@ static NOINLINE void ntp_init(char **argv)
2311 "\0" 2438 "\0"
2312 "dd:wn" /* -d: counter; -p: list; -w implies -n */ 2439 "dd:wn" /* -d: counter; -p: list; -w implies -n */
2313 IF_FEATURE_NTPD_SERVER(":Il") /* -I implies -l */ 2440 IF_FEATURE_NTPD_SERVER(":Il") /* -I implies -l */
2314 , &peers, &G.script_name, 2441 IF_FEATURE_NTP_AUTH(, &key_file_path)
2315#if ENABLE_FEATURE_NTPD_SERVER 2442 , &peers, &G.script_name
2316 &G.if_name, 2443 IF_FEATURE_NTPD_SERVER(, &G.if_name)
2317#endif 2444 , &G.verbose
2318 &G.verbose); 2445 );
2319 2446
2320// if (opts & OPT_x) /* disable stepping, only slew is allowed */ 2447// if (opts & OPT_x) /* disable stepping, only slew is allowed */
2321// G.time_was_stepped = 1; 2448// G.time_was_stepped = 1;
@@ -2341,19 +2468,107 @@ static NOINLINE void ntp_init(char **argv)
2341 logmode = LOGMODE_NONE; 2468 logmode = LOGMODE_NONE;
2342 } 2469 }
2343 2470
2471#if ENABLE_FEATURE_NTP_AUTH
2472 if (opts & OPT_k) {
2473 char *tokens[4];
2474 parser_t *parser;
2475
2476 parser = config_open(key_file_path);
2477 while (config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL | PARSE_MIN_DIE) == 3) {
2478 key_entry_t *key_entry;
2479 char buffer[40];
2480 smalluint hash_type;
2481 smalluint msg_size;
2482 smalluint key_length;
2483 char *key;
2484
2485 if ((tokens[1][0] | 0x20) == 'm')
2486 /* supports 'M' and 'md5' formats */
2487 hash_type = HASH_MD5;
2488 else
2489 if (strncasecmp(tokens[1], "sha", 3) == 0)
2490 /* supports 'sha' and 'sha1' formats */
2491 hash_type = HASH_SHA1;
2492 else
2493 bb_error_msg_and_die("only MD5 and SHA1 keys supported");
2494/* man ntp.keys:
2495 * MD5 The key is 1 to 16 printable characters terminated by an EOL,
2496 * whitespace, or a # (which is the "start of comment" character).
2497 * SHA
2498 * SHA1
2499 * RMD160 The key is a hex-encoded ASCII string of 40 characters, which
2500 * is truncated as necessary.
2501 */
2502 key_length = strnlen(tokens[2], sizeof(buffer)+1);
2503 if (key_length >= sizeof(buffer)+1) {
2504 err:
2505 bb_error_msg_and_die("malformed key at line %u", parser->lineno);
2506 }
2507 if (hash_type == HASH_MD5) {
2508 key = tokens[2];
2509 msg_size = NTP_MSGSIZE_MD5_AUTH;
2510 } else /* it's hash_type == HASH_SHA1 */
2511 if (!(key_length & 1)) {
2512 key_length >>= 1;
2513 if (!hex2bin(buffer, tokens[2], key_length))
2514 goto err;
2515 key = buffer;
2516 msg_size = NTP_MSGSIZE_SHA1_AUTH;
2517 } else {
2518 goto err;
2519 }
2520 key_entry = xzalloc(sizeof(*key_entry) + key_length);
2521 key_entry->type = hash_type;
2522 key_entry->msg_size = msg_size;
2523 key_entry->key_length = key_length;
2524 memcpy(key_entry->key, key, key_length);
2525 key_entry->id = xatou_range(tokens[0], 1, MAX_KEY_NUMBER);
2526 llist_add_to(&key_entries, key_entry);
2527 }
2528 config_close(parser);
2529 }
2530#endif
2344 if (peers) { 2531 if (peers) {
2532#if ENABLE_FEATURE_NTP_AUTH
2533 while (peers) {
2534 char *peer = llist_pop(&peers);
2535 key_entry_t *key_entry = NULL;
2536 if (strncmp(peer, "keyno:", 6) == 0) {
2537 char *end;
2538 int key_id;
2539 peer += 6;
2540 end = strchr(peer, ':');
2541 *end = '\0';
2542 key_id = xatou_range(peer, 1, MAX_KEY_NUMBER);
2543 *end = ':';
2544 key_entry = find_key_entry(key_entries, key_id);
2545 peer = end + 1;
2546 }
2547 add_peers(peer, key_entry);
2548 }
2549#else
2345 while (peers) 2550 while (peers)
2346 add_peers(llist_pop(&peers)); 2551 add_peers(llist_pop(&peers), NULL);
2552#endif
2347 } 2553 }
2348#if ENABLE_FEATURE_NTPD_CONF 2554#if ENABLE_FEATURE_NTPD_CONF
2349 else { 2555 else {
2350 parser_t *parser; 2556 parser_t *parser;
2351 char *token[3]; 2557 char *token[3 + 2*ENABLE_FEATURE_NTP_AUTH];
2352 2558
2353 parser = config_open("/etc/ntp.conf"); 2559 parser = config_open("/etc/ntp.conf");
2354 while (config_read(parser, token, 3, 1, "# \t", PARSE_NORMAL)) { 2560 while (config_read(parser, token, 3 + 2*ENABLE_FEATURE_NTP_AUTH, 1, "# \t", PARSE_NORMAL)) {
2355 if (strcmp(token[0], "server") == 0 && token[1]) { 2561 if (strcmp(token[0], "server") == 0 && token[1]) {
2356 add_peers(token[1]); 2562# if ENABLE_FEATURE_NTP_AUTH
2563 key_entry_t *key_entry = NULL;
2564 if (token[2] && token[3] && strcmp(token[2], "key") == 0) {
2565 unsigned key_id = xatou_range(token[3], 1, MAX_KEY_NUMBER);
2566 key_entry = find_key_entry(key_entries, key_id);
2567 }
2568 add_peers(token[1], key_entry);
2569# else
2570 add_peers(token[1], NULL);
2571# endif
2357 continue; 2572 continue;
2358 } 2573 }
2359 bb_error_msg("skipping %s:%u: unimplemented command '%s'", 2574 bb_error_msg("skipping %s:%u: unimplemented command '%s'",
@@ -2394,6 +2609,7 @@ static NOINLINE void ntp_init(char **argv)
2394 | (1 << SIGCHLD) 2609 | (1 << SIGCHLD)
2395 , SIG_IGN 2610 , SIG_IGN
2396 ); 2611 );
2612//TODO: free unused elements of key_entries?
2397} 2613}
2398 2614
2399int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE; 2615int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;