Add support for NTP authentication
Denys Vlasenko
vda.linux at googlemail.com
Thu Oct 25 17:23:57 UTC 2018
Sorry for the delay.
On Tue, Oct 16, 2018 at 9:49 PM Brandon Enochs <enochs.brandon at gmail.com> wrote:
>
> Here's the git format-patch:
>
> From e7637d4c2cee4fb9e1ebdb96d0330cf69b0bdf49 Mon Sep 17 00:00:00 2001
> From: "Brandon P. Enochs" <enochs.brandon at 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.brandon at gmail.com>
Google mail fails to extract it without whitespace damage.
Please resend as attachment.
Meanwhile I'm reviewing the patch.
> 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 at busybox.net
> http://lists.busybox.net/mailman/listinfo/busybox
More information about the busybox
mailing list