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 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/types.h>
30#include <unistd.h>
31#include <rpc/rpc.h>
32#include <rpc/key_prot.h>
33#include <rpcsvc/nis_dhext.h>
34#include <syslog.h>
35#include <note.h>
36
37/* defined in usr/src/libnsl/rpc/key_call.c */
38extern bool_t (*__key_encryptsession_pk_LOCAL)();
39extern bool_t (*__key_decryptsession_pk_LOCAL)();
40extern bool_t (*__key_gendes_LOCAL)();
41
42#define	CLASSIC_PK_DH(k, a)	(((k) == 192) && ((a) == 0))
43
44/*
45 * authsys_create_uid(uid_t uid)
46 *
47 * Create SYS (UNIX) style authenticator for the given uid/gid
48 * We don't include suplementary groups, since these are of no
49 * interest for the keyserv operations that we do.
50 */
51AUTH *
52authsys_create_uid(uid_t uid, gid_t gid)
53{
54	char	host[MAX_MACHINE_NAME + 1];
55	AUTH	*res;
56
57	if (gethostname(host, sizeof (host) - 1) == -1) {
58		syslog(LOG_ERR,
59			"pam_dhkeys: Can't determine hostname: %m");
60		return (NULL);
61	}
62	host[MAX_MACHINE_NAME] = '\0';
63
64	res = authsys_create(host, uid, gid, 0, (gid_t *)NULL);
65
66	return (res);
67}
68
69/*
70 * my_key_call(proc, xdr_arg, arg, xdr_rslt, rslt, uit, gid)
71 *
72 * my_key_call is a copy of key_call() from libnsl with the
73 * added AUTHSYS rpc credential to make the keyserver use our
74 * REAL UID instead of our EFFECTIVE UID when handling our keys.
75 */
76int
77my_key_call(rpcproc_t proc, xdrproc_t xdr_arg, char *arg,
78		xdrproc_t xdr_rslt, char *rslt, uid_t uid, gid_t gid)
79{
80	CLIENT		*clnt;
81	struct timeval	wait_time = {0, 0};
82	enum clnt_stat	status;
83	int		vers;
84
85	if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
86		cryptkeyres res;
87		bool_t r;
88		r = (*__key_encryptsession_pk_LOCAL)(uid, arg, &res);
89		if (r == TRUE) {
90			/* LINTED pointer alignment */
91			*(cryptkeyres*)rslt = res;
92			return (1);
93		}
94		return (0);
95	}
96	if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
97		cryptkeyres res;
98		bool_t r;
99		r = (*__key_decryptsession_pk_LOCAL)(uid, arg, &res);
100		if (r == TRUE) {
101			/* LINTED pointer alignment */
102			*(cryptkeyres*)rslt = res;
103			return (1);
104		}
105		return (0);
106	}
107	if (proc == KEY_GEN && __key_gendes_LOCAL) {
108		des_block res;
109		bool_t r;
110		r = (*__key_gendes_LOCAL)(uid, 0, &res);
111		if (r == TRUE) {
112			/* LINTED pointer alignment */
113			*(des_block*)rslt = res;
114			return (1);
115		}
116		return (0);
117	}
118
119	if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
120	    (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
121	    (proc == KEY_GET_CONV))
122		vers = 2;	/* talk to version 2 */
123	else
124		vers = 1;	/* talk to version 1 */
125
126	clnt = clnt_door_create(KEY_PROG, vers, 0);
127
128	if (clnt == NULL)
129		return (0);
130
131	clnt->cl_auth = authsys_create_uid(uid, gid);
132
133	status = CLNT_CALL(clnt, proc, xdr_arg, arg, xdr_rslt,
134			rslt, wait_time);
135
136	auth_destroy(clnt->cl_auth);
137	clnt_destroy(clnt);
138
139	return (status == RPC_SUCCESS ? 1 : 0);
140}
141
142int
143key_setnet_uid(struct key_netstarg *arg, uid_t uid, gid_t gid)
144{
145	keystatus status;
146
147	if (!my_key_call((rpcproc_t)KEY_NET_PUT, xdr_key_netstarg,
148	    (char *)arg, xdr_keystatus, (char *)&status, uid, gid)) {
149		return (-1);
150	}
151	if (status != KEY_SUCCESS) {
152		return (-1);
153	}
154
155	return (1);
156}
157
158int
159key_setnet_g_uid(const char *netname, const char *skey, keylen_t skeylen,
160    const char *pkey, keylen_t pkeylen, algtype_t algtype,
161    uid_t uid, gid_t gid)
162{
163	key_netstarg3 arg;
164	keystatus status;
165
166	arg.st_netname = (char *)netname;
167	arg.algtype = algtype;
168
169	if (skeylen == 0)
170		arg.st_priv_key.keybuf3_len = 0;
171	else
172		arg.st_priv_key.keybuf3_len = skeylen/4 + 1;
173
174	arg.st_priv_key.keybuf3_val = (char *)skey;
175
176	if (pkeylen == 0)
177		arg.st_pub_key.keybuf3_len = 0;
178	else
179		arg.st_pub_key.keybuf3_len = pkeylen/4 + 1;
180
181	arg.st_pub_key.keybuf3_val = (char *)pkey;
182
183	if (skeylen == 0) {
184		if (pkeylen == 0) {
185			/* debug("keylens are both 0"); */
186			return (-1);
187		}
188		arg.keylen = pkeylen;
189	} else {
190		if ((pkeylen != 0) && (skeylen != pkeylen)) {
191			/* debug("keylens don't match"); */
192			return (-1);
193		}
194		arg.keylen = skeylen;
195	}
196
197	if (CLASSIC_PK_DH(arg.keylen, arg.algtype)) {
198		key_netstarg tmp;
199
200		if (skeylen != 0) {
201			(void) memcpy(&tmp.st_priv_key, skey,
202				sizeof (tmp.st_priv_key));
203		} else {
204			(void) memset(&tmp.st_priv_key, 0,
205			    sizeof (tmp.st_priv_key));
206		}
207		if (pkeylen != 0) {
208			(void) memcpy(&tmp.st_pub_key, skey,
209			    sizeof (tmp.st_pub_key));
210		} else {
211			(void) memset(&tmp.st_pub_key, 0,
212			    sizeof (tmp.st_pub_key));
213		}
214		tmp.st_netname = (char *)netname;
215		return (key_setnet_uid(&tmp, uid, gid));
216	}
217
218	if (!my_key_call((rpcproc_t)KEY_NET_PUT_3, xdr_key_netstarg3,
219	    (char *)&arg, xdr_keystatus, (char *)&status, uid, gid)) {
220		return (-1);
221	}
222
223	if (status != KEY_SUCCESS) {
224		/* debug("key_setnet3 status is nonzero"); */
225		return (-1);
226	}
227	return (0);
228}
229
230
231/*
232 * key_secretkey_is_set_uid() returns 1 if the keyserver has a secret key
233 * stored for the caller's REAL uid; it returns 0 otherwise
234 */
235int
236key_secretkey_is_set_uid(uid_t uid, gid_t gid)
237{
238	struct key_netstres 	kres;
239
240	(void) memset((void*)&kres, 0, sizeof (kres));
241
242	if (my_key_call((rpcproc_t)KEY_NET_GET, xdr_void, (char *)NULL,
243			xdr_key_netstres, (char *)&kres, uid, gid) &&
244	    (kres.status == KEY_SUCCESS) &&
245	    (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
246		/* avoid leaving secret key in memory */
247		(void) memset(kres.key_netstres_u.knet.st_priv_key, 0,
248		    HEXKEYBYTES);
249		xdr_free(xdr_key_netstres, (char *)&kres);
250		return (1);
251	}
252	return (0);
253}
254
255int
256key_removesecret_g_uid(uid_t uid, gid_t gid)
257{
258	keystatus status;
259
260	if (my_key_call((rpcproc_t)KEY_CLEAR_3, xdr_void, (char *)NULL,
261	    xdr_keystatus, (char *)&status, uid, gid))
262		return (-1);
263
264	if (status != KEY_SUCCESS)
265		return (-1);
266
267	return (0);
268}
269