BSD 4_3_Net_2 release
[unix-history] / usr / src / lib / librpc / secure_rpc / keyserv / setkey.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);
}