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 309495 2016-12-03 18:21:28Z ngie $");
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)(uid_t, void *arg) = 0;
85cryptkeyres *(*__key_decryptsession_pk_LOCAL)(uid_t, void *arg) = 0;
86des_block *(*__key_gendes_LOCAL)(uid_t, void *) = 0;
87
88static int key_call( u_long, xdrproc_t, void *, xdrproc_t, void *);
89
90int
91key_setsecret(const char *secretkey)
92{
93	keystatus status;
94
95	if (!key_call((u_long) KEY_SET, (xdrproc_t)xdr_keybuf,
96			(void *)secretkey,
97			(xdrproc_t)xdr_keystatus, &status)) {
98		return (-1);
99	}
100	if (status != KEY_SUCCESS) {
101		debug("set status is nonzero");
102		return (-1);
103	}
104	return (0);
105}
106
107
108/* key_secretkey_is_set() returns 1 if the keyserver has a secret key
109 * stored for the caller's effective uid; it returns 0 otherwise
110 *
111 * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
112 * be using it, because it allows them to get the user's secret key.
113 */
114
115int
116key_secretkey_is_set(void)
117{
118	struct key_netstres 	kres;
119
120	memset((void*)&kres, 0, sizeof (kres));
121	if (key_call((u_long) KEY_NET_GET, (xdrproc_t)xdr_void, NULL,
122			(xdrproc_t)xdr_key_netstres, &kres) &&
123	    (kres.status == KEY_SUCCESS) &&
124	    (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
125		/* avoid leaving secret key in memory */
126		memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
127		return (1);
128	}
129	return (0);
130}
131
132int
133key_encryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
134{
135	cryptkeyarg2 arg;
136	cryptkeyres res;
137
138	arg.remotename = remotename;
139	arg.remotekey = *remotekey;
140	arg.deskey = *deskey;
141	if (!key_call((u_long)KEY_ENCRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
142			(xdrproc_t)xdr_cryptkeyres, &res)) {
143		return (-1);
144	}
145	if (res.status != KEY_SUCCESS) {
146		debug("encrypt status is nonzero");
147		return (-1);
148	}
149	*deskey = res.cryptkeyres_u.deskey;
150	return (0);
151}
152
153int
154key_decryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
155{
156	cryptkeyarg2 arg;
157	cryptkeyres res;
158
159	arg.remotename = remotename;
160	arg.remotekey = *remotekey;
161	arg.deskey = *deskey;
162	if (!key_call((u_long)KEY_DECRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
163			(xdrproc_t)xdr_cryptkeyres, &res)) {
164		return (-1);
165	}
166	if (res.status != KEY_SUCCESS) {
167		debug("decrypt status is nonzero");
168		return (-1);
169	}
170	*deskey = res.cryptkeyres_u.deskey;
171	return (0);
172}
173
174int
175key_encryptsession(const char *remotename, des_block *deskey)
176{
177	cryptkeyarg arg;
178	cryptkeyres res;
179
180	arg.remotename = (char *) remotename;
181	arg.deskey = *deskey;
182	if (!key_call((u_long)KEY_ENCRYPT, (xdrproc_t)xdr_cryptkeyarg, &arg,
183			(xdrproc_t)xdr_cryptkeyres, &res)) {
184		return (-1);
185	}
186	if (res.status != KEY_SUCCESS) {
187		debug("encrypt status is nonzero");
188		return (-1);
189	}
190	*deskey = res.cryptkeyres_u.deskey;
191	return (0);
192}
193
194int
195key_decryptsession(const char *remotename, des_block *deskey)
196{
197	cryptkeyarg arg;
198	cryptkeyres res;
199
200	arg.remotename = (char *) remotename;
201	arg.deskey = *deskey;
202	if (!key_call((u_long)KEY_DECRYPT, (xdrproc_t)xdr_cryptkeyarg, &arg,
203			(xdrproc_t)xdr_cryptkeyres, &res)) {
204		return (-1);
205	}
206	if (res.status != KEY_SUCCESS) {
207		debug("decrypt status is nonzero");
208		return (-1);
209	}
210	*deskey = res.cryptkeyres_u.deskey;
211	return (0);
212}
213
214int
215key_gendes(des_block *key)
216{
217	if (!key_call((u_long)KEY_GEN, (xdrproc_t)xdr_void, NULL,
218			(xdrproc_t)xdr_des_block, key)) {
219		return (-1);
220	}
221	return (0);
222}
223
224int
225key_setnet(struct key_netstarg *arg)
226{
227	keystatus status;
228
229
230	if (!key_call((u_long) KEY_NET_PUT, (xdrproc_t)xdr_key_netstarg, arg,
231			(xdrproc_t)xdr_keystatus, &status)){
232		return (-1);
233	}
234
235	if (status != KEY_SUCCESS) {
236		debug("key_setnet status is nonzero");
237		return (-1);
238	}
239	return (1);
240}
241
242
243int
244key_get_conv(char *pkey, des_block *deskey)
245{
246	cryptkeyres res;
247
248	if (!key_call((u_long) KEY_GET_CONV, (xdrproc_t)xdr_keybuf, pkey,
249			(xdrproc_t)xdr_cryptkeyres, &res)) {
250		return (-1);
251	}
252	if (res.status != KEY_SUCCESS) {
253		debug("get_conv status is nonzero");
254		return (-1);
255	}
256	*deskey = res.cryptkeyres_u.deskey;
257	return (0);
258}
259
260struct  key_call_private {
261	CLIENT	*client;	/* Client handle */
262	pid_t	pid;		/* process-id at moment of creation */
263	uid_t	uid;		/* user-id at last authorization */
264};
265static struct key_call_private *key_call_private_main = NULL;
266static thread_key_t key_call_key;
267static once_t key_call_once = ONCE_INITIALIZER;
268static int key_call_key_error;
269
270static void
271key_call_destroy(void *vp)
272{
273	struct key_call_private *kcp = (struct key_call_private *)vp;
274
275	if (kcp) {
276		if (kcp->client)
277			clnt_destroy(kcp->client);
278		free(kcp);
279	}
280}
281
282static void
283key_call_init(void)
284{
285
286	key_call_key_error = thr_keycreate(&key_call_key, key_call_destroy);
287}
288
289/*
290 * Keep the handle cached.  This call may be made quite often.
291 */
292static CLIENT *
293getkeyserv_handle(int vers)
294{
295	void *localhandle;
296	struct netconfig *nconf;
297	struct netconfig *tpconf;
298	struct key_call_private *kcp;
299	struct timeval wait_time;
300	struct utsname u;
301	int main_thread;
302	int fd;
303
304#define	TOTAL_TIMEOUT	30	/* total timeout talking to keyserver */
305#define	TOTAL_TRIES	5	/* Number of tries */
306
307	if ((main_thread = thr_main())) {
308		kcp = key_call_private_main;
309	} else {
310		if (thr_once(&key_call_once, key_call_init) != 0 ||
311		    key_call_key_error != 0)
312			return ((CLIENT *) NULL);
313		kcp = (struct key_call_private *)thr_getspecific(key_call_key);
314	}
315	if (kcp == (struct key_call_private *)NULL) {
316		kcp = (struct key_call_private *)malloc(sizeof (*kcp));
317		if (kcp == (struct key_call_private *)NULL) {
318			return ((CLIENT *) NULL);
319		}
320                if (main_thread)
321                        key_call_private_main = kcp;
322                else
323                        thr_setspecific(key_call_key, (void *) kcp);
324		kcp->client = NULL;
325	}
326
327	/* if pid has changed, destroy client and rebuild */
328	if (kcp->client != NULL && kcp->pid != getpid()) {
329		clnt_destroy(kcp->client);
330		kcp->client = NULL;
331	}
332
333	if (kcp->client != NULL) {
334		/* if uid has changed, build client handle again */
335		if (kcp->uid != geteuid()) {
336			kcp->uid = geteuid();
337			auth_destroy(kcp->client->cl_auth);
338			kcp->client->cl_auth =
339				authsys_create("", kcp->uid, 0, 0, NULL);
340			if (kcp->client->cl_auth == NULL) {
341				clnt_destroy(kcp->client);
342				kcp->client = NULL;
343				return ((CLIENT *) NULL);
344			}
345		}
346		/* Change the version number to the new one */
347		clnt_control(kcp->client, CLSET_VERS, (void *)&vers);
348		return (kcp->client);
349	}
350	if (!(localhandle = setnetconfig())) {
351		return ((CLIENT *) NULL);
352	}
353        tpconf = NULL;
354#if defined(__FreeBSD__)
355	if (uname(&u) == -1)
356#else
357#if defined(i386)
358	if (_nuname(&u) == -1)
359#elif defined(sparc)
360	if (_uname(&u) == -1)
361#else
362#error Unknown architecture!
363#endif
364#endif
365	{
366		endnetconfig(localhandle);
367		return ((CLIENT *) NULL);
368        }
369	while ((nconf = getnetconfig(localhandle)) != NULL) {
370		if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
371			/*
372			 * We use COTS_ORD here so that the caller can
373			 * find out immediately if the server is dead.
374			 */
375			if (nconf->nc_semantics == NC_TPI_COTS_ORD) {
376				kcp->client = clnt_tp_create(u.nodename,
377					KEY_PROG, vers, nconf);
378				if (kcp->client)
379					break;
380			} else {
381				tpconf = nconf;
382			}
383		}
384	}
385	if ((kcp->client == (CLIENT *) NULL) && (tpconf))
386		/* Now, try the CLTS or COTS loopback transport */
387		kcp->client = clnt_tp_create(u.nodename,
388			KEY_PROG, vers, tpconf);
389	endnetconfig(localhandle);
390
391	if (kcp->client == (CLIENT *) NULL) {
392		return ((CLIENT *) NULL);
393        }
394	kcp->uid = geteuid();
395	kcp->pid = getpid();
396	kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL);
397	if (kcp->client->cl_auth == NULL) {
398		clnt_destroy(kcp->client);
399		kcp->client = NULL;
400		return ((CLIENT *) NULL);
401	}
402
403	wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
404	wait_time.tv_usec = 0;
405	(void) clnt_control(kcp->client, CLSET_RETRY_TIMEOUT,
406		(char *)&wait_time);
407	if (clnt_control(kcp->client, CLGET_FD, (char *)&fd))
408		_fcntl(fd, F_SETFD, 1);	/* make it "close on exec" */
409
410	return (kcp->client);
411}
412
413/* returns  0 on failure, 1 on success */
414
415static int
416key_call(u_long proc, xdrproc_t xdr_arg, void *arg, xdrproc_t xdr_rslt,
417    void *rslt)
418{
419	CLIENT *clnt;
420	struct timeval wait_time;
421
422	if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
423		cryptkeyres *res;
424		res = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg);
425		*(cryptkeyres*)rslt = *res;
426		return (1);
427	} else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
428		cryptkeyres *res;
429		res = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg);
430		*(cryptkeyres*)rslt = *res;
431		return (1);
432	} else if (proc == KEY_GEN && __key_gendes_LOCAL) {
433		des_block *res;
434		res = (*__key_gendes_LOCAL)(geteuid(), 0);
435		*(des_block*)rslt = *res;
436		return (1);
437	}
438
439	if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
440	    (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
441	    (proc == KEY_GET_CONV))
442		clnt = getkeyserv_handle(2); /* talk to version 2 */
443	else
444		clnt = getkeyserv_handle(1); /* talk to version 1 */
445
446	if (clnt == NULL) {
447		return (0);
448	}
449
450	wait_time.tv_sec = TOTAL_TIMEOUT;
451	wait_time.tv_usec = 0;
452
453	if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
454		wait_time) == RPC_SUCCESS) {
455		return (1);
456	} else {
457		return (0);
458	}
459}
460