xref: /freebsd/lib/libc/rpc/key_call.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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 #ifndef 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 
383 	while (nconf = getnetconfig(localhandle)) {
384 		if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
385 			/*
386 			 * We use COTS_ORD here so that the caller can
387 			 * find out immediately if the server is dead.
388 			 */
389 			if (nconf->nc_semantics == NC_TPI_COTS_ORD) {
390 				kcp->client = clnt_tp_create(u.nodename,
391 					KEY_PROG, vers, nconf);
392 				if (kcp->client)
393 					break;
394 			} else {
395 				tpconf = nconf;
396 			}
397 		}
398 	}
399 	if ((kcp->client == (CLIENT *) NULL) && (tpconf))
400 		/* Now, try the CLTS or COTS loopback transport */
401 		kcp->client = clnt_tp_create(u.nodename,
402 			KEY_PROG, vers, tpconf);
403 	endnetconfig(localhandle);
404 
405 	if (kcp->client == (CLIENT *) NULL) {
406 		return ((CLIENT *) NULL);
407         }
408 	kcp->uid = geteuid();
409 	kcp->pid = getpid();
410 	kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL);
411 	if (kcp->client->cl_auth == NULL) {
412 		clnt_destroy(kcp->client);
413 		kcp->client = NULL;
414 		return ((CLIENT *) NULL);
415 	}
416 
417 	wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
418 	wait_time.tv_usec = 0;
419 	(void) clnt_control(kcp->client, CLSET_RETRY_TIMEOUT,
420 		(char *)&wait_time);
421 	if (clnt_control(kcp->client, CLGET_FD, (char *)&fd))
422 		_fcntl(fd, F_SETFD, 1);	/* make it "close on exec" */
423 
424 	return (kcp->client);
425 }
426 
427 /* returns  0 on failure, 1 on success */
428 
429 static int
430 key_call(proc, xdr_arg, arg, xdr_rslt, rslt)
431 	u_long proc;
432 	xdrproc_t xdr_arg;
433 	char *arg;
434 	xdrproc_t xdr_rslt;
435 	char *rslt;
436 {
437 	CLIENT *clnt;
438 	struct timeval wait_time;
439 
440 	if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
441 		cryptkeyres *res;
442 		res = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg);
443 		*(cryptkeyres*)rslt = *res;
444 		return (1);
445 	} else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
446 		cryptkeyres *res;
447 		res = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg);
448 		*(cryptkeyres*)rslt = *res;
449 		return (1);
450 	} else if (proc == KEY_GEN && __key_gendes_LOCAL) {
451 		des_block *res;
452 		res = (*__key_gendes_LOCAL)(geteuid(), 0);
453 		*(des_block*)rslt = *res;
454 		return (1);
455 	}
456 
457 	if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
458 	    (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
459 	    (proc == KEY_GET_CONV))
460 		clnt = getkeyserv_handle(2); /* talk to version 2 */
461 	else
462 		clnt = getkeyserv_handle(1); /* talk to version 1 */
463 
464 	if (clnt == NULL) {
465 		return (0);
466 	}
467 
468 	wait_time.tv_sec = TOTAL_TIMEOUT;
469 	wait_time.tv_usec = 0;
470 
471 	if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
472 		wait_time) == RPC_SUCCESS) {
473 		return (1);
474 	} else {
475 		return (0);
476 	}
477 }
478