614 lines
11 KiB
C
614 lines
11 KiB
C
|
/*
|
|||
|
* authkeys.c - routines to manage the storage of authentication keys
|
|||
|
*/
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#include "ntp_types.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 {
|
|||
|
long bogon; /* Make sure union isn't empty... */
|
|||
|
#ifdef DES
|
|||
|
u_int32 DES_key[2];
|
|||
|
#endif
|
|||
|
#ifdef MD5
|
|||
|
char MD5_key[32];
|
|||
|
#endif
|
|||
|
} k;
|
|||
|
u_long keyid;
|
|||
|
u_short flags;
|
|||
|
#ifdef MD5
|
|||
|
int keylen;
|
|||
|
#endif
|
|||
|
};
|
|||
|
|
|||
|
#define KEY_TRUSTED 0x1 /* this key is trusted */
|
|||
|
#define KEY_KNOWN 0x2 /* this key is known */
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
#define KEY_DES 0x100 /* this is a DES type key */
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
#define KEY_MD5 0x200 /* this is a MD5 type key */
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* 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_int32 authkeynotfound;
|
|||
|
u_int32 authkeylookups;
|
|||
|
u_int32 authnumkeys;
|
|||
|
u_int32 authuncached;
|
|||
|
u_int32 authkeyuncached;
|
|||
|
u_int32 authnokey; /* calls to encrypt with no key */
|
|||
|
u_int32 authencryptions;
|
|||
|
u_int32 authdecryptions;
|
|||
|
|
|||
|
/*
|
|||
|
* 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 at once */
|
|||
|
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
/*
|
|||
|
* Size of the key schedule (in u_longs for fast DES)
|
|||
|
*/
|
|||
|
#define KEY_SCHED_SIZE ((128+sizeof(u_long)-1)/sizeof(u_long))
|
|||
|
|
|||
|
/*
|
|||
|
* The zero key, which we always have. Store the permutted key
|
|||
|
* zero in here.
|
|||
|
*/
|
|||
|
#define ZEROKEY_L 0x01010101 /* odd parity zero key */
|
|||
|
#define ZEROKEY_R 0x01010101 /* right half of same */
|
|||
|
/*
|
|||
|
* fast DES code expects base address aligned to u_long
|
|||
|
*/
|
|||
|
u_long DESzeroekeys[KEY_SCHED_SIZE];
|
|||
|
u_long DESzerodkeys[KEY_SCHED_SIZE];
|
|||
|
u_long DEScache_ekeys[KEY_SCHED_SIZE];
|
|||
|
u_long DEScache_dkeys[KEY_SCHED_SIZE];
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* The key cache. We cache the last key we looked at here.
|
|||
|
*/
|
|||
|
u_long cache_keyid;
|
|||
|
u_short cache_flags;
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
int cache_keylen;
|
|||
|
char *cache_key;
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* init_auth - initialize internal data
|
|||
|
*/
|
|||
|
void
|
|||
|
init_auth()
|
|||
|
{
|
|||
|
#ifdef DES
|
|||
|
u_int32 zerokey[2];
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize hash table and free list
|
|||
|
*/
|
|||
|
memset((char *)key_hash, 0, sizeof key_hash);
|
|||
|
cache_keyid = 0;
|
|||
|
cache_flags = 0;
|
|||
|
|
|||
|
authnumfreekeys = authkeynotfound = authkeylookups = 0;
|
|||
|
authnumkeys = authuncached = authkeyuncached = authnokey = 0;
|
|||
|
authencryptions = authdecryptions = 0;
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
/*
|
|||
|
* Initialize the zero key
|
|||
|
*/
|
|||
|
zerokey[0] = ZEROKEY_L;
|
|||
|
zerokey[1] = ZEROKEY_R;
|
|||
|
/* could just zero all */
|
|||
|
DESauth_subkeys(zerokey, (u_char *)DESzeroekeys, (u_char *)DESzerodkeys);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* auth_findkey - find a key in the hash table
|
|||
|
*/
|
|||
|
struct savekey *
|
|||
|
auth_findkey(keyno)
|
|||
|
u_long keyno;
|
|||
|
{
|
|||
|
register 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 whether a key is known
|
|||
|
*/
|
|||
|
int
|
|||
|
auth_havekey(keyno)
|
|||
|
u_long keyno;
|
|||
|
{
|
|||
|
register struct savekey *sk;
|
|||
|
|
|||
|
if (keyno == 0 || (keyno == cache_keyid))
|
|||
|
return 1;
|
|||
|
|
|||
|
sk = key_hash[KEYHASH(keyno)];
|
|||
|
while (sk != 0) {
|
|||
|
if (keyno == sk->keyid) {
|
|||
|
if (sk->flags & KEY_KNOWN)
|
|||
|
return 1;
|
|||
|
else {
|
|||
|
authkeynotfound++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
authkeynotfound++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* authhavekey - return whether a key is known. Permute and cache
|
|||
|
* the key as a side effect.
|
|||
|
*/
|
|||
|
int
|
|||
|
authhavekey(keyno)
|
|||
|
u_long keyno;
|
|||
|
{
|
|||
|
register struct savekey *sk;
|
|||
|
|
|||
|
authkeylookups++;
|
|||
|
if (keyno == 0 || keyno == cache_keyid)
|
|||
|
return 1;
|
|||
|
|
|||
|
sk = key_hash[KEYHASH(keyno)];
|
|||
|
while (sk != 0) {
|
|||
|
if (keyno == sk->keyid)
|
|||
|
break;
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
|
|||
|
if (sk == 0 || !(sk->flags & KEY_KNOWN)) {
|
|||
|
authkeynotfound++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
cache_keyid = sk->keyid;
|
|||
|
cache_flags = sk->flags;
|
|||
|
#ifdef MD5
|
|||
|
if (sk->flags & KEY_MD5) {
|
|||
|
cache_keylen = sk->keylen;
|
|||
|
cache_key = (char *) sk->k.MD5_key; /* XXX */
|
|||
|
return 1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
if (sk->flags & KEY_DES) {
|
|||
|
DESauth_subkeys(sk->k.DES_key, (u_char *)DEScache_ekeys, (u_char *)DEScache_dkeys);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* auth_moremem - get some more free key structures
|
|||
|
*/
|
|||
|
int
|
|||
|
auth_moremem()
|
|||
|
{
|
|||
|
register struct savekey *sk;
|
|||
|
register int i;
|
|||
|
|
|||
|
sk = (struct savekey *)malloc(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(keyno, trust)
|
|||
|
u_long keyno;
|
|||
|
int trust;
|
|||
|
{
|
|||
|
register struct savekey *sk;
|
|||
|
|
|||
|
sk = key_hash[KEYHASH(keyno)];
|
|||
|
while (sk != 0) {
|
|||
|
if (keyno == sk->keyid)
|
|||
|
break;
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
|
|||
|
if (sk == 0 && !trust)
|
|||
|
return;
|
|||
|
|
|||
|
if (sk != 0) {
|
|||
|
if (cache_keyid == keyno) {
|
|||
|
cache_flags = 0;
|
|||
|
cache_keyid = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (trust) {
|
|||
|
sk->flags |= KEY_TRUSTED;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
sk->flags &= ~KEY_TRUSTED;
|
|||
|
if (!(sk->flags & KEY_KNOWN)) {
|
|||
|
register 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;
|
|||
|
}
|
|||
|
|
|||
|
if (authnumfreekeys == 0)
|
|||
|
if (auth_moremem() == 0)
|
|||
|
return;
|
|||
|
|
|||
|
sk = authfreekeys;
|
|||
|
authfreekeys = sk->next;
|
|||
|
authnumfreekeys--;
|
|||
|
|
|||
|
sk->keyid = keyno;
|
|||
|
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(keyno)
|
|||
|
u_long keyno;
|
|||
|
{
|
|||
|
register 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 || !(sk->flags & KEY_TRUSTED))
|
|||
|
return 0;
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
/*
|
|||
|
* DESauth_setkey - set a key into the key array
|
|||
|
*/
|
|||
|
void
|
|||
|
DESauth_setkey(keyno, key)
|
|||
|
u_long keyno;
|
|||
|
const u_int32 *key;
|
|||
|
{
|
|||
|
register 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 != 0) {
|
|||
|
if (keyno == sk->keyid) {
|
|||
|
sk->k.DES_key[0] = key[0];
|
|||
|
sk->k.DES_key[1] = key[1];
|
|||
|
sk->flags |= KEY_KNOWN | KEY_DES;
|
|||
|
if (cache_keyid == keyno)
|
|||
|
cache_flags = 0;
|
|||
|
cache_keyid = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Need to allocate new structure. Do it.
|
|||
|
*/
|
|||
|
if (authnumfreekeys == 0) {
|
|||
|
if (auth_moremem() == 0)
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
sk = authfreekeys;
|
|||
|
authfreekeys = sk->next;
|
|||
|
authnumfreekeys--;
|
|||
|
|
|||
|
sk->k.DES_key[0] = key[0];
|
|||
|
sk->k.DES_key[1] = key[1];
|
|||
|
sk->keyid = keyno;
|
|||
|
sk->flags = KEY_KNOWN | KEY_DES;
|
|||
|
sk->next = key_hash[KEYHASH(keyno)];
|
|||
|
key_hash[KEYHASH(keyno)] = sk;
|
|||
|
authnumkeys++;
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
void
|
|||
|
MD5auth_setkey(keyno, key)
|
|||
|
u_long keyno;
|
|||
|
const u_int32 *key;
|
|||
|
{
|
|||
|
register 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 != 0) {
|
|||
|
if (keyno == sk->keyid) {
|
|||
|
strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
|
|||
|
if ((sk->keylen = strlen((char *)key)) >
|
|||
|
sizeof(sk->k.MD5_key))
|
|||
|
sk->keylen = sizeof(sk->k.MD5_key);
|
|||
|
|
|||
|
sk->flags |= KEY_KNOWN | KEY_MD5;
|
|||
|
if (cache_keyid == keyno) {
|
|||
|
cache_flags = 0;
|
|||
|
cache_keyid = 0;
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Need to allocate new structure. Do it.
|
|||
|
*/
|
|||
|
if (authnumfreekeys == 0) {
|
|||
|
if (auth_moremem() == 0)
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
sk = authfreekeys;
|
|||
|
authfreekeys = sk->next;
|
|||
|
authnumfreekeys--;
|
|||
|
|
|||
|
strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
|
|||
|
if ((sk->keylen = strlen((char *)key)) > sizeof(sk->k.MD5_key))
|
|||
|
sk->keylen = sizeof(sk->k.MD5_key);
|
|||
|
|
|||
|
sk->keyid = keyno;
|
|||
|
sk->flags = KEY_KNOWN | KEY_MD5;
|
|||
|
sk->next = key_hash[KEYHASH(keyno)];
|
|||
|
key_hash[KEYHASH(keyno)] = sk;
|
|||
|
authnumkeys++;
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* auth_delkeys - delete all known keys, in preparation for rereading
|
|||
|
* the keys file (presumably)
|
|||
|
*/
|
|||
|
void
|
|||
|
auth_delkeys()
|
|||
|
{
|
|||
|
register struct savekey *sk;
|
|||
|
register struct savekey **skp;
|
|||
|
register int i;
|
|||
|
|
|||
|
for (i = 0; i < HASHSIZE; i++) {
|
|||
|
skp = &(key_hash[i]);
|
|||
|
sk = key_hash[i];
|
|||
|
while (sk != 0) {
|
|||
|
sk->flags &= ~(KEY_KNOWN
|
|||
|
#ifdef MD5
|
|||
|
| KEY_MD5
|
|||
|
#endif
|
|||
|
#ifdef DES
|
|||
|
| KEY_DES
|
|||
|
#endif
|
|||
|
);
|
|||
|
if (sk->flags == 0) {
|
|||
|
*skp = sk->next;
|
|||
|
authnumkeys--;
|
|||
|
sk->next = authfreekeys;
|
|||
|
authfreekeys = sk;
|
|||
|
authnumfreekeys++;
|
|||
|
sk = *skp;
|
|||
|
} else {
|
|||
|
skp = &(sk->next);
|
|||
|
sk = sk->next;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* auth1crypt - support for two stage encryption, part 1.
|
|||
|
*/
|
|||
|
void
|
|||
|
auth1crypt(keyno, pkt, length)
|
|||
|
u_long keyno;
|
|||
|
u_int32 *pkt;
|
|||
|
int length; /* length of all encrypted data */
|
|||
|
{
|
|||
|
if (keyno && keyno != cache_keyid) {
|
|||
|
authkeyuncached++;
|
|||
|
if (!authhavekey(keyno)) {
|
|||
|
authnokey++;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
if (!keyno || (cache_flags & KEY_DES)) {
|
|||
|
DESauth1crypt(keyno, pkt, length);
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
if (cache_flags & KEY_MD5) {
|
|||
|
MD5auth1crypt(keyno, pkt, length);
|
|||
|
return;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* auth1crypt - support for two stage encryption, part 1.
|
|||
|
*/
|
|||
|
int
|
|||
|
auth2crypt(keyno, pkt, length)
|
|||
|
u_long keyno;
|
|||
|
u_int32 *pkt;
|
|||
|
int length; /* total length of encrypted area */
|
|||
|
{
|
|||
|
if (keyno && keyno != cache_keyid) {
|
|||
|
authkeyuncached++;
|
|||
|
if (!authhavekey(keyno)) {
|
|||
|
authnokey++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
if (!keyno || (cache_flags & KEY_DES))
|
|||
|
return DESauth2crypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
if (cache_flags & KEY_MD5)
|
|||
|
return MD5auth2crypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
authencrypt(keyno, pkt, length)
|
|||
|
u_long keyno;
|
|||
|
u_int32 *pkt;
|
|||
|
int length; /* length of encrypted portion of packet */
|
|||
|
{
|
|||
|
int sendlength = 0;
|
|||
|
|
|||
|
if (keyno && keyno != cache_keyid) {
|
|||
|
authkeyuncached++;
|
|||
|
if (!authhavekey(keyno)) {
|
|||
|
authnokey++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
if (!keyno || (cache_flags & KEY_DES))
|
|||
|
return sendlength = DESauthencrypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
if (cache_flags & KEY_MD5)
|
|||
|
return MD5authencrypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
authdecrypt(keyno, pkt, length)
|
|||
|
u_long keyno;
|
|||
|
u_int32 *pkt;
|
|||
|
int length; /* length of variable data in octets */
|
|||
|
{
|
|||
|
if (keyno && (keyno != cache_keyid)) {
|
|||
|
authkeyuncached++;
|
|||
|
if (!authhavekey(keyno)) {
|
|||
|
authnokey++;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DES
|
|||
|
if (!keyno || (cache_flags & KEY_DES))
|
|||
|
return DESauthdecrypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef MD5
|
|||
|
if (cache_flags & KEY_MD5)
|
|||
|
return MD5authdecrypt(keyno, pkt, length);
|
|||
|
#endif
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|