keyserv.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* 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"
/*
* keyserv - server for storing private encryption keys
* keyserv(1M) performs multiple functions: it stores secret keys per uid; it
* performs public key encryption and decryption operations; and it generates
* "random" keys. keyserv(1M) will talk to no one but a local root process on
* the local transport only.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include <deflt.h>
#include <rpc/rpc.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/resource.h>
#include <pwd.h>
#include <rpc/des_crypt.h>
#include <rpc/key_prot.h>
#include <thread.h>
#include "rpc/svc_mt.h"
#include <rpcsvc/nis_dhext.h>
#include <syslog.h>
#include <libscf.h>
#include "debug.h"
#include "keyserv_cache.h"
#ifdef KEYSERV_RANDOM
extern long random();
#endif
extern keystatus pk_setkey();
extern keystatus pk_encrypt();
extern keystatus pk_decrypt();
extern keystatus pk_netput();
extern keystatus pk_netget();
extern keystatus pk_get_conv_key();
extern bool_t svc_get_local_cred();
extern keystatus pk_setkey3();
extern keystatus pk_encrypt3();
extern keystatus pk_decrypt3();
extern keystatus pk_netput3();
extern keystatus pk_netget3();
extern keystatus pk_get_conv_key3();
extern keystatus pk_clear3();
extern int init_mechs();
extern int addmasterkey();
extern int storeotherrootkeys();
extern int setdeskeyarray();
extern int getdomainname();
static void randomize();
static void usage();
static void defaults();
static int getrootkey();
static int get_cache_size(char *);
static bool_t get_auth();
#ifdef DEBUG
extern int test_debug();
extern int real_debug();
int debugging = 1;
#else
int debugging = 0;
#endif
static void keyprogram();
static des_block masterkey;
char *getenv();
static char ROOTKEY[] = "/etc/.rootkey";
static char *defaults_file = "/etc/default/keyserv";
static int use_nobody_keys = TRUE;
/*
* Hack to allow the keyserver to use AUTH_DES (for authenticated
* NIS+ calls, for example). The only functions that get called
* are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
*
* The approach is to have the keyserver fill in pointers to local
* implementations of these functions, and to call those in key_call().
*/
bool_t __key_encrypt_pk_2_svc();
bool_t __key_decrypt_pk_2_svc();
bool_t __key_gen_1_svc();
extern bool_t (*__key_encryptsession_pk_LOCAL)();
extern bool_t (*__key_decryptsession_pk_LOCAL)();
extern bool_t (*__key_gendes_LOCAL)();
static int nthreads = 32;
/* Disk caching of common keys on by default */
int disk_caching = 1;
mechanism_t **mechs;
/*
* The default size for all types of mech.
* positive integers denote multiples of 1MB
* negative integers denote number of entries
* same goes for non-null entries in cache_size
*/
static int default_cache = 1;
int *cache_size;
char **cache_options;
main(argc, argv)
int argc;
char *argv[];
{
int sflag = 0, s1flag = 0, s2flag = 0, nflag = 0, dflag = 0, eflag = 0;
char *options, *value;
extern char *optarg;
extern int optind;
int c, d;
struct rlimit rl;
int mode = RPC_SVC_MT_AUTO;
int maxrecsz = RPC_MAXDATASIZE;
int detachfromtty();
int setmodulus();
int pk_nodefaultkeys();
int svc_create_local_service();
char domainname[MAXNETNAMELEN + 1];
/*
* Set our allowed number of file descriptors to the max
* of what the system will allow, limited by FD_SETSIZE.
*/
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
rlim_t limit;
if ((limit = rl.rlim_max) > FD_SETSIZE)
limit = FD_SETSIZE;
rl.rlim_cur = limit;
(void) setrlimit(RLIMIT_NOFILE, &rl);
}
__key_encryptsession_pk_LOCAL = &__key_encrypt_pk_2_svc;
__key_decryptsession_pk_LOCAL = &__key_decrypt_pk_2_svc;
__key_gendes_LOCAL = &__key_gen_1_svc;
/*
* Pre-option initialisation
*/
(void) umask(066); /* paranoia */
if (geteuid() != 0) {
(void) fprintf(stderr, "%s must be run as root\n", argv[0]);
exit(1);
}
setmodulus(HEXMODULUS);
openlog("keyserv", LOG_PID, LOG_DAEMON);
/*
* keyserv will not work with a null domainname.
*/
if (getdomainname(domainname, MAXNETNAMELEN+1) ||
(domainname[0] == '\0')) {
syslog(LOG_ERR, "could not get a valid domainname.\n");
exit(SMF_EXIT_ERR_CONFIG);
}
/*
* Initialise security mechanisms
*/
cache_size = NULL;
cache_options = NULL;
if (init_mechs() == -1) {
disk_caching = 0;
}
defaults();
while ((c = getopt(argc, argv, "ndDet:cs:")) != -1)
switch (c) {
case 'n':
nflag++;
break;
case 'd':
dflag++;
use_nobody_keys = FALSE;
break;
case 'e':
eflag++;
use_nobody_keys = TRUE;
break;
case 'D':
debugging = 1;
break;
case 't':
nthreads = atoi(optarg);
break;
case 'c':
disk_caching = 0;
break;
case 's':
if (!disk_caching) {
fprintf(stderr, "missing configuration file");
fprintf(stderr, " or -c option specified\n");
usage();
}
sflag++;
/*
* Which version of [-s] do we have...?
*/
if (strchr((const char *) optarg, '=') == NULL) {
/*
* -s <size>
*/
if (s1flag) {
fprintf(stderr, "duplicate [-s <size>]\n");
usage();
}
s1flag++;
default_cache = get_cache_size(optarg);
break;
}
/*
* -s <mechtype>=<size>[,...]
*/
s2flag++;
options = optarg;
while (*options != '\0') {
d = getsubopt(&options, cache_options, &value);
if (d == -1) {
/* Ignore unknown mechtype */
continue;
}
if (value == NULL) {
fprintf(stderr,
"missing cache size for mechtype %s\n",
cache_options[d]);
usage();
}
cache_size[d] = get_cache_size(value);
}
break;
default:
usage();
break;
}
if (dflag && eflag) {
(void) fprintf(stderr, "specify only one of -d and -e\n");
usage();
}
if (use_nobody_keys == FALSE) {
pk_nodefaultkeys();
}
if (optind != argc) {
usage();
}
if (!disk_caching && sflag) {
fprintf(stderr, "missing configuration file");
fprintf(stderr, " or -c option specified\n");
usage();
}
if (debugging) {
if (disk_caching) {
char **cpp = cache_options;
int *ip = cache_size;
(void) fprintf(stderr, "default disk cache size: ");
if (default_cache < 0) {
(void) fprintf(stderr, "%d entries\n",
abs(default_cache));
} else {
(void) fprintf(stderr, "%dMB\n", default_cache);
}
(void) fprintf(stderr, "supported mechanisms:\n");
(void) fprintf(stderr, "\talias\t\tdisk cache size\n");
(void) fprintf(stderr, "\t=====\t\t===============\n");
while (*cpp != NULL) {
(void) fprintf(stderr, "\t%s\t\t", *cpp++);
if (*ip < 0) {
(void) fprintf(stderr, "%d entries\n",
abs(*ip));
} else {
(void) fprintf(stderr, "%dMB\n", *ip);
}
ip++;
}
} else {
(void) fprintf(stderr,
"common key disk caching disabled\n");
}
}
/*
* Post-option initialisation
*/
if (disk_caching) {
int i;
for (i = 0; mechs[i]; i++) {
if ((AUTH_DES_COMPAT_CHK(mechs[i])) ||
(mechs[i]->keylen < 0) || (mechs[i]->algtype < 0))
continue;
create_cache_file(mechs[i]->keylen, mechs[i]->algtype,
cache_size[i] ? cache_size[i] : default_cache);
}
}
getrootkey(&masterkey, nflag);
/*
* Set MT mode
*/
if (nthreads > 0) {
(void) rpc_control(RPC_SVC_MTMODE_SET, &mode);
(void) rpc_control(RPC_SVC_THRMAX_SET, &nthreads);
}
/*
* Enable non-blocking mode and maximum record size checks for
* connection oriented transports.
*/
if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrecsz)) {
syslog(LOG_INFO, "unable to set max RPC record size");
}
if (svc_create_local_service(keyprogram, KEY_PROG, KEY_VERS,
"netpath", "keyserv") == 0) {
syslog(LOG_ERR,
"%s: unable to create service for version %d\n",
argv[0], KEY_VERS);
exit(1);
}
if (svc_create_local_service(keyprogram, KEY_PROG, KEY_VERS2,
"netpath", "keyserv") == 0) {
syslog(LOG_ERR,
"%s: unable to create service for version %d\n",
argv[0], KEY_VERS2);
exit(1);
}
if (svc_create_local_service(keyprogram, KEY_PROG, KEY_VERS3,
"netpath", "keyserv") == 0) {
syslog(LOG_ERR,
"%s: unable to create service for version %d\n",
argv[0], KEY_VERS3);
exit(1);
}
if (!debugging) {
detachfromtty();
}
if (svc_create(keyprogram, KEY_PROG, KEY_VERS, "door") == 0) {
syslog(LOG_ERR,
"%s: unable to create service over doors for version %d\n",
argv[0], KEY_VERS);
exit(1);
}
if (svc_create(keyprogram, KEY_PROG, KEY_VERS2, "door") == 0) {
syslog(LOG_ERR,
"%s: unable to create service over doors for version %d\n",
argv[0], KEY_VERS2);
exit(1);
}
if (svc_create(keyprogram, KEY_PROG, KEY_VERS3, "door") == 0) {
syslog(LOG_ERR,
"%s: unable to create service over doors for version %d\n",
argv[0], KEY_VERS3);
exit(1);
}
svc_run();
abort();
/* NOTREACHED */
return (0);
}
/*
* In the event that we don't get a root password, we try to
* randomize the master key the best we can
*/
static void
randomize(master)
des_block *master;
{
int i;
int seed;
struct timeval tv;
int shift;
seed = 0;
for (i = 0; i < 1024; i++) {
(void) gettimeofday(&tv, (struct timezone *)NULL);
shift = i % 8 * sizeof (int);
seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift));
}
#ifdef KEYSERV_RANDOM
srandom(seed);
master->key.low = random();
master->key.high = random();
srandom(seed);
#else
/* use stupid dangerous bad rand() */
srand(seed);
master->key.low = rand();
master->key.high = rand();
srand(seed);
#endif
}
static char *
fgets_ignorenul(char *s, int n, FILE *stream)
{
int fildes = fileno(stream);
int i = 0;
int rs = 0;
char c;
if (fildes < 0)
return (NULL);
while (i < n - 1) {
rs = read(fildes, &c, 1);
switch (rs) {
case 1:
break;
case 0:
/* EOF */
if (i > 0)
s[i] = '\0';
return (NULL);
break;
default:
return (NULL);
}
switch (c) {
case '\0':
break;
case '\n':
s[i] = c;
s[++i] = '\0';
return (s);
default:
if (c != '\0')
s[i++] = c;
}
}
s[i] = '\0';
return (s);
}
/* Should last until 16384-bit DH keys */
#define MAXROOTKEY_LINE_LEN 4224
#define MAXROOTKEY_LEN 4096
#define ROOTKEY_FILE "/etc/.rootkey"
static int
getotherrootkeys(char *name)
{
FILE *rootkey;
char line[MAXROOTKEY_LINE_LEN];
char key[MAXROOTKEY_LEN];
algtype_t algtype;
int count = 0;
if (!(rootkey = fopen(ROOTKEY, "r")))
return (0);
while (fgets_ignorenul(line, MAXROOTKEY_LINE_LEN, rootkey)) {
debug(KEYSERV_DEBUG0, ("ROOTKEY %d: %s\n", count, line));
count++;
sscanf(line, "%s %d", key, &algtype);
if (!strlen(key))
continue;
addmasterkey(key, name, algtype);
}
fclose(rootkey);
return (1);
}
/*
* Try to get root's secret key, by prompting if terminal is a tty, else trying
* from standard input.
* Returns 1 on success.
*/
static
getrootkey(master, prompt)
des_block *master;
int prompt;
{
char *passwd;
char name[MAXNETNAMELEN + 1];
char secret[HEXKEYBYTES + 1];
FILE *fp;
int passwd2des();
int retval;
randomize(master);
if (!getnetname(name)) {
(void) fprintf(stderr, "keyserv: \
failed to generate host's netname when establishing root's key.\n");
return (0);
}
if (!prompt) {
return (getotherrootkeys(name));
}
/*
* Decrypt yellow pages publickey entry to get secret key
*/
passwd = getpass("root password:");
passwd2des(passwd, master);
if (!getsecretkey(name, secret, passwd)) {
(void) fprintf(stderr,
"Can't find %s's secret key\n", name);
return (0);
}
if (secret[0] == 0) {
(void) fprintf(stderr,
"Password does not decrypt secret key for %s\n", name);
return (0);
}
if ((fp = fopen(ROOTKEY, "w")) == NULL) {
(void) fprintf(stderr,
"Cannot open %s for write\n", ROOTKEY);
return (0);
}
retval = storeotherrootkeys(fp, name, passwd, secret);
fclose(fp);
return (retval);
}
/*
* Procedures to implement RPC service. These procedures are named
* differently from the definitions in key_prot.h (generated by rpcgen)
* because they take different arguments.
*/
char *
strstatus(status)
keystatus status;
{
switch (status) {
case KEY_SUCCESS:
return ("KEY_SUCCESS");
case KEY_NOSECRET:
return ("KEY_NOSECRET");
case KEY_UNKNOWN:
return ("KEY_UNKNOWN");
case KEY_SYSTEMERR:
return ("KEY_SYSTEMERR");
case KEY_BADALG:
return ("KEY_BADALG");
case KEY_BADLEN:
return ("KEY_BADLEN");
default:
return ("(bad result code)");
}
}
bool_t
__key_set_1_svc(uid, key, status)
uid_t uid;
keybuf key;
keystatus *status;
{
if (debugging) {
(void) fprintf(stderr, "set(%d, %.*s) = ", uid,
sizeof (keybuf), key);
}
*status = pk_setkey(uid, key);
if (debugging) {
(void) fprintf(stderr, "%s\n", strstatus(*status));
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_encrypt_pk_2_svc(uid, arg, res)
uid_t uid;
cryptkeyarg2 *arg;
cryptkeyres *res;
{
if (debugging) {
(void) fprintf(stderr, "encrypt(%d, %s, %08x%08x) = ", uid,
arg->remotename, arg->deskey.key.high,
arg->deskey.key.low);
}
res->cryptkeyres_u.deskey = arg->deskey;
res->status = pk_encrypt(uid, arg->remotename, &(arg->remotekey),
&res->cryptkeyres_u.deskey);
if (debugging) {
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "%08x%08x\n",
res->cryptkeyres_u.deskey.key.high,
res->cryptkeyres_u.deskey.key.low);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_decrypt_pk_2_svc(uid, arg, res)
uid_t uid;
cryptkeyarg2 *arg;
cryptkeyres *res;
{
if (debugging) {
(void) fprintf(stderr, "decrypt(%d, %s, %08x%08x) = ", uid,
arg->remotename, arg->deskey.key.high,
arg->deskey.key.low);
}
res->cryptkeyres_u.deskey = arg->deskey;
res->status = pk_decrypt(uid, arg->remotename, &(arg->remotekey),
&res->cryptkeyres_u.deskey);
if (debugging) {
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "%08x%08x\n",
res->cryptkeyres_u.deskey.key.high,
res->cryptkeyres_u.deskey.key.low);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_net_put_2_svc(uid, arg, status)
uid_t uid;
key_netstarg *arg;
keystatus *status;
{
if (debugging) {
(void) fprintf(stderr, "net_put(%s, %.*s, %.*s) = ",
arg->st_netname, sizeof (arg->st_pub_key),
arg->st_pub_key, sizeof (arg->st_priv_key),
arg->st_priv_key);
};
*status = pk_netput(uid, arg);
if (debugging) {
(void) fprintf(stderr, "%s\n", strstatus(*status));
(void) fflush(stderr);
}
return (TRUE);
}
/* ARGSUSED */
bool_t
__key_net_get_2_svc(uid, arg, keynetname)
uid_t uid;
void *arg;
key_netstres *keynetname;
{
if (debugging)
(void) fprintf(stderr, "net_get(%d) = ", uid);
keynetname->status = pk_netget(uid, &keynetname->key_netstres_u.knet);
if (debugging) {
if (keynetname->status == KEY_SUCCESS) {
fprintf(stderr, "<%s, %.*s, %.*s>\n",
keynetname->key_netstres_u.knet.st_netname,
sizeof (keynetname->key_netstres_u.knet.st_pub_key),
keynetname->key_netstres_u.knet.st_pub_key,
sizeof (keynetname->key_netstres_u.knet.st_priv_key),
keynetname->key_netstres_u.knet.st_priv_key);
} else {
(void) fprintf(stderr, "NOT FOUND\n");
}
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_get_conv_2_svc(uid, arg, res)
uid_t uid;
keybuf arg;
cryptkeyres *res;
{
if (debugging)
(void) fprintf(stderr, "get_conv(%d, %.*s) = ", uid,
sizeof (arg), arg);
res->status = pk_get_conv_key(uid, arg, res);
if (debugging) {
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "%08x%08x\n",
res->cryptkeyres_u.deskey.key.high,
res->cryptkeyres_u.deskey.key.low);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_encrypt_1_svc(uid, arg, res)
uid_t uid;
cryptkeyarg *arg;
cryptkeyres *res;
{
if (debugging) {
(void) fprintf(stderr, "encrypt(%d, %s, %08x%08x) = ", uid,
arg->remotename, arg->deskey.key.high,
arg->deskey.key.low);
}
res->cryptkeyres_u.deskey = arg->deskey;
res->status = pk_encrypt(uid, arg->remotename, NULL,
&res->cryptkeyres_u.deskey);
if (debugging) {
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "%08x%08x\n",
res->cryptkeyres_u.deskey.key.high,
res->cryptkeyres_u.deskey.key.low);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
bool_t
__key_decrypt_1_svc(uid, arg, res)
uid_t uid;
cryptkeyarg *arg;
cryptkeyres *res;
{
if (debugging) {
(void) fprintf(stderr, "decrypt(%d, %s, %08x%08x) = ", uid,
arg->remotename, arg->deskey.key.high,
arg->deskey.key.low);
}
res->cryptkeyres_u.deskey = arg->deskey;
res->status = pk_decrypt(uid, arg->remotename, NULL,
&res->cryptkeyres_u.deskey);
if (debugging) {
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "%08x%08x\n",
res->cryptkeyres_u.deskey.key.high,
res->cryptkeyres_u.deskey.key.low);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
/* ARGSUSED */
bool_t
__key_gen_1_svc(v, s, key)
void *v;
struct svc_req *s;
des_block *key;
{
struct timeval time;
static des_block keygen;
static mutex_t keygen_mutex = DEFAULTMUTEX;
int r;
(void) gettimeofday(&time, (struct timezone *)NULL);
(void) mutex_lock(&keygen_mutex);
keygen.key.high += (time.tv_sec ^ time.tv_usec);
keygen.key.low += (time.tv_sec ^ time.tv_usec);
r = ecb_crypt((char *)&masterkey, (char *)&keygen, sizeof (keygen),
DES_ENCRYPT | DES_HW);
if (r != DESERR_NONE && r != DESERR_NOHWDEVICE) {
mutex_unlock(&keygen_mutex);
return (FALSE);
}
*key = keygen;
mutex_unlock(&keygen_mutex);
des_setparity_g(key);
if (debugging) {
(void) fprintf(stderr, "gen() = %08x%08x\n", key->key.high,
key->key.low);
(void) fflush(stderr);
}
return (TRUE);
}
/* ARGSUSED */
bool_t
__key_getcred_1_svc(uid, name, res)
uid_t uid;
netnamestr *name;
getcredres *res;
{
struct unixcred *cred;
cred = &res->getcredres_u.cred;
if (!netname2user(*name, (uid_t *)&cred->uid, (gid_t *)&cred->gid,
(int *)&cred->gids.gids_len,
(gid_t *)cred->gids.gids_val)) {
res->status = KEY_UNKNOWN;
} else {
res->status = KEY_SUCCESS;
}
if (debugging) {
(void) fprintf(stderr, "getcred(%s) = ", *name);
if (res->status == KEY_SUCCESS) {
(void) fprintf(stderr, "uid=%d, gid=%d, grouplen=%d\n",
cred->uid, cred->gid, cred->gids.gids_len);
} else {
(void) fprintf(stderr, "%s\n", strstatus(res->status));
}
(void) fflush(stderr);
}
return (TRUE);
}
/*
* Version 3 procedures follow...
*/
static bool_t
__key_set_3_svc(uid_t uid, setkeyarg3 *arg, keystatus *status)
{
debug(KEYSERV_DEBUG, ("__key_set_3_svc(%d, %d, %d)",
uid, arg->algtype, arg->keylen));
*status = pk_setkey3(uid, arg);
debug(KEYSERV_DEBUG, ("__key_set_3_svc %s", strstatus(*status)));
return (TRUE);
}
static bool_t
__key_encrypt_3_svc(uid_t uid, cryptkeyarg3 *arg, cryptkeyres3 *res)
{
int len, i;
des_block *dp;
debug(KEYSERV_DEBUG, ("encrypt_3(%d %d %s)", uid,
arg->deskey.deskeyarray_len, arg->remotename));
res->status = pk_encrypt3(uid, arg, &res->cryptkeyres3_u.deskey);
len = res->cryptkeyres3_u.deskey.deskeyarray_len;
dp = res->cryptkeyres3_u.deskey.deskeyarray_val;
for (i = 0; i < len; i++) {
debug(KEYSERV_DEBUG0, ("encrypt_3 retval[%d] == (%x,%x)",
i, dp->key.high, dp->key.low));
dp++;
}
debug(KEYSERV_DEBUG, ("encrypt_3 returned %s", strstatus(res->status)));
return (TRUE);
}
static bool_t
__key_decrypt_3_svc(uid_t uid, cryptkeyarg3 *arg, cryptkeyres3 *res)
{
int len, i;
des_block *dp;
debug(KEYSERV_DEBUG, ("decrypt_3(%d, %d, %s)", uid,
arg->deskey.deskeyarray_len, arg->remotename));
res->status = pk_decrypt3(uid, arg, &res->cryptkeyres3_u.deskey);
len = res->cryptkeyres3_u.deskey.deskeyarray_len;
dp = res->cryptkeyres3_u.deskey.deskeyarray_val;
for (i = 0; i < len; i++) {
debug(KEYSERV_DEBUG0, ("decrypt_3 retval[%d] == (%x,%x)",
i, dp->key.high, dp->key.low));
dp++;
}
debug(KEYSERV_DEBUG, ("decrypt_3 returned %s", strstatus(res->status)));
return (TRUE);
}
/* ARGSUSED */
static bool_t
__key_gen_3_svc(void *v, keynum_t *kp, deskeyarray *res)
{
int i;
keynum_t keynum = *kp;
debug(KEYSERV_DEBUG, ("gen_3(%d %x)", keynum, res));
res->deskeyarray_val = 0;
if (!setdeskeyarray(res, keynum)) {
return (FALSE);
}
for (i = 0; i < keynum; i++) {
debug(KEYSERV_DEBUG, ("gen_3 calling gen_1 %x",
res->deskeyarray_val+i));
__key_gen_1_svc((void *) NULL, (struct svc_req *)NULL,
res->deskeyarray_val+i);
debug(KEYSERV_DEBUG, ("gen_3 val %d %x",
i, *(int *)(res->deskeyarray_val+i)));
}
return (TRUE);
}
static void
__key_gen_3_svc_free(deskeyarray *dp)
{
free(dp->deskeyarray_val);
}
static bool_t
__key_getcred_3_svc(uid_t uid, netnamestr *name, getcredres3 *res)
{
return (__key_getcred_1_svc(uid, name, (getcredres *)res));
}
static bool_t
__key_encrypt_pk_3_svc(uid_t uid, cryptkeyarg3 *arg, cryptkeyres3 *res)
{
debug(KEYSERV_DEBUG, ("encrypt_pk_3(%d, %s)", uid, arg->remotename));
res->status = pk_encrypt3(uid, arg, &res->cryptkeyres3_u.deskey);
debug(KEYSERV_DEBUG, ("encrypt returned %s", strstatus(res->status)));
return (TRUE);
}
static void
__key_encrypt_pk_3_svc_free(cryptkeyres3 *res)
{
if (res->status == KEY_SUCCESS) {
free(res->cryptkeyres3_u.deskey.deskeyarray_val);
}
}
static bool_t
__key_decrypt_pk_3(uid_t uid, cryptkeyarg3 *arg, cryptkeyres3 *res)
{
debug(KEYSERV_DEBUG, ("decrypt_pk_3(%d, %s)", uid, arg->remotename));
res->status = pk_decrypt3(uid, arg, &res->cryptkeyres3_u.deskey);
debug(KEYSERV_DEBUG, ("encrypt returned %s", strstatus(res->status)));
return (TRUE);
}
static void
__key_decrypt_pk_3_free(cryptkeyres3 *res)
{
if (res->status == KEY_SUCCESS) {
free(res->cryptkeyres3_u.deskey.deskeyarray_val);
}
}
static bool_t
__key_net_put_3_svc(uid_t uid, key_netstarg3 *arg, keystatus *status)
{
debug(KEYSERV_DEBUG, ("net_put_3 (%d, %x)", uid, arg));
*status = pk_netput3(uid, arg);
debug(KEYSERV_DEBUG, ("net_put_3 ret %s", strstatus(*status)));
return (TRUE);
}
static bool_t
__key_net_get_3_svc(uid_t uid, mechtype *arg, key_netstres3 *keynetname)
{
debug(KEYSERV_DEBUG, ("net_get_3 (%d, %x)", uid, arg));
keynetname->status = pk_netget3(uid,
arg, &keynetname->key_netstres3_u.knet);
debug(KEYSERV_DEBUG,
("net_get_3 ret %s", strstatus(keynetname->status)));
return (TRUE);
}
static void
__key_net_get_3_svc_free(key_netstres3 *keynetname)
{
if (keynetname->status == KEY_SUCCESS) {
free(keynetname->key_netstres3_u.knet.st_priv_key.keybuf3_val);
free(keynetname->key_netstres3_u.knet.st_pub_key.keybuf3_val);
free(keynetname->key_netstres3_u.knet.st_netname);
}
}
static bool_t
__key_get_conv_3_svc(uid_t uid, deskeyarg3 *arg, cryptkeyres3 *res)
{
debug(KEYSERV_DEBUG, ("get_conv_3(%d %x %x)", uid, arg, res));
res->status = pk_get_conv_key3(uid, arg, res);
debug(KEYSERV_DEBUG,
("get_conv_3 ret %s", strstatus(res->status)));
return (TRUE);
}
/* ARGSUSED */
static bool_t
__key_clear_3_svc(uid_t uid, void *arg, keystatus *status)
{
debug(KEYSERV_DEBUG, ("clear_3(%d)", uid));
*status = pk_clear3(uid);
debug(KEYSERV_DEBUG, ("clear_3 ret %s", strstatus(*status)));
return (TRUE);
}
/*
* RPC boilerplate
*/
static void
keyprogram(rqstp, transp)
struct svc_req *rqstp;
SVCXPRT *transp;
{
union {
keybuf key_set_1_arg;
cryptkeyarg key_encrypt_1_arg;
cryptkeyarg key_decrypt_1_arg;
netnamestr key_getcred_1_arg;
cryptkeyarg key_encrypt_2_arg;
cryptkeyarg key_decrypt_2_arg;
netnamestr key_getcred_2_arg;
cryptkeyarg2 key_encrypt_pk_2_arg;
cryptkeyarg2 key_decrypt_pk_2_arg;
key_netstarg key_net_put_2_arg;
netobj key_get_conv_2_arg;
keybuf3 key_set_3_arg;
cryptkeyarg3 key_encrypt_3_arg;
cryptkeyarg3 key_decrypt_3_arg;
cryptkeyarg3 key_encrypt_pk_3_arg;
cryptkeyarg3 key_decrypt_pk_3_arg;
keynum_t key_gen_3_arg;
netnamestr key_getcred_3_arg;
key_netstarg3 key_net_put_3_arg;
key_netstarg3 key_net_get_3_arg;
deskeyarg3 key_get_conv_3_arg;
} argument;
union {
keystatus status;
cryptkeyres cres;
des_block key;
getcredres gres;
key_netstres keynetname;
cryptkeyres3 cres3;
deskeyarray keyarray;
getcredres3 gres3;
key_netstres3 keynetname3;
} result;
uint_t gids[MAXGIDS];
char netname_str[MAXNETNAMELEN + 1];
bool_t (*xdr_argument)(), (*xdr_result)();
bool_t (*local)();
void (*local_free)() = NULL;
bool_t retval;
uid_t uid;
int check_auth;
switch (rqstp->rq_proc) {
case NULLPROC:
svc_sendreply(transp, xdr_void, (char *)NULL);
return;
case KEY_SET:
xdr_argument = xdr_keybuf;
xdr_result = xdr_int;
local = __key_set_1_svc;
check_auth = 1;
break;
case KEY_ENCRYPT:
xdr_argument = xdr_cryptkeyarg;
xdr_result = xdr_cryptkeyres;
local = __key_encrypt_1_svc;
check_auth = 1;
break;
case KEY_DECRYPT:
xdr_argument = xdr_cryptkeyarg;
xdr_result = xdr_cryptkeyres;
local = __key_decrypt_1_svc;
check_auth = 1;
break;
case KEY_GEN:
xdr_argument = xdr_void;
xdr_result = xdr_des_block;
local = __key_gen_1_svc;
check_auth = 0;
break;
case KEY_GETCRED:
xdr_argument = xdr_netnamestr;
xdr_result = xdr_getcredres;
local = __key_getcred_1_svc;
result.gres.getcredres_u.cred.gids.gids_val = gids;
check_auth = 0;
break;
case KEY_ENCRYPT_PK:
xdr_argument = xdr_cryptkeyarg2;
xdr_result = xdr_cryptkeyres;
local = __key_encrypt_pk_2_svc;
check_auth = 1;
break;
case KEY_DECRYPT_PK:
xdr_argument = xdr_cryptkeyarg2;
xdr_result = xdr_cryptkeyres;
local = __key_decrypt_pk_2_svc;
check_auth = 1;
break;
case KEY_NET_PUT:
xdr_argument = xdr_key_netstarg;
xdr_result = xdr_keystatus;
local = __key_net_put_2_svc;
check_auth = 1;
break;
case KEY_NET_GET:
xdr_argument = (xdrproc_t)xdr_void;
xdr_result = xdr_key_netstres;
local = __key_net_get_2_svc;
result.keynetname.key_netstres_u.knet.st_netname = netname_str;
check_auth = 1;
break;
case KEY_GET_CONV:
xdr_argument = (xdrproc_t)xdr_keybuf;
xdr_result = xdr_cryptkeyres;
local = __key_get_conv_2_svc;
check_auth = 1;
break;
/*
* Version 3 procedures follow...
*/
case KEY_SET_3:
xdr_argument = (xdrproc_t)xdr_setkeyarg3;
xdr_result = xdr_keystatus;
local = __key_set_3_svc;
check_auth = 1;
break;
case KEY_ENCRYPT_3:
xdr_argument = (xdrproc_t)xdr_cryptkeyarg3;
xdr_result = xdr_cryptkeyres3;
local = __key_encrypt_3_svc;
check_auth = 1;
break;
case KEY_DECRYPT_3:
xdr_argument = (xdrproc_t)xdr_cryptkeyarg3;
xdr_result = xdr_cryptkeyres3;
local = __key_decrypt_3_svc;
check_auth = 1;
break;
case KEY_GEN_3:
xdr_argument = (xdrproc_t)xdr_keynum_t;
xdr_result = xdr_deskeyarray;
local = __key_gen_3_svc;
local_free = __key_gen_3_svc_free;
check_auth = 0;
break;
case KEY_GETCRED_3:
xdr_argument = (xdrproc_t)xdr_netnamestr;
xdr_result = xdr_getcredres3;
local = __key_getcred_3_svc;
check_auth = 0;
break;
case KEY_ENCRYPT_PK_3:
xdr_argument = (xdrproc_t)xdr_cryptkeyarg3;
xdr_result = xdr_cryptkeyres3;
local = __key_encrypt_pk_3_svc;
local_free = __key_encrypt_pk_3_svc_free;
check_auth = 1;
break;
case KEY_DECRYPT_PK_3:
xdr_argument = (xdrproc_t)xdr_cryptkeyarg3;
xdr_result = xdr_cryptkeyres3;
local = __key_decrypt_pk_3;
local_free = __key_decrypt_pk_3_free;
check_auth = 1;
break;
case KEY_NET_PUT_3:
xdr_argument = (xdrproc_t)xdr_key_netstarg3;
xdr_result = xdr_keystatus;
local = __key_net_put_3_svc;
check_auth = 1;
break;
case KEY_NET_GET_3:
xdr_argument = (xdrproc_t)xdr_mechtype;
xdr_result = xdr_key_netstres3;
local = __key_net_get_3_svc;
local_free = __key_net_get_3_svc_free;
check_auth = 1;
break;
case KEY_GET_CONV_3:
xdr_argument = (xdrproc_t)xdr_deskeyarg3;
xdr_result = xdr_cryptkeyres3;
local = __key_get_conv_3_svc;
check_auth = 1;
break;
case KEY_CLEAR_3:
xdr_argument = (xdrproc_t)xdr_void;
xdr_result = xdr_keystatus;
local = __key_clear_3_svc;
check_auth = 1;
break;
default:
svcerr_noproc(transp);
return;
}
if (check_auth) {
if (!get_auth(transp, rqstp, &uid)) {
if (debugging) {
(void) fprintf(stderr,
"not local privileged process\n");
}
svcerr_weakauth(transp);
return;
}
}
memset((char *)&argument, 0, sizeof (argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
svcerr_decode(transp);
return;
}
retval = (*local)(uid, &argument, &result);
if (retval && !svc_sendreply(transp, xdr_result, (char *)&result)) {
if (debugging)
(void) fprintf(stderr, "unable to reply\n");
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
if (debugging)
(void) fprintf(stderr,
"unable to free arguments\n");
exit(1);
}
if (local_free) {
(*local_free)(&result);
}
}
static bool_t
get_auth(trans, rqstp, uid)
SVCXPRT *trans;
struct svc_req *rqstp;
uid_t *uid;
{
svc_local_cred_t cred;
if (!svc_get_local_cred(trans, &cred)) {
if (debugging)
fprintf(stderr, "svc_get_local_cred failed %s %s\n",
trans->xp_netid, trans->xp_tp);
return (FALSE);
}
if (debugging)
fprintf(stderr, "local_uid %d\n", cred.euid);
if (rqstp->rq_cred.oa_flavor == AUTH_SYS ||
rqstp->rq_cred.oa_flavor == AUTH_LOOPBACK) {
/* LINTED pointer alignment */
*uid = ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid;
return (*uid == cred.euid || cred.euid == 0);
} else {
*uid = cred.euid;
return (TRUE);
}
}
static int
get_cache_size(size)
char *size;
{
int csize, len;
len = (int)strlen(size);
if (len == 0) {
usage();
}
if (size[len-1] == 'M' || size[len-1] == 'm') {
/*
* cache size in MB
*/
size[len-1] = '\0';
csize = atoi(size);
} else {
csize = atoi(size);
/*
* negative size indicates number of entries in cache
*/
csize = 0 - csize;
}
if (csize == 0) {
(void) fprintf(stderr, "invalid cache size: %s\n", size);
usage();
}
return (csize);
}
static void
usage()
{
(void) fprintf(stderr, "usage: \n");
(void) fprintf(stderr, "keyserv [-c]|[-s ");
(void) fprintf(stderr, "<size>|<mechtype>=<size>[,...]] [-n] [-D] ");
(void) fprintf(stderr, "[-d | -e] ");
(void) fprintf(stderr, "[-t threads]\n");
(void) fprintf(stderr, "-d disables the use of default keys\n");
(void) fprintf(stderr, "-e enables the use of default keys\n");
exit(1);
}
static void
defaults(void)
{
register int flags;
register char *ptr;
if (defopen(defaults_file) == 0) {
/*
* ignore case
*/
flags = defcntl(DC_GETFLAGS, 0);
TURNOFF(flags, DC_CASE);
defcntl(DC_SETFLAGS, flags);
if ((ptr = defread("ENABLE_NOBODY_KEYS=")) != NULL) {
if (strcasecmp(ptr, "NO") == 0) {
use_nobody_keys = FALSE;
}
}
(void) defopen((char *)NULL);
}
}