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