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