xref: /freebsd/lib/libc/rpc/key_call.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  */
29 /*
30  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
31  */
32 
33 #ident	"@(#)key_call.c	1.25	94/04/24 SMI"
34 
35 #if defined(LIBC_SCCS) && !defined(lint)
36 static char rcsid[] =
37   "$FreeBSD$";
38 #endif /* not lint */
39 
40 /*
41  * key_call.c, Interface to keyserver
42  *
43  * setsecretkey(key) - set your secret key
44  * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent
45  * decryptsessionkey(agent, deskey) - decrypt ditto
46  * gendeskey(deskey) - generate a secure des key
47  */
48 
49 #include "namespace.h"
50 #include "reentrant.h"
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <rpc/rpc.h>
56 #include <rpc/auth.h>
57 #include <rpc/auth_unix.h>
58 #include <rpc/key_prot.h>
59 #include <string.h>
60 #include <netconfig.h>
61 #include <sys/utsname.h>
62 #include <stdlib.h>
63 #include <signal.h>
64 #include <sys/wait.h>
65 #include <sys/fcntl.h>
66 #include "un-namespace.h"
67 
68 
69 #define	KEY_TIMEOUT	5	/* per-try timeout in seconds */
70 #define	KEY_NRETRY	12	/* number of retries */
71 
72 #ifdef DEBUG
73 #define	debug(msg)	(void) fprintf(stderr, "%s\n", msg);
74 #else
75 #define	debug(msg)
76 #endif /* DEBUG */
77 
78 /*
79  * Hack to allow the keyserver to use AUTH_DES (for authenticated
80  * NIS+ calls, for example).  The only functions that get called
81  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
82  *
83  * The approach is to have the keyserver fill in pointers to local
84  * implementations of these functions, and to call those in key_call().
85  */
86 
87 cryptkeyres *(*__key_encryptsession_pk_LOCAL)() = 0;
88 cryptkeyres *(*__key_decryptsession_pk_LOCAL)() = 0;
89 des_block *(*__key_gendes_LOCAL)() = 0;
90 
91 static int key_call __P(( u_long, xdrproc_t, char *, xdrproc_t, char * ));
92 
93 int
94 key_setsecret(secretkey)
95 	const char *secretkey;
96 {
97 	keystatus status;
98 
99 	if (!key_call((u_long) KEY_SET, xdr_keybuf, (char *) secretkey,
100 			xdr_keystatus, (char *)&status)) {
101 		return (-1);
102 	}
103 	if (status != KEY_SUCCESS) {
104 		debug("set status is nonzero");
105 		return (-1);
106 	}
107 	return (0);
108 }
109 
110 
111 /* key_secretkey_is_set() returns 1 if the keyserver has a secret key
112  * stored for the caller's effective uid; it returns 0 otherwise
113  *
114  * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
115  * be using it, because it allows them to get the user's secret key.
116  */
117 
118 int
119 key_secretkey_is_set(void)
120 {
121 	struct key_netstres 	kres;
122 
123 	memset((void*)&kres, 0, sizeof (kres));
124 	if (key_call((u_long) KEY_NET_GET, xdr_void, (char *)NULL,
125 			xdr_key_netstres, (char *) &kres) &&
126 	    (kres.status == KEY_SUCCESS) &&
127 	    (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
128 		/* avoid leaving secret key in memory */
129 		memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
130 		return (1);
131 	}
132 	return (0);
133 }
134 
135 int
136 key_encryptsession_pk(remotename, remotekey, deskey)
137 	char *remotename;
138 	netobj *remotekey;
139 	des_block *deskey;
140 {
141 	cryptkeyarg2 arg;
142 	cryptkeyres res;
143 
144 	arg.remotename = remotename;
145 	arg.remotekey = *remotekey;
146 	arg.deskey = *deskey;
147 	if (!key_call((u_long)KEY_ENCRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
148 			xdr_cryptkeyres, (char *)&res)) {
149 		return (-1);
150 	}
151 	if (res.status != KEY_SUCCESS) {
152 		debug("encrypt status is nonzero");
153 		return (-1);
154 	}
155 	*deskey = res.cryptkeyres_u.deskey;
156 	return (0);
157 }
158 
159 int
160 key_decryptsession_pk(remotename, remotekey, deskey)
161 	char *remotename;
162 	netobj *remotekey;
163 	des_block *deskey;
164 {
165 	cryptkeyarg2 arg;
166 	cryptkeyres res;
167 
168 	arg.remotename = remotename;
169 	arg.remotekey = *remotekey;
170 	arg.deskey = *deskey;
171 	if (!key_call((u_long)KEY_DECRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
172 			xdr_cryptkeyres, (char *)&res)) {
173 		return (-1);
174 	}
175 	if (res.status != KEY_SUCCESS) {
176 		debug("decrypt status is nonzero");
177 		return (-1);
178 	}
179 	*deskey = res.cryptkeyres_u.deskey;
180 	return (0);
181 }
182 
183 int
184 key_encryptsession(remotename, deskey)
185 	const char *remotename;
186 	des_block *deskey;
187 {
188 	cryptkeyarg arg;
189 	cryptkeyres res;
190 
191 	arg.remotename = (char *) remotename;
192 	arg.deskey = *deskey;
193 	if (!key_call((u_long)KEY_ENCRYPT, xdr_cryptkeyarg, (char *)&arg,
194 			xdr_cryptkeyres, (char *)&res)) {
195 		return (-1);
196 	}
197 	if (res.status != KEY_SUCCESS) {
198 		debug("encrypt status is nonzero");
199 		return (-1);
200 	}
201 	*deskey = res.cryptkeyres_u.deskey;
202 	return (0);
203 }
204 
205 int
206 key_decryptsession(remotename, deskey)
207 	const char *remotename;
208 	des_block *deskey;
209 {
210 	cryptkeyarg arg;
211 	cryptkeyres res;
212 
213 	arg.remotename = (char *) remotename;
214 	arg.deskey = *deskey;
215 	if (!key_call((u_long)KEY_DECRYPT, xdr_cryptkeyarg, (char *)&arg,
216 			xdr_cryptkeyres, (char *)&res)) {
217 		return (-1);
218 	}
219 	if (res.status != KEY_SUCCESS) {
220 		debug("decrypt status is nonzero");
221 		return (-1);
222 	}
223 	*deskey = res.cryptkeyres_u.deskey;
224 	return (0);
225 }
226 
227 int
228 key_gendes(key)
229 	des_block *key;
230 {
231 	if (!key_call((u_long)KEY_GEN, xdr_void, (char *)NULL,
232 			xdr_des_block, (char *)key)) {
233 		return (-1);
234 	}
235 	return (0);
236 }
237 
238 int
239 key_setnet(arg)
240 struct key_netstarg *arg;
241 {
242 	keystatus status;
243 
244 
245 	if (!key_call((u_long) KEY_NET_PUT, xdr_key_netstarg, (char *) arg,
246 		xdr_keystatus, (char *) &status)){
247 		return (-1);
248 	}
249 
250 	if (status != KEY_SUCCESS) {
251 		debug("key_setnet status is nonzero");
252 		return (-1);
253 	}
254 	return (1);
255 }
256 
257 
258 int
259 key_get_conv(pkey, deskey)
260 	char *pkey;
261 	des_block *deskey;
262 {
263 	cryptkeyres res;
264 
265 	if (!key_call((u_long) KEY_GET_CONV, xdr_keybuf, pkey,
266 		xdr_cryptkeyres, (char *)&res)) {
267 		return (-1);
268 	}
269 	if (res.status != KEY_SUCCESS) {
270 		debug("get_conv status is nonzero");
271 		return (-1);
272 	}
273 	*deskey = res.cryptkeyres_u.deskey;
274 	return (0);
275 }
276 
277 struct  key_call_private {
278 	CLIENT	*client;	/* Client handle */
279 	pid_t	pid;		/* process-id at moment of creation */
280 	uid_t	uid;		/* user-id at last authorization */
281 };
282 static struct key_call_private *key_call_private_main = NULL;
283 
284 static void
285 key_call_destroy(void *vp)
286 {
287 	register struct key_call_private *kcp = (struct key_call_private *)vp;
288 
289 	if (kcp) {
290 		if (kcp->client)
291 			clnt_destroy(kcp->client);
292 		free(kcp);
293 	}
294 }
295 
296 /*
297  * Keep the handle cached.  This call may be made quite often.
298  */
299 static CLIENT *
300 getkeyserv_handle(vers)
301 int	vers;
302 {
303 	void *localhandle;
304 	struct netconfig *nconf;
305 	struct netconfig *tpconf;
306 	struct key_call_private *kcp = key_call_private_main;
307 	struct timeval wait_time;
308 	struct utsname u;
309 	int main_thread;
310 	int fd;
311 	static thread_key_t key_call_key;
312 	extern mutex_t tsd_lock;
313 
314 #define	TOTAL_TIMEOUT	30	/* total timeout talking to keyserver */
315 #define	TOTAL_TRIES	5	/* Number of tries */
316 
317 	if ((main_thread = thr_main())) {
318 		kcp = key_call_private_main;
319 	} else {
320 		if (key_call_key == 0) {
321 			mutex_lock(&tsd_lock);
322 			if (key_call_key == 0)
323 				thr_keycreate(&key_call_key, key_call_destroy);
324 			mutex_unlock(&tsd_lock);
325 		}
326 		kcp = (struct key_call_private *)thr_getspecific(key_call_key);
327 	}
328 	if (kcp == (struct key_call_private *)NULL) {
329 		kcp = (struct key_call_private *)malloc(sizeof (*kcp));
330 		if (kcp == (struct key_call_private *)NULL) {
331 			return ((CLIENT *) NULL);
332 		}
333                 if (main_thread)
334                         key_call_private_main = kcp;
335                 else
336                         thr_setspecific(key_call_key, (void *) kcp);
337 		kcp->client = NULL;
338 	}
339 
340 	/* if pid has changed, destroy client and rebuild */
341 	if (kcp->client != NULL && kcp->pid != getpid()) {
342 		clnt_destroy(kcp->client);
343 		kcp->client = NULL;
344 	}
345 
346 	if (kcp->client != NULL) {
347 		/* if uid has changed, build client handle again */
348 		if (kcp->uid != geteuid()) {
349 			kcp->uid = geteuid();
350 			auth_destroy(kcp->client->cl_auth);
351 			kcp->client->cl_auth =
352 				authsys_create("", kcp->uid, 0, 0, NULL);
353 			if (kcp->client->cl_auth == NULL) {
354 				clnt_destroy(kcp->client);
355 				kcp->client = NULL;
356 				return ((CLIENT *) NULL);
357 			}
358 		}
359 		/* Change the version number to the new one */
360 		clnt_control(kcp->client, CLSET_VERS, (void *)&vers);
361 		return (kcp->client);
362 	}
363 	if (!(localhandle = setnetconfig())) {
364 		return ((CLIENT *) NULL);
365 	}
366         tpconf = NULL;
367 #if defined(__FreeBSD__)
368 	if (uname(&u) == -1)
369 #else
370 #if defined(i386)
371 	if (_nuname(&u) == -1)
372 #elif defined(sparc)
373 	if (_uname(&u) == -1)
374 #else
375 #error Unknown architecture!
376 #endif
377 #endif
378 	{
379 		endnetconfig(localhandle);
380 		return ((CLIENT *) NULL);
381         }
382 	while ((nconf = getnetconfig(localhandle)) != NULL) {
383 		if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
384 			/*
385 			 * We use COTS_ORD here so that the caller can
386 			 * find out immediately if the server is dead.
387 			 */
388 			if (nconf->nc_semantics == NC_TPI_COTS_ORD) {
389 				kcp->client = clnt_tp_create(u.nodename,
390 					KEY_PROG, vers, nconf);
391 				if (kcp->client)
392 					break;
393 			} else {
394 				tpconf = nconf;
395 			}
396 		}
397 	}
398 	if ((kcp->client == (CLIENT *) NULL) && (tpconf))
399 		/* Now, try the CLTS or COTS loopback transport */
400 		kcp->client = clnt_tp_create(u.nodename,
401 			KEY_PROG, vers, tpconf);
402 	endnetconfig(localhandle);
403 
404 	if (kcp->client == (CLIENT *) NULL) {
405 		return ((CLIENT *) NULL);
406         }
407 	kcp->uid = geteuid();
408 	kcp->pid = getpid();
409 	kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL);
410 	if (kcp->client->cl_auth == NULL) {
411 		clnt_destroy(kcp->client);
412 		kcp->client = NULL;
413 		return ((CLIENT *) NULL);
414 	}
415 
416 	wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
417 	wait_time.tv_usec = 0;
418 	(void) clnt_control(kcp->client, CLSET_RETRY_TIMEOUT,
419 		(char *)&wait_time);
420 	if (clnt_control(kcp->client, CLGET_FD, (char *)&fd))
421 		_fcntl(fd, F_SETFD, 1);	/* make it "close on exec" */
422 
423 	return (kcp->client);
424 }
425 
426 /* returns  0 on failure, 1 on success */
427 
428 static int
429 key_call(proc, xdr_arg, arg, xdr_rslt, rslt)
430 	u_long proc;
431 	xdrproc_t xdr_arg;
432 	char *arg;
433 	xdrproc_t xdr_rslt;
434 	char *rslt;
435 {
436 	CLIENT *clnt;
437 	struct timeval wait_time;
438 
439 	if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
440 		cryptkeyres *res;
441 		res = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg);
442 		*(cryptkeyres*)rslt = *res;
443 		return (1);
444 	} else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
445 		cryptkeyres *res;
446 		res = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg);
447 		*(cryptkeyres*)rslt = *res;
448 		return (1);
449 	} else if (proc == KEY_GEN && __key_gendes_LOCAL) {
450 		des_block *res;
451 		res = (*__key_gendes_LOCAL)(geteuid(), 0);
452 		*(des_block*)rslt = *res;
453 		return (1);
454 	}
455 
456 	if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
457 	    (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
458 	    (proc == KEY_GET_CONV))
459 		clnt = getkeyserv_handle(2); /* talk to version 2 */
460 	else
461 		clnt = getkeyserv_handle(1); /* talk to version 1 */
462 
463 	if (clnt == NULL) {
464 		return (0);
465 	}
466 
467 	wait_time.tv_sec = TOTAL_TIMEOUT;
468 	wait_time.tv_usec = 0;
469 
470 	if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
471 		wait_time) == RPC_SUCCESS) {
472 		return (1);
473 	} else {
474 		return (0);
475 	}
476 }
477