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 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34#pragma ident	"%Z%%M%	%I%	%E% SMI"
35
36/*
37 * key_call.c, Interface to keyserver
38 * key_encryptsession(agent, deskey, cr)-encrypt a session key to talk to agent
39 * key_decryptsession(agent, deskey) - decrypt ditto
40 * key_gendes(deskey) - generate a secure des key
41 * key_getnetname(netname, cr) - get the netname from the keyserv
42 * netname2user(...) - get unix credential for given name (kernel only)
43 */
44
45#include <sys/param.h>
46#include <sys/types.h>
47#include <sys/time.h>
48#include <sys/systm.h>
49#include <sys/user.h>
50#include <sys/proc.h>
51#include <sys/pathname.h>
52#include <sys/sysmacros.h>
53#include <sys/vnode.h>
54#include <sys/uio.h>
55#include <sys/debug.h>
56#include <sys/utsname.h>
57#include <sys/cmn_err.h>
58
59#include <rpc/rpc.h>
60#include <rpc/key_prot.h>
61
62#define	KEY_TIMEOUT	30	/* per-try timeout in seconds */
63#define	KEY_NRETRY	6	/* number of retries */
64
65struct auth_globals {
66	struct knetconfig	auth_config;
67	char 			auth_keyname[SYS_NMLN+16];
68};
69
70static struct timeval keytrytimeout = { KEY_TIMEOUT, 0 };
71
72static enum clnt_stat key_call(rpcproc_t, xdrproc_t, char *, xdrproc_t, char *,
73    cred_t *);
74
75/* ARGSUSED */
76void *
77auth_zone_init(zoneid_t zoneid)
78{
79	struct auth_globals *authg;
80
81	authg = kmem_zalloc(sizeof (*authg), KM_SLEEP);
82	return (authg);
83}
84
85/* ARGSUSED */
86void
87auth_zone_fini(zoneid_t zoneid, void *data)
88{
89	struct auth_globals *authg = data;
90
91	kmem_free(authg, sizeof (*authg));
92}
93
94enum clnt_stat
95key_encryptsession(char *remotename, des_block *deskey, cred_t *cr)
96{
97	cryptkeyarg arg;
98	cryptkeyres res;
99	enum clnt_stat stat;
100
101	RPCLOG(8, "key_encryptsession(%s, ", remotename);
102	RPCLOG(8, "%x", *(uint32_t *)deskey);
103	RPCLOG(8, "%x)\n", *(((uint32_t *)(deskey))+1));
104
105	arg.remotename = remotename;
106	arg.deskey = *deskey;
107	if ((stat = key_call(KEY_ENCRYPT, xdr_cryptkeyarg, (char *)&arg,
108	    xdr_cryptkeyres, (char *)&res, cr)) != RPC_SUCCESS) {
109		RPCLOG(1, "key_encryptsession(%d, ", (int)crgetuid(cr));
110		RPCLOG(1, "%s): ", remotename);
111		RPCLOG(1, "rpc status %d ", stat);
112		RPCLOG(1, "(%s)\n", clnt_sperrno(stat));
113		return (stat);
114	}
115
116	if (res.status != KEY_SUCCESS) {
117		RPCLOG(1, "key_encryptsession(%d, ", (int)crgetuid(cr));
118		RPCLOG(1, "%s): ", remotename);
119		RPCLOG(1, "key status %d\n", res.status);
120		return (RPC_FAILED);	/* XXX */
121	}
122	*deskey = res.cryptkeyres_u.deskey;
123	return (RPC_SUCCESS);
124}
125
126enum clnt_stat
127key_decryptsession(char *remotename, des_block *deskey)
128{
129	cryptkeyarg arg;
130	cryptkeyres res;
131	enum clnt_stat stat;
132
133	RPCLOG(8, "key_decryptsession(%s, ", remotename);
134	RPCLOG(2, "%x", *(uint32_t *)deskey);
135	RPCLOG(2, "%x)\n", *(((uint32_t *)(deskey))+1));
136
137	arg.remotename = remotename;
138	arg.deskey = *deskey;
139	if ((stat = key_call(KEY_DECRYPT, xdr_cryptkeyarg, (char *)&arg,
140	    xdr_cryptkeyres, (char *)&res, kcred)) != RPC_SUCCESS) {
141		RPCLOG(1, "key_decryptsession(%s): ", remotename);
142		RPCLOG(1, "rpc status %d ", stat);
143		RPCLOG(1, "(%s)\n", clnt_sperrno(stat));
144		return (stat);
145	}
146
147	if (res.status != KEY_SUCCESS) {
148		RPCLOG(1, "key_decryptsession(%s): ", remotename);
149		RPCLOG(1, "key status %d\n", res.status);
150		return (RPC_FAILED);	/* XXX */
151	}
152	*deskey = res.cryptkeyres_u.deskey;
153	return (RPC_SUCCESS);
154}
155
156enum clnt_stat
157key_gendes(des_block *key)
158{
159
160	return (key_call(KEY_GEN, xdr_void, NULL, xdr_des_block, (char *)key,
161	    CRED()));
162}
163
164/*
165 *  Call up to keyserv to get the netname of the client based
166 *  on its uid.  The netname is written into the string that "netname"
167 *  points to; the caller is responsible for ensuring that sufficient space
168 *  is available.
169 */
170enum clnt_stat
171key_getnetname(netname, cr)
172	char *netname;
173	cred_t *cr;
174{
175	key_netstres kres;
176	enum clnt_stat stat;
177
178	/*
179	 * Look up the keyserv interface routines to see if
180	 * netname is stored there.
181	 */
182	kres.key_netstres_u.knet.st_netname = netname;
183	if ((stat = key_call((rpcproc_t)KEY_NET_GET, xdr_void, NULL,
184	    xdr_key_netstres, (char *)&kres, cr)) != RPC_SUCCESS) {
185		RPCLOG(1, "key_getnetname(%d): ", (int)crgetuid(cr));
186		RPCLOG(1, "rpc status %d ", stat);
187		RPCLOG(1, "(%s)\n", clnt_sperrno(stat));
188		return (stat);
189	}
190
191	if (kres.status != KEY_SUCCESS) {
192		RPCLOG(1, "key_getnetname(%d): ", (int)crgetuid(cr));
193		RPCLOG(1, "key status %d\n", kres.status);
194		return (RPC_FAILED);
195	}
196
197	return (RPC_SUCCESS);
198}
199
200enum clnt_stat
201netname2user(char *name, uid_t *uid, gid_t *gid, int *len, gid_t *groups)
202{
203	struct getcredres res;
204	enum clnt_stat stat;
205
206	res.getcredres_u.cred.gids.gids_val = (uint_t *)groups;
207	if ((stat = key_call(KEY_GETCRED, xdr_netnamestr, (char *)&name,
208	    xdr_getcredres, (char *)&res, CRED())) != RPC_SUCCESS) {
209		RPCLOG(1, "netname2user(%s): ", name);
210		RPCLOG(1, "rpc status %d ", stat);
211		RPCLOG(1, "(%s)\n", clnt_sperrno(stat));
212		return (stat);
213	}
214
215	if (res.status != KEY_SUCCESS) {
216		RPCLOG(1, "netname2user(%s): ", name);
217		RPCLOG(1, "key status %d\n", res.status);
218		return (RPC_FAILED);	/* XXX */
219	}
220	*uid = res.getcredres_u.cred.uid;
221	*gid = res.getcredres_u.cred.gid;
222	*len = res.getcredres_u.cred.gids.gids_len;
223	return (RPC_SUCCESS);
224}
225
226#define	NC_LOOPBACK		"loopback"	/* XXX */
227char loopback_name[] = NC_LOOPBACK;
228
229static enum clnt_stat
230key_call(rpcproc_t procn, xdrproc_t xdr_args, caddr_t args,
231	xdrproc_t xdr_rslt, caddr_t rslt, cred_t *cr)
232{
233	struct netbuf netaddr;
234	CLIENT *client;
235	enum clnt_stat stat;
236	vnode_t *vp;
237	int error;
238	struct auth_globals *authg;
239	char *keyname;
240	struct knetconfig *configp;
241	k_sigset_t smask;
242
243	authg = zone_getspecific(auth_zone_key, curproc->p_zone);
244	keyname = authg->auth_keyname;
245	configp = &authg->auth_config;
246
247	/*
248	 * Using a global here is obviously busted and fraught with danger.
249	 */
250	(void) strcpy(keyname, uts_nodename());
251	netaddr.len = strlen(keyname);
252	(void) strcpy(&keyname[netaddr.len], ".keyserv");
253
254	netaddr.buf = keyname;
255	/*
256	 * 8 = strlen(".keyserv");
257	 */
258	netaddr.len = netaddr.maxlen = netaddr.len + 8;
259
260	/*
261	 * filch a knetconfig structure.
262	 */
263	if (configp->knc_rdev == 0) {
264		if ((error = lookupname("/dev/ticlts", UIO_SYSSPACE,
265		    FOLLOW, NULLVPP, &vp)) != 0) {
266			RPCLOG(1, "key_call: lookupname: %d\n", error);
267			return (RPC_UNKNOWNPROTO);
268		}
269		configp->knc_rdev = vp->v_rdev;
270		configp->knc_protofmly = loopback_name;
271		VN_RELE(vp);
272	}
273	configp->knc_semantics = NC_TPI_CLTS;
274	RPCLOG(8, "key_call: procn %d, ", procn);
275	RPCLOG(8, "rdev %lx, ", configp->knc_rdev);
276	RPCLOG(8, "len %d, ", netaddr.len);
277	RPCLOG(8, "maxlen %d, ", netaddr.maxlen);
278	RPCLOG(8, "name %p\n", (void *)netaddr.buf);
279
280	/*
281	 * now call the proper stuff.
282	 */
283	error = clnt_tli_kcreate(configp, &netaddr, KEY_PROG, KEY_VERS,
284	    0, KEY_NRETRY, cr, &client);
285
286	if (error != 0) {
287		RPCLOG(1, "key_call: clnt_tli_kcreate: error %d\n", error);
288		switch (error) {
289		case EINTR:
290			return (RPC_INTR);
291		case ETIMEDOUT:
292			return (RPC_TIMEDOUT);
293		default:
294			return (RPC_FAILED);	/* XXX */
295		}
296	}
297
298	auth_destroy(client->cl_auth);
299	client->cl_auth = authloopback_create();
300	if (client->cl_auth == NULL) {
301		clnt_destroy(client);
302		RPCLOG(1, "key_call: authloopback_create: error %d\n", EINTR);
303		return (RPC_INTR);
304	}
305
306	/* Mask out all signals except SIGHUP, SIGQUIT, and SIGTERM. */
307	sigintr(&smask, 0);
308	stat = clnt_call(client, procn, xdr_args, args, xdr_rslt, rslt,
309	    keytrytimeout);
310	sigunintr(&smask);
311
312	auth_destroy(client->cl_auth);
313	clnt_destroy(client);
314	if (stat != RPC_SUCCESS) {
315		RPCLOG(1, "key_call: keyserver clnt_call failed: stat %d ",
316		    stat);
317		RPCLOG(1, "(%s)\n", clnt_sperrno(stat));
318		RPCLOG0(1, "\n");
319		return (stat);
320	}
321	RPCLOG(8, "key call: (%d) ok\n", procn);
322	return (RPC_SUCCESS);
323}
324