]> git.ipfire.org Git - thirdparty/glibc.git/blame - sunrpc/key_call.c
Update.
[thirdparty/glibc.git] / sunrpc / key_call.c
CommitLineData
800d775e
UD
1/*
2 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3 * unrestricted use provided that this legend is included on all tape
4 * media and as a part of the software program in whole or part. Users
5 * may copy or modify Sun RPC without charge, but are not authorized
6 * to license or distribute it to anyone else except as part of a product or
7 * program developed by the user.
8 *
9 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12 *
13 * Sun RPC is provided with no support and without any obligation on the
14 * part of Sun Microsystems, Inc. to assist in its use, correction,
15 * modification or enhancement.
16 *
17 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19 * OR ANY PART THEREOF.
20 *
21 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22 * or profits or other special, indirect and consequential damages, even if
23 * Sun has been advised of the possibility of such damages.
24 *
25 * Sun Microsystems, Inc.
26 * 2550 Garcia Avenue
27 * Mountain View, California 94043
28 */
54f4f73e
UD
29/*
30 * Copyright (c) 1988 by Sun Microsystems, Inc.
31 */
800d775e
UD
32/*
33 * The original source is from the RPCSRC 4.0 package from Sun Microsystems.
54f4f73e
UD
34 * The Interface to keyserver protocoll 2, RPC over AF_UNIX and Linux/doors
35 * was added by Thorsten Kukuk <kukuk@suse.de>
36 * Since the Linux/doors project was stopped, I doubt that this code will
37 * ever be useful <kukuk@suse.de>.
800d775e
UD
38 */
39
40#include <stdio.h>
9a0a462c 41#include <errno.h>
e852e889 42#include <fcntl.h>
800d775e
UD
43#include <signal.h>
44#include <unistd.h>
45#include <string.h>
46#include <rpc/rpc.h>
47#include <rpc/auth.h>
48#include <sys/wait.h>
49#include <sys/param.h>
50#include <sys/socket.h>
51#include <rpc/key_prot.h>
e852e889 52#include <bits/libc-lock.h>
800d775e 53
7cabd57c
UD
54#ifdef HAVE_DOORS
55# include "door/door.h"
56#endif
57
800d775e
UD
58#define KEY_TIMEOUT 5 /* per-try timeout in seconds */
59#define KEY_NRETRY 12 /* number of retries */
60
61#define debug(msg) /* turn off debugging */
62
54f4f73e 63#ifndef SO_PASSCRED
c4563d2d 64extern int _openchild (const char *command, FILE **fto, FILE **ffrom);
54f4f73e 65#endif
800d775e
UD
66
67static int key_call (u_long, xdrproc_t xdr_arg, char *,
dfd2257a 68 xdrproc_t xdr_rslt, char *) internal_function;
800d775e 69
c4563d2d
UD
70static const struct timeval trytimeout = {KEY_TIMEOUT, 0};
71static const struct timeval tottimeout = {KEY_TIMEOUT *KEY_NRETRY, 0};
800d775e
UD
72
73int
74key_setsecret (char *secretkey)
75{
76 keystatus status;
77
77fe0b9c
UD
78 if (!key_call ((u_long) KEY_SET, (xdrproc_t) INTUSE(xdr_keybuf), secretkey,
79 (xdrproc_t) INTUSE(xdr_keystatus), (char *) &status))
800d775e
UD
80 return -1;
81 if (status != KEY_SUCCESS)
82 {
83 debug ("set status is nonzero");
84 return -1;
85 }
86 return 0;
87}
88
89/* key_secretkey_is_set() returns 1 if the keyserver has a secret key
90 * stored for the caller's effective uid; it returns 0 otherwise
91 *
92 * N.B.: The KEY_NET_GET key call is undocumented. Applications shouldn't
93 * be using it, because it allows them to get the user's secret key.
94 */
95int
96key_secretkey_is_set (void)
97{
98 struct key_netstres kres;
99
100 memset (&kres, 0, sizeof (kres));
77fe0b9c
UD
101 if (key_call ((u_long) KEY_NET_GET, (xdrproc_t) INTUSE(xdr_void),
102 (char *) NULL, (xdrproc_t) INTUSE(xdr_key_netstres),
103 (char *) &kres) &&
800d775e
UD
104 (kres.status == KEY_SUCCESS) &&
105 (kres.key_netstres_u.knet.st_priv_key[0] != 0))
106 {
107 /* avoid leaving secret key in memory */
108 memset (kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
109 return 1;
110 }
111 return 0;
112}
113
114int
9a0a462c 115key_encryptsession (char *remotename, des_block *deskey)
800d775e
UD
116{
117 cryptkeyarg arg;
118 cryptkeyres res;
119
120 arg.remotename = remotename;
121 arg.deskey = *deskey;
77fe0b9c
UD
122 if (!key_call ((u_long) KEY_ENCRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
123 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
124 (char *) &res))
800d775e
UD
125 return -1;
126
127 if (res.status != KEY_SUCCESS)
128 {
129 debug ("encrypt status is nonzero");
130 return -1;
131 }
132 *deskey = res.cryptkeyres_u.deskey;
133 return 0;
134}
135
136int
9a0a462c 137key_decryptsession (char *remotename, des_block *deskey)
800d775e
UD
138{
139 cryptkeyarg arg;
140 cryptkeyres res;
141
142 arg.remotename = remotename;
143 arg.deskey = *deskey;
77fe0b9c
UD
144 if (!key_call ((u_long) KEY_DECRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
145 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
146 (char *) &res))
800d775e
UD
147 return -1;
148 if (res.status != KEY_SUCCESS)
149 {
150 debug ("decrypt status is nonzero");
151 return -1;
152 }
153 *deskey = res.cryptkeyres_u.deskey;
154 return 0;
155}
156
157int
9a0a462c
UD
158key_encryptsession_pk (char *remotename, netobj *remotekey,
159 des_block *deskey)
800d775e
UD
160{
161 cryptkeyarg2 arg;
162 cryptkeyres res;
163
164 arg.remotename = remotename;
165 arg.remotekey = *remotekey;
166 arg.deskey = *deskey;
77fe0b9c
UD
167 if (!key_call ((u_long) KEY_ENCRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
168 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
169 (char *) &res))
800d775e
UD
170 return -1;
171
172 if (res.status != KEY_SUCCESS)
173 {
174 debug ("encrypt status is nonzero");
175 return -1;
176 }
177 *deskey = res.cryptkeyres_u.deskey;
178 return 0;
179}
180
181int
9a0a462c
UD
182key_decryptsession_pk (char *remotename, netobj *remotekey,
183 des_block *deskey)
800d775e
UD
184{
185 cryptkeyarg2 arg;
186 cryptkeyres res;
187
188 arg.remotename = remotename;
189 arg.remotekey = *remotekey;
190 arg.deskey = *deskey;
77fe0b9c
UD
191 if (!key_call ((u_long) KEY_DECRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
192 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
193 (char *) &res))
800d775e
UD
194 return -1;
195
196 if (res.status != KEY_SUCCESS)
197 {
198 debug ("decrypt status is nonzero");
199 return -1;
200 }
201 *deskey = res.cryptkeyres_u.deskey;
202 return 0;
203}
204
205int
9a0a462c 206key_gendes (des_block *key)
800d775e
UD
207{
208 struct sockaddr_in sin;
209 CLIENT *client;
210 int socket;
211 enum clnt_stat stat;
212
213 sin.sin_family = AF_INET;
214 sin.sin_port = 0;
215 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
50304ef0 216 __bzero (sin.sin_zero, sizeof (sin.sin_zero));
800d775e
UD
217 socket = RPC_ANYSOCK;
218 client = clntudp_bufcreate (&sin, (u_long) KEY_PROG, (u_long) KEY_VERS,
219 trytimeout, &socket, RPCSMALLMSGSIZE,
220 RPCSMALLMSGSIZE);
221 if (client == NULL)
222 return -1;
223
77fe0b9c
UD
224 stat = clnt_call (client, KEY_GEN, (xdrproc_t) INTUSE(xdr_void), NULL,
225 (xdrproc_t) INTUSE(xdr_des_block), (caddr_t) key,
226 tottimeout);
800d775e 227 clnt_destroy (client);
50304ef0 228 __close (socket);
800d775e
UD
229 if (stat != RPC_SUCCESS)
230 return -1;
231
232 return 0;
233}
234
235int
236key_setnet (struct key_netstarg *arg)
237{
238 keystatus status;
239
77fe0b9c
UD
240 if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) INTUSE(xdr_key_netstarg),
241 (char *) arg,(xdrproc_t) INTUSE(xdr_keystatus),
242 (char *) &status))
800d775e
UD
243 return -1;
244
245 if (status != KEY_SUCCESS)
246 {
247 debug ("key_setnet status is nonzero");
248 return -1;
249 }
250 return 1;
251}
252
253int
9a0a462c 254key_get_conv (char *pkey, des_block *deskey)
800d775e
UD
255{
256 cryptkeyres res;
257
77fe0b9c
UD
258 if (!key_call ((u_long) KEY_GET_CONV, (xdrproc_t) INTUSE(xdr_keybuf), pkey,
259 (xdrproc_t) INTUSE(xdr_cryptkeyres), (char *) &res))
800d775e
UD
260 return -1;
261
262 if (res.status != KEY_SUCCESS)
263 {
264 debug ("get_conv status is nonzero");
265 return -1;
266 }
267 *deskey = res.cryptkeyres_u.deskey;
268 return 0;
269}
270
271/*
272 * Hack to allow the keyserver to use AUTH_DES (for authenticated
273 * NIS+ calls, for example). The only functions that get called
274 * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
275 *
276 * The approach is to have the keyserver fill in pointers to local
277 * implementations of these functions, and to call those in key_call().
278 */
279
c4563d2d
UD
280cryptkeyres *(*__key_encryptsession_pk_LOCAL) (uid_t, char *);
281cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *);
282des_block *(*__key_gendes_LOCAL) (uid_t, char *);
800d775e 283
54f4f73e 284#ifndef SO_PASSCRED
800d775e 285static int
dfd2257a 286internal_function
e852e889
UD
287key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg,
288 xdrproc_t xdr_rslt, char *rslt)
800d775e
UD
289{
290 XDR xdrargs;
291 XDR xdrrslt;
292 FILE *fargs;
293 FILE *frslt;
9a0a462c 294 sigset_t oldmask, mask;
800d775e
UD
295 union wait status;
296 int pid;
297 int success;
298 uid_t ruid;
299 uid_t euid;
c4563d2d 300 static const char MESSENGER[] = "/usr/etc/keyenvoy";
800d775e 301
9a0a462c
UD
302 success = 1;
303 sigemptyset (&mask);
304 sigaddset (&mask, SIGCHLD);
50304ef0 305 __sigprocmask (SIG_BLOCK, &mask, &oldmask);
9a0a462c 306
800d775e
UD
307 /*
308 * We are going to exec a set-uid program which makes our effective uid
309 * zero, and authenticates us with our real uid. We need to make the
310 * effective uid be the real uid for the setuid program, and
311 * the real uid be the effective uid so that we can change things back.
312 */
50304ef0
UD
313 euid = __geteuid ();
314 ruid = __getuid ();
315 __setreuid (euid, ruid);
800d775e 316 pid = _openchild (MESSENGER, &fargs, &frslt);
50304ef0 317 __setreuid (ruid, euid);
800d775e
UD
318 if (pid < 0)
319 {
320 debug ("open_streams");
50304ef0 321 __sigprocmask (SIG_SETMASK, &oldmask, NULL);
800d775e
UD
322 return (0);
323 }
324 xdrstdio_create (&xdrargs, fargs, XDR_ENCODE);
325 xdrstdio_create (&xdrrslt, frslt, XDR_DECODE);
326
77fe0b9c 327 if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg))
800d775e
UD
328 {
329 debug ("xdr args");
330 success = 0;
331 }
332 fclose (fargs);
333
334 if (success && !(*xdr_rslt) (&xdrrslt, rslt))
335 {
336 debug ("xdr rslt");
337 success = 0;
338 }
9a0a462c 339 fclose(frslt);
800d775e 340
9a0a462c 341 wait_again:
50304ef0 342 if (__wait4 (pid, &status, 0, NULL) < 0)
800d775e 343 {
9a0a462c
UD
344 if (errno == EINTR)
345 goto wait_again;
50304ef0 346 debug ("wait4");
9a0a462c 347 if (errno == ECHILD || errno == ESRCH)
50304ef0 348 perror ("wait");
9a0a462c
UD
349 else
350 success = 0;
800d775e 351 }
9a0a462c
UD
352 else
353 if (status.w_retcode)
354 {
50304ef0 355 debug ("wait4 1");
9a0a462c
UD
356 success = 0;
357 }
50304ef0 358 __sigprocmask (SIG_SETMASK, &oldmask, NULL);
800d775e 359
50304ef0 360 return success;
800d775e 361}
54f4f73e 362#endif
e852e889
UD
363
364struct key_call_private {
365 CLIENT *client; /* Client handle */
366 pid_t pid; /* process-id at moment of creation */
367 uid_t uid; /* user-id at last authorization */
368};
f1e4a4a4
UD
369#ifdef _RPC_THREAD_SAFE_
370#define key_call_private_main ((struct key_call_private *)RPC_THREAD_VARIABLE(key_call_private_s))
371#else
c4563d2d 372static struct key_call_private *key_call_private_main;
f1e4a4a4 373#endif
e852e889
UD
374__libc_lock_define_initialized (static, keycall_lock)
375
376/*
377 * Keep the handle cached. This call may be made quite often.
378 */
379static CLIENT *
380getkeyserv_handle (int vers)
381{
382 struct key_call_private *kcp = key_call_private_main;
383 struct timeval wait_time;
384 int fd;
385 struct sockaddr_un name;
386 int namelen = sizeof(struct sockaddr_un);
387
388#define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */
389#define TOTAL_TRIES 5 /* Number of tries */
390
391 if (kcp == (struct key_call_private *)NULL)
392 {
393 kcp = (struct key_call_private *)malloc (sizeof (*kcp));
394 if (kcp == (struct key_call_private *)NULL)
395 return (CLIENT *) NULL;
396
397 key_call_private_main = kcp;
398 kcp->client = NULL;
399 }
400
401 /* if pid has changed, destroy client and rebuild */
402 if (kcp->client != NULL && kcp->pid != __getpid ())
403 {
404 clnt_destroy (kcp->client);
405 kcp->client = NULL;
406 }
407
408 if (kcp->client != NULL)
409 {
410 /* if other side closed socket, build handle again */
411 clnt_control (kcp->client, CLGET_FD, (char *)&fd);
4aebaa6b 412 if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1)
e852e889
UD
413 {
414 auth_destroy (kcp->client->cl_auth);
415 clnt_destroy (kcp->client);
416 kcp->client = NULL;
417 }
418 }
419
420 if (kcp->client != NULL)
421 {
422 /* if uid has changed, build client handle again */
423 if (kcp->uid != __geteuid ())
424 {
425 kcp->uid = __geteuid ();
426 auth_destroy (kcp->client->cl_auth);
427 kcp->client->cl_auth =
428 authunix_create ((char *)"", kcp->uid, 0, 0, NULL);
429 if (kcp->client->cl_auth == NULL)
430 {
431 clnt_destroy (kcp->client);
432 kcp->client = NULL;
433 return ((CLIENT *) NULL);
434 }
435 }
436 /* Change the version number to the new one */
437 clnt_control (kcp->client, CLSET_VERS, (void *)&vers);
438 return kcp->client;
439 }
440
441 if ((kcp->client == (CLIENT *) NULL))
442 /* Use the AF_UNIX transport */
443 kcp->client = clnt_create ("/var/run/keyservsock", KEY_PROG, vers, "unix");
444
445 if (kcp->client == (CLIENT *) NULL)
446 return (CLIENT *) NULL;
447
448 kcp->uid = __geteuid ();
449 kcp->pid = __getpid ();
450 kcp->client->cl_auth = authunix_create ((char *)"", kcp->uid, 0, 0, NULL);
451 if (kcp->client->cl_auth == NULL)
452 {
453 clnt_destroy (kcp->client);
454 kcp->client = NULL;
455 return (CLIENT *) NULL;
456 }
457
458 wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
459 wait_time.tv_usec = 0;
460 clnt_control (kcp->client, CLSET_RETRY_TIMEOUT,
461 (char *)&wait_time);
462 if (clnt_control (kcp->client, CLGET_FD, (char *)&fd))
d17a729b 463 __fcntl (fd, F_SETFD, 1); /* make it "close on exec" */
e852e889
UD
464
465 return kcp->client;
466}
467
468/* returns 0 on failure, 1 on success */
469static int
470internal_function
471key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg,
472 xdrproc_t xdr_rslt, char *rslt)
473{
474 CLIENT *clnt;
475 struct timeval wait_time;
476 int result = 0;
477
478 __libc_lock_lock (keycall_lock);
479 if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
480 (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
481 (proc == KEY_GET_CONV))
482 clnt = getkeyserv_handle(2); /* talk to version 2 */
483 else
484 clnt = getkeyserv_handle(1); /* talk to version 1 */
485
486 if (clnt != NULL)
487 {
488 wait_time.tv_sec = TOTAL_TIMEOUT;
489 wait_time.tv_usec = 0;
490
491 if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
492 wait_time) == RPC_SUCCESS)
493 result = 1;
494 }
495
496 __libc_lock_unlock (keycall_lock);
497
498 return result;
499}
500
7cabd57c
UD
501#ifdef HAVE_DOORS
502/* returns 0 on failure, 1 on success */
503static int
504internal_function
505key_call_door (u_long proc, xdrproc_t xdr_arg, char *arg,
506 xdrproc_t xdr_rslt, char *rslt)
507{
508 XDR xdrs;
32abdb71 509 int fd, ret;
7cabd57c
UD
510 door_arg_t args;
511 char *data_ptr;
512 u_long data_len = 0;
513 char res[255];
514
515 if ((fd = open("/var/run/keyservdoor", O_RDONLY)) < 0)
516 return 0;
517 res[0] = 0;
518
519 data_len = xdr_sizeof (xdr_arg, arg);
520 data_ptr = calloc (1, data_len + 2 * sizeof (u_long));
521 if (data_ptr == NULL)
522 return 0;
523
77fe0b9c
UD
524 INTUSE(xdrmem_create) (&xdrs, &data_ptr[2 * sizeof (u_long)], data_len,
525 XDR_ENCODE);
7cabd57c
UD
526 if (!xdr_arg (&xdrs, arg))
527 {
528 xdr_destroy (&xdrs);
529 free (data_ptr);
530 return 0;
531 }
532 xdr_destroy (&xdrs);
533
534 memcpy (data_ptr, &proc, sizeof (u_long));
535 memcpy (&data_ptr[sizeof (proc)], &data_len, sizeof (u_long));
536
537 args.data_ptr = data_ptr;
538 args.data_size = data_len + 2 * sizeof (u_long);
539 args.desc_ptr = NULL;
540 args.desc_num = 0;
541 args.rbuf = res;
542 args.rsize = sizeof (res);
543
32abdb71 544 ret = __door_call (fd, &args);
7cabd57c
UD
545 free (data_ptr);
546 close (fd);
547
32abdb71
UD
548 if (ret < 0)
549 return 0;
550
7cabd57c
UD
551 memcpy (&data_len, args.data_ptr, sizeof (u_long));
552 if (data_len != 0)
553 return 0;
554
555 memcpy (&data_len, &args.data_ptr[sizeof (u_long)], sizeof (u_long));
77fe0b9c
UD
556 INTUSE(xdrmem_create) (&xdrs, &args.data_ptr[2 * sizeof (u_long)],
557 data_len, XDR_DECODE);
7cabd57c
UD
558 if (!xdr_rslt (&xdrs, rslt))
559 {
560 xdr_destroy (&xdrs);
561 return 0;
562 }
563 xdr_destroy (&xdrs);
564
565 return 1;
566}
567#endif
568
569/* returns 0 on failure, 1 on success */
e852e889
UD
570static int
571internal_function
572key_call (u_long proc, xdrproc_t xdr_arg, char *arg,
573 xdrproc_t xdr_rslt, char *rslt)
574{
54f4f73e 575#ifndef SO_PASSCRED
c4563d2d 576 static int use_keyenvoy;
54f4f73e 577#endif
7cabd57c 578#ifdef HAVE_DOORS
c4563d2d 579 static int not_use_doors;
7cabd57c 580#endif
e852e889
UD
581
582 if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL)
583 {
584 cryptkeyres *res;
585 res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg);
586 *(cryptkeyres *) rslt = *res;
587 return 1;
588 }
589 else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL)
590 {
591 cryptkeyres *res;
592 res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg);
593 *(cryptkeyres *) rslt = *res;
594 return 1;
595 }
596 else if (proc == KEY_GEN && __key_gendes_LOCAL)
597 {
598 des_block *res;
599 res = (*__key_gendes_LOCAL) (__geteuid (), 0);
600 *(des_block *) rslt = *res;
601 return 1;
602 }
603
7cabd57c 604#ifdef HAVE_DOORS
c4563d2d 605 if (!not_use_doors)
7cabd57c
UD
606 {
607 if (key_call_door (proc, xdr_arg, arg, xdr_rslt, rslt))
608 return 1;
c4563d2d 609 not_use_doors = 1;
7cabd57c
UD
610 }
611#endif
54f4f73e
UD
612
613#ifdef SO_PASSCRED
614 return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt);
615#else
e852e889
UD
616 if (!use_keyenvoy)
617 {
618 if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt))
619 return 1;
620 use_keyenvoy = 1;
621 }
622 return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt);
54f4f73e 623#endif
e852e889 624}
f1e4a4a4
UD
625
626#ifdef _RPC_THREAD_SAFE_
627void
628__rpc_thread_key_cleanup (void)
629{
630 struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s);
631
632 if (kcp) {
633 if (kcp->client)
634 clnt_destroy(kcp->client);
635 free (kcp);
636 }
637}
638#endif /* _RPC_THREAD_SAFE_ */