xref: /titanic_52/usr/src/lib/libnsl/rpc/key_call.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 /*
30  * Portions of this source code were derived from Berkeley
31  * 4.3 BSD under license from the Regents of the University of
32  * California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * key_call.c, Interface to keyserver
39  *
40  * setsecretkey(key) - set your secret key
41  * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent
42  * decryptsessionkey(agent, deskey) - decrypt ditto
43  * gendeskey(deskey) - generate a secure des key
44  */
45 
46 #include "mt.h"
47 #include "rpc_mt.h"
48 #include <errno.h>
49 #include <rpc/rpc.h>
50 #include <rpc/key_prot.h>
51 #include <stdio.h>
52 #include <syslog.h>
53 #include <string.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 
59 #define	CLASSIC_PK_DH(k, a)	(((k) == 192) && ((a) == 0))
60 
61 #if defined(sparc)
62 #define	_FSTAT _fstat
63 #else  /* !sparc */
64 #define	_FSTAT fstat
65 #endif /* sparc */
66 
67 #ifdef DEBUG
68 #define	debug(msg)	(void) fprintf(stderr, "%s\n", msg);
69 #else
70 #define	debug(msg)
71 #endif /* DEBUG */
72 
73 int key_call(rpcproc_t, xdrproc_t, char *, xdrproc_t, char *);
74 int key_call_ext(rpcproc_t, xdrproc_t, char *, xdrproc_t, char *, int);
75 int key_setnet(struct key_netstarg *);
76 
77 /*
78  * Hack to allow the keyserver to use AUTH_DES (for authenticated
79  * NIS+ calls, for example).  The only functions that get called
80  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
81  *
82  * The approach is to have the keyserver fill in pointers to local
83  * implementations of these functions, and to call those in key_call().
84  */
85 
86 bool_t (*__key_encryptsession_pk_LOCAL)() = NULL;
87 bool_t (*__key_decryptsession_pk_LOCAL)() = NULL;
88 bool_t (*__key_gendes_LOCAL)() = NULL;
89 
90 
91 int
92 key_setsecret(const char *secretkey)
93 {
94 	char netName[MAXNETNAMELEN+1];
95 	struct key_netstarg netst;
96 	int ret;
97 
98 	if (getnetname(netName) == 0) {
99 		debug("getnetname failed");
100 		return (-1);
101 	}
102 
103 	(void) memcpy(netst.st_priv_key, secretkey, HEXKEYBYTES);
104 	netst.st_pub_key[0] = 0;
105 	netst.st_netname = netName;
106 
107 	/*
108 	 * Actual key login
109 	 * We perform the KEY_NET_PUT instead of the SET_KEY
110 	 * rpc call because key_secretkey_is_set function uses
111 	 * the KEY_NET_GET call which expects the netname to be
112 	 * set along with the key. Keylogin also uses KEY_NET_PUT.
113 	 */
114 	ret = key_setnet(&netst);
115 
116 	/* erase our copy of the secret key */
117 	(void) memset(netst.st_priv_key, '\0', HEXKEYBYTES);
118 
119 	if (ret == 1)
120 		return (0);
121 
122 	return (-1);
123 }
124 
125 int
126 key_setsecret_g(
127 	char *secretkey,
128 	keylen_t keylen,
129 	algtype_t algtype,
130 	des_block userkey)
131 {
132 	setkeyarg3 arg;
133 	keystatus status;
134 
135 	if (CLASSIC_PK_DH(keylen, algtype))
136 		return (key_setsecret(secretkey));
137 	arg.key.keybuf3_len = keylen/4 + 1;
138 	arg.key.keybuf3_val = secretkey;
139 	arg.algtype = algtype;
140 	arg.keylen = keylen;
141 	arg.userkey = userkey;
142 	if (!key_call((rpcproc_t)KEY_SET_3, xdr_setkeyarg3, (char *)&arg,
143 			xdr_keystatus, (char *)&status))
144 		return (-1);
145 	if (status != KEY_SUCCESS) {
146 		debug("set3 status is nonzero");
147 		return (-1);
148 	}
149 	return (0);
150 }
151 
152 int
153 key_removesecret_g_ext(int use_uid)
154 {
155 	keystatus status;
156 
157 	if (!key_call_ext((rpcproc_t)KEY_CLEAR_3, xdr_void, NULL,
158 			xdr_keystatus, (char *)&status, use_uid)) {
159 		debug("remove secret key call failed");
160 		return (-1);
161 	}
162 	if (status != KEY_SUCCESS) {
163 		debug("remove secret status is nonzero");
164 		return (-1);
165 	}
166 	return (0);
167 }
168 
169 /*
170  * Use effective uid.
171  */
172 int
173 key_removesecret_g(void)
174 {
175 	return (key_removesecret_g_ext(0));
176 }
177 
178 /*
179  * Use real uid.
180  */
181 int
182 key_removesecret_g_ruid(void)
183 {
184 	return (key_removesecret_g_ext(1));
185 }
186 
187 /*
188  * key_secretkey_is_set() returns 1 if the keyserver has a secret key
189  * stored for the caller's effective uid if use_ruid is 0 or
190  * stored for the caller's real uid if use_ruid is 1.
191  * it returns 0 otherwise.
192  *
193  * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
194  * be using it, because it allows them to get the user's secret key.
195  *
196  */
197 int
198 key_secretkey_is_set_ext(int use_ruid)
199 {
200 	struct key_netstres 	kres;
201 
202 	(void) memset(&kres, 0, sizeof (kres));
203 	if (key_call_ext((rpcproc_t)KEY_NET_GET, xdr_void, NULL,
204 			xdr_key_netstres, (char *)&kres, use_ruid) &&
205 	    (kres.status == KEY_SUCCESS) &&
206 	    (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
207 		/* avoid leaving secret key in memory */
208 		(void) memset(kres.key_netstres_u.knet.st_priv_key, 0,
209 							HEXKEYBYTES);
210 		xdr_free(xdr_key_netstres, (char *)&kres);
211 		return (1);
212 	}
213 	return (0);
214 }
215 
216 /*
217  * Use effective uid.
218  */
219 int
220 key_secretkey_is_set(void)
221 {
222 	return (key_secretkey_is_set_ext(0));
223 }
224 
225 /*
226  * Use real uid.
227  */
228 int
229 key_secretkey_is_set_ruid(void)
230 {
231 	return (key_secretkey_is_set_ext(1));
232 }
233 
234 /*
235  * key_secretkey_is_set_g_ext() returns 1 if the keyserver has a secret key
236  * stored for the caller's uid, it returns 0 otherwise.
237  * If (use_ruid == 0), for the caller's effective uid.
238  * If (use_ruid == 1), for the caller's real uid.
239  *
240  * N.B.:  The KEY_NET_GET_3 key call is undocumented.  Applications shouldn't
241  * be using it, because it allows them to get the user's secret key.
242  */
243 int
244 key_secretkey_is_set_g_ext(keylen_t keylen, algtype_t algtype, int use_ruid)
245 {
246 	mechtype arg;
247 	key_netstres3 	kres;
248 
249 	/*
250 	 * key_secretkey_is_set_g_ext is tricky because keylen == 0
251 	 * means check if any key exists for the caller (old/new, 192/1024 ...)
252 	 * Rather than handle this on the server side, we call the old
253 	 * routine if keylen == 0 and try the newer stuff only if that fails
254 	 */
255 	if ((keylen == 0) && key_secretkey_is_set_ext(use_ruid))
256 		return (1);
257 	if (CLASSIC_PK_DH(keylen, algtype))
258 		return (key_secretkey_is_set_ext(use_ruid));
259 	arg.keylen = keylen;
260 	arg.algtype = algtype;
261 	(void) memset(&kres, 0, sizeof (kres));
262 	if (key_call_ext((rpcproc_t)KEY_NET_GET_3, xdr_mechtype, (char *)&arg,
263 			xdr_key_netstres3, (char *)&kres, use_ruid) &&
264 	    (kres.status == KEY_SUCCESS) &&
265 	    (kres.key_netstres3_u.knet.st_priv_key.keybuf3_len != 0)) {
266 		/* avoid leaving secret key in memory */
267 		(void) memset(kres.key_netstres3_u.knet.st_priv_key.keybuf3_val,
268 			0, kres.key_netstres3_u.knet.st_priv_key.keybuf3_len);
269 		xdr_free(xdr_key_netstres3, (char *)&kres);
270 		return (1);
271 	}
272 	return (0);
273 }
274 
275 /*
276  * Use effective uid.
277  */
278 int
279 key_secretkey_is_set_g(keylen_t keylen, algtype_t algtype)
280 {
281 	return (key_secretkey_is_set_g_ext(keylen, algtype, 0));
282 }
283 
284 /*
285  * Use real uid.
286  */
287 int
288 key_secretkey_is_set_g_ruid(keylen_t keylen, algtype_t algtype)
289 {
290 	return (key_secretkey_is_set_g_ext(keylen, algtype, 1));
291 }
292 
293 
294 int
295 key_encryptsession_pk(const char *remotename, netobj *remotekey,
296 							des_block *deskey)
297 {
298 	cryptkeyarg2 arg;
299 	cryptkeyres res;
300 
301 	arg.remotename = (char *)remotename;
302 	arg.remotekey = *remotekey;
303 	arg.deskey = *deskey;
304 	if (!key_call((rpcproc_t)KEY_ENCRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
305 			xdr_cryptkeyres, (char *)&res))
306 		return (-1);
307 	if (res.status != KEY_SUCCESS) {
308 		debug("encrypt status is nonzero");
309 		return (-1);
310 	}
311 	*deskey = res.cryptkeyres_u.deskey;
312 	return (0);
313 }
314 
315 int
316 key_encryptsession_pk_g(
317 	const char *remotename,
318 	const char *remotekey,
319 	keylen_t remotekeylen,
320 	algtype_t algtype,
321 	des_block deskey[],
322 	keynum_t keynum
323 )
324 {
325 	cryptkeyarg3 arg;
326 	cryptkeyres3 res;
327 
328 	if (CLASSIC_PK_DH(remotekeylen, algtype)) {
329 		int i;
330 		netobj npk;
331 
332 		npk.n_len = remotekeylen/4 + 1;
333 		npk.n_bytes = (char *)remotekey;
334 		for (i = 0; i < keynum; i++) {
335 			if (key_encryptsession_pk(remotename, &npk, &deskey[i]))
336 				return (-1);
337 		}
338 		return (0);
339 	}
340 	arg.remotename = (char *)remotename;
341 	arg.remotekey.keybuf3_len = remotekeylen/4 + 1;
342 	arg.remotekey.keybuf3_val = (char *)remotekey;
343 	arg.keylen = remotekeylen;
344 	arg.algtype = algtype;
345 	arg.deskey.deskeyarray_len = keynum;
346 	arg.deskey.deskeyarray_val = deskey;
347 	(void) memset(&res, 0, sizeof (res));
348 	res.cryptkeyres3_u.deskey.deskeyarray_val = deskey;
349 	if (!key_call((rpcproc_t)KEY_ENCRYPT_PK_3,
350 			xdr_cryptkeyarg3, (char *)&arg,
351 			xdr_cryptkeyres3, (char *)&res))
352 		return (-1);
353 	if (res.status != KEY_SUCCESS) {
354 		debug("encrypt3 status is nonzero");
355 		return (-1);
356 	}
357 	if (res.cryptkeyres3_u.deskey.deskeyarray_len != keynum) {
358 		debug("number of keys don't match");
359 		return (-1);
360 	}
361 	return (0);
362 }
363 
364 int
365 key_decryptsession_pk(const char *remotename, netobj *remotekey,
366 							des_block *deskey)
367 {
368 	cryptkeyarg2 arg;
369 	cryptkeyres res;
370 
371 	arg.remotename = (char *)remotename;
372 	arg.remotekey = *remotekey;
373 	arg.deskey = *deskey;
374 	if (!key_call((rpcproc_t)KEY_DECRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
375 			xdr_cryptkeyres, (char *)&res))
376 		return (-1);
377 	if (res.status != KEY_SUCCESS) {
378 		debug("decrypt status is nonzero");
379 		return (-1);
380 	}
381 	*deskey = res.cryptkeyres_u.deskey;
382 	return (0);
383 }
384 
385 int
386 key_decryptsession_pk_g(
387 	const char *remotename,
388 	const char *remotekey,
389 	keylen_t remotekeylen,
390 	algtype_t algtype,
391 	des_block deskey[],
392 	keynum_t keynum
393 )
394 {
395 	cryptkeyarg3 arg;
396 	cryptkeyres3 res;
397 
398 	if (CLASSIC_PK_DH(remotekeylen, algtype)) {
399 		int i;
400 		netobj npk;
401 
402 		npk.n_len = remotekeylen/4 + 1;
403 		npk.n_bytes = (char *)remotekey;
404 		for (i = 0; i < keynum; i++) {
405 			if (key_decryptsession_pk(remotename,
406 					&npk, &deskey[i]))
407 				return (-1);
408 		}
409 		return (0);
410 	}
411 	arg.remotename = (char *)remotename;
412 	arg.remotekey.keybuf3_len = remotekeylen/4 + 1;
413 	arg.remotekey.keybuf3_val = (char *)remotekey;
414 	arg.deskey.deskeyarray_len = keynum;
415 	arg.deskey.deskeyarray_val = deskey;
416 	arg.algtype = algtype;
417 	arg.keylen = remotekeylen;
418 	(void) memset(&res, 0, sizeof (res));
419 	res.cryptkeyres3_u.deskey.deskeyarray_val = deskey;
420 	if (!key_call((rpcproc_t)KEY_DECRYPT_PK_3,
421 			xdr_cryptkeyarg3, (char *)&arg,
422 			xdr_cryptkeyres3, (char *)&res))
423 		return (-1);
424 	if (res.status != KEY_SUCCESS) {
425 		debug("decrypt3 status is nonzero");
426 		return (-1);
427 	}
428 	if (res.cryptkeyres3_u.deskey.deskeyarray_len != keynum) {
429 		debug("number of keys don't match");
430 		return (-1);
431 	}
432 	return (0);
433 }
434 
435 int
436 key_encryptsession(const char *remotename, des_block *deskey)
437 {
438 	cryptkeyarg arg;
439 	cryptkeyres res;
440 
441 	arg.remotename = (char *)remotename;
442 	arg.deskey = *deskey;
443 	if (!key_call((rpcproc_t)KEY_ENCRYPT, xdr_cryptkeyarg, (char *)&arg,
444 			xdr_cryptkeyres, (char *)&res))
445 		return (-1);
446 	if (res.status != KEY_SUCCESS) {
447 		debug("encrypt status is nonzero");
448 		return (-1);
449 	}
450 	*deskey = res.cryptkeyres_u.deskey;
451 	return (0);
452 }
453 
454 int
455 key_encryptsession_g(
456 	const char *remotename,
457 	keylen_t keylen,
458 	algtype_t algtype,
459 	des_block deskey[],
460 	keynum_t keynum
461 )
462 {
463 	cryptkeyarg3 arg;
464 	cryptkeyres3 res;
465 
466 	if (CLASSIC_PK_DH(keylen, algtype))
467 		return (key_encryptsession(remotename, deskey));
468 	arg.remotename = (char *)remotename;
469 	arg.algtype = algtype;
470 	arg.keylen = keylen;
471 	arg.deskey.deskeyarray_len = keynum;
472 	arg.deskey.deskeyarray_val = deskey;
473 	arg.remotekey.keybuf3_len = 0;
474 	(void) memset(&res, 0, sizeof (res));
475 	res.cryptkeyres3_u.deskey.deskeyarray_val = deskey;
476 	if (!key_call((rpcproc_t)KEY_ENCRYPT_3, xdr_cryptkeyarg3, (char *)&arg,
477 			xdr_cryptkeyres3, (char *)&res))
478 		return (-1);
479 	if (res.status != KEY_SUCCESS) {
480 		debug("encrypt3 status is nonzero");
481 		return (-1);
482 	}
483 	if (res.cryptkeyres3_u.deskey.deskeyarray_len != keynum) {
484 		debug("encrypt3 didn't return same number of keys");
485 		return (-1);
486 	}
487 	return (0);
488 }
489 
490 
491 int
492 key_decryptsession(const char *remotename, des_block *deskey)
493 {
494 	cryptkeyarg arg;
495 	cryptkeyres res;
496 
497 	arg.remotename = (char *)remotename;
498 	arg.deskey = *deskey;
499 	if (!key_call((rpcproc_t)KEY_DECRYPT, xdr_cryptkeyarg, (char *)&arg,
500 			xdr_cryptkeyres, (char *)&res))
501 		return (-1);
502 	if (res.status != KEY_SUCCESS) {
503 		debug("decrypt status is nonzero");
504 		return (-1);
505 	}
506 	*deskey = res.cryptkeyres_u.deskey;
507 	return (0);
508 }
509 
510 int
511 key_decryptsession_g(
512 	const char *remotename,
513 	keylen_t keylen,
514 	algtype_t algtype,
515 	des_block deskey[],
516 	keynum_t keynum
517 )
518 {
519 	cryptkeyarg3 arg;
520 	cryptkeyres3 res;
521 
522 	if (CLASSIC_PK_DH(keylen, algtype))
523 		return (key_decryptsession(remotename, deskey));
524 	arg.remotename = (char *)remotename;
525 	arg.algtype = algtype;
526 	arg.keylen = keylen;
527 	arg.deskey.deskeyarray_len = keynum;
528 	arg.deskey.deskeyarray_val = deskey;
529 	arg.remotekey.keybuf3_len = 0;
530 	(void) memset(&res, 0, sizeof (res));
531 	res.cryptkeyres3_u.deskey.deskeyarray_val = deskey;
532 	if (!key_call((rpcproc_t)KEY_DECRYPT_3, xdr_cryptkeyarg3, (char *)&arg,
533 			xdr_cryptkeyres3, (char *)&res))
534 		return (-1);
535 	if (res.status != KEY_SUCCESS) {
536 		debug("decrypt3 status is nonzero");
537 		return (-1);
538 	}
539 	if (res.cryptkeyres3_u.deskey.deskeyarray_len != keynum) {
540 		debug("decrypt3 didn't return same number of keys");
541 		return (-1);
542 	}
543 	return (0);
544 }
545 
546 int
547 key_gendes(des_block *key)
548 {
549 	if (!key_call((rpcproc_t)KEY_GEN, xdr_void, NULL,
550 			xdr_des_block, (char *)key))
551 		return (-1);
552 	return (0);
553 }
554 
555 int
556 key_gendes_g(
557 	des_block deskey[],
558 	keynum_t keynum
559 )
560 {
561 	deskeyarray res;
562 
563 	res.deskeyarray_val = deskey;
564 	if (!key_call((rpcproc_t)KEY_GEN_3, xdr_keynum_t, (char *)&keynum,
565 			xdr_deskeyarray, (char *)&res))
566 		return (-1);
567 	if (res.deskeyarray_len != keynum) {
568 		debug("return length doesn't match\n");
569 		return (-1);
570 	}
571 	return (0);
572 }
573 
574 /*
575  * Call KEY_NET_PUT Operation to the keyserv.
576  *
577  * If use_ruid == 0, use effective uid.
578  * If use_ruid == 1, use real uid.
579  */
580 int
581 key_setnet_ext(struct key_netstarg *arg, int use_ruid)
582 {
583 	keystatus status;
584 
585 	if (!key_call_ext((rpcproc_t)KEY_NET_PUT, xdr_key_netstarg,
586 		(char *)arg, xdr_keystatus, (char *)&status, use_ruid))
587 		return (-1);
588 
589 	if (status != KEY_SUCCESS) {
590 		debug("key_setnet status is nonzero");
591 		return (-1);
592 	}
593 	return (1);
594 }
595 
596 /*
597  * Use effective uid.
598  */
599 int
600 key_setnet(struct key_netstarg *arg)
601 {
602 	return (key_setnet_ext(arg, 0));
603 }
604 
605 /*
606  * Use real uid.
607  */
608 int
609 key_setnet_ruid(struct key_netstarg *arg)
610 {
611 	return (key_setnet_ext(arg, 1));
612 }
613 
614 /*
615  * Input netname, secret and public keys (hex string representation)
616  * of length skeylen/pkeylen (bits), and algorithm type. One, but not
617  * both, of skey or pkey may have zero length. If both lengths are
618  * specified, they must be the same.
619  *
620  * Call KEY_NET_PUT_3 Operation to the keyserv.
621  * Stores the specified netname/pkey/skey triplet in the keyserv.
622  *
623  * If (use_ruid == 1), use real uid.
624  * If (use_ruid == 0), use effective uid.
625  */
626 int
627 key_setnet_g_ext(
628 	const char *netname,
629 	const char *skey,
630 	keylen_t skeylen,
631 	const char *pkey,
632 	keylen_t pkeylen,
633 	algtype_t algtype,
634 	int use_ruid)
635 {
636 	key_netstarg3 arg;
637 	keystatus status;
638 
639 	arg.st_netname = (char *)netname;
640 	arg.algtype = algtype;
641 	if (skeylen == 0) {
642 		arg.st_priv_key.keybuf3_len = 0;
643 	} else {
644 		arg.st_priv_key.keybuf3_len = skeylen/4 + 1;
645 	}
646 	arg.st_priv_key.keybuf3_val = (char *)skey;
647 	if (pkeylen == 0) {
648 		arg.st_pub_key.keybuf3_len = 0;
649 	} else {
650 		arg.st_pub_key.keybuf3_len = pkeylen/4 + 1;
651 	}
652 	arg.st_pub_key.keybuf3_val = (char *)pkey;
653 	if (skeylen == 0) {
654 		if (pkeylen == 0) {
655 			debug("keylens are both 0");
656 			return (-1);
657 		}
658 		arg.keylen = pkeylen;
659 	} else {
660 		if ((pkeylen != 0) && (skeylen != pkeylen)) {
661 			debug("keylens don't match");
662 			return (-1);
663 		}
664 		arg.keylen = skeylen;
665 	}
666 	if (CLASSIC_PK_DH(arg.keylen, arg.algtype)) {
667 		key_netstarg tmp;
668 
669 		if (skeylen != 0) {
670 			(void) memcpy(&tmp.st_priv_key, skey,
671 				sizeof (tmp.st_priv_key));
672 		} else {
673 			(void) memset(&tmp.st_priv_key, 0,
674 				sizeof (tmp.st_priv_key));
675 		}
676 		if (pkeylen != 0) {
677 			(void) memcpy(&tmp.st_pub_key, skey,
678 				sizeof (tmp.st_pub_key));
679 		} else {
680 			(void) memset(&tmp.st_pub_key, 0,
681 				sizeof (tmp.st_pub_key));
682 		}
683 		tmp.st_netname = (char *)netname;
684 		return (key_setnet(&tmp));
685 	}
686 	if (!key_call_ext((rpcproc_t)KEY_NET_PUT_3,
687 		xdr_key_netstarg3, (char *)&arg,
688 		xdr_keystatus, (char *)&status, use_ruid)) {
689 		return (-1);
690 	}
691 
692 	if (status != KEY_SUCCESS) {
693 		debug("key_setnet3 status is nonzero");
694 		return (-1);
695 	}
696 	return (0);
697 }
698 
699 /*
700  * Use effective uid.
701  */
702 int
703 key_setnet_g(const char *netname, const char *skey, keylen_t skeylen,
704 	const char *pkey, keylen_t pkeylen, algtype_t algtype)
705 {
706 	return (key_setnet_g_ext(netname, skey, skeylen, pkey, pkeylen,
707 			algtype, 0));
708 }
709 
710 /*
711  * Use real uid.
712  */
713 int
714 key_setnet_g_ruid(const char *netname, const char *skey, keylen_t skeylen,
715 	const char *pkey, keylen_t pkeylen, algtype_t algtype)
716 {
717 	return (key_setnet_g_ext(netname, skey, skeylen, pkey, pkeylen,
718 			algtype, 1));
719 }
720 
721 int
722 key_get_conv(char *pkey, des_block *deskey)
723 {
724 	cryptkeyres res;
725 
726 	if (!key_call((rpcproc_t)KEY_GET_CONV, xdr_keybuf, pkey,
727 		xdr_cryptkeyres, (char *)&res))
728 		return (-1);
729 	if (res.status != KEY_SUCCESS) {
730 		debug("get_conv status is nonzero");
731 		return (-1);
732 	}
733 	*deskey = res.cryptkeyres_u.deskey;
734 	return (0);
735 }
736 
737 int
738 key_get_conv_g(
739 	const char *pkey,
740 	keylen_t pkeylen,
741 	algtype_t algtype,
742 	des_block deskey[],
743 	keynum_t keynum
744 )
745 {
746 	deskeyarg3 arg;
747 	cryptkeyres3 res;
748 
749 	if (CLASSIC_PK_DH(pkeylen, algtype))
750 		return (key_get_conv((char *)pkey, deskey));
751 	arg.pub_key.keybuf3_len = pkeylen/4 + 1;
752 	arg.pub_key.keybuf3_val = (char *)pkey;
753 	arg.nkeys = keynum;
754 	arg.algtype = algtype;
755 	arg.keylen = pkeylen;
756 	(void) memset(&res, 0, sizeof (res));
757 	res.cryptkeyres3_u.deskey.deskeyarray_val = deskey;
758 	if (!key_call((rpcproc_t)KEY_GET_CONV_3, xdr_deskeyarg3, (char *)&arg,
759 		xdr_cryptkeyres3, (char *)&res))
760 		return (-1);
761 	if (res.status != KEY_SUCCESS) {
762 		debug("get_conv3 status is nonzero");
763 		return (-1);
764 	}
765 	if (res.cryptkeyres3_u.deskey.deskeyarray_len != keynum) {
766 		debug("get_conv3 number of keys dont match");
767 		return (-1);
768 	}
769 	return (0);
770 }
771 
772 struct  key_call_private {
773 	CLIENT	*client;	/* Client handle */
774 	pid_t	pid;		/* process-id at moment of creation */
775 	int	fd;		/* client handle fd */
776 	dev_t	rdev;		/* device client handle is using */
777 };
778 
779 static void set_rdev(struct key_call_private *);
780 static int check_rdev(struct key_call_private *);
781 
782 static void
783 key_call_destroy(void *vp)
784 {
785 	struct key_call_private *kcp = (struct key_call_private *)vp;
786 
787 	if (kcp != NULL && kcp->client != NULL) {
788 		(void) check_rdev(kcp);
789 		clnt_destroy(kcp->client);
790 		free(kcp);
791 	}
792 }
793 
794 static pthread_key_t key_call_key;
795 
796 void
797 _key_call_fini(void)
798 {
799 	struct key_call_private	*kcp = NULL;
800 
801 	if (key_call_key &&
802 	    (kcp = pthread_getspecific(key_call_key)) != NULL) {
803 		key_call_destroy(kcp);
804 		(void) pthread_setspecific(key_call_key, NULL);
805 	}
806 }
807 
808 /*
809  * Keep the handle cached.  This call may be made quite often.
810  */
811 static CLIENT *
812 getkeyserv_handle(int vers, int stale)
813 {
814 	struct key_call_private	*kcp = NULL;
815 	int _update_did();
816 
817 	kcp = thr_get_storage(&key_call_key, sizeof (*kcp), key_call_destroy);
818 	if (kcp == NULL) {
819 		syslog(LOG_CRIT, "getkeyserv_handle: out of memory");
820 		return (NULL);
821 	}
822 
823 	/*
824 	 * if pid has changed, destroy client and rebuild
825 	 * or if stale is '1' then destroy client and rebuild
826 	 */
827 	if (kcp->client &&
828 	    (!check_rdev(kcp) || kcp->pid != getpid() || stale)) {
829 		clnt_destroy(kcp->client);
830 		kcp->client = NULL;
831 	}
832 	if (kcp->client) {
833 		int	fd;
834 		/*
835 		 * Change the version number to the new one.
836 		 */
837 		clnt_control(kcp->client, CLSET_VERS, (void *)&vers);
838 		if (!_update_did(kcp->client, vers)) {
839 			if (rpc_createerr.cf_stat == RPC_SYSTEMERROR)
840 				syslog(LOG_DEBUG, "getkeyserv_handle: "
841 						"out of memory!");
842 			return (NULL);
843 		}
844 		/* Update fd in kcp because it was reopened in _update_did */
845 		if (clnt_control(kcp->client, CLGET_FD, (void *)&fd) &&
846 		    (fd >= 0))
847 			_fcntl(fd, F_SETFD, FD_CLOEXEC); /* "close on exec" */
848 		kcp->fd = fd;
849 		return (kcp->client);
850 	}
851 
852 	if ((kcp->client = clnt_door_create(KEY_PROG, vers, 0)) == NULL)
853 		return (NULL);
854 
855 	kcp->pid = getpid();
856 	set_rdev(kcp);
857 	_fcntl(kcp->fd, F_SETFD, FD_CLOEXEC);	/* make it "close on exec" */
858 
859 	return (kcp->client);
860 }
861 
862 /*
863  * RPC calls to the keyserv.
864  *
865  * If (use_ruid == 1), use real uid.
866  * If (use_ruid == 0), use effective uid.
867  * Returns  0 on failure, 1 on success
868  */
869 int
870 key_call_ext(rpcproc_t proc, xdrproc_t xdr_arg, char *arg, xdrproc_t xdr_rslt,
871 						char *rslt, int use_ruid)
872 {
873 	CLIENT		*clnt;
874 	struct timeval	wait_time = {0, 0};
875 	enum clnt_stat	status;
876 	int		vers;
877 
878 	if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
879 		cryptkeyres res;
880 		bool_t r;
881 		r = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg, &res);
882 		if (r == TRUE) {
883 /* LINTED pointer alignment */
884 			*(cryptkeyres*)rslt = res;
885 			return (1);
886 		}
887 		return (0);
888 	}
889 	if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
890 		cryptkeyres res;
891 		bool_t r;
892 		r = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg, &res);
893 		if (r == TRUE) {
894 /* LINTED pointer alignment */
895 			*(cryptkeyres*)rslt = res;
896 			return (1);
897 		}
898 		return (0);
899 	}
900 	if (proc == KEY_GEN && __key_gendes_LOCAL) {
901 		des_block res;
902 		bool_t r;
903 		r = (*__key_gendes_LOCAL)(geteuid(), 0, &res);
904 		if (r == TRUE) {
905 /* LINTED pointer alignment */
906 			*(des_block*)rslt = res;
907 			return (1);
908 		}
909 		return (0);
910 	}
911 
912 	if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
913 	    (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
914 	    (proc == KEY_GET_CONV))
915 		vers = 2;	/* talk to version 2 */
916 	else
917 		vers = 1;	/* talk to version 1 */
918 
919 	clnt = getkeyserv_handle(vers, 0);
920 	if (clnt == NULL)
921 		return (0);
922 
923 	auth_destroy(clnt->cl_auth);
924 	if (use_ruid)
925 		clnt->cl_auth = authsys_create_ruid();
926 	else
927 		clnt->cl_auth = authnone_create();
928 
929 	status = CLNT_CALL(clnt, proc, xdr_arg, arg, xdr_rslt,
930 			rslt, wait_time);
931 
932 	switch (status) {
933 	case RPC_SUCCESS:
934 		return (1);
935 
936 	case RPC_CANTRECV:
937 		/*
938 		 * keyserv was probably restarted, so we'll try once more
939 		 */
940 		if ((clnt = getkeyserv_handle(vers, 1)) == NULL)
941 			return (0);
942 
943 		auth_destroy(clnt->cl_auth);
944 		if (use_ruid)
945 			clnt->cl_auth = authsys_create_ruid();
946 		else
947 			clnt->cl_auth = authnone_create();
948 
949 
950 		if (CLNT_CALL(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
951 						wait_time) == RPC_SUCCESS)
952 			return (1);
953 		return (0);
954 
955 	default:
956 		return (0);
957 	}
958 }
959 
960 /*
961  * Use effective uid.
962  */
963 int
964 key_call(rpcproc_t proc, xdrproc_t xdr_arg, char *arg, xdrproc_t xdr_rslt,
965 	char *rslt)
966 {
967 	return (key_call_ext(proc, xdr_arg, arg, xdr_rslt, rslt, 0));
968 }
969 
970 /*
971  * Use real uid.
972  */
973 int
974 key_call_ruid(rpcproc_t proc, xdrproc_t xdr_arg, char *arg,
975 	xdrproc_t xdr_rslt, char *rslt)
976 {
977 	return (key_call_ext(proc, xdr_arg, arg, xdr_rslt, rslt, 1));
978 }
979 
980 static void
981 set_rdev(struct key_call_private *kcp)
982 {
983 	int fd;
984 	struct stat stbuf;
985 
986 	if (clnt_control(kcp->client, CLGET_FD, (char *)&fd) != TRUE ||
987 	    _FSTAT(fd, &stbuf) == -1) {
988 		syslog(LOG_DEBUG, "keyserv_client:  can't get info");
989 		kcp->fd = -1;
990 		return;
991 	}
992 	kcp->fd = fd;
993 	kcp->rdev = stbuf.st_rdev;
994 }
995 
996 static int
997 check_rdev(struct key_call_private *kcp)
998 {
999 	struct stat stbuf;
1000 
1001 	if (kcp->fd == -1)
1002 		return (1);    /* can't check it, assume it is okay */
1003 
1004 	if (_FSTAT(kcp->fd, &stbuf) == -1) {
1005 		syslog(LOG_DEBUG, "keyserv_client:  can't stat %d", kcp->fd);
1006 		/* could be because file descriptor was closed */
1007 		/* it's not our file descriptor, so don't try to close it */
1008 		clnt_control(kcp->client, CLSET_FD_NCLOSE, NULL);
1009 
1010 		return (0);
1011 	}
1012 	if (kcp->rdev != stbuf.st_rdev) {
1013 		syslog(LOG_DEBUG,
1014 		    "keyserv_client:  fd %d changed, old=0x%x, new=0x%x",
1015 		    kcp->fd, kcp->rdev, stbuf.st_rdev);
1016 		/* it's not our file descriptor, so don't try to close it */
1017 		clnt_control(kcp->client, CLSET_FD_NCLOSE, NULL);
1018 		return (0);
1019 	}
1020 	return (1);    /* fd is okay */
1021 }
1022