#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <rpc/rpc.h>
#include <rpc/key_prot.h>
#include <rpc/des_crypt.h>
#include <rpc/des.h>
#include <sys/errno.h>
#include "keyserv.h"
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
static BIGNUM *modulus;
static char *fetchsecretkey( uid_t );
static void writecache( char *, char *, des_block * );
static int readcache( char *, char *, des_block * );
static void extractdeskey ( BIGNUM *, des_block * );
static int storesecretkey( uid_t, keybuf );
static keystatus pk_crypt( uid_t, char *, netobj *, des_block *, int);
static int nodefaultkeys = 0;
void
pk_nodefaultkeys(void)
{
nodefaultkeys = 1;
}
void
setmodulus(char *modx)
{
modulus = NULL;
if (BN_hex2bn(&modulus, modx) == 0)
errx(1, "could not convert modulus to BIGNUM: %s",
ERR_error_string(ERR_get_error(), 0));
}
keystatus
pk_setkey(uid_t uid, keybuf skey)
{
if (!storesecretkey(uid, skey)) {
return (KEY_SYSTEMERR);
}
return (KEY_SUCCESS);
}
keystatus
pk_encrypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key)
{
return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT));
}
keystatus
pk_decrypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key)
{
return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT));
}
static int store_netname( uid_t, key_netstarg * );
static int fetch_netname( uid_t, key_netstarg * );
keystatus
pk_netput(uid_t uid, key_netstarg *netstore)
{
if (!store_netname(uid, netstore)) {
return (KEY_SYSTEMERR);
}
return (KEY_SUCCESS);
}
keystatus
pk_netget(uid_t uid, key_netstarg *netstore)
{
if (!fetch_netname(uid, netstore)) {
return (KEY_SYSTEMERR);
}
return (KEY_SUCCESS);
}
static keystatus
pk_crypt(uid_t uid, char *remote_name, netobj *remote_key, des_block *key,
int mode)
{
char *xsecret;
char xpublic[1024];
char xsecret_hold[1024];
des_block deskey;
int error;
BIGNUM *public, *secret, *common;
BN_CTX *ctx;
char zero[8];
xsecret = fetchsecretkey(uid);
if (xsecret == NULL || xsecret[0] == 0) {
memset(zero, 0, sizeof (zero));
xsecret = xsecret_hold;
if (nodefaultkeys)
return (KEY_NOSECRET);
if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) {
return (KEY_NOSECRET);
}
}
if (remote_key) {
memcpy(xpublic, remote_key->n_bytes, remote_key->n_len);
} else {
bzero((char *)&xpublic, sizeof(xpublic));
if (!getpublickey(remote_name, xpublic)) {
if (nodefaultkeys || !getpublickey("nobody", xpublic))
return (KEY_UNKNOWN);
}
}
if (!readcache(xpublic, xsecret, &deskey)) {
if ((ctx = BN_CTX_new()) == NULL)
return (KEY_SYSTEMERR);
public = NULL;
if (BN_hex2bn(&public, xpublic) == 0) {
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
secret = NULL;
if (BN_hex2bn(&secret, xsecret) == 0) {
BN_free(public);
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
if ((common = BN_new()) == NULL) {
BN_free(secret);
BN_free(public);
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
BN_zero(common);
BN_mod_exp(common, public, secret, modulus, ctx);
extractdeskey(common, &deskey);
writecache(xpublic, xsecret, &deskey);
BN_free(secret);
BN_free(public);
BN_free(common);
BN_CTX_free(ctx);
}
error = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block),
DES_HW | mode);
if (DES_FAILED(error)) {
return (KEY_SYSTEMERR);
}
return (KEY_SUCCESS);
}
keystatus
pk_get_conv_key(uid_t uid, keybuf xpublic, cryptkeyres *result)
{
char *xsecret;
char xsecret_hold[1024];
BIGNUM *public, *secret, *common;
BN_CTX *ctx;
char zero[8];
xsecret = fetchsecretkey(uid);
if (xsecret == NULL || xsecret[0] == 0) {
memset(zero, 0, sizeof (zero));
xsecret = xsecret_hold;
if (nodefaultkeys)
return (KEY_NOSECRET);
if (!getsecretkey("nobody", xsecret, zero) ||
xsecret[0] == 0)
return (KEY_NOSECRET);
}
if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey)) {
if ((ctx = BN_CTX_new()) == NULL)
return (KEY_SYSTEMERR);
public = NULL;
if (BN_hex2bn(&public, xpublic) == 0) {
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
secret = NULL;
if (BN_hex2bn(&secret, xsecret) == 0) {
BN_free(public);
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
if ((common = BN_new()) == NULL) {
BN_free(secret);
BN_free(public);
BN_CTX_free(ctx);
return (KEY_SYSTEMERR);
}
BN_zero(common);
BN_mod_exp(common, public, secret, modulus, ctx);
extractdeskey(common, &result->cryptkeyres_u.deskey);
writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey);
BN_free(secret);
BN_free(public);
BN_free(common);
BN_CTX_free(ctx);
}
return (KEY_SUCCESS);
}
static void
extractdeskey(BIGNUM *ck, des_block *deskey)
{
BIGNUM *a;
int i;
BN_ULONG r, base = (1 << 8);
char *k;
if ((a = BN_dup(ck)) == NULL)
errx(1, "could not copy BIGNUM");
for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
r = BN_div_word(a, base);
}
k = deskey->c;
for (i = 0; i < 8; i++) {
r = BN_div_word(a, base);
*k++ = r;
}
BN_free(a);
des_setparity((char *)deskey);
}
#define KEY_ONLY 0
#define KEY_NAME 1
struct secretkey_netname_list {
uid_t uid;
key_netstarg keynetdata;
u_char sc_flag;
struct secretkey_netname_list *next;
};
static struct secretkey_netname_list *g_secretkey_netname;
static int
store_netname(uid_t uid, key_netstarg *netstore)
{
struct secretkey_netname_list *new;
struct secretkey_netname_list **l;
for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
l = &(*l)->next) {
}
if (*l == NULL) {
new = (struct secretkey_netname_list *)malloc(sizeof (*new));
if (new == NULL) {
return (0);
}
new->uid = uid;
new->next = NULL;
*l = new;
} else {
new = *l;
if (new->keynetdata.st_netname)
free(new->keynetdata.st_netname);
}
memcpy(new->keynetdata.st_priv_key, netstore->st_priv_key,
HEXKEYBYTES);
memcpy(new->keynetdata.st_pub_key, netstore->st_pub_key, HEXKEYBYTES);
if (netstore->st_netname)
new->keynetdata.st_netname = strdup(netstore->st_netname);
else
new->keynetdata.st_netname = NULL;
new->sc_flag = KEY_NAME;
return (1);
}
static int
fetch_netname(uid_t uid, struct key_netstarg *key_netst)
{
struct secretkey_netname_list *l;
for (l = g_secretkey_netname; l != NULL; l = l->next) {
if ((l->uid == uid) && (l->sc_flag == KEY_NAME)){
memcpy(key_netst->st_priv_key,
l->keynetdata.st_priv_key, HEXKEYBYTES);
memcpy(key_netst->st_pub_key,
l->keynetdata.st_pub_key, HEXKEYBYTES);
if (l->keynetdata.st_netname)
key_netst->st_netname =
strdup(l->keynetdata.st_netname);
else
key_netst->st_netname = NULL;
return (1);
}
}
return (0);
}
static char *
fetchsecretkey(uid_t uid)
{
struct secretkey_netname_list *l;
for (l = g_secretkey_netname; l != NULL; l = l->next) {
if (l->uid == uid) {
return (l->keynetdata.st_priv_key);
}
}
return (NULL);
}
static int
storesecretkey(uid_t uid, keybuf key)
{
struct secretkey_netname_list *new;
struct secretkey_netname_list **l;
for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
l = &(*l)->next) {
}
if (*l == NULL) {
new = (struct secretkey_netname_list *) malloc(sizeof (*new));
if (new == NULL) {
return (0);
}
new->uid = uid;
new->sc_flag = KEY_ONLY;
memset(new->keynetdata.st_pub_key, 0, HEXKEYBYTES);
new->keynetdata.st_netname = NULL;
new->next = NULL;
*l = new;
} else {
new = *l;
}
memcpy(new->keynetdata.st_priv_key, key,
HEXKEYBYTES);
return (1);
}
static int
hexdigit(int val)
{
return ("0123456789abcdef"[val]);
}
void
bin2hex(unsigned char *bin, unsigned char *hex, int size)
{
int i;
for (i = 0; i < size; i++) {
*hex++ = hexdigit(*bin >> 4);
*hex++ = hexdigit(*bin++ & 0xf);
}
}
static int
hexval(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);
}
}
void
hex2bin(unsigned char *hex, unsigned char *bin, int size)
{
int i;
for (i = 0; i < size; i++) {
*bin = hexval(*hex++) << 4;
*bin++ |= hexval(*hex++);
}
}
struct cachekey_list {
keybuf secret;
keybuf public;
des_block deskey;
struct cachekey_list *next;
};
static struct cachekey_list *g_cachedkeys;
static void
writecache(char *pub, char *sec, des_block *deskey)
{
struct cachekey_list *new;
new = (struct cachekey_list *) malloc(sizeof (struct cachekey_list));
if (new == NULL) {
return;
}
memcpy(new->public, pub, sizeof (keybuf));
memcpy(new->secret, sec, sizeof (keybuf));
new->deskey = *deskey;
new->next = g_cachedkeys;
g_cachedkeys = new;
}
static int
readcache(char *pub, char *sec, des_block *deskey)
{
struct cachekey_list *found;
struct cachekey_list **l;
#define cachehit(pub, sec, list) \
(memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \
memcmp(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);
}