|  | /* | 
|  | * Copyright (c) 2010, Oracle America, Inc. | 
|  | * | 
|  | * 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 the "Oracle America, 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. | 
|  | */ | 
|  | /* | 
|  | * The original source is from the RPCSRC 4.0 package from Sun Microsystems. | 
|  | * The Interface to keyserver protocoll 2, RPC over AF_UNIX and Linux/doors | 
|  | * was added by Thorsten Kukuk <kukuk@suse.de> | 
|  | * Since the Linux/doors project was stopped, I doubt that this code will | 
|  | * ever be useful <kukuk@suse.de>. | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <signal.h> | 
|  | #include <unistd.h> | 
|  | #include <string.h> | 
|  | #include <rpc/rpc.h> | 
|  | #include <rpc/auth.h> | 
|  | #include <sys/wait.h> | 
|  | #include <sys/param.h> | 
|  | #include <sys/socket.h> | 
|  | #include <rpc/key_prot.h> | 
|  | #include <libc-lock.h> | 
|  |  | 
|  | #define KEY_TIMEOUT	5	/* per-try timeout in seconds */ | 
|  | #define KEY_NRETRY	12	/* number of retries */ | 
|  |  | 
|  | #define debug(msg)		/* turn off debugging */ | 
|  |  | 
|  | #ifndef SO_PASSCRED | 
|  | extern int _openchild (const char *command, FILE **fto, FILE **ffrom); | 
|  | #endif | 
|  |  | 
|  | static int key_call (u_long, xdrproc_t xdr_arg, char *, | 
|  | xdrproc_t xdr_rslt, char *) internal_function; | 
|  |  | 
|  | static const struct timeval trytimeout = {KEY_TIMEOUT, 0}; | 
|  | static const struct timeval tottimeout = {KEY_TIMEOUT *KEY_NRETRY, 0}; | 
|  |  | 
|  | int | 
|  | key_setsecret (char *secretkey) | 
|  | { | 
|  | keystatus status; | 
|  |  | 
|  | if (!key_call ((u_long) KEY_SET, (xdrproc_t) xdr_keybuf, secretkey, | 
|  | (xdrproc_t) xdr_keystatus, (char *) &status)) | 
|  | return -1; | 
|  | if (status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("set status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_setsecret, GLIBC_2_1) | 
|  |  | 
|  | /* 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 (&kres, 0, sizeof (kres)); | 
|  | if (key_call ((u_long) KEY_NET_GET, (xdrproc_t) xdr_void, | 
|  | (char *) NULL, (xdrproc_t) xdr_key_netstres, | 
|  | (char *) &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; | 
|  | } | 
|  | #ifdef EXPORT_RPC_SYMBOLS | 
|  | libc_hidden_def (key_secretkey_is_set) | 
|  | #else | 
|  | libc_hidden_nolink_sunrpc (key_secretkey_is_set, GLIBC_2_1) | 
|  | #endif | 
|  |  | 
|  | int | 
|  | key_encryptsession (char *remotename, des_block *deskey) | 
|  | { | 
|  | cryptkeyarg arg; | 
|  | cryptkeyres res; | 
|  |  | 
|  | arg.remotename = remotename; | 
|  | arg.deskey = *deskey; | 
|  | if (!key_call ((u_long) KEY_ENCRYPT, (xdrproc_t) xdr_cryptkeyarg, | 
|  | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, | 
|  | (char *) &res)) | 
|  | return -1; | 
|  |  | 
|  | if (res.status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("encrypt status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | *deskey = res.cryptkeyres_u.deskey; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_encryptsession, GLIBC_2_1) | 
|  |  | 
|  | int | 
|  | key_decryptsession (char *remotename, des_block *deskey) | 
|  | { | 
|  | cryptkeyarg arg; | 
|  | cryptkeyres res; | 
|  |  | 
|  | arg.remotename = remotename; | 
|  | arg.deskey = *deskey; | 
|  | if (!key_call ((u_long) KEY_DECRYPT, (xdrproc_t) xdr_cryptkeyarg, | 
|  | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, | 
|  | (char *) &res)) | 
|  | return -1; | 
|  | if (res.status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("decrypt status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | *deskey = res.cryptkeyres_u.deskey; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_decryptsession, GLIBC_2_1) | 
|  |  | 
|  | 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, | 
|  | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, | 
|  | (char *) &res)) | 
|  | return -1; | 
|  |  | 
|  | if (res.status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("encrypt status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | *deskey = res.cryptkeyres_u.deskey; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_encryptsession_pk, GLIBC_2_1) | 
|  |  | 
|  | 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, | 
|  | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, | 
|  | (char *) &res)) | 
|  | return -1; | 
|  |  | 
|  | if (res.status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("decrypt status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | *deskey = res.cryptkeyres_u.deskey; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_decryptsession_pk, GLIBC_2_1) | 
|  |  | 
|  | int | 
|  | key_gendes (des_block *key) | 
|  | { | 
|  | struct sockaddr_in sin; | 
|  | CLIENT *client; | 
|  | int socket; | 
|  | enum clnt_stat stat; | 
|  |  | 
|  | sin.sin_family = AF_INET; | 
|  | sin.sin_port = 0; | 
|  | sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | 
|  | __bzero (sin.sin_zero, sizeof (sin.sin_zero)); | 
|  | socket = RPC_ANYSOCK; | 
|  | client = clntudp_bufcreate (&sin, (u_long) KEY_PROG, (u_long) KEY_VERS, | 
|  | trytimeout, &socket, RPCSMALLMSGSIZE, | 
|  | RPCSMALLMSGSIZE); | 
|  | if (client == NULL) | 
|  | return -1; | 
|  |  | 
|  | stat = clnt_call (client, KEY_GEN, (xdrproc_t) xdr_void, NULL, | 
|  | (xdrproc_t) xdr_des_block, (caddr_t) key, | 
|  | tottimeout); | 
|  | clnt_destroy (client); | 
|  | __close (socket); | 
|  | if (stat != RPC_SUCCESS) | 
|  | return -1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #ifdef EXPORT_RPC_SYMBOLS | 
|  | libc_hidden_def (key_gendes) | 
|  | #else | 
|  | libc_hidden_nolink_sunrpc (key_gendes, GLIBC_2_1) | 
|  | #endif | 
|  |  | 
|  | int | 
|  | key_setnet (struct key_netstarg *arg) | 
|  | { | 
|  | keystatus status; | 
|  |  | 
|  | if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) xdr_key_netstarg, | 
|  | (char *) arg,(xdrproc_t) xdr_keystatus, | 
|  | (char *) &status)) | 
|  | return -1; | 
|  |  | 
|  | if (status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("key_setnet status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_setnet, GLIBC_2_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, (char *) &res)) | 
|  | return -1; | 
|  |  | 
|  | if (res.status != KEY_SUCCESS) | 
|  | { | 
|  | debug ("get_conv status is nonzero"); | 
|  | return -1; | 
|  | } | 
|  | *deskey = res.cryptkeyres_u.deskey; | 
|  | return 0; | 
|  | } | 
|  | libc_hidden_nolink_sunrpc (key_get_conv, GLIBC_2_1) | 
|  |  | 
|  | /* | 
|  | * 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) (uid_t, char *); | 
|  | cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *); | 
|  | des_block *(*__key_gendes_LOCAL) (uid_t, char *); | 
|  |  | 
|  | #ifndef SO_PASSCRED | 
|  | static int | 
|  | internal_function | 
|  | key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg, | 
|  | xdrproc_t xdr_rslt, char *rslt) | 
|  | { | 
|  | XDR xdrargs; | 
|  | XDR xdrrslt; | 
|  | FILE *fargs; | 
|  | FILE *frslt; | 
|  | sigset_t oldmask, mask; | 
|  | union wait status; | 
|  | int pid; | 
|  | int success; | 
|  | uid_t ruid; | 
|  | uid_t euid; | 
|  | static const char MESSENGER[] = "/usr/etc/keyenvoy"; | 
|  |  | 
|  | success = 1; | 
|  | sigemptyset (&mask); | 
|  | sigaddset (&mask, SIGCHLD); | 
|  | __sigprocmask (SIG_BLOCK, &mask, &oldmask); | 
|  |  | 
|  | /* | 
|  | * We are going to exec a set-uid program which makes our effective uid | 
|  | * zero, and authenticates us with our real uid. We need to make the | 
|  | * effective uid be the real uid for the setuid program, and | 
|  | * the real uid be the effective uid so that we can change things back. | 
|  | */ | 
|  | euid = __geteuid (); | 
|  | ruid = __getuid (); | 
|  | __setreuid (euid, ruid); | 
|  | pid = _openchild (MESSENGER, &fargs, &frslt); | 
|  | __setreuid (ruid, euid); | 
|  | if (pid < 0) | 
|  | { | 
|  | debug ("open_streams"); | 
|  | __sigprocmask (SIG_SETMASK, &oldmask, NULL); | 
|  | return (0); | 
|  | } | 
|  | xdrstdio_create (&xdrargs, fargs, XDR_ENCODE); | 
|  | xdrstdio_create (&xdrrslt, frslt, XDR_DECODE); | 
|  |  | 
|  | if (!xdr_u_long (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg)) | 
|  | { | 
|  | debug ("xdr args"); | 
|  | success = 0; | 
|  | } | 
|  | fclose (fargs); | 
|  |  | 
|  | if (success && !(*xdr_rslt) (&xdrrslt, rslt)) | 
|  | { | 
|  | debug ("xdr rslt"); | 
|  | success = 0; | 
|  | } | 
|  | fclose(frslt); | 
|  |  | 
|  | wait_again: | 
|  | if (__wait4 (pid, &status, 0, NULL) < 0) | 
|  | { | 
|  | if (errno == EINTR) | 
|  | goto wait_again; | 
|  | debug ("wait4"); | 
|  | if (errno == ECHILD || errno == ESRCH) | 
|  | perror ("wait"); | 
|  | else | 
|  | success = 0; | 
|  | } | 
|  | else | 
|  | if (status.w_retcode) | 
|  | { | 
|  | debug ("wait4 1"); | 
|  | success = 0; | 
|  | } | 
|  | __sigprocmask (SIG_SETMASK, &oldmask, NULL); | 
|  |  | 
|  | return success; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | 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 */ | 
|  | }; | 
|  | #ifdef _RPC_THREAD_SAFE_ | 
|  | #define key_call_private_main RPC_THREAD_VARIABLE(key_call_private_s) | 
|  | #else | 
|  | static struct key_call_private *key_call_private_main; | 
|  | #endif | 
|  | __libc_lock_define_initialized (static, keycall_lock) | 
|  |  | 
|  | /* | 
|  | * Keep the handle cached.  This call may be made quite often. | 
|  | */ | 
|  | static CLIENT * | 
|  | getkeyserv_handle (int vers) | 
|  | { | 
|  | struct key_call_private *kcp = key_call_private_main; | 
|  | struct timeval wait_time; | 
|  | int fd; | 
|  | struct sockaddr_un name; | 
|  | socklen_t namelen = sizeof(struct sockaddr_un); | 
|  |  | 
|  | #define TOTAL_TIMEOUT   30      /* total timeout talking to keyserver */ | 
|  | #define TOTAL_TRIES     5       /* Number of tries */ | 
|  |  | 
|  | if (kcp == (struct key_call_private *)NULL) | 
|  | { | 
|  | kcp = (struct key_call_private *)malloc (sizeof (*kcp)); | 
|  | if (kcp == (struct key_call_private *)NULL) | 
|  | return (CLIENT *) NULL; | 
|  |  | 
|  | key_call_private_main = kcp; | 
|  | kcp->client = NULL; | 
|  | } | 
|  |  | 
|  | /* if pid has changed, destroy client and rebuild */ | 
|  | if (kcp->client != NULL && kcp->pid != __getpid ()) | 
|  | { | 
|  | auth_destroy (kcp->client->cl_auth); | 
|  | clnt_destroy (kcp->client); | 
|  | kcp->client = NULL; | 
|  | } | 
|  |  | 
|  | if (kcp->client != NULL) | 
|  | { | 
|  | /* if other side closed socket, build handle again */ | 
|  | clnt_control (kcp->client, CLGET_FD, (char *)&fd); | 
|  | if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1) | 
|  | { | 
|  | auth_destroy (kcp->client->cl_auth); | 
|  | 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 = | 
|  | authunix_create ((char *)"", 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; | 
|  | } | 
|  |  | 
|  | if ((kcp->client == (CLIENT *) NULL)) | 
|  | /* Use the AF_UNIX transport */ | 
|  | kcp->client = clnt_create ("/var/run/keyservsock", KEY_PROG, vers, "unix"); | 
|  |  | 
|  | if (kcp->client == (CLIENT *) NULL) | 
|  | return (CLIENT *) NULL; | 
|  |  | 
|  | kcp->uid = __geteuid (); | 
|  | kcp->pid = __getpid (); | 
|  | kcp->client->cl_auth = authunix_create ((char *)"", 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; | 
|  | clnt_control (kcp->client, CLSET_RETRY_TIMEOUT, | 
|  | (char *)&wait_time); | 
|  | if (clnt_control (kcp->client, CLGET_FD, (char *)&fd)) | 
|  | __fcntl (fd, F_SETFD, FD_CLOEXEC);  /* make it "close on exec" */ | 
|  |  | 
|  | return kcp->client; | 
|  | } | 
|  |  | 
|  | /* returns  0 on failure, 1 on success */ | 
|  | static int | 
|  | internal_function | 
|  | key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg, | 
|  | xdrproc_t xdr_rslt, char *rslt) | 
|  | { | 
|  | CLIENT *clnt; | 
|  | struct timeval wait_time; | 
|  | int result = 0; | 
|  |  | 
|  | __libc_lock_lock (keycall_lock); | 
|  | 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) | 
|  | { | 
|  | 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) | 
|  | result = 1; | 
|  | } | 
|  |  | 
|  | __libc_lock_unlock (keycall_lock); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* returns 0 on failure, 1 on success */ | 
|  | static int | 
|  | internal_function | 
|  | key_call (u_long proc, xdrproc_t xdr_arg, char *arg, | 
|  | xdrproc_t xdr_rslt, char *rslt) | 
|  | { | 
|  | #ifndef SO_PASSCRED | 
|  | static int use_keyenvoy; | 
|  | #endif | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #ifdef SO_PASSCRED | 
|  | return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt); | 
|  | #else | 
|  | if (!use_keyenvoy) | 
|  | { | 
|  | if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt)) | 
|  | return 1; | 
|  | use_keyenvoy = 1; | 
|  | } | 
|  | return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef _RPC_THREAD_SAFE_ | 
|  | void | 
|  | __rpc_thread_key_cleanup (void) | 
|  | { | 
|  | struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s); | 
|  |  | 
|  | if (kcp) { | 
|  | if (kcp->client) { | 
|  | if (kcp->client->cl_auth) | 
|  | auth_destroy (kcp->client->cl_auth); | 
|  | clnt_destroy(kcp->client); | 
|  | } | 
|  | free (kcp); | 
|  | } | 
|  | } | 
|  | #endif /* _RPC_THREAD_SAFE_ */ |