DESTDIR=
+HASH_OBJ = @HASH_OBJ@
+
OBJS = util.o sched.o regress.o local.o \
sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \
sources.o sourcestats.o reference.o \
- logging.o conf.o cmdmon.o md5.o keys.o \
+ logging.o conf.o cmdmon.o keys.o \
nameserv.o acquire.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_shm.o refclock_sock.o \
- refclock_pps.o tempcomp.o
+ refclock_pps.o tempcomp.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
-CLI_OBJS = client.o md5.o nameserv.o getdate.o cmdparse.o \
- pktlength.o util.o
+CLI_OBJS = client.o nameserv.o getdate.o cmdparse.o \
+ pktlength.o util.o $(HASH_OBJ)
ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
-SRCS = $(patsubst %.o,%.c,$(OBJS))
-EXTRA_SRCS = $(patsubst %.o,%.c,$(EXTRA_OBJS))
-
-CLI_SRCS = $(patsubst %.o,%.c,$(CLI_OBJS))
-
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
all : chronyd chronyc
chronyd : $(OBJS) $(EXTRA_OBJS)
- $(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
+ $(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) @HASH_LINK@ $(LIBS) $(EXTRA_LIBS)
chronyc : $(CLI_OBJS)
- $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) @READLINE_LINK@ $(LIBS) $(EXTRA_CLI_LIBS)
+ $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) @READLINE_LINK@ @HASH_LINK@ $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
+$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
+ $(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
+
distclean : clean
-rm -f Makefile
#include "sysincl.h"
#include "addressing.h"
+#include "hash.h"
/* This is the default port to use for CANDM, if no alternative is
defined */
and used also instead of integer microseconds, new commands: modify stratum,
modify polltarget, modify maxdelaydevratio, reselect, reselectdistance
+ Version 5 : auth data moved to the end of the packet to allow different hashes
*/
-#define PROTO_VERSION_NUMBER 4
+#define PROTO_VERSION_NUMBER 5
/* The oldest protocol version that is compatible enough with
the current version to report a version mismatch */
uint32_t sequence; /* Client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* Command token (to prevent replay attack) */
- uint32_t auth[4]; /* MD5 authentication of the packet */
union {
REQ_Online online;
REQ_ReselectDistance reselect_distance;
} data; /* Command specific parameters */
+ /* authentication of the packet, there is no hole after the actual data
+ from the data union, this field only sets the maximum auth size */
+ uint8_t auth[MAX_HASH_LENGTH];
+
} CMD_Request;
/* ================================================== */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* New command token (only if command was successfully
authenticated) */
- uint32_t auth[4]; /* MD5 authentication of the packet */
-
union {
RPY_Null null;
RPY_N_Sources n_sources;
RPY_Activity activity;
} data; /* Reply specific parameters */
+ /* authentication of the packet, there is no hole after the actual data
+ from the data union, this field only sets the maximum auth size */
+ uint8_t auth[MAX_HASH_LENGTH];
+
} CMD_Reply;
/* ================================================== */
#include "candm.h"
#include "nameserv.h"
-#include "md5.h"
+#include "hash.h"
#include "getdate.h"
#include "cmdparse.h"
#include "pktlength.h"
/* ================================================== */
+static char *password;
static int password_seen = 0;
-static MD5_CTX md5_after_just_password;
+static int auth_hash_id;
/* ================================================== */
process_cmd_password(CMD_Request *msg, char *line)
{
char *p, *q;
- char *password;
struct timeval now;
p = line;
password_seen = 1;
}
- /* Generate MD5 initial context */
- MD5Init(&md5_after_just_password);
- MD5Update(&md5_after_just_password, (unsigned char *) password, strlen(password));
-
- /* Blank the password for security */
- for (p = password; *p; p++) {
- *p = 0;
- }
-
if (gettimeofday(&now, NULL) < 0) {
printf("500 - Could not read time of day\n");
return 0;
/* ================================================== */
-static void
+static int
generate_auth(CMD_Request *msg)
{
- MD5_CTX ctx;
- int pkt_len;
+ int data_len;
- pkt_len = PKL_CommandLength(msg);
- ctx = md5_after_just_password;
- MD5Update(&ctx, (unsigned char *) msg, offsetof(CMD_Request, auth));
- if (pkt_len > offsetof(CMD_Request, data)) {
- MD5Update(&ctx, (unsigned char *) &(msg->data), pkt_len - offsetof(CMD_Request, data));
- }
- MD5Final(&ctx);
- memcpy(&(msg->auth), &ctx.digest, 16);
+ data_len = PKL_CommandLength(msg);
+
+ assert(auth_hash_id >= 0);
+
+ return UTI_GenerateNTPAuth(auth_hash_id, (unsigned char *)password, strlen(password),
+ (unsigned char *)msg, data_len, ((unsigned char *)msg) + data_len, sizeof (msg->auth));
}
/* ================================================== */
static int
-check_reply_auth(CMD_Reply *msg)
+check_reply_auth(CMD_Reply *msg, int len)
{
- int pkt_len;
- MD5_CTX ctx;
+ int data_len;
- pkt_len = PKL_ReplyLength(msg);
- ctx = md5_after_just_password;
- MD5Update(&ctx, (unsigned char *) msg, offsetof(CMD_Request, auth));
- if (pkt_len > offsetof(CMD_Reply, data)) {
- MD5Update(&ctx, (unsigned char *) &(msg->data), pkt_len - offsetof(CMD_Reply, data));
- }
- MD5Final(&ctx);
+ data_len = PKL_ReplyLength(msg);
- if (!memcmp((void *) &ctx.digest, (void *) &(msg->auth), 16)) {
- return 1;
- } else {
- return 0;
- }
+ assert(auth_hash_id >= 0);
+
+ return UTI_CheckNTPAuth(auth_hash_id, (unsigned char *)password, strlen(password),
+ (unsigned char *)msg, data_len,
+ ((unsigned char *)msg) + data_len, len - data_len);
}
/* ================================================== */
printf("waitsync [max-tries [max-correction [max-skew]]] : Wait until synchronised\n");
printf("writertc : Save RTC parameters to file\n");
printf("\n");
+ printf("authhash <name>: Set command authentication hash function\n");
printf("dns -n|+n : Disable/enable resolving IP addresses to hostnames\n");
printf("dns -4|-6|-46 : Resolve hostnames only to IPv4/IPv6/both addresses\n");
printf("timeout <milliseconds> : Set initial response timeout\n");
int read_length;
int expected_length;
int command_length;
+ int auth_length;
struct timeval tv;
int timeout;
int n_attempts;
packet and we won't get a token back */
request->utoken = htonl(SPECIAL_UTOKEN);
}
- generate_auth(request);
+ auth_length = generate_auth(request);
} else {
- memset(request->auth, 0, sizeof (request->auth));
+ auth_length = 0;
}
command_length = PKL_CommandLength(request);
assert(command_length > 0);
+ /* add empty MD5 auth so older servers will not drop the request
+ due to bad length */
+ if (!auth_length) {
+ memset(((char *)request) + command_length, 0, 16);
+ auth_length = 16;
+ }
+
#if 0
- printf("Sent command length=%d bytes\n", command_length);
+ printf("Sent command length=%d bytes auth length=%d bytes\n", command_length, auth_length);
#endif
- if (sendto(sock_fd, (void *) request, command_length, 0,
+ if (sendto(sock_fd, (void *) request, command_length + auth_length, 0,
&his_addr.u, his_addr_len) < 0) {
read_length = recvfrom_status;
expected_length = PKL_ReplyLength(reply);
- bad_length = (read_length != expected_length);
+ bad_length = (read_length < expected_length);
bad_sender = (where_from.u.sa_family != his_addr.u.sa_family ||
(where_from.u.sa_family == AF_INET &&
(where_from.in4.sin_addr.s_addr != his_addr.in4.sin_addr.s_addr ||
#endif
if (password_seen) {
- *reply_auth_ok = check_reply_auth(reply);
+ *reply_auth_ok = check_reply_auth(reply, read_length);
} else {
/* Assume in this case that the reply is always considered
to be authentic */
/* ================================================== */
+static int
+process_cmd_authhash(const char *line)
+{
+ char hash_name[50];
+ int new_hash_id;
+
+ assert(auth_hash_id >= 0);
+
+ if (sscanf(line, "%49s", hash_name) != 1) {
+ fprintf(stderr, "Could not parse hash name\n");
+ return 0;
+ }
+
+ new_hash_id = HSH_GetHashId(hash_name);
+ if (new_hash_id < 0) {
+ fprintf(stderr, "Unknown hash name: %s\n", hash_name);
+ return 0;
+ }
+
+ auth_hash_id = new_hash_id;
+
+ return 1;
+}
+
+/* ================================================== */
+
static int
process_cmd_timeout(const char *line)
{
} else if (!strncmp(p, "waitsync", 8)) {
ret = process_cmd_waitsync(p+8);
do_normal_submit = 0;
+ } else if (!strncmp(p, "authhash", 8)) {
+ ret = process_cmd_authhash(p+8);
+ do_normal_submit = 0;
} else if (!strncmp(p, "dns ", 4)) {
ret = process_cmd_dns(p+4);
do_normal_submit = 0;
if (on_terminal && (argc == 0)) {
display_gpl();
}
+
+ /* MD5 is the default authentication hash */
+ auth_hash_id = HSH_GetHashId("MD5");
+ assert(auth_hash_id >= 0);
open_io(hostname, port);
rest of the packet */
static int
-check_rx_packet_auth(CMD_Request *packet)
+check_rx_packet_auth(CMD_Request *packet, int packet_len)
{
-
- char *key;
- int keylen;
- int pkt_len;
- MD5_CTX ctx;
+ int pkt_len, auth_len;
pkt_len = PKL_CommandLength(packet);
+ auth_len = packet_len - pkt_len;
- KEY_CommandKey(&key, &keylen);
-
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned char *) key, keylen);
- MD5Update(&ctx, (unsigned char *) packet, offsetof(CMD_Request, auth));
- if (pkt_len > offsetof(CMD_Request, data)) {
- MD5Update(&ctx, (unsigned char *) &(packet->data), pkt_len - offsetof(CMD_Request, data));
- }
- MD5Final(&ctx);
-
- if (!memcmp((void *) &ctx.digest, (void *) &(packet->auth), 16)) {
- return 1;
- } else {
- return 0;
- }
+ return KEY_CheckAuth(KEY_GetCommandKey(), (unsigned char *)packet,
+ pkt_len, ((unsigned char *)packet) + pkt_len, auth_len);
}
/* ================================================== */
-static void
+static int
generate_tx_packet_auth(CMD_Reply *packet)
{
- char *key;
- int keylen;
- MD5_CTX ctx;
int pkt_len;
pkt_len = PKL_ReplyLength(packet);
- KEY_CommandKey(&key, &keylen);
-
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned char *) key, keylen);
- MD5Update(&ctx, (unsigned char *) packet, offsetof(CMD_Request, auth));
- if (pkt_len > offsetof(CMD_Reply, data)) {
- MD5Update(&ctx, (unsigned char *) &(packet->data), pkt_len - offsetof(CMD_Reply, data));
- }
- MD5Final(&ctx);
-
- memcpy(&(packet->auth), &ctx.digest, 16);
-
+ return KEY_GenerateAuth(KEY_GetCommandKey(), (unsigned char *)packet,
+ pkt_len, ((unsigned char *)packet) + pkt_len, sizeof (packet->auth));
}
/* ================================================== */
/* ================================================== */
static void
-transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to)
+transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
{
int status;
int tx_message_length;
assert(0);
}
- tx_message_length = PKL_ReplyLength(msg);
+ tx_message_length = PKL_ReplyLength(msg) + auth_len;
status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
&where_to->u, addrlen);
{
int status;
int read_length; /* Length of packet read */
- int expected_length; /* Expected length of packet */
+ int expected_length; /* Expected length of packet without auth data */
unsigned long flags;
CMD_Request rx_message;
CMD_Reply tx_message, *prev_tx_message;
socklen_t from_length;
IPAddr remote_ip;
unsigned short remote_port;
- int md5_ok;
+ int auth_length;
+ int auth_ok;
int utoken_ok, token_ok;
int issue_token;
int valid_ts;
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT) {
tx_message.status = htons(STT_BADPKTVERSION);
- transmit_reply(&tx_message, &where_from);
+ /* add empty MD5 auth so older clients will not drop
+ the reply due to bad length */
+ memset(((char *)&tx_message) + PKL_ReplyLength(&tx_message), 0, 16);
+ transmit_reply(&tx_message, &where_from, 16);
}
return;
}
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
tx_message.status = htons(STT_INVALID);
- transmit_reply(&tx_message, &where_from);
+ transmit_reply(&tx_message, &where_from, 0);
return;
}
- if (read_length != expected_length) {
+ if (read_length < expected_length) {
if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port);
}
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
tx_message.status = htons(STT_BADPKTLENGTH);
- transmit_reply(&tx_message, &where_from);
+ transmit_reply(&tx_message, &where_from, 0);
return;
}
}
tx_message.status = htons(STT_NOHOSTACCESS);
- transmit_reply(&tx_message, &where_from);
+ transmit_reply(&tx_message, &where_from, 0);
return;
}
clients will set their utokens to 0 to save us wasting our time
if the packet is unauthenticatable. */
if (rx_message.utoken != 0) {
- md5_ok = check_rx_packet_auth(&rx_message);
+ auth_ok = check_rx_packet_auth(&rx_message, read_length);
} else {
- md5_ok = 0;
+ auth_ok = 0;
}
/* All this malarky is to protect the system against various forms
of attack.
Simple packet forgeries are blocked by requiring the packet to
- authenticate properly with MD5. (The assumption is that the
- command key is in a read-only keys file read by the daemon, and
- is known only to administrators.)
+ authenticate properly with MD5 or other crypto hash. (The
+ assumption is that the command key is in a read-only keys file
+ read by the daemon, and is known only to administrators.)
Replay attacks are prevented by 2 fields in the packet. The
'token' field is where the client plays back to us a token that
rx_message_seq = ntohl(rx_message.sequence);
rx_attempt = ntohs(rx_message.attempt);
- if (md5_ok && utoken_ok) {
+ if (auth_ok && utoken_ok) {
token_ok = check_token(rx_message_token);
} else {
token_ok = 0;
}
- if (md5_ok && utoken_ok && !token_ok) {
+ if (auth_ok && utoken_ok && !token_ok) {
/* This might be a resent message, due to the client not getting
our reply to the first attempt. See if we can find the message. */
prev_tx_message = lookup_reply(rx_message_token, rx_message_seq, rx_attempt);
}
- if (md5_ok && utoken_ok && token_ok) {
+ if (auth_ok && utoken_ok && token_ok) {
/* See whether we can discard the previous reply from storage */
token_acknowledged(rx_message_token, &now);
}
valid_ts = 0;
- if (md5_ok) {
+ if (auth_ok) {
struct timeval ts;
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
issue_token = 0;
}
- authenticated = md5_ok & utoken_ok & token_ok;
+ authenticated = auth_ok & utoken_ok & token_ok;
if (authenticated) {
CLG_LogCommandAccess(&remote_ip, CLG_CMD_AUTH, cooked_now.tv_sec);
/* If the log-on fails, record the reason why */
if (!issue_token && !LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon,
- "Bad command logon from %s port %d (md5_ok=%d valid_ts=%d)\n",
+ "Bad command logon from %s port %d (auth_ok=%d valid_ts=%d)",
UTI_IPToString(&remote_ip),
remote_port,
- md5_ok, valid_ts);
+ auth_ok, valid_ts);
}
if (issue_token == 1) {
tx_message.status = htons(STT_SUCCESS);
- } else if (!md5_ok) {
+ } else if (!auth_ok) {
tx_message.status = htons(STT_UNAUTH);
} else if (!valid_ts) {
tx_message.status = htons(STT_INVALIDTS);
}
}
- if (md5_ok) {
- generate_tx_packet_auth(&tx_message);
+ if (auth_ok) {
+ auth_length = generate_tx_packet_auth(&tx_message);
+ } else {
+ auth_length = 0;
}
if (token_ok) {
static int do_it=1;
if (do_it) {
- transmit_reply(&tx_message, &where_from);
+ transmit_reply(&tx_message, &where_from, auth_length);
}
#if 0
fi
fi
+HASH_OBJ="hash_intmd5.o"
+HASH_COMPILE=""
+HASH_LINK=""
+
SYSCONFDIR=/etc
if [ "x$SETSYSCONFDIR" != "x" ]; then
SYSCONFDIR=$SETSYSCONFDIR
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@READLINE_LINK@%${READLINE_LINK}%;\
+ s%@HASH_OBJ@%${HASH_OBJ}%;\
+ s%@HASH_LINK@%${HASH_LINK}%;\
+ s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\
s%@SBINDIR@%${SBINDIR}%;\
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for crypto hashing.
+
+ */
+
+#ifndef GOT_HASH_H
+#define GOT_HASH_H
+
+/* length of hash values produced by SHA512 */
+#define MAX_HASH_LENGTH 64
+
+extern int HSH_GetHashId(const char *name);
+
+extern unsigned int HSH_Hash(int id,
+ const unsigned char *in1, unsigned int in1_len,
+ const unsigned char *in2, unsigned int in2_len,
+ unsigned char *out, unsigned int out_len);
+
+#endif
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines implementing crypto hashing using internal MD5 implementation.
+
+ */
+
+#include "config.h"
+#include "sysincl.h"
+#include "hash.h"
+#include "memory.h"
+
+#include "md5.c"
+
+static MD5_CTX ctx;
+
+int
+HSH_GetHashId(const char *name)
+{
+ /* only MD5 is supported */
+ if (strcmp(name, "MD5"))
+ return -1;
+
+ return 0;
+}
+
+unsigned int
+HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
+ const unsigned char *in2, unsigned int in2_len,
+ unsigned char *out, unsigned int out_len)
+{
+ if (out_len < 16)
+ return 0;
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, in1, in1_len);
+ if (in2)
+ MD5Update(&ctx, in2, in2_len);
+ MD5Final(&ctx);
+
+ memcpy(out, ctx.digest, 16);
+
+ return 16;
+}
#include "keys.h"
#include "conf.h"
#include "memory.h"
+#include "util.h"
+#include "local.h"
+#include "logging.h"
+
typedef struct {
unsigned long id;
char *val;
int len;
+ int hash_id;
+ int auth_delay;
} Key;
#define MAX_KEYS 256
static Key keys[MAX_KEYS];
static int command_key_valid;
-static int command_key_pos;
+static int command_key_id;
static int cache_valid;
static unsigned long cache_key_id;
static int cache_key_pos;
/* ================================================== */
+static int
+determine_hash_delay(int key_id)
+{
+ NTP_Packet pkt;
+ struct timeval before, after;
+ unsigned long usecs, min_usecs=0;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ LCL_ReadRawTime(&before);
+ KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_SIZE,
+ (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
+ LCL_ReadRawTime(&after);
+
+ usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
+
+ if (i == 0 || usecs < min_usecs) {
+ min_usecs = usecs;
+ }
+ }
+
+#if 0
+ LOG(LOGS_INFO, LOGF_Keys, "authentication delay for key %d: %d useconds", key_id, min_usecs);
+#endif
+
+ /* Add on a bit extra to allow for copying, conversions etc */
+ return min_usecs + (min_usecs >> 4);
+}
+
+/* ================================================== */
+
/* Compare two keys */
static int
void
KEY_Reload(void)
{
- int i, len1;
+ int i, len1, fields;
char *key_file;
FILE *in;
unsigned long key_id;
- char line[KEYLEN+1], keyval[KEYLEN+1];
+ char line[KEYLEN+1], buf1[KEYLEN+1], buf2[KEYLEN+1];
+ char *keyval, *hashname;
for (i=0; i<n_keys; i++) {
Free(keys[i].val);
}
-
n_keys = 0;
key_file = CNF_GetKeysFile();
if (line[len1] == '\n') {
line[len1] = '\0';
}
+ fields = sscanf(line, "%lu%" SKEYLEN "s%" SKEYLEN "s", &key_id, buf1, buf2);
+ if (fields >= 2 && fields <= 3) {
+ if (fields == 3) {
+ hashname = buf1;
+ keyval = buf2;
+ } else {
+ hashname = "MD5";
+ keyval = buf1;
+ }
+ keys[n_keys].hash_id = HSH_GetHashId(hashname);
+ if (keys[n_keys].hash_id < 0) {
+ LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %d", key_id);
+ continue;
+ }
- if (sscanf(line, "%lu%" SKEYLEN "s", &key_id, keyval) == 2) {
keys[n_keys].id = key_id;
keys[n_keys].len = strlen(keyval);
keys[n_keys].val = MallocArray(char, 1 + keys[n_keys].len);
command_key_valid = 0;
cache_valid = 0;
+ for (i=0; i<n_keys; i++) {
+ keys[i].auth_delay = determine_hash_delay(keys[i].id);
+ }
+
return;
}
/* ================================================== */
-void
-KEY_CommandKey(char **key, int *len)
+static int
+get_key_pos(unsigned long key_id)
{
- unsigned long command_key_id;
-
- if (!command_key_valid) {
- command_key_id = CNF_GetCommandKey();
- command_key_pos = lookup_key(command_key_id);
- command_key_valid = 1;
+ if (!cache_valid || key_id != cache_key_id) {
+ cache_valid = 1;
+ cache_key_pos = lookup_key(key_id);
+ cache_key_id = key_id;
}
- if (command_key_pos >= 0) {
- *key = keys[command_key_pos].val;
- *len = keys[command_key_pos].len;
- } else {
- *key = "";
- *len = 0;
- }
+ return cache_key_pos;
}
/* ================================================== */
-int
-KEY_GetKey(unsigned long key_id, char **key, int *len)
+unsigned long
+KEY_GetCommandKey(void)
{
- if (!cache_valid || key_id != cache_key_id) {
- cache_valid = 1;
- cache_key_pos = lookup_key(key_id);
- cache_key_id = key_id;
+ if (!command_key_valid) {
+ command_key_id = CNF_GetCommandKey();
}
- if (cache_key_pos >= 0) {
- *key = keys[cache_key_pos].val;
- *len = keys[cache_key_pos].len;
- return 1;
- } else {
- *key = "";
- *len = 0;
- return 0;
- }
+ return command_key_id;
}
/* ================================================== */
}
}
}
+
+/* ================================================== */
+
+int
+KEY_GetAuthDelay(unsigned long key_id)
+{
+ int key_pos;
+
+ key_pos = get_key_pos(key_id);
+
+ if (key_pos < 0) {
+ return 0;
+ }
+
+ return keys[key_pos].auth_delay;
+}
+
+/* ================================================== */
+
+int
+KEY_GenerateAuth(unsigned long key_id, const unsigned char *data, int data_len,
+ unsigned char *auth, int auth_len)
+{
+ int key_pos;
+
+ key_pos = get_key_pos(key_id);
+
+ if (key_pos < 0) {
+ return 0;
+ }
+
+ return UTI_GenerateNTPAuth(keys[key_pos].hash_id,
+ (unsigned char *)keys[key_pos].val, keys[key_pos].len,
+ data, data_len, auth, auth_len);
+}
+
+/* ================================================== */
+
+int
+KEY_CheckAuth(unsigned long key_id, const unsigned char *data, int data_len,
+ const unsigned char *auth, int auth_len)
+{
+ int key_pos;
+
+ key_pos = get_key_pos(key_id);
+
+ if (key_pos < 0) {
+ return 0;
+ }
+
+ return UTI_CheckNTPAuth(keys[key_pos].hash_id,
+ (unsigned char *)keys[key_pos].val, keys[key_pos].len,
+ data, data_len, auth, auth_len);
+}
extern void KEY_Reload(void);
-extern void KEY_CommandKey(char **key, int *len);
+extern unsigned long KEY_GetCommandKey(void);
extern int KEY_GetKey(unsigned long key_id, char **key, int *len);
extern int KEY_KeyKnown(unsigned long key_id);
+extern int KEY_GetAuthDelay(unsigned long key_id);
+
+extern int KEY_GenerateAuth(unsigned long key_id, const unsigned char *data,
+ int data_len, unsigned char *auth, int auth_len);
+extern int KEY_CheckAuth(unsigned long key_id, const unsigned char *data,
+ int data_len, const unsigned char *auth, int auth_len);
#endif /* GOT_KEYS_H */
LOGF_CmdMon,
LOGF_Acquire,
LOGF_Manual,
+ LOGF_Keys,
LOGF_Logging,
LOGF_Rtc,
LOGF_Regress,
***********************************************************************
*/
-#include "config.h"
-
#include "md5.h"
/*
#include <inttypes.h>
#endif
+#include "hash.h"
+
typedef struct {
uint32_t hi;
uint32_t lo;
typedef uint32_t NTP_int32;
-#define AUTH_DATA_LEN 16
+#define MAX_NTP_AUTH_DATA_LEN MAX_HASH_LENGTH
/* Type definition for leap bits */
typedef enum {
NTP_int64 receive_ts;
NTP_int64 transmit_ts;
NTP_int32 auth_keyid;
- uint8_t auth_data[AUTH_DATA_LEN];
+ uint8_t auth_data[MAX_NTP_AUTH_DATA_LEN];
} NTP_Packet;
/* We have to declare a buffer type to hold a datagram read from the
uint8_t arbitrary[MAX_NTP_MESSAGE_SIZE];
} ReceiveBuffer;
-#define NTP_NORMAL_PACKET_SIZE (sizeof(NTP_Packet) - (sizeof(NTP_int32) + AUTH_DATA_LEN))
+#define NTP_NORMAL_PACKET_SIZE offsetof(NTP_Packet, auth_keyid)
/* ================================================== */
#include "conf.h"
#include "logging.h"
#include "keys.h"
-#include "md5.h"
#include "addrfilt.h"
#include "clientlog.h"
static ADF_AuthTable access_auth_table;
-static int md5_offset_usecs;
-
/* ================================================== */
/* Forward prototypes */
static void transmit_timeout(void *arg);
-static void determine_md5_delay(void);
/* ================================================== */
: -1;
access_auth_table = ADF_CreateTable();
-
- determine_md5_delay();
-
}
/* ================================================== */
/* ================================================== */
-/* ================================================== */
-
static int
-generate_packet_auth(NTP_Packet *pkt, unsigned long keyid)
+check_packet_auth(NTP_Packet *pkt, unsigned long keyid, int auth_len)
{
- int keylen;
- char *keytext;
- int keyok;
- MD5_CTX ctx;
-
- keyok = KEY_GetKey(keyid, &keytext, &keylen);
- if (keyok) {
- pkt->auth_keyid = htonl(keyid);
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned char *) keytext, keylen);
- MD5Update(&ctx, (unsigned char *) pkt, offsetof(NTP_Packet, auth_keyid));
- MD5Final(&ctx);
- memcpy(&(pkt->auth_data), &ctx.digest, 16);
- return 1;
- } else {
- pkt->auth_keyid = htonl(0);
- return 0;
- }
-}
-
-/* ================================================== */
-
-static void
-determine_md5_delay(void)
-{
- NTP_Packet pkt;
- struct timeval before, after;
- unsigned long usecs, min_usecs=0;
- MD5_CTX ctx;
- static const char *example_key = "#a0,243asd=-b ds";
- int slen;
- int i;
-
- slen = strlen(example_key);
-
- for (i=0; i<10; i++) {
- LCL_ReadRawTime(&before);
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned const char *) example_key, slen);
- MD5Update(&ctx, (unsigned const char *) &pkt, offsetof(NTP_Packet, auth_keyid));
- MD5Final(&ctx);
- LCL_ReadRawTime(&after);
-
- usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
-
- if (i == 0) {
- min_usecs = usecs;
- } else {
- if (usecs < min_usecs) {
- min_usecs = usecs;
- }
- }
-
- }
-
-#ifdef TRACEON
- LOG(LOGS_INFO, LOGF_NtpCore, "MD5 took %d useconds", min_usecs);
-#endif
-
- /* Add on a bit extra to allow for copying, conversions etc */
- md5_offset_usecs = min_usecs + (min_usecs >> 4);
-
-}
-
-/* ================================================== */
-
-static int
-check_packet_auth(NTP_Packet *pkt, unsigned long keyid)
-{
- int keylen;
- char *keytext;
- int keyok;
- MD5_CTX ctx;
-
- keyok = KEY_GetKey(keyid, &keytext, &keylen);
- if (keyok) {
- pkt->auth_keyid = htonl(keyid);
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned char *) keytext, keylen);
- MD5Update(&ctx, (unsigned char *) pkt, offsetof(NTP_Packet, auth_keyid));
- MD5Final(&ctx);
- if (!memcmp((void *) &ctx.digest, (void *) &(pkt->auth_data), 16)) {
- return 1;
- } else {
- return 0;
- }
- } else {
- return 0;
- }
+ return KEY_CheckAuth(keyid, (void *)pkt, offsetof(NTP_Packet, auth_keyid),
+ (void *)&(pkt->auth_data), auth_len);
}
/* ================================================== */
/* Authenticate */
if (do_auth) {
+ int auth_len;
/* Pre-compensate the transmit time by approx. how long it will
- take to generate the MD5 authentication bytes. */
- local_transmit.tv_usec += md5_offset_usecs;
+ take to generate the authentication data. */
+ local_transmit.tv_usec += KEY_GetAuthDelay(key_id);
UTI_NormaliseTimeval(&local_transmit);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts);
- generate_packet_auth(&message, key_id);
- NIO_SendAuthenticatedPacket(&message, where_to);
+
+ auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
+ offsetof(NTP_Packet, auth_keyid),
+ (unsigned char *)&message.auth_data, sizeof (message.auth_data));
+ if (auth_len > 0) {
+ message.auth_keyid = htonl(key_id);
+ NIO_SendAuthenticatedPacket(&message, where_to,
+ sizeof (message.auth_keyid) + auth_len);
+ }
} else {
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts);
NIO_SendNormalPacket(&message, where_to);
/* ================================================== */
static void
-receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, int do_auth)
+receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, int auth_len)
{
int pkt_leap;
int source_is_synchronized;
/* Test 5 relates to authentication. */
if (inst->do_auth) {
- if (do_auth) {
+ if (auth_len > 0) {
auth_key_id = ntohl(message->auth_keyid);
if (!KEY_KeyKnown(auth_key_id)) {
test5 = 0;
} else {
- test5 = check_packet_auth(message, auth_key_id);
+ test5 = check_packet_auth(message, auth_key_id, auth_len);
}
} else {
/* If we expect authenticated info from this peer/server and the packet
struct timeval *now, /* timestamp at time of receipt */
double now_err,
NCR_Instance inst, /* the instance record for this peer/server */
- int do_auth /* whether the received packet allegedly contains
- authentication info*/
+ int length /* the length of the received packet */
)
{
int pkt_mode;
int version;
int valid_auth, valid_key;
- int authenticate_reply;
+ int authenticate_reply, auth_len;
unsigned long auth_key_id;
unsigned long reply_auth_key_id;
/* Perform tests mentioned in RFC1305 to validate packet contents */
pkt_mode = (message->lvm >> 0) & 0x7;
+ /* Length of the authentication data, if any */
+ auth_len = length - (NTP_NORMAL_PACKET_SIZE + sizeof (message->auth_keyid));
+ if (auth_len < 0) {
+ auth_len = 0;
+ }
+
/* Now, depending on the mode we decide what to do */
switch (pkt_mode) {
case MODE_CLIENT:
CLG_LogNTPClientAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
- if (do_auth) {
+ if (auth_len > 0) {
auth_key_id = ntohl(message->auth_keyid);
valid_key = KEY_KeyKnown(auth_key_id);
if (valid_key) {
- valid_auth = check_packet_auth(message, auth_key_id);
+ valid_auth = check_packet_auth(message, auth_key_id, auth_len);
} else {
valid_auth = 0;
}
case MODE_ACTIVE:
/* Ordinary symmetric peering */
CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_PASSIVE:
/* In this software this case should not arise, we don't
/* This is where we have the remote configured as a server and he has
us configured as a peer - fair enough. */
CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_SERVER:
/* Nonsense - we can't have a preconfigured server */
case MODE_ACTIVE:
/* Slightly bizarre combination, but we can still process it */
CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_PASSIVE:
/* We have no passive peers in this software */
break;
case MODE_CLIENT:
/* Standard case where he's a server and we're the client */
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_SERVER:
/* RFC1305 error condition. */
/* This would arise if we have the remote configured as a peer and
he does not have us configured */
CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_PASSIVE:
/* Error condition in RFC1305. Also, we can't have any
break;
case MODE_CLIENT:
/* This is a wierd combination - how could it arise? */
- receive_packet(message, now, now_err, inst, do_auth);
+ receive_packet(message, now, now_err, inst, auth_len);
break;
case MODE_SERVER:
/* Error condition in RFC1305 */
struct timeval *now, /* timestamp at time of receipt */
double now_err, /* assumed error in the timestamp */
NTP_Remote_Address *remote_addr,
- int do_auth /* whether the received packet allegedly contains
- authentication info */
+ int length /* the length of the received packet */
)
{
NTP_Mode his_mode;
NTP_Mode my_mode;
int my_poll, version;
- int valid_key, valid_auth;
+ int valid_key, valid_auth, auth_len;
unsigned long key_id;
/* Check version */
he has supplied a wierd mode in his request, so ignore it. */
if (my_mode != MODE_UNDEFINED) {
+ int do_auth = 0;
+ auth_len = length - (NTP_NORMAL_PACKET_SIZE + sizeof (message->auth_keyid));
- if (do_auth) {
+ if (auth_len > 0) {
/* Only reply if we know the key and the packet authenticates
properly. */
key_id = ntohl(message->auth_keyid);
valid_key = KEY_KeyKnown(key_id);
+ do_auth = 1;
if (valid_key) {
- valid_auth = check_packet_auth(message, key_id);
+ valid_auth = check_packet_auth(message, key_id, auth_len);
} else {
valid_auth = 0;
}
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
-extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int do_auth);
+extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int length);
/* This routine is called when a new packet arrives off the network,
and we do not recognize its source */
-extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int do_auth);
+extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length);
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
#endif
}
- if (status == NTP_NORMAL_PACKET_SIZE) {
+ if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
- NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr);
-
- } else if (status == sizeof(NTP_Packet)) {
-
- NSR_ProcessAuthenticatedReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr);
+ NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr, status);
} else {
/* Send an authenticated packet to a given address */
void
-NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
+NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len)
{
- send_packet((void *) packet, sizeof(NTP_Packet), remote_addr);
+ send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr);
}
/* ================================================== */
extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
/* Function to transmit an authenticated packet */
-extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
+extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len);
/* Function to send a datagram to a remote machine's UDP echo port. */
extern void NIO_SendEcho(NTP_Remote_Address *remote_addr);
/* ================================================== */
-/* This routine is called by ntp_io when a new packet arrives off the network.*/
+/* This routine is called by ntp_io when a new packet arrives off the network,
+ possibly with an authentication tail */
void
-NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr)
+NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length)
{
int slot, found;
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
- NCR_ProcessKnown(message, now, now_err, records[slot].data, 0);
+ NCR_ProcessKnown(message, now, now_err, records[slot].data, length);
} else {
- NCR_ProcessUnknown(message, now, now_err, remote_addr, 0);
- }
-}
-
-/* ================================================== */
-
-/* This routine is called by ntp_io when a new packet with an authentication tail arrives off the network */
-void
-NSR_ProcessAuthenticatedReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr)
-{
- int slot, found;
-
- assert(initialised);
-
- find_slot(remote_addr, &slot, &found);
- if (found == 2) {
- NCR_ProcessKnown(message, now, now_err, records[slot].data, 1);
- } else {
- NCR_ProcessUnknown(message, now, now_err, remote_addr, 1);
+ NCR_ProcessUnknown(message, now, now_err, remote_addr, length);
}
}
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
/* This routine is called by ntp_io when a new packet arrives off the network */
-extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr);
-
-/* This routine is called by ntp_io when a new packet with an authentication tail arrives off the network */
-extern void NSR_ProcessAuthenticatedReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr);
+extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length);
/* Initialisation function */
extern void NSR_Initialise(void);
#include "sysincl.h"
#include "util.h"
-#include "md5.h"
+#include "hash.h"
/* ================================================== */
uint32_t
UTI_IPToRefid(IPAddr *ip)
{
- MD5_CTX ctx;
+ static int MD5_hash = -1;
+ unsigned char buf[16];
switch (ip->family) {
case IPADDR_INET4:
return ip->addr.in4;
case IPADDR_INET6:
- MD5Init(&ctx);
- MD5Update(&ctx, (unsigned const char *) ip->addr.in6, sizeof (ip->addr.in6));
- MD5Final(&ctx);
- return ctx.digest[0] << 24 | ctx.digest[1] << 16 | ctx.digest[2] << 8 | ctx.digest[3];
+ if (MD5_hash < 0) {
+ MD5_hash = HSH_GetHashId("MD5");
+ assert(MD5_hash >= 0);
+ }
+
+ if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof
+ (ip->addr.in6), NULL, 0, buf, 16) != 16) {
+ assert(0);
+ return 0;
+ };
+ return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
return 0;
}
}
/* ================================================== */
+
+int
+UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len, unsigned char *auth, int auth_len)
+{
+ return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
+}
+
+/* ================================================== */
+
+int
+UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len, const unsigned char *auth, int auth_len)
+{
+ unsigned char buf[MAX_HASH_LENGTH];
+
+ return UTI_GenerateNTPAuth(hash_id, key, key_len, data, data_len,
+ buf, sizeof (buf)) == auth_len && !memcmp(buf, auth, auth_len);
+}
#include "addressing.h"
#include "ntp.h"
#include "candm.h"
+#include "hash.h"
/* Convert a timeval into a floating point number of seconds */
extern void UTI_TimevalToDouble(struct timeval *a, double *b);
/* Set FD_CLOEXEC on descriptor */
extern void UTI_FdSetCloexec(int fd);
+extern int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len, unsigned char *auth, int auth_len);
+extern int UTI_CheckNTPAuth(int hash_id, const unsigned char *key, int key_len,
+ const unsigned char *data, int data_len, const unsigned char *auth, int auth_len);
+
#if defined (INLINE_UTILITIES)
#define INLINE_STATIC inline static
#include "util.c"