CI opt-test: Drop Python2 & Bash in Fedora latest.
[ntpsec.git] / libntp / authkeys.c
blob696a43405c92b4daed2790bdf28bea10dae5cc1d
1 /*
2 * authkeys.c - routines to manage the storage of authentication keys
3 */
4 #include "config.h"
6 #include <math.h>
7 #include <stdio.h>
8 #include <string.h>
10 #include "ntp.h"
11 #include "ntpd.h"
12 #include "ntp_lists.h"
13 #include "ntp_malloc.h"
14 #include "ntp_stdlib.h"
15 #include "ntp_auth.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 */
25 #ifdef DEBUG
26 typedef struct auth_alloc_tag auth_alloc;
28 struct auth_alloc_tag {
29 auth_alloc * link;
30 void * mem; /* enable free() atexit */
33 auth_alloc * auth_allocs;
34 #endif /* DEBUG */
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,
40 const char *,
41 unsigned short, unsigned short, uint8_t *);
42 static void free_auth_info(auth_info *, auth_info **);
43 #ifdef DEBUG
44 static void free_auth_mem(void);
45 #endif
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
74 * never free them.
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
94 void
95 auth_init(void)
97 size_t newalloc;
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);
109 #ifdef DEBUG
110 atexit(&free_auth_mem);
111 #endif
116 * auth_reset_stats - reset the authentication stat counters.
117 * can't use global current_time - not in library.
119 void
120 auth_reset_stats(uptime_t reset_time)
122 authkeylookups = 0;
123 authkeynotfound = 0;
124 authencryptions = 0;
125 authdigestencrypt = 0;
126 authcmacencrypt = 0;
127 authdecryptions = 0;
128 authdigestdecrypt = 0;
129 authdigestfail = 0;
130 authcmacdecrypt = 0;
131 authcmacfail = 0;
132 auth_timereset = reset_time;
138 * free_auth_mem - assist in leak detection by freeing all dynamic
139 * allocations from this module.
141 #ifdef DEBUG
142 static void
143 free_auth_mem(void)
145 auth_info * sk;
146 auth_alloc * alloc;
147 auth_alloc * next_alloc;
149 while (NULL != (sk = HEAD_DLIST(key_listhead, llink))) {
150 free_auth_info(sk, &key_hash[KEYHASH(sk->keyid)]);
152 free(key_hash);
153 key_hash = NULL;
154 for (alloc = auth_allocs; NULL != alloc; alloc = next_alloc) {
155 next_alloc = alloc->link;
156 free(alloc->mem);
158 authfreekeys = NULL;
159 authnumfreekeys = 0;
161 #endif /* DEBUG */
165 * auth_moremem - get some more free key structures
167 static void
168 auth_moremem(
169 int keycount
172 auth_info * auth;
173 int i;
174 #ifdef DEBUG
175 void * base;
176 auth_alloc * allocrec;
177 # define MOREMEM_EXTRA_ALLOC (sizeof(*allocrec))
178 #else
179 # define MOREMEM_EXTRA_ALLOC (0)
180 #endif
182 i = (keycount > 0)
183 ? keycount
184 : MEMINC;
185 auth = emalloc_zero((unsigned int)i * sizeof(*auth) + MOREMEM_EXTRA_ALLOC);
186 #ifdef DEBUG
187 base = auth;
188 #endif
189 authnumfreekeys += i;
191 for (; i > 0; i--, auth++) {
192 LINK_SLIST(authfreekeys, auth, llink.f);
195 #ifdef DEBUG
196 allocrec = (void *)auth;
197 allocrec->mem = base;
198 LINK_SLIST(auth_allocs, allocrec, link);
199 #endif
204 * auth_prealloc
206 void
207 auth_prealloc(
208 int keycount
211 int allocated;
212 int additional;
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
224 auth_log2(double x)
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.
237 static void
238 auth_resize_hashtable(void)
240 unsigned int totalkeys;
241 unsigned short hashbits;
242 unsigned short hash;
243 size_t newalloc;
244 auth_info * auth;
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);
261 ITER_DLIST_END()
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
269 * free_auth_info().
271 static void
272 alloc_auth_info(
273 auth_info ** bucket,
274 keyid_t keyid,
275 AUTH_Type type,
276 const char * name,
277 unsigned short flags,
278 unsigned short key_size,
279 uint8_t * key
282 auth_info * auth;
284 if (authnumfreekeys < 1) {
285 auth_moremem(-1);
287 UNLINK_HEAD_SLIST(auth, authfreekeys, llink.f);
288 if (1) {
289 /* FIXME -fanalyze
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
294 if (NULL == auth) {
295 msyslog(LOG_ERR, "AUTH: Bug in alloc_auth_info");
296 exit(3);
299 //ENSURE(sk != NULL);
300 auth->keyid = keyid;
301 auth->type = type;
302 auth->flags = flags;
303 auth->key_size = key_size;
304 auth->key = key;
305 switch (type) {
306 case AUTH_NONE:
307 auth->digest = NULL;
308 #if OPENSSL_VERSION_NUMBER > 0x20000000L
309 auth->mac_ctx = NULL;
310 #else
311 auth->cipher = NULL;
312 #endif
313 break;
314 case AUTH_DIGEST:
315 auth->digest = EVP_get_digestbyname(name);
316 #if OPENSSL_VERSION_NUMBER > 0x20000000L
317 auth->mac_ctx = NULL;
318 #else
319 auth->cipher = NULL;
320 #endif
321 break;
322 case AUTH_CMAC:
323 auth->digest = NULL;
324 #if OPENSSL_VERSION_NUMBER > 0x20000000L
325 auth->mac_ctx = Setup_MAC_CTX(name, auth->key, auth->key_size);
326 #else
327 auth->cipher = EVP_get_cipherbyname(name);
328 #endif
329 break;
330 default:
331 msyslog(LOG_ERR, "BUG: alloc_auth_info: bogus type %u", type);
332 exit(1);
334 LINK_SLIST(*bucket, auth, hlink);
335 LINK_TAIL_DLIST(key_listhead, auth, llink);
336 authnumfreekeys--;
337 authnumkeys++;
342 * free_auth_info - common code to remove a auth_info and recycle its entry.
344 static void
345 free_auth_info(
346 auth_info * auth,
347 auth_info ** bucket
350 auth_info * unlinked;
352 if (NULL != auth->key) {
353 memset(auth->key, '\0', auth->key_size);
354 free(auth->key);
355 auth->key = NULL;
357 #if OPENSSL_VERSION_NUMBER > 0x20000000L
358 EVP_MAC_CTX_free(auth->mac_ctx);
359 #endif
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);
366 authnumkeys--;
367 authnumfreekeys++;
372 * authtrust - declare a key to be trusted/untrusted
373 * untrusted case not used (except for test code) 2018-Jun
375 void
376 authtrust(
377 keyid_t id,
378 bool trust
381 auth_info ** bucket;
382 auth_info * auth;
385 * Search bin for key; if it does not exist and is untrusted,
386 * forget it.
388 bucket = &key_hash[KEYHASH(id)];
389 for (auth = *bucket; NULL != auth; auth = auth->hlink) {
390 if (id == auth->keyid)
391 break;
393 if (!trust && NULL == auth)
394 return;
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
399 * not to be trusted.
401 if (NULL != auth) {
402 /* Key exists. Leave it around so we can trust it again. */
403 if (trust) {
404 auth->flags |= KEY_TRUSTED;
405 } else {
406 auth->flags &= ~KEY_TRUSTED;
408 return;
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
419 auth_info *
420 authlookup(
421 keyid_t keyno,
422 bool needtrust
425 auth_info * auth;
426 auth_info ** bucket;
428 authkeylookups++;
429 bucket = &key_hash[KEYHASH(keyno)];
430 for (auth = *bucket; NULL != auth; auth = auth->hlink) {
431 if (keyno == auth->keyid)
432 break;
434 if (NULL == auth ||
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");
440 authkeynotfound++;
441 return NULL;
443 return auth;
446 void
447 auth_setkey(
448 keyid_t keyno,
449 AUTH_Type type,
450 const char * name,
451 const uint8_t *key,
452 size_t key_size
455 auth_info ** bucket;
456 uint8_t * newkey;
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
465 * new value.
467 bucket = &key_hash[KEYHASH(keyno)];
468 for (auth_info * auth = *bucket; NULL != auth; auth = auth->hlink) {
469 if (keyno == auth->keyid) {
470 auth->type = type;
471 switch (type) {
472 case AUTH_NONE:
473 auth->digest = NULL;
474 #if OPENSSL_VERSION_NUMBER > 0x20000000L
475 auth->mac_ctx = NULL;
476 #else
477 auth->cipher = NULL;
478 #endif
479 break;
480 case AUTH_DIGEST:
481 auth->digest = EVP_get_digestbyname(name);
482 #if OPENSSL_VERSION_NUMBER > 0x20000000L
483 auth->mac_ctx = NULL;
484 #else
485 auth->cipher = NULL;
486 #endif
487 break;
488 case AUTH_CMAC:
489 auth->digest = 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);
494 #else
495 auth->cipher = EVP_get_cipherbyname(name);
496 #endif
497 break;
498 default:
499 msyslog(LOG_ERR, "BUG: auth_setkey: bogus type %u", type);
500 exit(1);
502 if (NULL != auth->key) {
503 memset(auth->key, '\0', auth->key_size);
504 free(auth->key);
506 auth->key_size = (unsigned short)key_size;
507 auth->key = emalloc(key_size);
508 memcpy(auth->key, key, key_size);
509 return;
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);
520 #ifdef DEBUG
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]);
526 printf("\n");
528 #endif
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.
537 void
538 auth_delkeys(void)
540 auth_info * auth;
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);
549 free(auth->key);
550 auth->key = NULL;
552 auth->key_size = 0;
553 auth->type = AUTH_NONE;
554 auth->digest = NULL;
555 #if OPENSSL_VERSION_NUMBER > 0x20000000L
556 auth->mac_ctx = NULL;
557 #else
558 auth->cipher = NULL;
559 #endif
560 } else {
561 free_auth_info(auth, &key_hash[KEYHASH(auth->keyid)]);
563 ITER_DLIST_END()
568 * authencrypt - generate message authenticator
569 * fills in keyid in packet
570 * Returns length of authenticator field
573 authencrypt(
574 auth_info * auth, /* assumed setup correctly */
575 uint32_t * pkt,
576 int length
580 /* Pre-CMAC versions of this code had checking here.
581 * That logic has been pushed up a layer. 2018-June
584 authencryptions++;
585 pkt[length / 4] = htonl(auth->keyid);
586 switch (auth->type) {
587 case AUTH_DIGEST:
588 authdigestencrypt++;
589 return digest_encrypt(auth, pkt, length);
590 case AUTH_CMAC:
591 authcmacencrypt++;
592 return cmac_encrypt(auth, pkt, length);
593 case AUTH_NONE:
594 default:
595 msyslog(LOG_ERR, "BUG: authencrypt: bogus type %u", auth->type);
596 exit(1);
598 return 0;
603 * authdecrypt - verify message authenticator
605 * Returns true if authenticator valid.
607 bool
608 authdecrypt(
609 auth_info * auth, /* assumed setup correctly */
610 uint32_t * pkt,
611 int length,
612 int size
615 bool answer;
617 /* Pre-CMAC versions of this code had checking here.
618 * That logic has been pushed up a layer. 2018-June
621 authdecryptions++;
622 switch (auth->type) {
623 case AUTH_DIGEST:
624 authdigestdecrypt++;
625 answer = digest_decrypt(auth, pkt, length, size);
626 if (!answer) authdigestfail++;
627 return answer;
628 case AUTH_CMAC:
629 authcmacdecrypt++;
630 answer = cmac_decrypt(auth, pkt, length, size);
631 if (!answer) authcmacfail++;
632 return answer;
633 case AUTH_NONE:
634 default:
635 msyslog(LOG_ERR, "BUG: authdecrypt: bogus type %u", auth->type);
636 exit(1);
638 return false;
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);
648 if (NULL == 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);
652 exit(1);
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.",
663 str, name);
664 exit(1);
666 return ctx;
668 #endif