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