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