/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rpc/svc_mt.h" #include #include #include #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; int main(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; void detachfromtty(void); 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 */ if (s1flag) { fprintf(stderr, "duplicate [-s ]\n"); usage(); } s1flag++; default_cache = get_cache_size(optarg); break; } /* * -s =[,...] */ 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++; if (sscanf(line, "%s %d", key, &algtype) < 2) { /* * No encryption algorithm found in the file * (algtype) so default to DES. */ algtype = AUTH_DES_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 int 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, "|=[,...]] [-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); } }