515 lines
9.8 KiB
C
515 lines
9.8 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)setkey.c 2.2 88/08/10 4.0 RPCSRC; from Copyr 1988 Sun Micro";
|
|
#endif
|
|
/*
|
|
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
|
|
* unrestricted use provided that this legend is included on all tape
|
|
* media and as a part of the software program in whole or part. Users
|
|
* may copy or modify Sun RPC without charge, but are not authorized
|
|
* to license or distribute it to anyone else except as part of a product or
|
|
* program developed by the user.
|
|
*
|
|
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
|
|
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
|
|
*
|
|
* Sun RPC is provided with no support and without any obligation on the
|
|
* part of Sun Microsystems, Inc. to assist in its use, correction,
|
|
* modification or enhancement.
|
|
*
|
|
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
|
|
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
|
|
* OR ANY PART THEREOF.
|
|
*
|
|
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
|
|
* or profits or other special, indirect and consequential damages, even if
|
|
* Sun has been advised of the possibility of such damages.
|
|
*
|
|
* Sun Microsystems, Inc.
|
|
* 2550 Garcia Avenue
|
|
* Mountain View, California 94043
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 1986, Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Do the real work of the keyserver .
|
|
* Store secret keys. Compute common keys,
|
|
* and use them to decrypt and encrypt DES keys .
|
|
* Cache the common keys, so the
|
|
* expensive computation is avoided.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <sys/file.h>
|
|
#include <mp.h>
|
|
#include <rpc/rpc.h>
|
|
#include <rpc/key_prot.h>
|
|
#include <des_crypt.h>
|
|
#include <sys/errno.h>
|
|
|
|
extern char *malloc();
|
|
extern char ROOTKEY[];
|
|
|
|
static MINT *MODULUS;
|
|
static char *fetchsecretkey();
|
|
static keystatus pk_crypt();
|
|
|
|
|
|
/*
|
|
* Set the modulus for all our Diffie-Hellman operations
|
|
*/
|
|
setmodulus(modx)
|
|
char *modx;
|
|
{
|
|
MODULUS = xtom(modx);
|
|
}
|
|
|
|
|
|
/*
|
|
* Set the secretkey key for this uid
|
|
*/
|
|
keystatus
|
|
pk_setkey(uid, skey)
|
|
short uid;
|
|
keybuf skey;
|
|
{
|
|
if (!storesecretkey(uid, skey)) {
|
|
return (KEY_SYSTEMERR);
|
|
}
|
|
return (KEY_SUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* Encrypt the key using the public key associated with remote_name and the
|
|
* secret key associated with uid.
|
|
*/
|
|
keystatus
|
|
pk_encrypt(uid, remote_name, key)
|
|
short uid;
|
|
char *remote_name;
|
|
des_block *key;
|
|
{
|
|
return (pk_crypt(uid, remote_name, key, DES_ENCRYPT));
|
|
}
|
|
|
|
|
|
/*
|
|
* Decrypt the key using the public key associated with remote_name and the
|
|
* secret key associated with uid.
|
|
*/
|
|
keystatus
|
|
pk_decrypt(uid, remote_name, key)
|
|
short uid;
|
|
char *remote_name;
|
|
des_block *key;
|
|
{
|
|
return (pk_crypt(uid, remote_name, key, DES_DECRYPT));
|
|
}
|
|
|
|
|
|
/*
|
|
* Do the work of pk_encrypt && pk_decrypt
|
|
*/
|
|
static keystatus
|
|
pk_crypt(uid, remote_name, key, mode)
|
|
short uid;
|
|
char *remote_name;
|
|
des_block *key;
|
|
int mode;
|
|
{
|
|
char *xsecret;
|
|
char xpublic[HEXKEYBYTES + 1];
|
|
char xsecret_hold[HEXKEYBYTES + 1];
|
|
des_block deskey;
|
|
int err;
|
|
MINT *public;
|
|
MINT *secret;
|
|
MINT *common;
|
|
char zero[8];
|
|
|
|
xsecret = fetchsecretkey(uid);
|
|
if (xsecret == NULL) {
|
|
bzero(zero, sizeof(zero));
|
|
xsecret = xsecret_hold;
|
|
if (!getsecretkey("nobody", xsecret, zero) ||
|
|
xsecret[0] == 0) {
|
|
return (KEY_NOSECRET);
|
|
}
|
|
}
|
|
if (!getpublickey(remote_name, xpublic) &&
|
|
!getpublickey("nobody", xpublic)) {
|
|
return (KEY_UNKNOWN);
|
|
}
|
|
if (!readcache(xpublic, xsecret, &deskey)) {
|
|
public = xtom(xpublic);
|
|
secret = xtom(xsecret);
|
|
common = itom(0);
|
|
pow(public, secret, MODULUS, common);
|
|
extractdeskey(common, &deskey);
|
|
writecache(xpublic, xsecret, &deskey);
|
|
mfree(secret);
|
|
mfree(public);
|
|
mfree(common);
|
|
}
|
|
err = ecb_crypt(&deskey, key, sizeof(des_block), DES_HW | mode);
|
|
if (DES_FAILED(err)) {
|
|
return (KEY_SYSTEMERR);
|
|
}
|
|
return (KEY_SUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* Choose middle 64 bits of the common key to use as our des key, possibly
|
|
* overwriting the lower order bits by setting parity.
|
|
*/
|
|
static
|
|
extractdeskey(ck, deskey)
|
|
MINT *ck;
|
|
des_block *deskey;
|
|
{
|
|
MINT *a;
|
|
short r;
|
|
int i;
|
|
short base = (1 << 8);
|
|
char *k;
|
|
|
|
|
|
a = itom(0);
|
|
move(ck, a);
|
|
for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
|
|
sdiv(a, base, a, &r);
|
|
}
|
|
k = deskey->c;
|
|
for (i = 0; i < 8; i++) {
|
|
sdiv(a, base, a, &r);
|
|
*k++ = r;
|
|
}
|
|
mfree(a);
|
|
des_setparity(deskey);
|
|
}
|
|
|
|
|
|
/*
|
|
* Key storage management
|
|
*/
|
|
|
|
struct secretkey_list {
|
|
short uid;
|
|
char secretkey[HEXKEYBYTES+1];
|
|
struct secretkey_list *next;
|
|
};
|
|
|
|
static struct secretkey_list *g_secretkeys;
|
|
|
|
/*
|
|
* Fetch the secret key for this uid
|
|
*/
|
|
static char *
|
|
fetchsecretkey(uid)
|
|
short uid;
|
|
{
|
|
struct secretkey_list *l;
|
|
|
|
for (l = g_secretkeys; l != NULL; l = l->next) {
|
|
if (l->uid == uid) {
|
|
return (l->secretkey);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Store the secretkey for this uid
|
|
*/
|
|
storesecretkey(uid, key)
|
|
short uid;
|
|
keybuf key;
|
|
{
|
|
struct secretkey_list *new;
|
|
struct secretkey_list **l;
|
|
int nitems;
|
|
|
|
|
|
nitems = 0;
|
|
for (l = &g_secretkeys; *l != NULL && (*l)->uid != uid;
|
|
l = &(*l)->next) {
|
|
nitems++;
|
|
}
|
|
if (*l == NULL) {
|
|
new = (struct secretkey_list *)malloc(sizeof(*new));
|
|
if (new == NULL) {
|
|
return (0);
|
|
}
|
|
new->uid = uid;
|
|
new->next = NULL;
|
|
*l = new;
|
|
} else {
|
|
new = *l;
|
|
}
|
|
bcopy(key, new->secretkey, HEXKEYBYTES);
|
|
new->secretkey[HEXKEYBYTES] = 0;
|
|
seekitem(nitems);
|
|
writeitem(uid, new->secretkey);
|
|
return (1);
|
|
}
|
|
|
|
|
|
hexdigit(val)
|
|
int val;
|
|
{
|
|
return ("0123456789abcdef"[val]);
|
|
|
|
}
|
|
bin2hex(bin, hex, size)
|
|
unsigned char *bin;
|
|
unsigned char *hex;
|
|
int size;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
*hex++ = hexdigit(*bin >> 4);
|
|
*hex++ = hexdigit(*bin++ & 0xf);
|
|
}
|
|
}
|
|
|
|
hexval(dig)
|
|
char dig;
|
|
{
|
|
if ('0' <= dig && dig <= '9') {
|
|
return (dig - '0');
|
|
} else if ('a' <= dig && dig <= 'f') {
|
|
return (dig - 'a' + 10);
|
|
} else if ('A' <= dig && dig <= 'F') {
|
|
return (dig - 'A' + 10);
|
|
} else {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
hex2bin(hex, bin, size)
|
|
unsigned char *hex;
|
|
unsigned char *bin;
|
|
int size;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
*bin = hexval(*hex++) << 4;
|
|
*bin++ |= hexval(*hex++);
|
|
}
|
|
}
|
|
|
|
static char KEYSTORE[] = "/etc/keystore";
|
|
FILE *kf;
|
|
|
|
openstore()
|
|
{
|
|
kf = fopen(KEYSTORE, "r+");
|
|
if (kf == NULL) {
|
|
kf = fopen(KEYSTORE, "w+");
|
|
if (kf == NULL) {
|
|
return (0);
|
|
}
|
|
}
|
|
setbuf(kf, NULL);
|
|
return (1);
|
|
}
|
|
|
|
static char rootkey[KEYBYTES];
|
|
static int haverootkey;
|
|
struct storedkey {
|
|
short uid;
|
|
char crypt[KEYBYTES];
|
|
};
|
|
|
|
readkeys()
|
|
{
|
|
struct secretkey_list *node;
|
|
struct secretkey_list **l;
|
|
int uid;
|
|
char secretkey[HEXKEYBYTES+1];
|
|
|
|
if (kf == NULL) {
|
|
return;
|
|
}
|
|
l = &g_secretkeys;
|
|
seekitem(0);
|
|
while (readitem(&uid, secretkey)) {
|
|
node = (struct secretkey_list *)malloc(sizeof(*node));
|
|
if (node == NULL) {
|
|
return;
|
|
}
|
|
node->uid = uid;
|
|
bcopy(secretkey, node->secretkey, HEXKEYBYTES + 1);
|
|
node->next = NULL;
|
|
*l = node;
|
|
l = &node->next;
|
|
}
|
|
}
|
|
|
|
writekeys()
|
|
{
|
|
struct secretkey_list *k;
|
|
|
|
seekitem(0);
|
|
for (k = g_secretkeys; k != NULL; k = k->next) {
|
|
writeitem(k->uid, k->secretkey);
|
|
}
|
|
}
|
|
|
|
seekitem(item)
|
|
int item;
|
|
{
|
|
if (kf != NULL) {
|
|
fseek(kf, item * sizeof(struct storedkey), 0);
|
|
}
|
|
}
|
|
|
|
writeitem(uid, key)
|
|
int uid;
|
|
char *key;
|
|
{
|
|
struct storedkey item;
|
|
char rootkey_tmp[KEYBYTES];
|
|
int reencrypt;
|
|
|
|
if (kf == NULL) {
|
|
return (1);
|
|
}
|
|
if (uid == 0) {
|
|
writerootkey(key);
|
|
hex2bin(key, rootkey_tmp, KEYBYTES);
|
|
reencrypt = (haverootkey &&
|
|
bcmp(rootkey, rootkey_tmp, KEYBYTES) != 0);
|
|
bcopy(rootkey_tmp, rootkey, KEYBYTES);
|
|
haverootkey = 1;
|
|
if (reencrypt) {
|
|
writekeys();
|
|
return (1);
|
|
}
|
|
}
|
|
if (!haverootkey) {
|
|
return (1);
|
|
}
|
|
item.uid = uid;
|
|
hex2bin(key, item.crypt, KEYBYTES);
|
|
ecb_crypt(rootkey, item.crypt, KEYBYTES, DES_ENCRYPT|DES_HW);
|
|
return (fwrite(&item, sizeof(item), 1, kf) >= 0);
|
|
}
|
|
|
|
|
|
readitem(uidp, key)
|
|
int *uidp;
|
|
char *key;
|
|
{
|
|
struct storedkey item;
|
|
|
|
if (!haverootkey || kf == NULL) {
|
|
return (0);
|
|
}
|
|
if (fread(&item, sizeof(item), 1, kf) != 1) {
|
|
return (0);
|
|
}
|
|
*uidp = item.uid;
|
|
ecb_crypt(rootkey, item.crypt, KEYBYTES, DES_DECRYPT|DES_HW);
|
|
bin2hex(item.crypt, key, KEYBYTES);
|
|
key[HEXKEYBYTES] = 0;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Root users store their key in /etc/$ROOTKEY so
|
|
* that they can auto reboot without having to be
|
|
* around to type a password. Storing this in a file
|
|
* is rather dubious: it should really be in the EEPROM
|
|
* so it does not go over the net for diskless machines.
|
|
*/
|
|
writerootkey(secret)
|
|
char *secret;
|
|
{
|
|
char newline = '\n';
|
|
int fd;
|
|
|
|
fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0);
|
|
if (fd < 0) {
|
|
perror(ROOTKEY);
|
|
} else {
|
|
if (write(fd, secret, strlen(secret)) < 0 ||
|
|
write(fd, &newline, sizeof(newline)) < 0) {
|
|
(void)fprintf(stderr, "%s: ", ROOTKEY);
|
|
perror("write");
|
|
}
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Exponential caching management
|
|
*/
|
|
struct cachekey_list {
|
|
keybuf secret;
|
|
keybuf public;
|
|
des_block deskey;
|
|
struct cachekey_list *next;
|
|
};
|
|
static struct cachekey_list *g_cachedkeys;
|
|
|
|
|
|
/*
|
|
* cache result of expensive multiple precision exponential operation
|
|
*/
|
|
static
|
|
writecache(pub, sec, deskey)
|
|
char *pub;
|
|
char *sec;
|
|
des_block *deskey;
|
|
{
|
|
struct cachekey_list *new;
|
|
|
|
new = (struct cachekey_list *) malloc(sizeof(struct cachekey_list));
|
|
if (new == NULL) {
|
|
return;
|
|
}
|
|
bcopy(pub, new->public, sizeof(keybuf));
|
|
bcopy(sec, new->secret, sizeof(keybuf));
|
|
new->deskey = *deskey;
|
|
new->next = g_cachedkeys;
|
|
g_cachedkeys = new;
|
|
}
|
|
|
|
/*
|
|
* Try to find the common key in the cache
|
|
*/
|
|
static
|
|
readcache(pub, sec, deskey)
|
|
char *pub;
|
|
char *sec;
|
|
des_block *deskey;
|
|
{
|
|
struct cachekey_list *found;
|
|
register struct cachekey_list **l;
|
|
|
|
#define cachehit(pub, sec, list) \
|
|
(bcmp(pub, (list)->public, sizeof(keybuf)) == 0 && \
|
|
bcmp(sec, (list)->secret, sizeof(keybuf)) == 0)
|
|
|
|
for (l = &g_cachedkeys;
|
|
(*l) != NULL && !cachehit(pub, sec, *l);
|
|
l = &(*l)->next);
|
|
if ((*l) == NULL) {
|
|
return (0);
|
|
}
|
|
found = *l;
|
|
(*l) = (*l)->next;
|
|
found->next = g_cachedkeys;
|
|
g_cachedkeys = found;
|
|
*deskey = found->deskey;
|
|
return (1);
|
|
}
|