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