2 * authkeys.c - routines to manage the storage of authentication keys
12 #include "ntp_lists.h"
13 #include "ntp_malloc.h"
14 #include "ntp_stdlib.h"
17 #include <openssl/err.h>
20 /* define the payload region of auth_data beyond the list pointers */
21 #define auth_info_payload keyid
23 #define KEY_TRUSTED 0x001 /* this key is trusted */
26 typedef struct auth_alloc_tag auth_alloc
;
28 struct auth_alloc_tag
{
30 void * mem
; /* enable free() atexit */
33 auth_alloc
* auth_allocs
;
36 static inline unsigned short auth_log2(double x
);
37 static void auth_moremem (int);
38 static void auth_resize_hashtable(void);
39 static void alloc_auth_info(auth_info
**, keyid_t
, AUTH_Type
,
41 unsigned short, unsigned short, uint8_t *);
42 static void free_auth_info(auth_info
*, auth_info
**);
44 static void free_auth_mem(void);
47 static auth_info key_listhead
; /* list of all in-use keys */
49 * The hash table. This is indexed by the low order bits of the
50 * keyid. This gets updated in auth_resize_hashtable
52 #define KEYHASH(keyid) ((keyid) & authhashmask)
53 #define INIT_AUTHHASHSIZE 64
54 static unsigned short authhashbuckets
= INIT_AUTHHASHSIZE
;
55 static unsigned short authhashmask
= INIT_AUTHHASHSIZE
- 1;
56 static auth_info
**key_hash
;
58 unsigned int authnumkeys
; /* number of active keys */
59 unsigned int authnumfreekeys
; /* number of free keys */
60 unsigned long authkeylookups
; /* calls to lookup keys */
61 unsigned long authkeynotfound
; /* keys not found */
62 unsigned long authencryptions
; /* calls to authencrypt */
63 unsigned long authdigestencrypt
;/* calls to digest_encrypt */
64 unsigned long authcmacencrypt
; /* calls to cmac_encrypt */
65 unsigned long authdecryptions
; /* calls to authdecrypt */
66 unsigned long authdigestdecrypt
;/* calls to digest_decrypt */
67 unsigned long authdigestfail
; /* fails from digest_decrypt */
68 unsigned long authcmacdecrypt
; /* calls to cmac_decrypt*/
69 unsigned long authcmacfail
; /* fails from cmac_decrypt*/
70 uptime_t auth_timereset
; /* current_time when stats reset */
73 * Storage for free auth_info structures. We malloc() such things but
76 static auth_info
*authfreekeys
;
78 #define MEMINC 16 /* number of new free ones to get */
81 * There used to be a cache for the last key we used.
82 * It was also a kludge to pass arguments.
83 * We now pass a pointer to auth_data.
84 * A cache isn't all that useful:
85 * On a busy server, we can use the same auth_data for the reply.
86 * On a client, where the reply might hit the cache from the request,
87 * the extra cost of a lookup isn't significant.
92 * auth_init - initialize internal data
100 * Initialize hash table and free list
102 newalloc
= authhashbuckets
* sizeof(key_hash
[0]);
104 key_hash
= erealloc(key_hash
, newalloc
);
105 memset(key_hash
, '\0', newalloc
);
107 INIT_DLIST(key_listhead
, llink
);
110 atexit(&free_auth_mem
);
116 * auth_reset_stats - reset the authentication stat counters.
117 * can't use global current_time - not in library.
120 auth_reset_stats(uptime_t reset_time
)
125 authdigestencrypt
= 0;
128 authdigestdecrypt
= 0;
132 auth_timereset
= reset_time
;
138 * free_auth_mem - assist in leak detection by freeing all dynamic
139 * allocations from this module.
147 auth_alloc
* next_alloc
;
149 while (NULL
!= (sk
= HEAD_DLIST(key_listhead
, llink
))) {
150 free_auth_info(sk
, &key_hash
[KEYHASH(sk
->keyid
)]);
154 for (alloc
= auth_allocs
; NULL
!= alloc
; alloc
= next_alloc
) {
155 next_alloc
= alloc
->link
;
165 * auth_moremem - get some more free key structures
176 auth_alloc
* allocrec
;
177 # define MOREMEM_EXTRA_ALLOC (sizeof(*allocrec))
179 # define MOREMEM_EXTRA_ALLOC (0)
185 auth
= emalloc_zero((unsigned int)i
* sizeof(*auth
) + MOREMEM_EXTRA_ALLOC
);
189 authnumfreekeys
+= i
;
191 for (; i
> 0; i
--, auth
++) {
192 LINK_SLIST(authfreekeys
, auth
, llink
.f
);
196 allocrec
= (void *)auth
;
197 allocrec
->mem
= base
;
198 LINK_SLIST(auth_allocs
, allocrec
, link
);
214 allocated
= (int)authnumkeys
+ authnumfreekeys
;
215 additional
= keycount
- allocated
;
216 if (additional
> 0) {
217 auth_moremem(additional
);
219 auth_resize_hashtable();
223 static inline unsigned short
226 return (unsigned short)(log10(x
) / log10(2));
231 * auth_resize_hashtable
233 * Size hash table to average 4 or fewer entries per bucket initially,
234 * within the bounds of at least 4 and no more than 15 bits for the hash
235 * table index. Populate the hash table.
238 auth_resize_hashtable(void)
240 unsigned int totalkeys
;
241 unsigned short hashbits
;
246 totalkeys
= authnumkeys
+ (unsigned int)authnumfreekeys
;
247 hashbits
= auth_log2(totalkeys
/ 4.0) + 1;
248 hashbits
= max(4, hashbits
);
249 hashbits
= min(15, hashbits
);
251 authhashbuckets
= 1 << hashbits
;
252 authhashmask
= authhashbuckets
- 1;
253 newalloc
= authhashbuckets
* sizeof(key_hash
[0]);
255 key_hash
= erealloc(key_hash
, newalloc
);
256 memset(key_hash
, '\0', newalloc
);
258 ITER_DLIST_BEGIN(key_listhead
, auth
, llink
, auth_info
)
259 hash
= KEYHASH(auth
->keyid
);
260 LINK_SLIST(key_hash
[hash
], auth
, hlink
);
266 * alloc_auth_info - allocate and link in an auth_info slot.
267 * secret must be allocated with a free-compatible allocator.
268 * It is owned by the new auth_info and will be free()d by
277 unsigned short flags
,
278 unsigned short key_size
,
284 if (authnumfreekeys
< 1) {
287 UNLINK_HEAD_SLIST(auth
, authfreekeys
, llink
.f
);
290 * Hack to keep compiler -fanalyze happy
291 * If mru_entries !=0, the list is not empty
292 * and TAIL_DLIST will return a valid pointer
295 msyslog(LOG_ERR
, "AUTH: Bug in alloc_auth_info");
299 //ENSURE(sk != NULL);
303 auth
->key_size
= key_size
;
308 #if OPENSSL_VERSION_NUMBER > 0x20000000L
309 auth
->mac_ctx
= NULL
;
315 auth
->digest
= EVP_get_digestbyname(name
);
316 #if OPENSSL_VERSION_NUMBER > 0x20000000L
317 auth
->mac_ctx
= NULL
;
324 #if OPENSSL_VERSION_NUMBER > 0x20000000L
325 auth
->mac_ctx
= Setup_MAC_CTX(name
, auth
->key
, auth
->key_size
);
327 auth
->cipher
= EVP_get_cipherbyname(name
);
331 msyslog(LOG_ERR
, "BUG: alloc_auth_info: bogus type %u", type
);
334 LINK_SLIST(*bucket
, auth
, hlink
);
335 LINK_TAIL_DLIST(key_listhead
, auth
, llink
);
342 * free_auth_info - common code to remove a auth_info and recycle its entry.
350 auth_info
* unlinked
;
352 if (NULL
!= auth
->key
) {
353 memset(auth
->key
, '\0', auth
->key_size
);
357 #if OPENSSL_VERSION_NUMBER > 0x20000000L
358 EVP_MAC_CTX_free(auth
->mac_ctx
);
360 UNLINK_SLIST(unlinked
, *bucket
, auth
, hlink
, auth_info
);
361 //ENSURE(sk == unlinked);
362 UNLINK_DLIST(auth
, llink
);
363 memset((char *)auth
+ offsetof(auth_info
, auth_info_payload
), '\0',
364 sizeof(*auth
) - offsetof(auth_info
, auth_info_payload
));
365 LINK_SLIST(authfreekeys
, auth
, llink
.f
);
372 * authtrust - declare a key to be trusted/untrusted
373 * untrusted case not used (except for test code) 2018-Jun
385 * Search bin for key; if it does not exist and is untrusted,
388 bucket
= &key_hash
[KEYHASH(id
)];
389 for (auth
= *bucket
; NULL
!= auth
; auth
= auth
->hlink
) {
390 if (id
== auth
->keyid
)
393 if (!trust
&& NULL
== auth
)
397 * There are two conditions remaining. Either it does not
398 * exist and is to be trusted or it does exist and is or is
402 /* Key exists. Leave it around so we can trust it again. */
404 auth
->flags
|= KEY_TRUSTED
;
406 auth
->flags
&= ~KEY_TRUSTED
;
412 /* Create empty slot to hold trusted flag. No key. */
413 alloc_auth_info(bucket
, id
, AUTH_NONE
, 0, KEY_TRUSTED
, 0, NULL
);
417 * authlookup - find key, check trust
429 bucket
= &key_hash
[KEYHASH(keyno
)];
430 for (auth
= *bucket
; NULL
!= auth
; auth
= auth
->hlink
) {
431 if (keyno
== auth
->keyid
)
435 (AUTH_NONE
== auth
->type
) ||
436 (needtrust
&& !(KEY_TRUSTED
& auth
->flags
))) {
437 if (0) msyslog(LOG_INFO
, "DEBUG: authlookup fail: key %u, %s, auth: %s",
438 keyno
, needtrust
? "T" : "F",
439 (NULL
== auth
)? "NULL:" : "Ok");
458 if (0) msyslog(LOG_INFO
, "DEBUG: auth_setkey: key %u, %s, length %zu",
459 keyno
, name
, key_size
);
461 //ENSURE(??? <= USHRT_MAX);
462 //ENSURE(len < 4 * 1024);
464 * See if we already have the key. If so just stick in the
467 bucket
= &key_hash
[KEYHASH(keyno
)];
468 for (auth_info
* auth
= *bucket
; NULL
!= auth
; auth
= auth
->hlink
) {
469 if (keyno
== auth
->keyid
) {
474 #if OPENSSL_VERSION_NUMBER > 0x20000000L
475 auth
->mac_ctx
= NULL
;
481 auth
->digest
= EVP_get_digestbyname(name
);
482 #if OPENSSL_VERSION_NUMBER > 0x20000000L
483 auth
->mac_ctx
= NULL
;
490 #if OPENSSL_VERSION_NUMBER > 0x20000000L
491 EVP_MAC_CTX_free(auth
->mac_ctx
);
492 auth
->mac_ctx
= Setup_MAC_CTX(name
, \
493 auth
->key
, auth
->key_size
);
495 auth
->cipher
= EVP_get_cipherbyname(name
);
499 msyslog(LOG_ERR
, "BUG: auth_setkey: bogus type %u", type
);
502 if (NULL
!= auth
->key
) {
503 memset(auth
->key
, '\0', auth
->key_size
);
506 auth
->key_size
= (unsigned short)key_size
;
507 auth
->key
= emalloc(key_size
);
508 memcpy(auth
->key
, key
, key_size
);
514 * Need to allocate new structure. Do it.
516 newkey
= emalloc(key_size
);
517 memcpy(newkey
, key
, key_size
);
518 alloc_auth_info(bucket
, keyno
, type
, name
, 0,
519 (unsigned short)key_size
, newkey
);
521 if (debug
>= 4) { /* SPECIAL DEBUG */
522 printf("auth_setkey: key %d type %s len %d ", (int)keyno
,
523 name
, (int)key_size
);
524 for (size_t j
= 0; j
< key_size
; j
++)
525 printf("%02x", key
[j
]);
533 * auth_delkeys - delete untrusted keys, and clear all info
534 * except the trusted bit of trusted keys, in
535 * preparation for rereading the keys file.
542 ITER_DLIST_BEGIN(key_listhead
, auth
, llink
, auth_info
)
544 * Don't lose info as to which keys are trusted.
546 if (KEY_TRUSTED
& auth
->flags
) {
547 if (NULL
!= auth
->key
) {
548 memset(auth
->key
, '\0', auth
->key_size
);
553 auth
->type
= AUTH_NONE
;
555 #if OPENSSL_VERSION_NUMBER > 0x20000000L
556 auth
->mac_ctx
= NULL
;
561 free_auth_info(auth
, &key_hash
[KEYHASH(auth
->keyid
)]);
568 * authencrypt - generate message authenticator
569 * fills in keyid in packet
570 * Returns length of authenticator field
574 auth_info
* auth
, /* assumed setup correctly */
580 /* Pre-CMAC versions of this code had checking here.
581 * That logic has been pushed up a layer. 2018-June
585 pkt
[length
/ 4] = htonl(auth
->keyid
);
586 switch (auth
->type
) {
589 return digest_encrypt(auth
, pkt
, length
);
592 return cmac_encrypt(auth
, pkt
, length
);
595 msyslog(LOG_ERR
, "BUG: authencrypt: bogus type %u", auth
->type
);
603 * authdecrypt - verify message authenticator
605 * Returns true if authenticator valid.
609 auth_info
* auth
, /* assumed setup correctly */
617 /* Pre-CMAC versions of this code had checking here.
618 * That logic has been pushed up a layer. 2018-June
622 switch (auth
->type
) {
625 answer
= digest_decrypt(auth
, pkt
, length
, size
);
626 if (!answer
) authdigestfail
++;
630 answer
= cmac_decrypt(auth
, pkt
, length
, size
);
631 if (!answer
) authcmacfail
++;
635 msyslog(LOG_ERR
, "BUG: authdecrypt: bogus type %u", auth
->type
);
641 #if OPENSSL_VERSION_NUMBER > 0x20000000L
642 /* Name needs "-CBC" already appended */
643 EVP_MAC_CTX
* Setup_MAC_CTX(const char *name
, uint8_t *key
, int keylen
) {
644 OSSL_PARAM params
[3];
645 char temp
[100]; /* Hack: OSSL_PARAM doesn't like const */
647 EVP_MAC_CTX
*ctx
= EVP_MAC_CTX_dup(evp_ctx
);
649 unsigned long err
= ERR_get_error();
650 char * str
= ERR_error_string(err
, NULL
);
651 msyslog(LOG_ERR
, "Setup_MAC_CTX: EVP_MAC_CTX_dup failed: %s", str
);
655 strlcpy(temp
, name
, sizeof(temp
));
656 params
[0] = OSSL_PARAM_construct_utf8_string("cipher", temp
, 0);
657 params
[1] = OSSL_PARAM_construct_octet_string("key", key
, keylen
);
658 params
[2] = OSSL_PARAM_construct_end();
659 if (0 == EVP_MAC_CTX_set_params(ctx
, params
)) {
660 unsigned long err
= ERR_get_error();
661 char * str
= ERR_error_string(err
, NULL
);
662 msyslog(LOG_ERR
, "EVP_MAC_CTX_set_params() failed: %s: %s.",