Here's the git format-patch: >From e7637d4c2cee4fb9e1ebdb96d0330cf69b0bdf49 Mon Sep 17 00:00:00 2001 From: "Brandon P. Enochs" <enochs.bran...@gmail.com> Date: Tue, 16 Oct 2018 15:48:19 -0400 Subject: [PATCH] Added support for MD5/SHA1 NTP authentication
Added support for MD5 NTP authentication as described in RFC 5905 [1]. This patch also supports SHA1 authentication, which is supported by ntpd [2]. The key file format is the same file format as used by ntpd. The configuration file format follows standard Unix conventions (# comments) with lines consist of the following fields separated by whitespace: <key identifier, [1,65535]> <SHA1|MD5> <an ASCII string of up to 20 characters|an octet string [a-zA-F0-9] of up to 64 characters>. [1] https://www.ietf.org/rfc/rfc5905.txt [2] http://www.ntp.org/ function old new delta .rodata 166664 166882 +218 ------------------------------------------------------------------------------ Signed-off-by: Brandon P. Enochs <enochs.bran...@gmail.com> --- networking/ntpd.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 2 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index 1ebdc34..42cb0e7 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -62,6 +62,24 @@ //config: help //config: Make ntpd look in /etc/ntp.conf for peers. Only "server address" //config: is supported. +//config:config FEATURE_NTP_AUTH +//config: bool "Make ntpd support authentication" +//config: default n +//config: depends on NTPD +//config: help +//config: Make ntpd support authentication" +//config:config FEATURE_NTP_AUTH_MD5 +//config: bool "Make ntpd support MD5 authentication" +//config: default n +//config: depends on FEATURE_NTP_AUTH +//config: help +//config: Make ntpd support MD5 authentication" +//config:config FEATURE_NTP_AUTH_SHA1 +//config: bool "Make ntpd support SHA1 authentication" +//config: default n +//config: depends on FEATURE_NTP_AUTH +//config: help +//config: Make ntpd support SHA1 authentication" //applet:IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP)) @@ -78,6 +96,8 @@ //usage: "\n -w Do not set time (only query peers), implies -n" //usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins" //usage: "\n -p PEER Obtain time from PEER (may be repeated)" +//usage: "\n -K key number for preceding PEER (may be repeated)" +//usage: "\n -k key file (see man 5 ntp.keys)" //usage: IF_FEATURE_NTPD_CONF( //usage: "\n If -p is not given, 'server HOST' lines" //usage: "\n from /etc/ntp.conf are used" @@ -227,15 +247,24 @@ #define FLL (MAXPOLL + 1) /* Parameter averaging constant */ #define AVG 4 - +#define MAX_KEY_NUMBER 65535 +#define KEYID_SIZE sizeof(uint32_t) enum { NTP_VERSION = 4, NTP_MAXSTRATUM = 15, +#if ENABLE_FEATURE_NTP_AUTH + NTP_MD5_DIGESTSIZE = 16, + NTP_SHA1_DIGESTSIZE = 20, + NTP_MSGSIZE_NOAUTH = 48, + NTP_MSGSIZE_MD5_AUTH = NTP_MSGSIZE_NOAUTH + NTP_MD5_DIGESTSIZE + KEYID_SIZE, + NTP_MSGSIZE_SHA1_AUTH = NTP_MSGSIZE_NOAUTH + NTP_SHA1_DIGESTSIZE + KEYID_SIZE, +#else NTP_DIGESTSIZE = 16, NTP_MSGSIZE_NOAUTH = 48, - NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE), + NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + KEYID_SIZE + NTP_DIGESTSIZE), +#endif /* Status Masks */ MODE_MASK = (7 << 0), @@ -288,7 +317,11 @@ typedef struct { l_fixedpt_t m_rectime; l_fixedpt_t m_xmttime; uint32_t m_keyid; +#if ENABLE_FEATURE_NTP_AUTH + uint8_t m_digest[NTP_SHA1_DIGESTSIZE]; +#else uint8_t m_digest[NTP_DIGESTSIZE]; +#endif } msg_t; typedef struct { @@ -297,6 +330,26 @@ typedef struct { double d_dispersion; } datapoint_t; +#if ENABLE_FEATURE_NTP_AUTH +enum hash_type { + HASH_NONE, +#if ENABLE_FEATURE_NTP_AUTH_MD5 + HASH_MD5, +#endif +#if ENABLE_FEATURE_NTP_AUTH_SHA1 + HASH_SHA1 +#endif +}; + +typedef struct { + unsigned id; + enum hash_type type; + char *key; + size_t key_length; + size_t msg_size; +} key_entry_t; +#endif + typedef struct { len_and_sockaddr *p_lsa; char *p_dotted; @@ -326,6 +379,9 @@ typedef struct { /* last sent packet: */ msg_t p_xmt_msg; char p_hostname[1]; +#if ENABLE_FEATURE_NTP_AUTH + key_entry_t *key_entry; +#endif } peer_t; @@ -337,13 +393,25 @@ enum { OPT_q = (1 << 1), OPT_N = (1 << 2), OPT_x = (1 << 3), +#if ENABLE_FEATURE_NTP_AUTH + OPT_k = (1 << 4), + OPT_K = (1 << 5), +#endif /* Insert new options above this line. */ /* Non-compat options: */ +#if ENABLE_FEATURE_NTP_AUTH + OPT_w = (1 << 6), + OPT_p = (1 << 7), + OPT_S = (1 << 8), + OPT_l = (1 << 9) * ENABLE_FEATURE_NTPD_SERVER, + OPT_I = (1 << 10) * ENABLE_FEATURE_NTPD_SERVER, +#else OPT_w = (1 << 4), OPT_p = (1 << 5), OPT_S = (1 << 6), OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER, OPT_I = (1 << 8) * ENABLE_FEATURE_NTPD_SERVER, +#endif /* We hijack some bits for other purposes */ OPT_qq = (1 << 31), }; @@ -816,8 +884,21 @@ resolve_peer_hostname(peer_t *p) return lsa; } +#if ENABLE_FEATURE_NTP_AUTH +static void +free_key_entry(void *data) { + key_entry_t *key_entry = (key_entry_t *) data; + + free(key_entry->key); + free(key_entry); +} + +static void +add_peers(const char *s, key_entry_t *key_entry) +#else static void add_peers(const char *s) +#endif { llist_t *item; peer_t *p; @@ -840,12 +921,18 @@ add_peers(const char *s) bb_error_msg("duplicate peer %s (%s)", s, p->p_dotted); free(p->p_lsa); free(p->p_dotted); +#if ENABLE_FEATURE_NTP_AUTH + free_key_entry(p->key_entry); +#endif free(p); return; } } } +#if ENABLE_FEATURE_NTP_AUTH + p->key_entry = key_entry; +#endif llist_add_to(&G.ntp_peers, p); G.peer_cnt++; } @@ -870,6 +957,59 @@ do_sendto(int fd, return 0; } +#if ENABLE_FEATURE_NTP_AUTH + +static void +hash(key_entry_t *key_entry, const msg_t *msg, uint8_t *output) +{ + size_t hash_size = sizeof(msg_t) - sizeof(msg->m_keyid) - sizeof(msg->m_digest); + + switch (key_entry->type) { +#if ENABLE_FEATURE_NTP_AUTH_MD5 + case HASH_MD5: { + md5_ctx_t ctx; + + md5_begin(&ctx); + md5_hash(&ctx, key_entry->key, key_entry->key_length); + md5_hash(&ctx, msg, hash_size); + md5_end(&ctx, output); + } + break; +#endif +#if ENABLE_FEATURE_NTP_AUTH_SHA1 + case HASH_SHA1: { + sha1_ctx_t ctx; + + sha1_begin(&ctx); + sha1_hash(&ctx, key_entry->key, key_entry->key_length); + sha1_hash(&ctx, msg, hash_size); + sha1_end(&ctx, output); + } + break; +#endif + case HASH_NONE: + default: + bb_perror_msg_and_die("ntpd is in an invalid state."); + break; + } +} + +static void +hash_peer(peer_t *p) +{ + p->p_xmt_msg.m_keyid = htonl(p->key_entry->id); + hash(p->key_entry, &p->p_xmt_msg, p->p_xmt_msg.m_digest); +} + +static bool compare_hashes(peer_t *p, const msg_t *msg) +{ + uint8_t digest[NTP_SHA1_DIGESTSIZE]; + + hash(p->key_entry, msg, digest); + return !memcmp(digest, msg->m_digest, p->key_entry->msg_size - NTP_MSGSIZE_NOAUTH - KEYID_SIZE); +} +#endif + static void send_query_to_peer(peer_t *p) { @@ -946,9 +1086,18 @@ send_query_to_peer(peer_t *p) */ p->reachable_bits <<= 1; +#if ENABLE_FEATURE_NTP_AUTH + if (p->key_entry != NULL) { + hash_peer(p); + } + if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len, + &p->p_xmt_msg, p->key_entry == NULL ? NTP_MSGSIZE_NOAUTH : p->key_entry->msg_size) < 0 + ) { +#else if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len, &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1 ) { +#endif close(p->p_fd); p->p_fd = -1; /* @@ -1924,10 +2073,21 @@ recv_and_process_peer_pkt(peer_t *p) bb_perror_msg_and_die("recv(%s) error", p->p_dotted); } +#if ENABLE_FEATURE_NTP_AUTH + if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_MD5_AUTH && size != NTP_MSGSIZE_SHA1_AUTH) { + bb_error_msg("malformed packet received from %s", p->p_dotted); + return; + } + if (p->key_entry != NULL && !compare_hashes(p, &msg)) { + bb_error_msg("invalid cryptographic hash received from %s", p->p_dotted); + return; + } +#else if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { bb_error_msg("malformed packet received from %s", p->p_dotted); return; } +#endif if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl @@ -2135,7 +2295,11 @@ recv_and_process_client_pkt(void /*int fd*/) from = xzalloc(to->len); size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len); +#if ENABLE_FEATURE_NTP_AUTH + if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_SHA1_AUTH && size != NTP_MSGSIZE_MD5_AUTH) { +#else if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) { +#endif char *addr; if (size < 0) { if (errno == EAGAIN) @@ -2278,6 +2442,26 @@ recv_and_process_client_pkt(void /*int fd*/) * with the -g and -q options. See the tinker command for other options. * Note: The kernel time discipline is disabled with this option. */ +#if ENABLE_FEATURE_NTP_AUTH +static key_entry_t * +find_key_entry(llist_t *key_entries, unsigned id) +{ + key_entry_t *result = NULL; + llist_t *iterator = key_entries; + key_entry_t *temporary; + + while(iterator != NULL && result == NULL) { + temporary = (key_entry_t *) iterator->data; + if(temporary->id == id) { + result = xzalloc(sizeof(key_entry_t)); + memcpy(result, temporary, sizeof(key_entry_t)); + result->key = xstrdup(temporary->key); + } + iterator = iterator->link; + } + return result; +} +#endif /* By doing init in a separate function we decrease stack usage * in main loop. @@ -2286,6 +2470,11 @@ static NOINLINE void ntp_init(char **argv) { unsigned opts; llist_t *peers; +#if ENABLE_FEATURE_NTP_AUTH + llist_t *key_ids; + llist_t *key_entries = NULL; + char *key_file_path = NULL; +#endif srand(getpid()); @@ -2304,6 +2493,7 @@ static NOINLINE void ntp_init(char **argv) peers = NULL; opts = getopt32(argv, "^" "nqNx" /* compat */ + IF_FEATURE_NTP_AUTH("K:*k:") "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */ IF_FEATURE_NTPD_SERVER("I:") /* compat */ "d" /* compat */ @@ -2311,6 +2501,10 @@ static NOINLINE void ntp_init(char **argv) "\0" "dd:wn" /* -d: counter; -p: list; -w implies -n */ IF_FEATURE_NTPD_SERVER(":Il") /* -I implies -l */ + IF_FEATURE_NTP_AUTH(":K?k") /* -k is required if -K appears */ +#if ENABLE_FEATURE_NTP_AUTH + , &key_ids, &key_file_path +#endif , &peers, &G.script_name, #if ENABLE_FEATURE_NTPD_SERVER &G.if_name, @@ -2341,9 +2535,76 @@ static NOINLINE void ntp_init(char **argv) logmode = LOGMODE_NONE; } +#if ENABLE_FEATURE_NTP_AUTH + if (opts & OPT_k) { + char *tokens[4]; + parser_t *key_file_parser; + int token_count; + + key_file_parser = config_open(key_file_path); + while ((token_count = config_read(key_file_parser, tokens, 4, 3, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) == 3) { + enum hash_type hash_type = HASH_NONE; + size_t key_length; + key_entry_t *key_entry = (key_entry_t *) xzalloc(sizeof(key_entry_t)); + +#if ENABLE_FEATURE_NTP_AUTH_MD5 + if (strcasecmp(tokens[1], "MD5") == 0) { + hash_type = HASH_MD5; + key_entry->msg_size = NTP_MSGSIZE_MD5_AUTH; + } +#endif +#if ENABLE_FEATURE_NTP_AUTH_SHA1 + if (strcasecmp(tokens[1], "SHA1") == 0) { + hash_type = HASH_SHA1; + key_entry->msg_size = NTP_MSGSIZE_SHA1_AUTH; + } +#endif + if (hash_type == HASH_NONE) { + bb_error_msg_and_die("busybox only supports MD5 and SHA1 NTP keys"); + } + key_entry->type = hash_type; + key_entry->id = xatou_range(tokens[0], 1, MAX_KEY_NUMBER); + key_length = strlen(tokens[2]); + if (key_length <= 20) { + key_entry->key = xstrdup(tokens[2]); + key_entry->key_length = key_length; + } else if ((key_length & 1) == 0 && key_length <= 64) { + char buffer[64]; + char *key; + size_t byte_count = key_length / 2; + + memset(buffer, 0, sizeof(buffer)); + key = hex2bin(buffer, tokens[2], byte_count); + if (key == NULL) { + bb_error_msg_and_die("malformed key #%d", key_entry->id); + } + key_entry->key = xstrdup(buffer); + key_entry->key_length = byte_count; + } else { + bb_error_msg_and_die("malformed key #%d", key_entry->id); + } + llist_add_to(&key_entries, key_entry); + } + config_close(key_file_parser); + } + +#endif if (peers) { +#if ENABLE_FEATURE_NTP_AUTH + while (peers) { + key_entry_t *key_entry = NULL; + + if (key_ids) { + int key_id = xatou_range(llist_pop(&key_ids), 1, MAX_KEY_NUMBER); + + key_entry = find_key_entry(key_entries, key_id); + } + add_peers(llist_pop(&peers), key_entry); + } +#else while (peers) add_peers(llist_pop(&peers)); +#endif } #if ENABLE_FEATURE_NTPD_CONF else { @@ -2353,7 +2614,11 @@ static NOINLINE void ntp_init(char **argv) parser = config_open("/etc/ntp.conf"); while (config_read(parser, token, 3, 1, "# \t", PARSE_NORMAL)) { if (strcmp(token[0], "server") == 0 && token[1]) { +#if ENABLE_FEATURE_NTP_AUTH + add_peers(token[1], NULL); +#else add_peers(token[1]); +#endif continue; } bb_error_msg("skipping %s:%u: unimplemented command '%s'", @@ -2394,6 +2659,9 @@ static NOINLINE void ntp_init(char **argv) | (1 << SIGCHLD) , SIG_IGN ); +#if ENABLE_FEATURE_NTP_AUTH + llist_free(key_entries, free_key_entry); +#endif } int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE; -- 2.9.3
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox