| /* |
| * Copyright (c) 2009, Sun Microsystems, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * - Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * - Neither the name of Sun Microsystems, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| /* |
| * Copyright (c) 1986-1991 by Sun Microsystems Inc. |
| */ |
| |
| #include <sys/cdefs.h> |
| |
| /* |
| * key_call.c, Interface to keyserver |
| * |
| * setsecretkey(key) - set your secret key |
| * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent |
| * decryptsessionkey(agent, deskey) - decrypt ditto |
| * gendeskey(deskey) - generate a secure des key |
| */ |
| |
| #include <pthread.h> |
| #include <reentrant.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <rpc/rpc.h> |
| #include <rpc/auth.h> |
| #include <rpc/auth_unix.h> |
| #include <rpc/key_prot.h> |
| #include <string.h> |
| #include <netconfig.h> |
| #include <sys/utsname.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <sys/wait.h> |
| #include <sys/fcntl.h> |
| |
| #define KEY_TIMEOUT 5 /* per-try timeout in seconds */ |
| #define KEY_NRETRY 12 /* number of retries */ |
| |
| #ifdef DEBUG |
| #define debug(msg) \ |
| ((void) fprintf(stderr, "%s\n", msg)) |
| |
| #else |
| #define debug(msg) |
| #endif /* DEBUG */ |
| |
| /* |
| * 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(). |
| */ |
| |
| cryptkeyres *(*__key_encryptsession_pk_LOCAL) () = 0; |
| cryptkeyres *(*__key_decryptsession_pk_LOCAL) () = 0; |
| des_block *(*__key_gendes_LOCAL) () = 0; |
| |
| static int key_call(u_long, xdrproc_t, void *, xdrproc_t, void *); |
| |
| int |
| key_setsecret(const char *secretkey) |
| { |
| keystatus status; |
| |
| if (!key_call |
| ((u_long) KEY_SET, (xdrproc_t) xdr_keybuf, (void *)secretkey, |
| (xdrproc_t) xdr_keystatus, &status)) { |
| return (-1); |
| } |
| if (status != KEY_SUCCESS) { |
| debug("set status is nonzero"); |
| return (-1); |
| } |
| return (0); |
| } |
| |
| /* key_secretkey_is_set() returns 1 if the keyserver has a secret key |
| * stored for the caller's effective uid; it returns 0 otherwise |
| * |
| * N.B.: The KEY_NET_GET key call is undocumented. Applications shouldn't |
| * be using it, because it allows them to get the user's secret key. |
| */ |
| |
| int |
| key_secretkey_is_set(void) |
| { |
| struct key_netstres kres; |
| |
| memset((void *)&kres, 0, sizeof(kres)); |
| if (key_call |
| ((u_long) KEY_NET_GET, (xdrproc_t) xdr_void, NULL, |
| (xdrproc_t) xdr_key_netstres, &kres) |
| && (kres.status == KEY_SUCCESS) |
| && (kres.key_netstres_u.knet.st_priv_key[0] != 0)) { |
| /* avoid leaving secret key in memory */ |
| memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES); |
| return (1); |
| } |
| return (0); |
| } |
| |
| int |
| key_encryptsession_pk(char *remotename, netobj *remotekey, |
| des_block *deskey) |
| { |
| cryptkeyarg2 arg; |
| cryptkeyres res; |
| |
| arg.remotename = remotename; |
| arg.remotekey = *remotekey; |
| arg.deskey = *deskey; |
| if (!key_call |
| ((u_long) KEY_ENCRYPT_PK, (xdrproc_t) xdr_cryptkeyarg2, &arg, |
| (xdrproc_t) xdr_cryptkeyres, &res)) { |
| return (-1); |
| } |
| if (res.status != KEY_SUCCESS) { |
| debug("encrypt status is nonzero"); |
| return (-1); |
| } |
| *deskey = res.cryptkeyres_u.deskey; |
| return (0); |
| } |
| |
| int |
| key_decryptsession_pk(char *remotename, netobj *remotekey, |
| des_block *deskey) |
| { |
| cryptkeyarg2 arg; |
| cryptkeyres res; |
| |
| arg.remotename = remotename; |
| arg.remotekey = *remotekey; |
| arg.deskey = *deskey; |
| if (!key_call |
| ((u_long) KEY_DECRYPT_PK, (xdrproc_t) xdr_cryptkeyarg2, &arg, |
| (xdrproc_t) xdr_cryptkeyres, &res)) { |
| return (-1); |
| } |
| if (res.status != KEY_SUCCESS) { |
| debug("decrypt status is nonzero"); |
| return (-1); |
| } |
| *deskey = res.cryptkeyres_u.deskey; |
| return (0); |
| } |
| |
| int |
| key_encryptsession(const char *remotename, des_block *deskey) |
| { |
| cryptkeyarg arg; |
| cryptkeyres res; |
| |
| arg.remotename = (char *)remotename; |
| arg.deskey = *deskey; |
| if (!key_call |
| ((u_long) KEY_ENCRYPT, (xdrproc_t) xdr_cryptkeyarg, &arg, |
| (xdrproc_t) xdr_cryptkeyres, &res)) { |
| return (-1); |
| } |
| if (res.status != KEY_SUCCESS) { |
| debug("encrypt status is nonzero"); |
| return (-1); |
| } |
| *deskey = res.cryptkeyres_u.deskey; |
| return (0); |
| } |
| |
| int |
| key_decryptsession(const char *remotename, des_block *deskey) |
| { |
| cryptkeyarg arg; |
| cryptkeyres res; |
| |
| arg.remotename = (char *)remotename; |
| arg.deskey = *deskey; |
| if (!key_call |
| ((u_long) KEY_DECRYPT, (xdrproc_t) xdr_cryptkeyarg, &arg, |
| (xdrproc_t) xdr_cryptkeyres, &res)) { |
| return (-1); |
| } |
| if (res.status != KEY_SUCCESS) { |
| debug("decrypt status is nonzero"); |
| return (-1); |
| } |
| *deskey = res.cryptkeyres_u.deskey; |
| return (0); |
| } |
| |
| int |
| key_gendes(des_block *key) |
| { |
| if (!key_call |
| ((u_long) KEY_GEN, (xdrproc_t) xdr_void, NULL, |
| (xdrproc_t) xdr_des_block, key)) { |
| return (-1); |
| } |
| return (0); |
| } |
| |
| int |
| key_setnet(struct key_netstarg *arg) |
| { |
| keystatus status; |
| |
| if (!key_call |
| ((u_long) KEY_NET_PUT, (xdrproc_t) xdr_key_netstarg, arg, |
| (xdrproc_t) xdr_keystatus, &status)) { |
| return (-1); |
| } |
| |
| if (status != KEY_SUCCESS) { |
| debug("key_setnet status is nonzero"); |
| return (-1); |
| } |
| return (1); |
| } |
| |
| int |
| key_get_conv(char *pkey, des_block *deskey) |
| { |
| cryptkeyres res; |
| |
| if (!key_call |
| ((u_long) KEY_GET_CONV, (xdrproc_t) xdr_keybuf, pkey, |
| (xdrproc_t) xdr_cryptkeyres, &res)) { |
| return (-1); |
| } |
| if (res.status != KEY_SUCCESS) { |
| debug("get_conv status is nonzero"); |
| return (-1); |
| } |
| *deskey = res.cryptkeyres_u.deskey; |
| return (0); |
| } |
| |
| struct key_call_private { |
| CLIENT *client; /* Client handle */ |
| pid_t pid; /* process-id at moment of creation */ |
| uid_t uid; /* user-id at last authorization */ |
| }; |
| static struct key_call_private *key_call_private_mainL; |
| |
| static void |
| key_call_destroy(void *vp) |
| { |
| struct key_call_private *kcp = (struct key_call_private *)vp; |
| |
| if (kcp) { |
| if (kcp->client) |
| clnt_destroy(kcp->client); |
| __free(kcp); |
| } |
| } |
| |
| /* |
| * Keep the handle cached. This call may be made quite often. |
| */ |
| static CLIENT * |
| getkeyserv_handle(int vers) |
| { |
| void *localhandle; |
| struct netconfig *nconf; |
| struct netconfig *tpconf; |
| struct key_call_private *kcp = key_call_private_main; |
| struct timeval wait_time; |
| struct utsname u; |
| int fd; |
| extern thread_key_t key_call_key; |
| extern mutex_t tsd_lock; |
| |
| #define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */ |
| #define TOTAL_TRIES 5 /* Number of tries */ |
| |
| if (key_call_key == -1) { |
| mutex_lock(&tsd_lock); |
| if (key_call_key == -1) |
| thr_keycreate(&key_call_key, key_call_destroy); |
| mutex_unlock(&tsd_lock); |
| } |
| kcp = (struct key_call_private *)thr_getspecific(key_call_key); |
| if (kcp == (struct key_call_private *)NULL) { |
| kcp = (struct key_call_private *)mem_alloc(sizeof(*kcp)); |
| if (kcp == (struct key_call_private *)NULL) |
| return ((CLIENT *) NULL); |
| thr_setspecific(key_call_key, (void *)kcp); |
| kcp->client = NULL; |
| } |
| |
| /* if pid has changed, destroy client and rebuild */ |
| if (kcp->client != NULL && kcp->pid != getpid()) { |
| clnt_destroy(kcp->client); |
| kcp->client = NULL; |
| } |
| |
| if (kcp->client != NULL) { |
| /* if uid has changed, build client handle again */ |
| if (kcp->uid != geteuid()) { |
| kcp->uid = geteuid(); |
| auth_destroy(kcp->client->cl_auth); |
| kcp->client->cl_auth = |
| authsys_create("", kcp->uid, 0, 0, NULL); |
| if (kcp->client->cl_auth == NULL) { |
| clnt_destroy(kcp->client); |
| kcp->client = NULL; |
| return ((CLIENT *) NULL); |
| } |
| } |
| /* Change the version number to the new one */ |
| clnt_control(kcp->client, CLSET_VERS, (void *)&vers); |
| return (kcp->client); |
| } |
| localhandle = setnetconfig(); |
| if (!localhandle) |
| return ((CLIENT *) NULL); |
| tpconf = NULL; |
| #if defined(__FreeBSD__) |
| if (uname(&u) == -1) |
| #else |
| #if defined(i386) |
| if (uname(&u) == -1) |
| #elif defined(sparc) |
| if (uname(&u) == -1) |
| #else |
| #error Unknown architecture! |
| #endif |
| #endif |
| { |
| endnetconfig(localhandle); |
| return ((CLIENT *) NULL); |
| } |
| while ((nconf = getnetconfig(localhandle)) != NULL) { |
| if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { |
| /* |
| * We use COTS_ORD here so that the caller can |
| * find out immediately if the server is dead. |
| */ |
| if (nconf->nc_semantics == NC_TPI_COTS_ORD) { |
| kcp->client = |
| clnt_tp_create(u.nodename, KEY_PROG, vers, |
| nconf); |
| if (kcp->client) |
| break; |
| } else { |
| tpconf = nconf; |
| } |
| } |
| } |
| if ((kcp->client == (CLIENT *) NULL) && (tpconf)) |
| /* Now, try the CLTS or COTS loopback transport */ |
| kcp->client = |
| clnt_tp_create(u.nodename, KEY_PROG, vers, tpconf); |
| endnetconfig(localhandle); |
| |
| if (kcp->client == (CLIENT *) NULL) |
| return ((CLIENT *) NULL); |
| kcp->uid = geteuid(); |
| kcp->pid = getpid(); |
| kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL); |
| if (kcp->client->cl_auth == NULL) { |
| clnt_destroy(kcp->client); |
| kcp->client = NULL; |
| return ((CLIENT *) NULL); |
| } |
| |
| wait_time.tv_sec = TOTAL_TIMEOUT / TOTAL_TRIES; |
| wait_time.tv_usec = 0; |
| (void)clnt_control(kcp->client, CLSET_RETRY_TIMEOUT, |
| (char *)&wait_time); |
| if (clnt_control(kcp->client, CLGET_FD, (char *)&fd)) |
| fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ |
| |
| return (kcp->client); |
| } |
| |
| /* returns 0 on failure, 1 on success */ |
| |
| static int |
| key_call(u_long proc, xdrproc_t xdr_arg, void *arg, |
| xdrproc_t xdr_rslt, void *rslt) |
| { |
| CLIENT *clnt; |
| struct timeval wait_time; |
| |
| if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) { |
| cryptkeyres *res; |
| res = (*__key_encryptsession_pk_LOCAL) (geteuid(), arg); |
| *(cryptkeyres *) rslt = *res; |
| return (1); |
| } else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) { |
| cryptkeyres *res; |
| res = (*__key_decryptsession_pk_LOCAL) (geteuid(), arg); |
| *(cryptkeyres *) rslt = *res; |
| return (1); |
| } else if (proc == KEY_GEN && __key_gendes_LOCAL) { |
| des_block *res; |
| res = (*__key_gendes_LOCAL) (geteuid(), 0); |
| *(des_block *) rslt = *res; |
| return (1); |
| } |
| |
| if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) |
| || (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) |
| || (proc == KEY_GET_CONV)) |
| clnt = getkeyserv_handle(2); /* talk to version 2 */ |
| else |
| clnt = getkeyserv_handle(1); /* talk to version 1 */ |
| |
| if (clnt == NULL) |
| return (0); |
| |
| wait_time.tv_sec = TOTAL_TIMEOUT; |
| wait_time.tv_usec = 0; |
| |
| if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt, wait_time) == |
| RPC_SUCCESS) |
| return (1); |
| else |
| return (0); |
| } |