diff --git a/cmdparse.c b/cmdparse.c index 6fae81c..37037d1 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -261,7 +261,7 @@ CPS_SplitWord(char *line) /* ================================================== */ int -CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) +CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key) { char *s1, *s2, *s3, *s4; @@ -278,10 +278,10 @@ CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) return 0; if (*s3) { - *hash = s2; + *type = s2; *key = s3; } else { - *hash = "MD5"; + *type = "MD5"; *key = s2; } diff --git a/cmdparse.h b/cmdparse.h index 19f4bb7..694d12d 100644 --- a/cmdparse.h +++ b/cmdparse.h @@ -49,6 +49,6 @@ extern void CPS_NormalizeLine(char *line); extern char *CPS_SplitWord(char *line); /* Parse a key from keyfile */ -extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key); +extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key); #endif /* GOT_CMDPARSE_H */ diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index 1516d7e..117b650 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -96,7 +96,7 @@ interval in order to allow a burst with two requests. *key* _ID_::: The NTP protocol supports a message authentication code (MAC) to prevent computers having their system time upset by rogue packets being sent to them. -The MAC is generated as a function of a password specified in the key file, +The MAC is generated as a function of a key specified in the key file, which is specified by the <> directive. + The *key* option specifies which key (with an ID in the range 1 through 2^32-1) @@ -2017,8 +2017,10 @@ include @SYSCONFDIR@/chrony.d/*.conf ---- [[keyfile]]*keyfile* _file_:: -This directive is used to specify the location of the file containing ID-key -pairs for authentication of NTP packets. +This directive is used to specify the location of the file containing symmetric +keys which are shared between NTP servers and clients, or peers, in order to +authenticate NTP packets with a message authentication code (MAC) using a +cryptographic hash function or cipher. + The format of the directive is shown in the example below: + @@ -2033,30 +2035,41 @@ format of the file is shown below: 10 tulip 11 hyacinth 20 MD5 ASCII:crocus -25 SHA1 HEX:1dc764e0791b11fa67efc7ecbc4b0d73f68a070c +25 SHA1 HEX:933F62BE1D604E68A81B557F18CFA200483F5B70 +30 AES128 HEX:7EA62AE64D190114D46D5A082F948EC1 +31 AES256 HEX:37DDCBC67BB902BCB8E995977FAB4D2B5642F5B32EBCEEE421921D97E5CBFE39 ... ---- + -Each line consists of an ID, name of an authentication hash function (optional), -and a password. The ID can be any unsigned integer in the range 1 through -2^32-1. The default hash function is *MD5*, which is always supported. +Each line consists of an ID, optional type, and key. + +The ID can be any positive integer in the range 1 through 2^32-1. ++ +The type is a name of a cryptographic hash function or cipher which is used to +generate and verify the MAC. The default type is *MD5*, which is always +supported. If *chronyd* was built with enabled support for hashing using a crypto library (nettle, nss, or libtomcrypt), the following functions are available: *MD5*, *SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is -*chronyd* using, some or all of the following functions may also be available: -*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*. +*chronyd* using, some of the following hash functions and ciphers may +also be available: +*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *TIGER*, *WHIRLPOOL*, *AES128*, +*AES256*. + -The password can be specified as a string of characters not containing white +The key can be specified as a string of ASCII characters not containing white space with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:* prefix. The maximum length of the line is 2047 characters. +If the type is a cipher, the length of the key must match the cipher (i.e. 128 +bits for AES128 and 256 bits for AES256). + -The password is used with the hash function to generate and verify a message -authentication code (MAC) in NTP packets. It is recommended to use SHA1, or -stronger, hash function with random passwords specified in the hexadecimal -format that have at least 128 bits. *chronyd* will log a warning to -syslog on start if a source is specified in the configuration file with a key -that has password shorter than 80 bits. +It is recommended to use randomly generated keys, specified in the hexadecimal +format, which are at least 128 bits long (i.e. they have at least 32 characters +after the *HEX:* prefix). *chronyd* will log a warning to syslog on start if a +source is specified in the configuration file with a key shorter than 80 bits. ++ +The recommended key types are AES ciphers and SHA3 hash functions. MD5 should +be avoided unless no other type is supported on the server and client, or +peers. + The <> command of *chronyc* can be used to generate random keys for the key file. By default, it generates 160-bit MD5 or diff --git a/keys.c b/keys.c index 44a3f7a..912257e 100644 --- a/keys.c +++ b/keys.c @@ -32,6 +32,7 @@ #include "array.h" #include "keys.h" +#include "cmac.h" #include "cmdparse.h" #include "conf.h" #include "memory.h" @@ -42,11 +43,22 @@ /* Consider 80 bits as the absolute minimum for a secure key */ #define MIN_SECURE_KEY_LENGTH 10 +typedef enum { + NTP_MAC, + CMAC, +} KeyClass; + typedef struct { uint32_t id; - char *val; - int len; - int hash_id; + KeyClass class; + union { + struct { + unsigned char *value; + int length; + int hash_id; + } ntp_mac; + CMC_Instance cmac; + } data; int auth_delay; } Key; @@ -62,9 +74,21 @@ static void free_keys(void) { unsigned int i; + Key *key; - for (i = 0; i < ARR_GetSize(keys); i++) - Free(((Key *)ARR_GetElement(keys, i))->val); + for (i = 0; i < ARR_GetSize(keys); i++) { + key = ARR_GetElement(keys, i); + switch (key->class) { + case NTP_MAC: + Free(key->data.ntp_mac.value); + break; + case CMAC: + CMC_DestroyInstance(key->data.cmac); + break; + default: + assert(0); + } + } ARR_SetSize(keys, 0); cache_valid = 0; @@ -129,10 +153,10 @@ determine_hash_delay(uint32_t key_id) } /* ================================================== */ -/* Decode password encoded in ASCII or HEX */ +/* Decode key encoded in ASCII or HEX */ static int -decode_password(char *key) +decode_key(char *key) { int i, j, len = strlen(key); char buf[3], *p; @@ -184,11 +208,11 @@ compare_keys_by_id(const void *a, const void *b) void KEY_Reload(void) { - unsigned int i, line_number; + unsigned int i, line_number, key_length, cmac_key_length; FILE *in; - uint32_t key_id; - char line[2048], *keyval, *key_file; - const char *hashname; + char line[2048], *key_file, *key_value; + const char *key_type; + int hash_id; Key key; free_keys(); @@ -212,26 +236,43 @@ KEY_Reload(void) if (!*line) continue; - if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) { + memset(&key, 0, sizeof (key)); + + if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) { LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file); continue; } - key.hash_id = HSH_GetHashId(hashname); - if (key.hash_id < 0) { - LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id); + key_length = decode_key(key_value); + if (key_length == 0) { + LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id); continue; } - key.len = decode_password(keyval); - if (!key.len) { - LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id); + hash_id = HSH_GetHashId(key_type); + cmac_key_length = CMC_GetKeyLength(key_type); + + if (hash_id >= 0) { + key.class = NTP_MAC; + key.data.ntp_mac.value = MallocArray(unsigned char, key_length); + memcpy(key.data.ntp_mac.value, key_value, key_length); + key.data.ntp_mac.length = key_length; + key.data.ntp_mac.hash_id = hash_id; + } else if (cmac_key_length > 0) { + if (cmac_key_length != key_length) { + LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)", + key_type, key.id, 8 * cmac_key_length); + continue; + } + + key.class = CMAC; + key.data.cmac = CMC_CreateInstance(key_type, (unsigned char *)key_value, key_length); + assert(key.data.cmac); + } else { + LOG(LOGS_WARN, "Unknown hash function or cipher in key %"PRIu32, key.id); continue; } - key.id = key_id; - key.val = MallocArray(char, key.len); - memcpy(key.val, keyval, key.len); ARR_AppendElement(keys, &key); } @@ -334,7 +375,15 @@ KEY_GetAuthLength(uint32_t key_id) if (!key) return 0; - return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf)); + switch (key->class) { + case NTP_MAC: + return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf)); + case CMAC: + return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf)); + default: + assert(0); + return 0; + } } /* ================================================== */ @@ -349,30 +398,41 @@ KEY_CheckKeyLength(uint32_t key_id) if (!key) return 0; - return key->len >= MIN_SECURE_KEY_LENGTH; + switch (key->class) { + case NTP_MAC: + return key->data.ntp_mac.length >= MIN_SECURE_KEY_LENGTH; + default: + return 1; + } } /* ================================================== */ static int -generate_ntp_auth(int hash_id, const unsigned char *key, int key_len, - const unsigned char *data, int data_len, - unsigned char *auth, int auth_len) +generate_auth(Key *key, 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); + switch (key->class) { + case NTP_MAC: + return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value, + key->data.ntp_mac.length, data, data_len, auth, auth_len); + case CMAC: + return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len); + default: + return 0; + } } /* ================================================== */ static int -check_ntp_auth(int hash_id, const unsigned char *key, int key_len, - const unsigned char *data, int data_len, - const unsigned char *auth, int auth_len, int trunc_len) +check_auth(Key *key, const unsigned char *data, int data_len, + const unsigned char *auth, int auth_len, int trunc_len) { unsigned char buf[MAX_HASH_LENGTH]; int hash_len; - hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf)); + hash_len = generate_auth(key, data, data_len, buf, sizeof (buf)); return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len); } @@ -381,7 +441,7 @@ check_ntp_auth(int hash_id, const unsigned char *key, int key_len, int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, - unsigned char *auth, int auth_len) + unsigned char *auth, int auth_len) { Key *key; @@ -390,8 +450,7 @@ KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len, if (!key) return 0; - return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, - data, data_len, auth, auth_len); + return generate_auth(key, data, data_len, auth, auth_len); } /* ================================================== */ @@ -407,6 +466,5 @@ KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, if (!key) return 0; - return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, - data, data_len, auth, auth_len, trunc_len); + return check_auth(key, data, data_len, auth, auth_len, trunc_len); }