/* * authkeys.c - routines to manage the storage of authentication keys */ #ifdef HAVE_CONFIG_H # include #endif #include #include "ntp_types.h" #include "ntp_fp.h" #include "ntp.h" #include "ntpd.h" #include "ntp_string.h" #include "ntp_malloc.h" #include "ntp_stdlib.h" /* * Structure to store keys in in the hash table. */ struct savekey { struct savekey *next; union { u_char MD5_key[64]; /* for keys up to to 512 bits */ } k; keyid_t keyid; /* key identifier */ int type; /* key type */ u_short flags; /* flags that wave */ u_long lifetime; /* remaining lifetime */ int keylen; /* key length */ }; #define KEY_TRUSTED 0x001 /* this key is trusted */ /* * The hash table. This is indexed by the low order bits of the * keyid. We make this fairly big for potentially busy servers. */ #define HASHSIZE 64 #define HASHMASK ((HASHSIZE)-1) #define KEYHASH(keyid) ((keyid) & HASHMASK) struct savekey *key_hash[HASHSIZE]; u_long authkeynotfound; /* keys not found */ u_long authkeylookups; /* calls to lookup keys */ u_long authnumkeys; /* number of active keys */ u_long authkeyexpired; /* key lifetime expirations */ u_long authkeyuncached; /* cache misses */ u_long authnokey; /* calls to encrypt with no key */ u_long authencryptions; /* calls to encrypt */ u_long authdecryptions; /* calls to decrypt */ /* * Storage for free key structures. We malloc() such things but * never free them. */ struct savekey *authfreekeys; int authnumfreekeys; #define MEMINC 12 /* number of new free ones to get */ /* * The key cache. We cache the last key we looked at here. */ keyid_t cache_keyid; /* key identifier */ u_char *cache_key; /* key pointer */ u_int cache_keylen; /* key length */ int cache_type; /* key type */ u_short cache_flags; /* flags that wave */ /* * init_auth - initialize internal data */ void init_auth(void) { /* * Initialize hash table and free list */ memset((char *)key_hash, 0, sizeof key_hash); } /* * auth_findkey - find a key in the hash table */ struct savekey * auth_findkey( keyid_t keyno ) { struct savekey *sk; sk = key_hash[KEYHASH(keyno)]; while (sk != 0) { if (keyno == sk->keyid) return (sk); sk = sk->next; } return (0); } /* * auth_havekey - return one if the key is known */ int auth_havekey( keyid_t keyno ) { struct savekey *sk; if (keyno == 0 || (keyno == cache_keyid)) return (1); sk = key_hash[KEYHASH(keyno)]; while (sk != 0) { if (keyno == sk->keyid) return (1); sk = sk->next; } return (0); } /* * authhavekey - return one and cache the key, if known and trusted. */ int authhavekey( keyid_t keyno ) { struct savekey *sk; authkeylookups++; if (keyno == 0 || keyno == cache_keyid) return (1); /* * Seach the bin for the key. If found and the key type * is zero, somebody marked it trusted without specifying * a key or key type. In this case consider the key missing. */ authkeyuncached++; sk = key_hash[KEYHASH(keyno)]; while (sk != NULL) { if (keyno == sk->keyid) { if (sk->type == 0) { authkeynotfound++; return (0); } break; } sk = sk->next; } /* * If the key is not found, or if it is found but not trusted, * the key is not considered found. */ if (sk == NULL) { authkeynotfound++; return (0); } if (!(sk->flags & KEY_TRUSTED)) { authnokey++; return (0); } /* * The key is found and trusted. Initialize the key cache. */ cache_keyid = sk->keyid; cache_type = sk->type; cache_flags = sk->flags; cache_key = sk->k.MD5_key; cache_keylen = sk->keylen; return (1); } /* * auth_moremem - get some more free key structures */ int auth_moremem(void) { struct savekey *sk; int i; sk = (struct savekey *)calloc(MEMINC, sizeof(struct savekey)); if (sk == 0) return (0); for (i = MEMINC; i > 0; i--) { sk->next = authfreekeys; authfreekeys = sk++; } authnumfreekeys += MEMINC; return (authnumfreekeys); } /* * authtrust - declare a key to be trusted/untrusted */ void authtrust( keyid_t keyno, u_long trust ) { struct savekey *sk; /* * Search bin for key; if it does not exist and is untrusted, * forget it. */ sk = key_hash[KEYHASH(keyno)]; while (sk != 0) { if (keyno == sk->keyid) break; sk = sk->next; } if (sk == 0 && !trust) return; /* * There are two conditions remaining. Either it does not * exist and is to be trusted or it does exist and is or is * not to be trusted. */ if (sk != 0) { if (cache_keyid == keyno) { cache_flags = 0; cache_keyid = 0; } /* * Key exists. If it is to be trusted, say so and * update its lifetime. If not, return it to the * free list. */ if (trust > 0) { sk->flags |= KEY_TRUSTED; if (trust > 1) sk->lifetime = current_time + trust; else sk->lifetime = 0; return; } sk->flags &= ~KEY_TRUSTED; { struct savekey *skp; skp = key_hash[KEYHASH(keyno)]; if (skp == sk) { key_hash[KEYHASH(keyno)] = sk->next; } else { while (skp->next != sk) skp = skp->next; skp->next = sk->next; } authnumkeys--; sk->next = authfreekeys; authfreekeys = sk; authnumfreekeys++; } return; } /* * Here there is not key, but the key is to be trusted. There * seems to be a disconnect here. Here we allocate a new key, * but do not specify a key type, key or key length. */ if (authnumfreekeys == 0) if (auth_moremem() == 0) return; sk = authfreekeys; authfreekeys = sk->next; authnumfreekeys--; sk->keyid = keyno; sk->type = 0; sk->keylen = 0; sk->flags = KEY_TRUSTED; sk->next = key_hash[KEYHASH(keyno)]; key_hash[KEYHASH(keyno)] = sk; authnumkeys++; return; } /* * authistrusted - determine whether a key is trusted */ int authistrusted( keyid_t keyno ) { struct savekey *sk; if (keyno == cache_keyid) return ((cache_flags & KEY_TRUSTED) != 0); authkeyuncached++; sk = key_hash[KEYHASH(keyno)]; while (sk != 0) { if (keyno == sk->keyid) break; sk = sk->next; } if (sk == 0) { authkeynotfound++; return (0); } else if (!(sk->flags & KEY_TRUSTED)) { authkeynotfound++; return (0); } return (1); } void MD5auth_setkey( keyid_t keyno, int keytype, const u_char *key, const int len ) { struct savekey *sk; /* * See if we already have the key. If so just stick in the * new value. */ sk = key_hash[KEYHASH(keyno)]; while (sk != NULL) { if (keyno == sk->keyid) { sk->type = keytype; sk->keylen = min(len, sizeof(sk->k.MD5_key)); #ifndef DISABLE_BUG1243_FIX memcpy(sk->k.MD5_key, key, sk->keylen); #else strncpy((char *)sk->k.MD5_key, (const char *)key, sizeof(sk->k.MD5_key)); #endif if (cache_keyid == keyno) { cache_flags = 0; cache_keyid = 0; } return; } sk = sk->next; } /* * Need to allocate new structure. Do it. */ if (0 == authnumfreekeys && !auth_moremem()) return; sk = authfreekeys; authfreekeys = sk->next; authnumfreekeys--; sk->keyid = keyno; sk->type = keytype; sk->flags = 0; sk->lifetime = 0; sk->keylen = min(len, sizeof(sk->k.MD5_key)); #ifndef DISABLE_BUG1243_FIX memcpy(sk->k.MD5_key, key, sk->keylen); #else strncpy((char *)sk->k.MD5_key, (const char *)key, sizeof(sk->k.MD5_key)); #endif sk->next = key_hash[KEYHASH(keyno)]; key_hash[KEYHASH(keyno)] = sk; #ifdef DEBUG if (debug > 1) { char hex[] = "0123456789abcdef"; int j; printf("auth_setkey: key %d type %d len %d ", sk->keyid, sk->type, sk->keylen); for (j = 0; j < sk->keylen; j++) printf("%c%c", hex[key[j] >> 4], hex[key[j] & 0xf]); printf("\n"); } #endif authnumkeys++; } /* * auth_delkeys - delete all known keys, in preparation for rereading * the keys file (presumably) */ void auth_delkeys(void) { struct savekey *sk; struct savekey **skp; int i; for (i = 0; i < HASHSIZE; i++) { skp = &(key_hash[i]); sk = key_hash[i]; /* * Leave autokey keys alone. */ while (sk != 0 && sk->keyid <= NTP_MAXKEY) { /* * Don't lose info as to which keys are trusted. */ if (sk->flags & KEY_TRUSTED) { skp = &(sk->next); memset(&sk->k, 0, sizeof(sk->k)); sk->lifetime = 0; sk->keylen = 0; sk = sk->next; } else { *skp = sk->next; authnumkeys--; sk->next = authfreekeys; authfreekeys = sk; authnumfreekeys++; sk = *skp; } } } } /* * auth_agekeys - delete keys whose lifetimes have expired */ void auth_agekeys(void) { struct savekey *sk; struct savekey *skp; int i; for (i = 0; i < HASHSIZE; i++) { sk = skp = key_hash[i]; while (sk != 0) { skp = sk->next; if (sk->lifetime > 0 && current_time > sk->lifetime) { authtrust(sk->keyid, 0); authkeyexpired++; } sk = skp; } } #ifdef DEBUG if (debug) printf("auth_agekeys: at %lu keys %lu expired %lu\n", current_time, authnumkeys, authkeyexpired); #endif } /* * authencrypt - generate message authenticator * * Returns length of authenticator field, zero if key not found. */ int authencrypt( keyid_t keyno, u_int32 *pkt, int length ) { /* * A zero key identifier means the sender has not verified * the last message was correctly authenticated. The MAC * consists of a single word with value zero. */ authencryptions++; pkt[length / 4] = htonl(keyno); if (keyno == 0) { return (4); } if (!authhavekey(keyno)) return (0); return (MD5authencrypt(cache_type, cache_key, pkt, length)); } /* * authdecrypt - verify message authenticator * * Returns one if authenticator valid, zero if invalid or key not found. */ int authdecrypt( keyid_t keyno, u_int32 *pkt, int length, int size ) { /* * A zero key identifier means the sender has not verified * the last message was correctly authenticated. Nevertheless, * the authenticator itself is considered valid. */ authdecryptions++; if (keyno == 0) return (0); if (!authhavekey(keyno) || size < 4) return (0); return (MD5authdecrypt(cache_type, cache_key, pkt, length, size)); }