diff options
Diffstat (limited to 'lib/librpc/secure_rpc/keyserv/setkey.c')
-rw-r--r-- | lib/librpc/secure_rpc/keyserv/setkey.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/lib/librpc/secure_rpc/keyserv/setkey.c b/lib/librpc/secure_rpc/keyserv/setkey.c new file mode 100644 index 0000000..d62dc9c --- /dev/null +++ b/lib/librpc/secure_rpc/keyserv/setkey.c @@ -0,0 +1,514 @@ +#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); +} |