/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * 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 #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "keyserv_cache.h" extern char ROOTKEY[]; extern mechanism_t **mechs; extern char **cache_options; extern int *cache_size; extern int disk_caching; static MINT *MODULUS; static int hash_keys(); static keystatus pk_crypt(); static keystatus pk_crypt3(); static int nodefaultkeys = 0; #define DES "des" #define DESALIAS "dh192-0" #define DHMECHSTR "diffie_hellman" #define CLASSIC_PK_DH(k, a) (((k) == 192) && ((a) == 0)) /* * Exponential caching management */ struct cachekey_list { keybuf secret; keybuf public; des_block deskey; struct cachekey_list *next; }; #define KEY_HASH_SIZE 256 static struct cachekey_list *g_cachedkeys[KEY_HASH_SIZE]; static rwlock_t g_cachedkeys_lock = DEFAULTRWLOCK; #ifdef DEBUG int test_debug(debug_level level, char *file, int line) { if (level < debugging) return (0); fprintf(stderr, "file %s,\tline %d :\t", file, line); return (1); } int real_debug(char *fmt, ...) { va_list args; va_start(args, fmt); (void) vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); fflush(stderr); return (1); } #endif /* DEBUG */ struct cacheuid_list { uid_t uid; int refcnt; keybuf3 *secretkey; keybuf3 *publickey; netnamestr netname; des_block key; struct cacheuid_list *next; }; #define NUMHASHBUCKETS 256 #define HASH_UID(x) (x & 0xff) struct mechdata { struct cacheuid_list *bucket[NUMHASHBUCKETS]; }; struct psdata { struct cachekey3_list *common[NUMHASHBUCKETS]; }; struct mechentry { mutex_t mech_lock; struct mechdata *mechdata; mutex_t ps_lock; struct psdata *psdata; }; /* * we don't need to worry about locking for the keylen + algtype * sparse array because it is created once and for all during * initialization when there are no threads. The mechentry field * and everything underneath it needs protection and this is what * the *_lock fields are for. */ struct algtypelist { algtype_t algtype; struct algtypelist *next; struct mechentry mech; }; struct keylenlist { keylen_t keylen; struct algtypelist *ap; struct keylenlist *next; }; #define KEYSERV_VERSION "1.0" static struct mechtable { char *version; struct keylenlist *kp; } mechtable = {KEYSERV_VERSION, NULL}; static struct keylenlist ** getkeylen(keylen_t k) { struct keylenlist **kpp; debug(KEYSERV_DEBUG1, ("getkeylen key: %d", k)); for (kpp = &mechtable.kp; *kpp != NULL && (*kpp)->keylen != k; kpp = &(*kpp)->next) debug(KEYSERV_DEBUG0, ("getkeylen failed %x", kpp)); debug(KEYSERV_DEBUG0, ("getkeylen return: %x", kpp)); return (kpp); } static void appendkeylist(struct keylenlist **kpp, keylen_t k) { struct keylenlist *kp; if (*kpp == NULL) { kp = (struct keylenlist *)malloc(sizeof (*kp)); if (kp == NULL) { debug(KEYSERV_INFO, ("appendkeylist : malloc failed")); return; } debug(KEYSERV_DEBUG, ("appendkeylist : %x %x %d", kpp, kp, k)); kp->keylen = k; kp->ap = NULL; kp->next = NULL; *kpp = kp; } else { /*EMPTY*/ /* do nothing; only happens for multiple algtypes */ debug(KEYSERV_DEBUG0, ("appendkeylist called for non tail element")); } } static struct algtypelist ** getalgtype(struct keylenlist **kpp, algtype_t a) { struct algtypelist **app; debug(KEYSERV_DEBUG1, ("getalgtype key: %d", a)); for (app = &(*kpp)->ap; *app != NULL && (*app)->algtype != a; app = &(*app)->next) debug(KEYSERV_DEBUG0, ("getalgtype key: %x", app)); debug(KEYSERV_DEBUG0, ("getalgtype return: %x", app)); return (app); } static void appendalgtype(struct algtypelist **app, algtype_t a) { struct algtypelist *ap; if (*app == NULL) { ap = (struct algtypelist *)malloc(sizeof (*ap)); if (ap == NULL) { debug(KEYSERV_INFO, ("appendalgtype : malloc failed")); return; } debug(KEYSERV_DEBUG, ("appendalgtype : %x %x %d", app, ap, a)); ap->algtype = a; mutex_init(&ap->mech.mech_lock, USYNC_THREAD, NULL); mutex_init(&ap->mech.ps_lock, USYNC_THREAD, NULL); ap->mech.mechdata = NULL; ap->mech.psdata = NULL; ap->next = NULL; *app = ap; } else { /*EMPTY*/ /* don't mind duplicate (keylen,algtype) paris for now. */ debug(KEYSERV_DEBUG0, ("appendalgtype called for non tail element")); } } static struct mechentry * getmechtype(keylen_t k, algtype_t a) { struct keylenlist **kpp; struct algtypelist **app; debug(KEYSERV_DEBUG1, ("getmechtype %d %d", k, a)); kpp = getkeylen(k); if (*kpp == NULL) { debug(KEYSERV_DEBUG0, ("getmechtype %d not found in keys", k)); return (0); } app = getalgtype(kpp, a); if (*app == NULL) { debug(KEYSERV_DEBUG0, ("getmechtype %d not found in algs", a)); return (0); } debug(KEYSERV_DEBUG0, ("getmechtype found %x", app)); debug(KEYSERV_DEBUG0, ("getmechtype return %x", &(*app)->mech)); return (&(*app)->mech); } static keybuf3 * getkeybuf3(int k) { keybuf3 *buf; debug(KEYSERV_DEBUG, ("getkeybuf3 malloc %d", k)); buf = (keybuf3 *) malloc(sizeof (*buf)); if (buf == NULL) { debug(KEYSERV_DEBUG, ("getkeybuf3 malloc failed")); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (NULL); } buf->keybuf3_len = k; /* XXX special case k==0 */ if (k == 0) { buf->keybuf3_val = NULL; } else { buf->keybuf3_val = (char *)malloc(k); if (buf->keybuf3_val == NULL) { debug(KEYSERV_DEBUG, ("getkeybuf3 malloc failed")); free(buf); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (NULL); } } debug(KEYSERV_DEBUG1, ("getkeybuf3 ret %x", buf)); return (buf); } static void freekeybuf3(keybuf3 *kp) { debug(KEYSERV_DEBUG1, ("freekeybuf3 %x", kp)); if (kp == NULL) return; if (kp->keybuf3_val) { /* XXX kp->keybuf3_len != 0? */ free(kp->keybuf3_val); } free(kp); } static keybuf3 * cpykeybuf3(keybuf3 *src) { keybuf3 *dst; if (src == NULL) { return (NULL); } if ((dst = getkeybuf3(src->keybuf3_len)) == NULL) { return (NULL); } memcpy(dst->keybuf3_val, src->keybuf3_val, src->keybuf3_len); debug(KEYSERV_DEBUG0, ("cpykeybuf3 ret %x", dst)); return (dst); } static keybuf3 * setkeybuf3(char *src, int len) { keybuf3 *dst; if ((dst = getkeybuf3(++len)) == NULL) { return (NULL); } memcpy(dst->keybuf3_val, src, len); return (dst); } static int cmpkeybuf3(keybuf3 *k1, keybuf3 *k2) { if ((k1 == NULL) || (k2 == NULL)) { syslog(LOG_ERR, "cmpkeybuf3: invalid parameter: %x, %x", k1, k2); return (0); } if (k1->keybuf3_len != k2->keybuf3_len) { return (0); } return (!memcmp(k1->keybuf3_val, k2->keybuf3_val, k1->keybuf3_len)); } static int storekeybuf3(keybuf3 *dst, keybuf3 *src) { keybuf3 *tmp; if ((tmp = cpykeybuf3(src)) == NULL) { return (0); } *dst = *tmp; free(tmp); /* but not the contents */ debug(KEYSERV_DEBUG0, ("storekeybuf3 ret %d %x", dst->keybuf3_len, dst->keybuf3_val)); return (1); } static deskeyarray * getdeskeyarray(int k) { deskeyarray *buf; debug(KEYSERV_DEBUG, ("getdeskeyarray malloc %d", k)); buf = (deskeyarray *) malloc(sizeof (*buf)); if (buf == NULL) { debug(KEYSERV_DEBUG, ("getdeskeyarray malloc failed")); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (NULL); } buf->deskeyarray_len = k; /* XXX special case k==0 */ if (k == 0) { buf->deskeyarray_val = NULL; } else { buf->deskeyarray_val = (des_block *) malloc(k * sizeof (des_block)); if (buf->deskeyarray_val == NULL) { debug(KEYSERV_DEBUG, ("getdeskeyarray malloc failed")); free(buf); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (NULL); } } debug(KEYSERV_DEBUG1, ("getdeskeyarray ret %x", buf)); return (buf); } static deskeyarray * cpydeskeyarray(deskeyarray *src) { deskeyarray *dst; if (src == NULL) { return (NULL); } if ((dst = getdeskeyarray(src->deskeyarray_len)) == NULL) { return (NULL); } memcpy(dst->deskeyarray_val, src->deskeyarray_val, src->deskeyarray_len * sizeof (des_block)); debug(KEYSERV_DEBUG0, ("cpydeskeyarray ret %x", dst)); return (dst); } static int storedeskeyarray(deskeyarray *dst, deskeyarray *src) { deskeyarray *tmp; if ((tmp = cpydeskeyarray(src)) == NULL) { return (0); } *dst = *tmp; free(tmp); /* but not the contents */ debug(KEYSERV_DEBUG0, ("storedeskeyarray ret %d %x", dst->deskeyarray_len, dst->deskeyarray_val)); return (1); } int setdeskeyarray(deskeyarray *dst, int k) { deskeyarray *tmp; if ((tmp = getdeskeyarray(k)) == NULL) { return (0); } *dst = *tmp; free(tmp); /* but not the contents */ debug(KEYSERV_DEBUG0, ("setdeskeyarray ret %d %x", dst->deskeyarray_len, dst->deskeyarray_val)); return (1); } static int cachehit3(keybuf3 *public, keybuf3 *secret, struct cachekey3_list *cp) { return (cmpkeybuf3(public, cp->public) && cmpkeybuf3(secret, cp->secret)); } static struct cacheuid_list ** mapuid2cache(uid_t uid, struct mechdata *mdp) { struct cacheuid_list **cpp; int hash = HASH_UID(uid); debug(KEYSERV_DEBUG, ("mapuid2cache %d %d %x", uid, hash, mdp)); for (cpp = &mdp->bucket[hash]; *cpp != NULL && (*cpp)->uid != uid; cpp = &(*cpp)->next) { debug(KEYSERV_DEBUG0, ("mapuid2cache %x", cpp)); } debug(KEYSERV_DEBUG, ("mapuid2cache ret %x", cpp)); return (cpp); } static int appendsecretkey3(struct mechentry *mp, uid_t uid, setkeyarg3 *skey) { struct mechdata *mdp; struct cacheuid_list **cpp, *cp; keybuf3 nullkey = {0, NULL}; debug(KEYSERV_DEBUG, ("appendsecretkey3 %x", mp)); if ((skey == NULL) || (mp == NULL)) { return (0); } if (skey->key.keybuf3_len == 0) { return (0); } mutex_lock(&mp->mech_lock); if ((mdp = mp->mechdata) == NULL) { mdp = (struct mechdata *)calloc(1, sizeof (*mdp)); if (mdp == NULL) { mutex_unlock(&mp->mech_lock); debug(KEYSERV_INFO, ("appendsecretkey3 : calloc failed")); return (0); } mp->mechdata = mdp; } cpp = mapuid2cache(uid, mdp); if (*cpp == NULL) { cp = (struct cacheuid_list *)malloc(sizeof (*cp)); if (cp == NULL) { mutex_unlock(&mp->mech_lock); debug(KEYSERV_INFO, ("appendsecretkey3 : malloc failed")); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (0); } memset(cp, 0, sizeof (*cp)); cp->uid = uid; *cpp = cp; } else { cp = *cpp; } freekeybuf3(cp->secretkey); if ((cp->secretkey = cpykeybuf3(&skey->key)) == NULL) { mutex_unlock(&mp->mech_lock); return (0); } freekeybuf3(cp->publickey); if ((cp->publickey = cpykeybuf3(&nullkey)) == NULL) { mutex_unlock(&mp->mech_lock); return (0); } mutex_unlock(&mp->mech_lock); return (1); } /* * Store the vers 3 secretkey for this uid */ static int storesecretkey3(uid_t uid, setkeyarg3 *skey) { struct mechentry *mp; if (skey == NULL) { return (0); } if ((mp = getmechtype(skey->keylen, skey->algtype)) == NULL) { return (0); } return (appendsecretkey3(mp, uid, skey)); } /* * Set the vers 3 secretkey key for this uid */ keystatus pk_setkey3(uid_t uid, setkeyarg3 *skey) { if (!storesecretkey3(uid, skey)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } /* * Set the secretkey key for this uid */ keystatus pk_setkey(uid, skey) uid_t uid; keybuf skey; { int storesecretkey(uid_t, keybuf); if (!storesecretkey(uid, skey)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } int storeotherrootkeys(FILE *fp, char *netname, char *passwd, char *osecret) { des_block master; struct keylenlist *kp; struct algtypelist *ap; keybuf3 *secret; setkeyarg3 skey; debug(KEYSERV_DEBUG, ("storeotherrootkeys %s %s", netname, passwd)); passwd2des_g(passwd, netname, strlen(netname), &master, FALSE); for (kp = mechtable.kp; kp != NULL; kp = kp->next) { debug(KEYSERV_DEBUG0, ("storeotherrootkeys key %d", kp->keylen)); for (ap = kp->ap; ap != NULL; ap = ap->next) { debug(KEYSERV_DEBUG, ("storeotherrootkeys alg: %d", ap->algtype)); if ((secret = getkeybuf3(kp->keylen/4+1)) == NULL) { return (0); } debug(KEYSERV_DEBUG, ("storeotherrootkeys calling getsecretkey_g")); if (!getsecretkey_g(netname, kp->keylen, ap->algtype, secret->keybuf3_val, secret->keybuf3_len, passwd)) { debug(KEYSERV_INFO, ("Can't find %s's secret key", netname)); return (0); } if (*secret->keybuf3_val == 0) { /* XXX */ debug(KEYSERV_INFO, ("Password does not decrypt secret key for %s", netname)); return (0); } skey.key = *secret; free(secret); /* but not the buffer it points to */ skey.userkey = master; skey.keylen = kp->keylen; skey.algtype = ap->algtype; if (CLASSIC_PK_DH(kp->keylen, ap->algtype)) { pk_setkey((uid_t)0, osecret); fprintf(fp, "%s\n", osecret); } if (pk_setkey3(0, &skey) != KEY_SUCCESS) { return (0); } if (!CLASSIC_PK_DH(kp->keylen, ap->algtype)) { fprintf(fp, "%s %d\n", skey.key.keybuf3_val, ap->algtype); } } } return (1); } /* * prohibit the nobody key on this machine k (the -d flag) */ int pk_nodefaultkeys() { nodefaultkeys = 1; return (0); } static void freedisklist(struct cacheuid_list *cp) { if (cp == NULL) { return; } free(cp->netname); /* ok even if this is NULL */ freekeybuf3(cp->secretkey); freekeybuf3(cp->publickey); } keystatus pk_clear3(uid_t uid) { struct keylenlist *kp; struct algtypelist *ap; struct mechdata *mdp; struct cacheuid_list **cpp, *cp; debug(KEYSERV_DEBUG, ("pk_clear3 %d", uid)); for (kp = mechtable.kp; kp != NULL; kp = kp->next) { debug(KEYSERV_DEBUG0, ("pk_clear3 key %d", kp->keylen)); for (ap = kp->ap; ap != NULL; ap = ap->next) { debug(KEYSERV_DEBUG0, ("pk_clear3 alg: %d", ap->algtype)); mutex_lock(&ap->mech.mech_lock); if ((mdp = ap->mech.mechdata) == NULL) { mutex_unlock(&ap->mech.mech_lock); continue; } cpp = mapuid2cache(uid, mdp); if (*cpp == NULL) { mutex_unlock(&ap->mech.mech_lock); continue; } cp = (*cpp)->next; freedisklist(*cpp); *cpp = cp; mutex_unlock(&ap->mech.mech_lock); } } /* XXX clear stuff out of the common key cache as well? */ /* XXX return success only if something was removed? */ return (KEY_SUCCESS); } /* * Set the modulus for all our Diffie-Hellman operations */ int setmodulus(modx) char *modx; { MODULUS = mp_xtom(modx); return (0); } /* * Encrypt the key using the public key associated with remote_name and the * secret key associated with uid. */ keystatus pk_encrypt(uid, remote_name, remote_key, key) uid_t uid; char *remote_name; netobj *remote_key; des_block *key; { return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT)); } /* * Encrypt the key using the public key associated with remote_name and the * secret key associated with uid using vers 3 */ keystatus pk_encrypt3( uid_t uid, cryptkeyarg3 *arg, deskeyarray *key ) { return (pk_crypt3(uid, arg, 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, remote_key, key) uid_t uid; char *remote_name; netobj *remote_key; des_block *key; { return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT)); } /* * Decrypt the key using the public key associated with remote_name and the * secret key associated with uid using vers 3 */ keystatus pk_decrypt3( uid_t uid, cryptkeyarg3 *arg, deskeyarray *key ) { return (pk_crypt3(uid, arg, key, DES_DECRYPT)); } /* * Key storage management */ #define KEY_ONLY 0 #define KEY_NAME 1 struct secretkey_netname_list { uid_t uid; key_netstarg keynetdata; uchar_t sc_flag; struct secretkey_netname_list *next; }; #define HASH_UID(x) (x & 0xff) static struct secretkey_netname_list *g_secretkey_netname[KEY_HASH_SIZE]; static rwlock_t g_secretkey_netname_lock = DEFAULTRWLOCK; /* * Store the keys and netname for this uid */ static int store_netname(uid, netstore) uid_t uid; key_netstarg *netstore; { struct secretkey_netname_list *new; struct secretkey_netname_list **l; int hash = HASH_UID(uid); (void) rw_wrlock(&g_secretkey_netname_lock); for (l = &g_secretkey_netname[hash]; *l != NULL && (*l)->uid != uid; l = &(*l)->next) { } if (*l == NULL) { /* LINTED pointer alignment */ new = (struct secretkey_netname_list *)malloc(sizeof (*new)); if (new == NULL) { (void) rw_unlock(&g_secretkey_netname_lock); return (0); } new->uid = uid; new->next = NULL; *l = new; } else { new = *l; if (new->keynetdata.st_netname) (void) 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 = (char *)NULL; new->sc_flag = KEY_NAME; (void) rw_unlock(&g_secretkey_netname_lock); return (1); } static int appendnetname3(struct mechentry *mp, uid_t uid, key_netstarg3 *net) { struct mechdata *mdp; struct cacheuid_list **cpp, *cp; debug(KEYSERV_DEBUG, ("appendnetname3 %x", mp)); if ((mp == NULL) || (net == NULL)) { return (0); } mutex_lock(&mp->mech_lock); if ((mdp = mp->mechdata) == NULL) { mdp = (struct mechdata *)calloc(1, sizeof (*mdp)); if (mdp == NULL) { mutex_unlock(&mp->mech_lock); debug(KEYSERV_INFO, ("appendnetname3 : calloc failed")); return (0); } mp->mechdata = mdp; } cpp = mapuid2cache(uid, mdp); if (*cpp == NULL) { cp = (struct cacheuid_list *)malloc(sizeof (*cp)); if (cp == NULL) { mutex_unlock(&mp->mech_lock); debug(KEYSERV_INFO, ("appendnetname3 : malloc failed")); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (0); } memset(cp, 0, sizeof (*cp)); cp->uid = uid; *cpp = cp; } else { cp = *cpp; } freekeybuf3(cp->secretkey); if ((cp->secretkey = cpykeybuf3(&net->st_priv_key)) == NULL) { mutex_unlock(&mp->mech_lock); return (0); } freekeybuf3(cp->publickey); if ((cp->publickey = cpykeybuf3(&net->st_pub_key)) == NULL) { mutex_unlock(&mp->mech_lock); return (0); } free(cp->netname); if (net->st_netname) { cp->netname = strdup(net->st_netname); } else { cp->netname = (char *)NULL; } mutex_unlock(&mp->mech_lock); return (1); } keystatus pk_netput(uid, netstore) uid_t uid; key_netstarg *netstore; { if (!store_netname(uid, netstore)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } /* * Store the keys and netname for this uid vers 3 */ static int store_netname3(uid_t uid, key_netstarg3 *net) { struct mechentry *mp; key_netstarg netstore; if (net == NULL) { return (0); } if ((mp = getmechtype(net->keylen, net->algtype)) == NULL) { return (0); } if (uid == 0 && CLASSIC_PK_DH(net->keylen, net->algtype)) { memcpy(netstore.st_priv_key, net->st_priv_key.keybuf3_val, HEXKEYBYTES); memset(netstore.st_pub_key, 0, HEXKEYBYTES); netstore.st_netname = net->st_netname; if (pk_netput(uid, &netstore) != KEY_SUCCESS) { (void) fprintf(stderr, "keyserv: could not set root's key and netname.\n"); return (0); } } return (appendnetname3(mp, uid, net)); } keystatus pk_netput3(uid_t uid, key_netstarg3 *netstore) { if (!store_netname3(uid, netstore)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } int addmasterkey(char *master, char *netname, algtype_t algtype) { keybuf3 *secret, *public; int bytelen = strlen(master); keylen_t keylen = bytelen*4; key_netstarg3 tmp; if ((secret = setkeybuf3(master, bytelen)) == NULL) { return (0); } if ((public = getkeybuf3(bytelen+1)) == NULL) { /* the +1 is mandated by getpublickey_g() */ return (0); } /* * getpublickey_g(netname, keylen, algtype, * public->keybuf3_val, public->keybuf3_len); * cannot be called since rpc.nisd is not up yet * so we continue to return a zero filled public key * as in the earlier version */ memset(public->keybuf3_val, 0, bytelen+1); tmp.st_priv_key = *secret; free(secret); tmp.st_pub_key = *public; free(public); tmp.st_netname = strdup(netname); tmp.keylen = keylen; tmp.algtype = algtype; return (store_netname3(0, &tmp)); } /* * Fetch the keys and netname for this uid */ static int fetch_netname(uid, key_netst) uid_t uid; struct key_netstarg *key_netst; { struct secretkey_netname_list *l; int hash = HASH_UID(uid); (void) rw_rdlock(&g_secretkey_netname_lock); for (l = g_secretkey_netname[hash]; 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) strcpy(key_netst->st_netname, l->keynetdata.st_netname); else key_netst->st_netname = NULL; (void) rw_unlock(&g_secretkey_netname_lock); return (1); } } (void) rw_unlock(&g_secretkey_netname_lock); return (0); } static void remove_ref(struct cacheuid_list *cp) { debug(KEYSERV_DEBUG0, ("remove_ref %x", cp)); /* * XXX * if we are going to do this along the lines of vn_rele, * more stuff needs to be done here and the access to refcnt * needs to be mutex locked. Keep it simple for now. */ cp->refcnt--; } static void add_ref(struct cacheuid_list **cpp) { struct cacheuid_list *cp; if (cpp == NULL) { return; } /*LINTED assignment operator "=" found where "==" was expected*/ if (cp = *cpp) { debug(KEYSERV_DEBUG0, ("add_ref %x", cp)); cp->refcnt++; } } static struct cacheuid_list * getcachekey3(uid_t uid, struct mechentry *mp) { struct cacheuid_list **cpp, *cp; struct mechdata *mdp; debug(KEYSERV_DEBUG1, ("getcachekey3 %d %x", uid, mp)); if (mp == NULL) { return (0); } mutex_lock(&mp->mech_lock); if ((mdp = mp->mechdata) == NULL) { mutex_unlock(&mp->mech_lock); debug(KEYSERV_DEBUG0, ("getcachekey3 ret 0")); return (0); } cpp = mapuid2cache(uid, mdp); cp = *cpp; add_ref(cpp); mutex_unlock(&mp->mech_lock); debug(KEYSERV_DEBUG0, ("getcachekey3 ret %x", *cpp)); return (cp); } /* * Fetch any available cache for this uid (vers 3) */ static struct cacheuid_list * getanycache3(uid_t uid) { struct keylenlist *kp; struct algtypelist *ap; struct mechdata *mdp; struct cacheuid_list **cpp, *cp; debug(KEYSERV_DEBUG, ("getanycache3 %d", uid)); for (kp = mechtable.kp; kp != NULL; kp = kp->next) { debug(KEYSERV_DEBUG0, ("getanycache3 key %d", kp->keylen)); for (ap = kp->ap; ap != NULL; ap = ap->next) { debug(KEYSERV_DEBUG0, ("getanycache3 alg: %d", ap->algtype)); mutex_lock(&ap->mech.mech_lock); if ((mdp = ap->mech.mechdata) == NULL) { mutex_unlock(&ap->mech.mech_lock); continue; } cpp = mapuid2cache(uid, mdp); if (*cpp == NULL) { mutex_unlock(&ap->mech.mech_lock); continue; } cp = *cpp; cp->refcnt++; mutex_unlock(&ap->mech.mech_lock); return (cp); } } return (NULL); } static struct cacheuid_list * fetchcache3(uid_t uid, keylen_t k, algtype_t a) { struct mechentry *mp; struct cacheuid_list *cp; debug(KEYSERV_DEBUG, ("fetchcache3 %d %d %d", uid, k, a)); if ((mp = getmechtype(k, a)) == NULL) { return (NULL); } if ((cp = getcachekey3(uid, mp)) == NULL) { return (NULL); } debug(KEYSERV_DEBUG, ("fetchcache3 ret %x", cp)); return (cp); } /* * Fetch the keys and netname for this uid vers 3 */ static int fetch_netname3(uid_t uid, mechtype *net, key_netstarg3 *ret) { struct cacheuid_list *cp; if ((net == NULL) || (ret == NULL)) { return (0); } debug(KEYSERV_DEBUG, ("fetch_netname3 %d %d %d", uid, net->keylen, net->algtype)); if (net->keylen == 0) { cp = getanycache3(uid); } else { cp = fetchcache3(uid, net->keylen, net->algtype); } debug(KEYSERV_DEBUG, ("fetch_netname3 cp %x", cp)); if (cp == NULL) { return (0); } debug(KEYSERV_DEBUG, ("fetch_netname3 sec %x", cp->secretkey)); if (!storekeybuf3(&ret->st_priv_key, cp->secretkey)) { return (0); } debug(KEYSERV_DEBUG, ("fetch_netname3 pub %x", cp->publickey)); if (!storekeybuf3(&ret->st_pub_key, cp->publickey)) { return (0); } if (cp->netname) { debug(KEYSERV_DEBUG, ("fetch_netname3 net %s", cp->netname)); ret->st_netname = strdup(cp->netname); } else { ret->st_netname = NULL; } remove_ref(cp); return (1); } keystatus pk_netget(uid, netstore) uid_t uid; key_netstarg *netstore; { if (!fetch_netname(uid, netstore)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } keystatus pk_netget3(uid_t uid, mechtype *net, key_netstarg3 *ret) { if (!fetch_netname3(uid, net, ret)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } #define cachehit(pub, sec, list) \ (memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \ memcmp(sec, (list)->secret, sizeof (keybuf)) == 0) /* * Try to find the common key in the cache */ static int readcache(pub, sec, deskey, hash) char *pub; char *sec; des_block *deskey; int hash; { register struct cachekey_list **l; for (l = &g_cachedkeys[hash]; (*l) != NULL && !cachehit(pub, sec, *l); l = &(*l)->next) ; if ((*l) == NULL) return (0); *deskey = (*l)->deskey; return (1); } /* * cache result of expensive multiple precision exponential operation */ static int writecache(pub, sec, deskey, hash) char *pub; char *sec; des_block *deskey; int hash; { struct cachekey_list *new; new = (struct cachekey_list *)malloc(sizeof (struct cachekey_list)); if (new == NULL) { return (0); } memcpy(new->public, pub, sizeof (keybuf)); memcpy(new->secret, sec, sizeof (keybuf)); new->deskey = *deskey; new->next = g_cachedkeys[hash]; g_cachedkeys[hash] = new; return (1); } /* * Choose middle 64 bits of the common key to use as our des key, possibly * overwriting the lower order bits by setting parity. */ static int extractdeskey(ck, deskey) MINT *ck; des_block *deskey; { void _mp_move(MINT *, MINT *); MINT *a; short r; int i; short base = (1 << 8); char *k; a = mp_itom(0); _mp_move(ck, a); for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) { mp_sdiv(a, base, a, &r); } k = deskey->c; for (i = 0; i < 8; i++) { mp_sdiv(a, base, a, &r); *k++ = r; } mp_mfree(a); des_setparity((char *)deskey); return (0); } static bool_t fetchsecretkey(uid, buf) uid_t uid; char *buf; { struct secretkey_netname_list *l; int hash = HASH_UID(uid); (void) rw_rdlock(&g_secretkey_netname_lock); for (l = g_secretkey_netname[hash]; l != NULL; l = l->next) { if (l->uid == uid) { memcpy(buf, l->keynetdata.st_priv_key, sizeof (keybuf)); (void) rw_unlock(&g_secretkey_netname_lock); return (TRUE); } } (void) rw_unlock(&g_secretkey_netname_lock); return (FALSE); } static keybuf3 * fetchsecretkey3(uid_t uid, keylen_t k, algtype_t a) { struct cacheuid_list *cp; debug(KEYSERV_DEBUG, ("fetchsecretkey3 %d %d %d", uid, k, a)); if ((cp = fetchcache3(uid, k, a)) == NULL) { return (NULL); } debug(KEYSERV_DEBUG, ("fetchsecretkey3 ret %x", cp->secretkey)); return (cp->secretkey); } /* * Do the work of pk_encrypt && pk_decrypt */ static keystatus pk_crypt(uid, remote_name, remote_key, key, mode) uid_t uid; char *remote_name; netobj *remote_key; des_block *key; int mode; { char xsecret[1024]; char xpublic[1024]; des_block deskey; int err; MINT *public; MINT *secret; MINT *common; char zero[8]; int hash; if (!fetchsecretkey(uid, xsecret) || xsecret[0] == 0) { memset(zero, 0, sizeof (zero)); 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 { if (!getpublickey(remote_name, xpublic)) { if (nodefaultkeys || !getpublickey("nobody", xpublic)) return (KEY_UNKNOWN); } } xsecret[HEXKEYBYTES] = '\0'; xpublic[HEXKEYBYTES] = '\0'; hash = hash_keys(xpublic, xsecret); (void) rw_rdlock(&g_cachedkeys_lock); if (!readcache(xpublic, xsecret, &deskey, hash)) { (void) rw_unlock(&g_cachedkeys_lock); (void) rw_wrlock(&g_cachedkeys_lock); if (!readcache(xpublic, xsecret, &deskey, hash)) { public = mp_xtom(xpublic); secret = mp_xtom(xsecret); /* Sanity Check on public and private keys */ if (public == NULL || secret == NULL) { (void) rw_unlock(&g_cachedkeys_lock); return (KEY_SYSTEMERR); } common = mp_itom(0); mp_pow(public, secret, MODULUS, common); extractdeskey(common, &deskey); writecache(xpublic, xsecret, &deskey, hash); mp_mfree(secret); mp_mfree(public); mp_mfree(common); } } (void) rw_unlock(&g_cachedkeys_lock); err = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block), DES_HW | mode); if (DES_FAILED(err)) { return (KEY_SYSTEMERR); } return (KEY_SUCCESS); } static int hash_keys3(keybuf3 *p, keybuf3 *s) { int i; int hash = 0; char *pub = p->keybuf3_val; char *sec = s->keybuf3_val; debug(KEYSERV_DEBUG, ("hash_keys3 public %d %s", p->keybuf3_len, pub)); debug(KEYSERV_DEBUG, ("hash_keys3 secret %d %s", s->keybuf3_len, sec)); for (i = 0; i < s->keybuf3_len; i += 6, pub += 6, sec += 6) { hash ^= *pub; hash ^= *sec; } debug(KEYSERV_DEBUG, ("hash_keys3 ret %d", hash & 0xff)); return (hash & 0xff); } static struct cachekey3_list ** map_ps2cache(keybuf3 *public, keybuf3 *secret, struct psdata *pdp) { struct cachekey3_list **cpp; int hash = hash_keys3(public, secret); debug(KEYSERV_DEBUG, ("map_ps2cache %x %d", pdp, hash)); for (cpp = &pdp->common[hash]; *cpp != NULL && !(cachehit3(public, secret, *cpp)); cpp = &(*cpp)->next) { debug(KEYSERV_DEBUG0, ("map_ps2cache %x", cpp)); } debug(KEYSERV_DEBUG, ("map_ps2cache ret %x", cpp)); return (cpp); } static struct cachekey3_list * getdeskey3( keylen_t keylen, algtype_t algtype, int desarylen, keybuf3 *public, keybuf3 *secret, uid_t uid ) { struct mechentry *mp; struct psdata *pdp; struct cachekey3_list **cpp, *cp, *cachep; struct cacheuid_list *cu; int i; int cached = 0; debug(KEYSERV_DEBUG, ("getdeskey3 %d %d %d %x %x", keylen, algtype, desarylen, public, secret)); if ((mp = getmechtype(keylen, algtype)) == NULL) { return (0); } (void) mutex_lock(&mp->ps_lock); if ((pdp = mp->psdata) == NULL) { if ((pdp = (struct psdata *)calloc(1, sizeof (*pdp))) == NULL) { mutex_unlock(&mp->ps_lock); debug(KEYSERV_INFO, ("getdeskey3 : calloc failed")); return (0); } mp->psdata = pdp; } debug(KEYSERV_DEBUG, ("getdeskey3 %x", pdp)); cpp = map_ps2cache(public, secret, pdp); if (*cpp == NULL) { debug(KEYSERV_DEBUG, ("getdeskey3 calling fetchcache3")); if (disk_caching && (cu = fetchcache3(uid, keylen, algtype)) != NULL) { debug(KEYSERV_DEBUG, ("getdeskey3 calling cache_retrieve")); if ((cachep = cache_retrieve(keylen, algtype, uid, public, cu->key)) != NULL) { if (cmpkeybuf3(cachep->secret, cu->secretkey)) { cached = 1; } else { debug(KEYSERV_DEBUG, ("getdeskey3 calling cache_remove")); cache_remove(keylen, algtype, uid, NULL); } } } if (cached) { cp = cachep; } else { if ((cp = (struct cachekey3_list *) malloc(sizeof (*cp))) == NULL) { mutex_unlock(&mp->ps_lock); debug(KEYSERV_INFO, ("getdeskey3 : malloc failed")); syslog(LOG_ERR, "file %s line %d: malloc failed", __FILE__, __LINE__); return (0); } cp->refcnt = 0; cp->next = NULL; if ((cp->public = cpykeybuf3(public)) == NULL) { mutex_unlock(&mp->ps_lock); return (0); } if ((cp->secret = cpykeybuf3(secret)) == NULL) { mutex_unlock(&mp->ps_lock); return (0); } if (!setdeskeyarray(&cp->deskey, desarylen)) { mutex_unlock(&mp->ps_lock); return (0); } debug(KEYSERV_DEBUG, ("getdeskey3 %x %x %x", cp->public, cp->secret, cp->deskey.deskeyarray_val)); debug(KEYSERV_DEBUG, ("getdeskey3 calling __gen_common_dhkeys_g")); if (!__gen_common_dhkeys_g(public->keybuf3_val, secret->keybuf3_val, keylen, algtype, cp->deskey.deskeyarray_val, desarylen)) { mutex_unlock(&mp->ps_lock); return (0); } for (i = 0; i < desarylen; i++) { debug(KEYSERV_DEBUG0, ("getdeskey3 gendh key : (%x,%x)", cp->deskey.deskeyarray_val[i].key.high, cp->deskey.deskeyarray_val[i].key.low)); } if (disk_caching && cu != NULL) { debug(KEYSERV_DEBUG, ("getdeskey3 calling cache_insert")); cache_insert(keylen, algtype, uid, cp->deskey, cu->key, public, secret); } } *cpp = cp; } else { cp = *cpp; } cp->refcnt++; mutex_unlock(&mp->ps_lock); debug(KEYSERV_DEBUG, ("getdeskey3 ret %x", cp)); return (cp); } keystatus pk_get_conv_key3(uid_t uid, deskeyarg3 *arg, cryptkeyres3 *res) { keybuf3 *xsecret, *xpublic; char zero[8]; struct cachekey3_list *cp; debug(KEYSERV_DEBUG, ("pk_get_conv_key3 %d %x %x", uid, arg, res)); if ((xsecret = fetchsecretkey3(uid, arg->keylen, arg->algtype)) == NULL) { if (nodefaultkeys) return (KEY_NOSECRET); memset(zero, 0, sizeof (zero)); if ((xsecret = getkeybuf3(arg->keylen/4+1)) == NULL) { return (KEY_SYSTEMERR); } debug(KEYSERV_DEBUG, ("pk_get_conv_key3 calling getsecretkey_g")); if (!getsecretkey_g("nobody", arg->keylen, arg->algtype, xsecret->keybuf3_val, xsecret->keybuf3_len, zero) || *xsecret->keybuf3_val == 0) { /* XXX */ debug(KEYSERV_DEBUG, ("pk_get_conv_key3 calling getsecretkey_g failed")); return (KEY_NOSECRET); } debug(KEYSERV_DEBUG, ("pk_get_conv_key3 calling getsecretkey_g succeeded")); } xpublic = &arg->pub_key; if ((cp = getdeskey3(arg->keylen, arg->algtype, arg->nkeys, xpublic, xsecret, uid)) == NULL) { return (KEY_SYSTEMERR); } storedeskeyarray(&res->cryptkeyres3_u.deskey, &cp->deskey); return (KEY_SUCCESS); } /* * Do the work of pk_encrypt3 && pk_decrypt3 */ static keystatus pk_crypt3( uid_t uid, cryptkeyarg3 *arg, deskeyarray *key, int mode ) { keybuf3 *xsecret = NULL, *xpublic = NULL; char zero[8]; struct cachekey3_list *cp; int err; int xsecret_alloc = 0; char ivec[8]; memset(ivec, 0, 8); debug(KEYSERV_DEBUG1, ("pk_crypt3 %d %x %x %d", uid, arg, key, mode)); if ((xsecret = fetchsecretkey3(uid, arg->keylen, arg->algtype)) == NULL) { if (nodefaultkeys) return (KEY_NOSECRET); memset(zero, 0, sizeof (zero)); if ((xsecret = getkeybuf3(arg->keylen/4+1)) == NULL) { return (KEY_SYSTEMERR); } xsecret_alloc = 1; debug(KEYSERV_DEBUG1, ("pk_crypt3 calling getsecretkey_g")); if (!getsecretkey_g("nobody", arg->keylen, arg->algtype, xsecret->keybuf3_val, xsecret->keybuf3_len, zero) || *xsecret->keybuf3_val == 0) { /* XXX */ debug(KEYSERV_DEBUG, ("pk_crypt3 calling getsecretkey_g failed")); freekeybuf3(xsecret); return (KEY_NOSECRET); } /* XXX optimize to cache nobody's secret key? */ debug(KEYSERV_DEBUG0, ("pk_crypt3 calling getsecretkey_g succeeded")); } if (arg->remotekey.keybuf3_len) { if ((xpublic = cpykeybuf3(&arg->remotekey)) == NULL) { if (xsecret_alloc) freekeybuf3(xsecret); return (KEY_SYSTEMERR); } } else { if ((xpublic = getkeybuf3(arg->keylen/4+1)) == NULL) { if (xsecret_alloc) freekeybuf3(xsecret); return (KEY_SYSTEMERR); } debug(KEYSERV_DEBUG1, ("pk_crypt3 calling getpublickey_g")); if (!getpublickey_g(arg->remotename, arg->keylen, arg->algtype, xpublic->keybuf3_val, xpublic->keybuf3_len)) { debug(KEYSERV_DEBUG0, ("pk_crypt3 calling getpublickey_g nobody")); if (nodefaultkeys || !getpublickey_g("nobody", arg->keylen, arg->algtype, xpublic->keybuf3_val, xpublic->keybuf3_len)) { debug(KEYSERV_DEBUG, ("pk_crypt3 calling getpublickey_g nobody failed")); if (xsecret_alloc) freekeybuf3(xsecret); freekeybuf3(xpublic); return (KEY_UNKNOWN); } } debug(KEYSERV_DEBUG0, ("pk_crypt3 calling getpublickey_g succeeded")); } if ((cp = getdeskey3(arg->keylen, arg->algtype, arg->deskey.deskeyarray_len, xpublic, xsecret, uid)) == NULL) { if (xsecret_alloc) freekeybuf3(xsecret); freekeybuf3(xpublic); return (KEY_SYSTEMERR); } storedeskeyarray(key, &arg->deskey); if (CLASSIC_PK_DH(arg->keylen, arg->algtype)) { /*EMPTY*/ debug(KEYSERV_DEBUG1, ("pk_crypt3 WARNING received 192-bit key")); } else { debug(KEYSERV_DEBUG, ("pk_crypt3 calling __cbc_triple_crypt")); err = __cbc_triple_crypt(cp->deskey.deskeyarray_val, (char *)key->deskeyarray_val, cp->deskey.deskeyarray_len*sizeof (des_block), DES_HW | mode, ivec); if (DES_FAILED(err)) { debug(KEYSERV_DEBUG, ("pk_crypt3 calling ecb_crypt/__cbc_triple_crypt failed")); if (xsecret_alloc) freekeybuf3(xsecret); freekeybuf3(xpublic); return (KEY_SYSTEMERR); } debug(KEYSERV_DEBUG, ("pk_crypt3 calling __cbc_triple_crypt succeeded")); } if (xsecret_alloc) freekeybuf3(xsecret); freekeybuf3(xpublic); return (KEY_SUCCESS); } keystatus pk_get_conv_key(uid, pubkey, result) uid_t uid; keybuf pubkey; cryptkeyres *result; { char xsecret[1024]; char xpublic[1024]; MINT *public; MINT *secret; MINT *common; char zero[8]; int hash; if (!fetchsecretkey(uid, xsecret) || xsecret[0] == 0) { memset(zero, 0, sizeof (zero)); if (nodefaultkeys) return (KEY_NOSECRET); if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) return (KEY_NOSECRET); } memcpy(xpublic, pubkey, sizeof (keybuf)); xsecret[HEXKEYBYTES] = '\0'; xpublic[HEXKEYBYTES] = '\0'; hash = hash_keys(xpublic, xsecret); (void) rw_rdlock(&g_cachedkeys_lock); if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey, hash)) { (void) rw_unlock(&g_cachedkeys_lock); (void) rw_wrlock(&g_cachedkeys_lock); if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey, hash)) { public = mp_xtom(xpublic); secret = mp_xtom(xsecret); /* Sanity Check on public and private keys */ if (public == NULL || secret == NULL) { (void) rw_unlock(&g_cachedkeys_lock); return (KEY_SYSTEMERR); } common = mp_itom(0); mp_pow(public, secret, MODULUS, common); extractdeskey(common, &result->cryptkeyres_u.deskey); writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey, hash); mp_mfree(secret); mp_mfree(public); mp_mfree(common); } } (void) rw_unlock(&g_cachedkeys_lock); return (KEY_SUCCESS); } #define findsec(sec, list) \ (memcmp(sec, (list)->secret, sizeof (keybuf)) == 0) /* * Remove common keys from the cache. */ static int removecache(sec) char *sec; { struct cachekey_list *found; register struct cachekey_list **l; int i; (void) rw_wrlock(&g_cachedkeys_lock); for (i = 0; i < KEY_HASH_SIZE; i++) { for (l = &g_cachedkeys[i]; (*l) != NULL; ) { if (findsec(sec, *l)) { found = *l; *l = (*l)->next; memset((char *)found, 0, sizeof (struct cachekey_list)); free(found); } else { l = &(*l)->next; } } } (void) rw_unlock(&g_cachedkeys_lock); return (1); } /* * Store the secretkey for this uid */ int storesecretkey(uid, key) uid_t uid; keybuf key; { struct secretkey_netname_list *new; struct secretkey_netname_list **l; int hash = HASH_UID(uid); (void) rw_wrlock(&g_secretkey_netname_lock); for (l = &g_secretkey_netname[hash]; *l != NULL && (*l)->uid != uid; l = &(*l)->next) { } if (*l == NULL) { if (key[0] == '\0') { (void) rw_unlock(&g_secretkey_netname_lock); return (0); } new = (struct secretkey_netname_list *)malloc(sizeof (*new)); if (new == NULL) { (void) rw_unlock(&g_secretkey_netname_lock); 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; if (key[0] == '\0') removecache(new->keynetdata.st_priv_key); } memcpy(new->keynetdata.st_priv_key, key, HEXKEYBYTES); (void) rw_unlock(&g_secretkey_netname_lock); return (1); } static int hexdigit(val) int val; { return ("0123456789abcdef"[val]); } int 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); } return (0); } static int 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); } } int 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++); } return (0); } static int hash_keys(pub, sec) char *pub; char *sec; { int i; int hash = 0; for (i = 0; i < HEXKEYBYTES; i += 6, pub += 6, sec += 6) { hash ^= *pub; hash ^= *sec; } return (hash & 0xff); } /* * problem: keyserv loads keys from /etc/.rootkey based on nisauthconf(1M) * which is too nis+-centric (see secure_rpc(3N)). * * So we want to make sure there is always a AUTH_DES compat entry * in the "list" of nis+ mechs so that the 192bit key always gets loaded so * non-nis+ services that use AUTH_DES (e.g. nfs) won't get hosed. The real * hacky part of it is we muck with the array returned from * __nis_get_mechanisms which we really don't have any business * doing cause we should not know/care how that is implemented. A better * way would be to change the __nis_get_mechanisms interface or add another * one similiar to it that forces the "des" compat entry into the list. * * Return ptr to mechs array on success, else NULL on memory errs. */ mechanism_t ** getmechwrap() { mechanism_t **mechs = __nis_get_mechanisms(FALSE); mechanism_t **mechsbak = NULL; mechanism_t *desmech = NULL; int i = 0; if (mechs) { /* got some valid mechs and possibly the AUTH_DES compat one */ for (i = 0; mechs[i]; i++) { if (AUTH_DES_COMPAT_CHK(mechs[i])) return (mechs); } /* i == number of ptrs not counting terminating NULL */ } /* AUTH_DES compat entry not found, let's add it */ if ((desmech = malloc(sizeof (mechanism_t))) == NULL) { if (mechs) __nis_release_mechanisms(mechs); return (NULL); } desmech->mechname = NULL; desmech->alias = NIS_SEC_CF_DES_ALIAS; desmech->keylen = AUTH_DES_KEYLEN; desmech->algtype = AUTH_DES_ALGTYPE; desmech->qop = NULL; desmech->secserv = rpc_gss_svc_default; mechsbak = mechs; /* mechs == NULL and i == 0 is valid "no mechs configed" case */ if ((mechs = (mechanism_t **)realloc(mechs, sizeof (mechanism_t *) * (i + 2))) == NULL) { if (mechsbak) __nis_release_mechanisms(mechsbak); free(desmech); return (NULL); } mechs[i] = desmech; mechs[i+1] = NULL; return (mechs); } int init_mechs() { int nmechs, oldmechseen; mechanism_t **mechpp; char **cpp; if (!(mechs = getmechwrap())) return (-1); /* * find how many mechanisms were specified and also * setup the mechanism table for unique keylen/algtype pair */ nmechs = 0; for (mechpp = mechs; *mechpp != NULL; mechpp++) { struct keylenlist **kpp; struct algtypelist **app; nmechs++; if (((*mechpp)->keylen < 0) || ((*mechpp)->algtype < 0)) { continue; } kpp = getkeylen((*mechpp)->keylen); appendkeylist(kpp, (*mechpp)->keylen); app = getalgtype(kpp, (*mechpp)->algtype); appendalgtype(app, (*mechpp)->algtype); } /* * set of mechs for getsubopt() */ cache_options = (char **)calloc((size_t)nmechs + 1, sizeof (*cache_options)); if (cache_options == NULL) { (void) fprintf(stderr, "unable to allocate option array"); return (-1); } /* * cache sizes */ cache_size = (int *)calloc((size_t)nmechs, sizeof (int)); if (cache_size == NULL) { (void) fprintf(stderr, "unable to allocate cache array"); return (-1); } oldmechseen = 0; cpp = cache_options; for (mechpp = mechs; *mechpp != NULL; mechpp++) { /* * usual case: a DH-style mechanism type, with an alias */ if ((*mechpp)->mechname != NULL && strncmp((*mechpp)->mechname, DHMECHSTR, strlen(DHMECHSTR)) == 0 && (*mechpp)->alias != NULL) { /* * Is this trad 192-DH? already added? */ if (strcmp((*mechpp)->alias, DESALIAS) == 0) { if (oldmechseen) { continue; } oldmechseen++; } *cpp++ = (*mechpp)->alias; continue; } /* * HACK: we recognise a special alias for traditional * 192-bit DH, unless the latter has already been mentioned * in it's full form */ if ((*mechpp)->mechname == NULL && (*mechpp)->alias != NULL && strcmp((*mechpp)->alias, DES) == 0 && !oldmechseen) { *cpp++ = DESALIAS; oldmechseen++; continue; } /* * Ignore anything else */ } /* Terminate the options list */ *cpp = NULL; return (0); }