]> git.ipfire.org Git - thirdparty/glibc.git/blob - sunrpc/key_call.c
Update.
[thirdparty/glibc.git] / sunrpc / key_call.c
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) 1988 by Sun Microsystems, Inc.
31 */
32 /*
33 * The original source is from the RPCSRC 4.0 package from Sun Microsystems.
34 * The Interface to keyserver protocoll 2, RPC over AF_UNIX and Linux/doors
35 * was added by Thorsten Kukuk <kukuk@suse.de>
36 * Since the Linux/doors project was stopped, I doubt that this code will
37 * ever be useful <kukuk@suse.de>.
38 */
39
40 #include <stdio.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <rpc/rpc.h>
47 #include <rpc/auth.h>
48 #include <sys/wait.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <rpc/key_prot.h>
52 #include <bits/libc-lock.h>
53
54 #ifdef HAVE_DOORS
55 # include "door/door.h"
56 #endif
57
58 #define KEY_TIMEOUT 5 /* per-try timeout in seconds */
59 #define KEY_NRETRY 12 /* number of retries */
60
61 #define debug(msg) /* turn off debugging */
62
63 #ifndef SO_PASSCRED
64 extern int _openchild (const char *command, FILE **fto, FILE **ffrom);
65 #endif
66
67 static int key_call (u_long, xdrproc_t xdr_arg, char *,
68 xdrproc_t xdr_rslt, char *) internal_function;
69
70 static const struct timeval trytimeout = {KEY_TIMEOUT, 0};
71 static const struct timeval tottimeout = {KEY_TIMEOUT *KEY_NRETRY, 0};
72
73 int
74 key_setsecret (char *secretkey)
75 {
76 keystatus status;
77
78 if (!key_call ((u_long) KEY_SET, (xdrproc_t) INTUSE(xdr_keybuf), secretkey,
79 (xdrproc_t) INTUSE(xdr_keystatus), (char *) &status))
80 return -1;
81 if (status != KEY_SUCCESS)
82 {
83 debug ("set status is nonzero");
84 return -1;
85 }
86 return 0;
87 }
88
89 /* key_secretkey_is_set() returns 1 if the keyserver has a secret key
90 * stored for the caller's effective uid; it returns 0 otherwise
91 *
92 * N.B.: The KEY_NET_GET key call is undocumented. Applications shouldn't
93 * be using it, because it allows them to get the user's secret key.
94 */
95 int
96 key_secretkey_is_set (void)
97 {
98 struct key_netstres kres;
99
100 memset (&kres, 0, sizeof (kres));
101 if (key_call ((u_long) KEY_NET_GET, (xdrproc_t) INTUSE(xdr_void),
102 (char *) NULL, (xdrproc_t) INTUSE(xdr_key_netstres),
103 (char *) &kres) &&
104 (kres.status == KEY_SUCCESS) &&
105 (kres.key_netstres_u.knet.st_priv_key[0] != 0))
106 {
107 /* avoid leaving secret key in memory */
108 memset (kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
109 return 1;
110 }
111 return 0;
112 }
113
114 int
115 key_encryptsession (char *remotename, des_block *deskey)
116 {
117 cryptkeyarg arg;
118 cryptkeyres res;
119
120 arg.remotename = remotename;
121 arg.deskey = *deskey;
122 if (!key_call ((u_long) KEY_ENCRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
123 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
124 (char *) &res))
125 return -1;
126
127 if (res.status != KEY_SUCCESS)
128 {
129 debug ("encrypt status is nonzero");
130 return -1;
131 }
132 *deskey = res.cryptkeyres_u.deskey;
133 return 0;
134 }
135
136 int
137 key_decryptsession (char *remotename, des_block *deskey)
138 {
139 cryptkeyarg arg;
140 cryptkeyres res;
141
142 arg.remotename = remotename;
143 arg.deskey = *deskey;
144 if (!key_call ((u_long) KEY_DECRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
145 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
146 (char *) &res))
147 return -1;
148 if (res.status != KEY_SUCCESS)
149 {
150 debug ("decrypt status is nonzero");
151 return -1;
152 }
153 *deskey = res.cryptkeyres_u.deskey;
154 return 0;
155 }
156
157 int
158 key_encryptsession_pk (char *remotename, netobj *remotekey,
159 des_block *deskey)
160 {
161 cryptkeyarg2 arg;
162 cryptkeyres res;
163
164 arg.remotename = remotename;
165 arg.remotekey = *remotekey;
166 arg.deskey = *deskey;
167 if (!key_call ((u_long) KEY_ENCRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
168 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
169 (char *) &res))
170 return -1;
171
172 if (res.status != KEY_SUCCESS)
173 {
174 debug ("encrypt status is nonzero");
175 return -1;
176 }
177 *deskey = res.cryptkeyres_u.deskey;
178 return 0;
179 }
180
181 int
182 key_decryptsession_pk (char *remotename, netobj *remotekey,
183 des_block *deskey)
184 {
185 cryptkeyarg2 arg;
186 cryptkeyres res;
187
188 arg.remotename = remotename;
189 arg.remotekey = *remotekey;
190 arg.deskey = *deskey;
191 if (!key_call ((u_long) KEY_DECRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
192 (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
193 (char *) &res))
194 return -1;
195
196 if (res.status != KEY_SUCCESS)
197 {
198 debug ("decrypt status is nonzero");
199 return -1;
200 }
201 *deskey = res.cryptkeyres_u.deskey;
202 return 0;
203 }
204
205 int
206 key_gendes (des_block *key)
207 {
208 struct sockaddr_in sin;
209 CLIENT *client;
210 int socket;
211 enum clnt_stat stat;
212
213 sin.sin_family = AF_INET;
214 sin.sin_port = 0;
215 sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
216 __bzero (sin.sin_zero, sizeof (sin.sin_zero));
217 socket = RPC_ANYSOCK;
218 client = clntudp_bufcreate (&sin, (u_long) KEY_PROG, (u_long) KEY_VERS,
219 trytimeout, &socket, RPCSMALLMSGSIZE,
220 RPCSMALLMSGSIZE);
221 if (client == NULL)
222 return -1;
223
224 stat = clnt_call (client, KEY_GEN, (xdrproc_t) INTUSE(xdr_void), NULL,
225 (xdrproc_t) INTUSE(xdr_des_block), (caddr_t) key,
226 tottimeout);
227 clnt_destroy (client);
228 __close (socket);
229 if (stat != RPC_SUCCESS)
230 return -1;
231
232 return 0;
233 }
234
235 int
236 key_setnet (struct key_netstarg *arg)
237 {
238 keystatus status;
239
240 if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) INTUSE(xdr_key_netstarg),
241 (char *) arg,(xdrproc_t) INTUSE(xdr_keystatus),
242 (char *) &status))
243 return -1;
244
245 if (status != KEY_SUCCESS)
246 {
247 debug ("key_setnet status is nonzero");
248 return -1;
249 }
250 return 1;
251 }
252
253 int
254 key_get_conv (char *pkey, des_block *deskey)
255 {
256 cryptkeyres res;
257
258 if (!key_call ((u_long) KEY_GET_CONV, (xdrproc_t) INTUSE(xdr_keybuf), pkey,
259 (xdrproc_t) INTUSE(xdr_cryptkeyres), (char *) &res))
260 return -1;
261
262 if (res.status != KEY_SUCCESS)
263 {
264 debug ("get_conv status is nonzero");
265 return -1;
266 }
267 *deskey = res.cryptkeyres_u.deskey;
268 return 0;
269 }
270
271 /*
272 * Hack to allow the keyserver to use AUTH_DES (for authenticated
273 * NIS+ calls, for example). The only functions that get called
274 * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
275 *
276 * The approach is to have the keyserver fill in pointers to local
277 * implementations of these functions, and to call those in key_call().
278 */
279
280 cryptkeyres *(*__key_encryptsession_pk_LOCAL) (uid_t, char *);
281 cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *);
282 des_block *(*__key_gendes_LOCAL) (uid_t, char *);
283
284 #ifndef SO_PASSCRED
285 static int
286 internal_function
287 key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg,
288 xdrproc_t xdr_rslt, char *rslt)
289 {
290 XDR xdrargs;
291 XDR xdrrslt;
292 FILE *fargs;
293 FILE *frslt;
294 sigset_t oldmask, mask;
295 union wait status;
296 int pid;
297 int success;
298 uid_t ruid;
299 uid_t euid;
300 static const char MESSENGER[] = "/usr/etc/keyenvoy";
301
302 success = 1;
303 sigemptyset (&mask);
304 sigaddset (&mask, SIGCHLD);
305 __sigprocmask (SIG_BLOCK, &mask, &oldmask);
306
307 /*
308 * We are going to exec a set-uid program which makes our effective uid
309 * zero, and authenticates us with our real uid. We need to make the
310 * effective uid be the real uid for the setuid program, and
311 * the real uid be the effective uid so that we can change things back.
312 */
313 euid = __geteuid ();
314 ruid = __getuid ();
315 __setreuid (euid, ruid);
316 pid = _openchild (MESSENGER, &fargs, &frslt);
317 __setreuid (ruid, euid);
318 if (pid < 0)
319 {
320 debug ("open_streams");
321 __sigprocmask (SIG_SETMASK, &oldmask, NULL);
322 return (0);
323 }
324 xdrstdio_create (&xdrargs, fargs, XDR_ENCODE);
325 xdrstdio_create (&xdrrslt, frslt, XDR_DECODE);
326
327 if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg))
328 {
329 debug ("xdr args");
330 success = 0;
331 }
332 fclose (fargs);
333
334 if (success && !(*xdr_rslt) (&xdrrslt, rslt))
335 {
336 debug ("xdr rslt");
337 success = 0;
338 }
339 fclose(frslt);
340
341 wait_again:
342 if (__wait4 (pid, &status, 0, NULL) < 0)
343 {
344 if (errno == EINTR)
345 goto wait_again;
346 debug ("wait4");
347 if (errno == ECHILD || errno == ESRCH)
348 perror ("wait");
349 else
350 success = 0;
351 }
352 else
353 if (status.w_retcode)
354 {
355 debug ("wait4 1");
356 success = 0;
357 }
358 __sigprocmask (SIG_SETMASK, &oldmask, NULL);
359
360 return success;
361 }
362 #endif
363
364 struct key_call_private {
365 CLIENT *client; /* Client handle */
366 pid_t pid; /* process-id at moment of creation */
367 uid_t uid; /* user-id at last authorization */
368 };
369 #ifdef _RPC_THREAD_SAFE_
370 #define key_call_private_main ((struct key_call_private *)RPC_THREAD_VARIABLE(key_call_private_s))
371 #else
372 static struct key_call_private *key_call_private_main;
373 #endif
374 __libc_lock_define_initialized (static, keycall_lock)
375
376 /*
377 * Keep the handle cached. This call may be made quite often.
378 */
379 static CLIENT *
380 getkeyserv_handle (int vers)
381 {
382 struct key_call_private *kcp = key_call_private_main;
383 struct timeval wait_time;
384 int fd;
385 struct sockaddr_un name;
386 int namelen = sizeof(struct sockaddr_un);
387
388 #define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */
389 #define TOTAL_TRIES 5 /* Number of tries */
390
391 if (kcp == (struct key_call_private *)NULL)
392 {
393 kcp = (struct key_call_private *)malloc (sizeof (*kcp));
394 if (kcp == (struct key_call_private *)NULL)
395 return (CLIENT *) NULL;
396
397 key_call_private_main = kcp;
398 kcp->client = NULL;
399 }
400
401 /* if pid has changed, destroy client and rebuild */
402 if (kcp->client != NULL && kcp->pid != __getpid ())
403 {
404 clnt_destroy (kcp->client);
405 kcp->client = NULL;
406 }
407
408 if (kcp->client != NULL)
409 {
410 /* if other side closed socket, build handle again */
411 clnt_control (kcp->client, CLGET_FD, (char *)&fd);
412 if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1)
413 {
414 auth_destroy (kcp->client->cl_auth);
415 clnt_destroy (kcp->client);
416 kcp->client = NULL;
417 }
418 }
419
420 if (kcp->client != NULL)
421 {
422 /* if uid has changed, build client handle again */
423 if (kcp->uid != __geteuid ())
424 {
425 kcp->uid = __geteuid ();
426 auth_destroy (kcp->client->cl_auth);
427 kcp->client->cl_auth =
428 authunix_create ((char *)"", kcp->uid, 0, 0, NULL);
429 if (kcp->client->cl_auth == NULL)
430 {
431 clnt_destroy (kcp->client);
432 kcp->client = NULL;
433 return ((CLIENT *) NULL);
434 }
435 }
436 /* Change the version number to the new one */
437 clnt_control (kcp->client, CLSET_VERS, (void *)&vers);
438 return kcp->client;
439 }
440
441 if ((kcp->client == (CLIENT *) NULL))
442 /* Use the AF_UNIX transport */
443 kcp->client = clnt_create ("/var/run/keyservsock", KEY_PROG, vers, "unix");
444
445 if (kcp->client == (CLIENT *) NULL)
446 return (CLIENT *) NULL;
447
448 kcp->uid = __geteuid ();
449 kcp->pid = __getpid ();
450 kcp->client->cl_auth = authunix_create ((char *)"", kcp->uid, 0, 0, NULL);
451 if (kcp->client->cl_auth == NULL)
452 {
453 clnt_destroy (kcp->client);
454 kcp->client = NULL;
455 return (CLIENT *) NULL;
456 }
457
458 wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
459 wait_time.tv_usec = 0;
460 clnt_control (kcp->client, CLSET_RETRY_TIMEOUT,
461 (char *)&wait_time);
462 if (clnt_control (kcp->client, CLGET_FD, (char *)&fd))
463 __fcntl (fd, F_SETFD, 1); /* make it "close on exec" */
464
465 return kcp->client;
466 }
467
468 /* returns 0 on failure, 1 on success */
469 static int
470 internal_function
471 key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg,
472 xdrproc_t xdr_rslt, char *rslt)
473 {
474 CLIENT *clnt;
475 struct timeval wait_time;
476 int result = 0;
477
478 __libc_lock_lock (keycall_lock);
479 if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
480 (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
481 (proc == KEY_GET_CONV))
482 clnt = getkeyserv_handle(2); /* talk to version 2 */
483 else
484 clnt = getkeyserv_handle(1); /* talk to version 1 */
485
486 if (clnt != NULL)
487 {
488 wait_time.tv_sec = TOTAL_TIMEOUT;
489 wait_time.tv_usec = 0;
490
491 if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
492 wait_time) == RPC_SUCCESS)
493 result = 1;
494 }
495
496 __libc_lock_unlock (keycall_lock);
497
498 return result;
499 }
500
501 #ifdef HAVE_DOORS
502 /* returns 0 on failure, 1 on success */
503 static int
504 internal_function
505 key_call_door (u_long proc, xdrproc_t xdr_arg, char *arg,
506 xdrproc_t xdr_rslt, char *rslt)
507 {
508 XDR xdrs;
509 int fd, ret;
510 door_arg_t args;
511 char *data_ptr;
512 u_long data_len = 0;
513 char res[255];
514
515 if ((fd = open("/var/run/keyservdoor", O_RDONLY)) < 0)
516 return 0;
517 res[0] = 0;
518
519 data_len = xdr_sizeof (xdr_arg, arg);
520 data_ptr = calloc (1, data_len + 2 * sizeof (u_long));
521 if (data_ptr == NULL)
522 return 0;
523
524 INTUSE(xdrmem_create) (&xdrs, &data_ptr[2 * sizeof (u_long)], data_len,
525 XDR_ENCODE);
526 if (!xdr_arg (&xdrs, arg))
527 {
528 xdr_destroy (&xdrs);
529 free (data_ptr);
530 return 0;
531 }
532 xdr_destroy (&xdrs);
533
534 memcpy (data_ptr, &proc, sizeof (u_long));
535 memcpy (&data_ptr[sizeof (proc)], &data_len, sizeof (u_long));
536
537 args.data_ptr = data_ptr;
538 args.data_size = data_len + 2 * sizeof (u_long);
539 args.desc_ptr = NULL;
540 args.desc_num = 0;
541 args.rbuf = res;
542 args.rsize = sizeof (res);
543
544 ret = __door_call (fd, &args);
545 free (data_ptr);
546 close (fd);
547
548 if (ret < 0)
549 return 0;
550
551 memcpy (&data_len, args.data_ptr, sizeof (u_long));
552 if (data_len != 0)
553 return 0;
554
555 memcpy (&data_len, &args.data_ptr[sizeof (u_long)], sizeof (u_long));
556 INTUSE(xdrmem_create) (&xdrs, &args.data_ptr[2 * sizeof (u_long)],
557 data_len, XDR_DECODE);
558 if (!xdr_rslt (&xdrs, rslt))
559 {
560 xdr_destroy (&xdrs);
561 return 0;
562 }
563 xdr_destroy (&xdrs);
564
565 return 1;
566 }
567 #endif
568
569 /* returns 0 on failure, 1 on success */
570 static int
571 internal_function
572 key_call (u_long proc, xdrproc_t xdr_arg, char *arg,
573 xdrproc_t xdr_rslt, char *rslt)
574 {
575 #ifndef SO_PASSCRED
576 static int use_keyenvoy;
577 #endif
578 #ifdef HAVE_DOORS
579 static int not_use_doors;
580 #endif
581
582 if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL)
583 {
584 cryptkeyres *res;
585 res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg);
586 *(cryptkeyres *) rslt = *res;
587 return 1;
588 }
589 else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL)
590 {
591 cryptkeyres *res;
592 res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg);
593 *(cryptkeyres *) rslt = *res;
594 return 1;
595 }
596 else if (proc == KEY_GEN && __key_gendes_LOCAL)
597 {
598 des_block *res;
599 res = (*__key_gendes_LOCAL) (__geteuid (), 0);
600 *(des_block *) rslt = *res;
601 return 1;
602 }
603
604 #ifdef HAVE_DOORS
605 if (!not_use_doors)
606 {
607 if (key_call_door (proc, xdr_arg, arg, xdr_rslt, rslt))
608 return 1;
609 not_use_doors = 1;
610 }
611 #endif
612
613 #ifdef SO_PASSCRED
614 return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt);
615 #else
616 if (!use_keyenvoy)
617 {
618 if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt))
619 return 1;
620 use_keyenvoy = 1;
621 }
622 return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt);
623 #endif
624 }
625
626 #ifdef _RPC_THREAD_SAFE_
627 void
628 __rpc_thread_key_cleanup (void)
629 {
630 struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s);
631
632 if (kcp) {
633 if (kcp->client)
634 clnt_destroy(kcp->client);
635 free (kcp);
636 }
637 }
638 #endif /* _RPC_THREAD_SAFE_ */