This patches adds support for MD5 and SHA1 authentication to ntpd.  I tried
to key the code style as consistent as possible with what I say in busybox.

diff --git a/networking/ntpd.c b/networking/ntpd.c
index 1ebdc34..d05c6ab 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -78,6 +78,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 +229,18 @@
 #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,

-    NTP_DIGESTSIZE     = 16,
-    NTP_MSGSIZE_NOAUTH = 48,
-    NTP_MSGSIZE        = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE),
+    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,

     /* Status Masks */
     MODE_MASK       = (7 << 0),
@@ -288,7 +293,7 @@ typedef struct {
     l_fixedpt_t m_rectime;
     l_fixedpt_t m_xmttime;
     uint32_t    m_keyid;
-    uint8_t     m_digest[NTP_DIGESTSIZE];
+    uint8_t     m_digest[NTP_SHA1_DIGESTSIZE];
 } msg_t;

 typedef struct {
@@ -297,6 +302,19 @@ typedef struct {
     double d_dispersion;
 } datapoint_t;

+enum hash_type {
+    HASH_MD5,
+    HASH_SHA1
+};
+
+typedef struct {
+    unsigned id;
+    enum hash_type type;
+    char *key;
+    size_t key_length;
+    size_t msg_size;
+} key_entry_t;
+
 typedef struct {
     len_and_sockaddr *p_lsa;
     char             *p_dotted;
@@ -326,6 +344,7 @@ typedef struct {
     /* last sent packet: */
     msg_t            p_xmt_msg;
     char             p_hostname[1];
+    key_entry_t      *key_entry;
 } peer_t;


@@ -337,13 +356,15 @@ enum {
     OPT_q = (1 << 1),
     OPT_N = (1 << 2),
     OPT_x = (1 << 3),
+    OPT_k = (1 << 4),
+    OPT_K = (1 << 5),
     /* Insert new options above this line. */
     /* Non-compat options: */
-    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,
+    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,
     /* We hijack some bits for other purposes */
     OPT_qq = (1 << 31),
 };
@@ -817,7 +838,15 @@ resolve_peer_hostname(peer_t *p)
 }

 static void
-add_peers(const char *s)
+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)
 {
     llist_t *item;
     peer_t *p;
@@ -840,12 +869,15 @@ 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 (p->key_entry != NULL) {
+                    free_key_entry(p->key_entry);
+                }
                 free(p);
                 return;
             }
         }
     }
-
+    p->key_entry = key_entry;
     llist_add_to(&G.ntp_peers, p);
     G.peer_cnt++;
 }
@@ -871,6 +903,65 @@ do_sendto(int fd,
 }

 static void
+hash(peer_t *p) {
+    size_t hash_size = sizeof(msg_t) - sizeof(p->p_xmt_msg.m_keyid) -
sizeof(p->p_xmt_msg.m_digest);
+
+    p->p_xmt_msg.m_keyid = htonl(p->key_entry->id);
+    switch (p->key_entry->type) {
+        case HASH_MD5: {
+            md5_ctx_t ctx;
+
+            md5_begin(&ctx);
+            md5_hash(&ctx, p->key_entry->key, p->key_entry->key_length);
+            md5_hash(&ctx, &p->p_xmt_msg, hash_size);
+            md5_end(&ctx, &p->p_xmt_msg.m_digest);
+        }
+            break;
+        case HASH_SHA1: {
+            sha1_ctx_t ctx;
+
+            sha1_begin(&ctx);
+            sha1_hash(&ctx, p->key_entry->key, p->key_entry->key_length);
+            sha1_hash(&ctx, &p->p_xmt_msg, hash_size);
+            sha1_end(&ctx, &p->p_xmt_msg.m_digest);
+        }
+            break;
+    }
+}
+
+static int compare_hashes(peer_t *p, const msg_t *msg) {
+    size_t hash_size = sizeof(msg_t) - sizeof(msg->m_keyid) -
sizeof(msg->m_digest);
+    key_entry_t *key_entry = p->key_entry;
+    int result = 1;
+
+    switch (p->key_entry->type) {
+        case HASH_MD5: {
+            md5_ctx_t ctx;
+            char digest[NTP_MD5_DIGESTSIZE];
+
+            md5_begin(&ctx);
+            md5_hash(&ctx, key_entry->key, key_entry->key_length);
+            md5_hash(&ctx, msg, hash_size);
+            md5_end(&ctx, &digest);
+            result = memcmp(digest, msg->m_digest, NTP_MD5_DIGESTSIZE);
+        }
+            break;
+        case HASH_SHA1: {
+            sha1_ctx_t ctx;
+            char digest[NTP_SHA1_DIGESTSIZE];
+
+            sha1_begin(&ctx);
+            sha1_hash(&ctx, key_entry->key, key_entry->key_length);
+            sha1_hash(&ctx, msg, hash_size);
+            sha1_end(&ctx, &digest);
+            result = memcmp(digest, msg->m_digest, NTP_SHA1_DIGESTSIZE);
+        }
+            break;
+    }
+    return result;
+}
+
+static void
 send_query_to_peer(peer_t *p)
 {
     if (!p->p_lsa)
@@ -945,9 +1036,11 @@ send_query_to_peer(peer_t *p)
      * We still need to declare "unsync" if this condition persists.
      */
     p->reachable_bits <<= 1;
-
+    if (p->key_entry != NULL) {
+        hash(p);
+    }
     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
+            &p->p_xmt_msg, p->key_entry == NULL ? NTP_MSGSIZE_NOAUTH :
p->key_entry->msg_size) == -1
     ) {
         close(p->p_fd);
         p->p_fd = -1;
@@ -1924,10 +2017,14 @@ recv_and_process_peer_pkt(peer_t *p)
         bb_perror_msg_and_die("recv(%s) error", p->p_dotted);
     }

-    if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
+    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) != 0) {
+        bb_error_msg("invalid cryptographic hash received from %s",
p->p_dotted);
+        return;
+    }

     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 +2232,7 @@ 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 (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
+    if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE_SHA1_AUTH &&
size != NTP_MSGSIZE_MD5_AUTH) {
         char *addr;
         if (size < 0) {
             if (errno == EAGAIN)
@@ -2279,6 +2376,27 @@ recv_and_process_client_pkt(void /*int fd*/)
  *      Note: The kernel time discipline is disabled with this option.
  */

+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));
+            result->id = temporary->id;
+            result->type = temporary->type;
+            result->key = xstrdup(temporary->key);
+            result->key_length = temporary->key_length;
+            result->msg_size = temporary->msg_size;
+        }
+        iterator = iterator->link;
+    }
+    return result;
+}
+
 /* By doing init in a separate function we decrease stack usage
  * in main loop.
  */
@@ -2286,6 +2404,9 @@ static NOINLINE void ntp_init(char **argv)
 {
     unsigned opts;
     llist_t *peers;
+    llist_t *key_ids;
+    llist_t *key_entries = NULL;
+    char *key_file_path = NULL;

     srand(getpid());

@@ -2304,6 +2425,7 @@ static NOINLINE void ntp_init(char **argv)
     peers = NULL;
     opts = getopt32(argv, "^"
             "nqNx" /* compat */
+            "K:*k:"
             "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
             IF_FEATURE_NTPD_SERVER("I:") /* compat */
             "d" /* compat */
@@ -2311,7 +2433,7 @@ 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 */
-            , &peers, &G.script_name,
+            , &key_ids, &key_file_path, &peers, &G.script_name,
 #if ENABLE_FEATURE_NTPD_SERVER
             &G.if_name,
 #endif
@@ -2341,9 +2463,67 @@ static NOINLINE void ntp_init(char **argv)
         logmode = LOGMODE_NONE;
     }

+    if (key_ids && !(opts & OPT_k)) {
+        bb_error_msg_and_die("A key file (-k) is required to use a key
(-K) option");
+    }
+
+    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;
+            size_t key_length;
+            key_entry_t *key_entry = (key_entry_t *)
xzalloc(sizeof(key_entry_t));
+
+            if (strcasecmp(tokens[1], "MD5") == 0) {
+                hash_type = HASH_MD5;
+                key_entry->msg_size = NTP_MSGSIZE_MD5_AUTH;
+            } else if (strcasecmp(tokens[1], "SHA1") == 0) {
+                hash_type = HASH_SHA1;
+                key_entry->msg_size = NTP_MSGSIZE_SHA1_AUTH;
+            } else {
+                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) {
+                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("key #%d is malformed",
key_entry->id);
+                }
+                key_entry->key = xstrdup(buffer);
+                key_entry->key_length = byte_count;
+            } else {
+                bb_error_msg_and_die("key #%d is malformed",
key_entry->id);
+            }
+            llist_add_to(&key_entries, key_entry);
+        }
+        config_close(key_file_parser);
+    }
+
     if (peers) {
-        while (peers)
-            add_peers(llist_pop(&peers));
+        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);
+        }
     }
 #if ENABLE_FEATURE_NTPD_CONF
     else {
@@ -2353,7 +2533,7 @@ 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]) {
-                add_peers(token[1]);
+                add_peers(token[1], NULL);
                 continue;
             }
             bb_error_msg("skipping %s:%u: unimplemented command '%s'",
@@ -2394,6 +2574,7 @@ static NOINLINE void ntp_init(char **argv)
         | (1 << SIGCHLD)
         , SIG_IGN
     );
+    llist_free(key_entries, free_key_entry);
 }

 int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to