0b09a5a66ebf510ee562ad94ea6e97a3f9d0fbb1
[kopensolaris-gnu/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 libc_hidden_def (key_encryptsession_pk)
181
182 int
183 key_decryptsession_pk (char *remotename, netobj *remotekey,
184                        des_block *deskey)
185 {
186   cryptkeyarg2 arg;
187   cryptkeyres res;
188
189   arg.remotename = remotename;
190   arg.remotekey = *remotekey;
191   arg.deskey = *deskey;
192   if (!key_call ((u_long) KEY_DECRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
193                  (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
194                  (char *) &res))
195     return -1;
196
197   if (res.status != KEY_SUCCESS)
198     {
199       debug ("decrypt status is nonzero");
200       return -1;
201     }
202   *deskey = res.cryptkeyres_u.deskey;
203   return 0;
204 }
205 libc_hidden_def (key_decryptsession_pk)
206
207 int
208 key_gendes (des_block *key)
209 {
210   struct sockaddr_in sin;
211   CLIENT *client;
212   int socket;
213   enum clnt_stat stat;
214
215   sin.sin_family = AF_INET;
216   sin.sin_port = 0;
217   sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
218   __bzero (sin.sin_zero, sizeof (sin.sin_zero));
219   socket = RPC_ANYSOCK;
220   client = INTUSE(clntudp_bufcreate) (&sin, (u_long) KEY_PROG,
221                                       (u_long) KEY_VERS, trytimeout, &socket,
222                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
223   if (client == NULL)
224     return -1;
225
226   stat = clnt_call (client, KEY_GEN, (xdrproc_t) INTUSE(xdr_void), NULL,
227                     (xdrproc_t) INTUSE(xdr_des_block), (caddr_t) key,
228                     tottimeout);
229   clnt_destroy (client);
230   __close (socket);
231   if (stat != RPC_SUCCESS)
232     return -1;
233
234   return 0;
235 }
236 libc_hidden_def (key_gendes)
237
238 int
239 key_setnet (struct key_netstarg *arg)
240 {
241   keystatus status;
242
243   if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) INTUSE(xdr_key_netstarg),
244                  (char *) arg,(xdrproc_t) INTUSE(xdr_keystatus),
245                  (char *) &status))
246     return -1;
247
248   if (status != KEY_SUCCESS)
249     {
250       debug ("key_setnet status is nonzero");
251       return -1;
252     }
253   return 1;
254 }
255
256 int
257 key_get_conv (char *pkey, des_block *deskey)
258 {
259   cryptkeyres res;
260
261   if (!key_call ((u_long) KEY_GET_CONV, (xdrproc_t) INTUSE(xdr_keybuf), pkey,
262                  (xdrproc_t) INTUSE(xdr_cryptkeyres), (char *) &res))
263     return -1;
264
265   if (res.status != KEY_SUCCESS)
266     {
267       debug ("get_conv status is nonzero");
268       return -1;
269     }
270   *deskey = res.cryptkeyres_u.deskey;
271   return 0;
272 }
273
274 /*
275  * Hack to allow the keyserver to use AUTH_DES (for authenticated
276  * NIS+ calls, for example).  The only functions that get called
277  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
278  *
279  * The approach is to have the keyserver fill in pointers to local
280  * implementations of these functions, and to call those in key_call().
281  */
282
283 cryptkeyres *(*__key_encryptsession_pk_LOCAL) (uid_t, char *);
284 cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *);
285 des_block *(*__key_gendes_LOCAL) (uid_t, char *);
286
287 #ifndef SO_PASSCRED
288 static int
289 internal_function
290 key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg,
291                    xdrproc_t xdr_rslt, char *rslt)
292 {
293   XDR xdrargs;
294   XDR xdrrslt;
295   FILE *fargs;
296   FILE *frslt;
297   sigset_t oldmask, mask;
298   union wait status;
299   int pid;
300   int success;
301   uid_t ruid;
302   uid_t euid;
303   static const char MESSENGER[] = "/usr/etc/keyenvoy";
304
305   success = 1;
306   sigemptyset (&mask);
307   sigaddset (&mask, SIGCHLD);
308   __sigprocmask (SIG_BLOCK, &mask, &oldmask);
309
310   /*
311    * We are going to exec a set-uid program which makes our effective uid
312    * zero, and authenticates us with our real uid. We need to make the
313    * effective uid be the real uid for the setuid program, and
314    * the real uid be the effective uid so that we can change things back.
315    */
316   euid = __geteuid ();
317   ruid = __getuid ();
318   __setreuid (euid, ruid);
319   pid = _openchild (MESSENGER, &fargs, &frslt);
320   __setreuid (ruid, euid);
321   if (pid < 0)
322     {
323       debug ("open_streams");
324       __sigprocmask (SIG_SETMASK, &oldmask, NULL);
325       return (0);
326     }
327   xdrstdio_create (&xdrargs, fargs, XDR_ENCODE);
328   xdrstdio_create (&xdrrslt, frslt, XDR_DECODE);
329
330   if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg))
331     {
332       debug ("xdr args");
333       success = 0;
334     }
335   fclose (fargs);
336
337   if (success && !(*xdr_rslt) (&xdrrslt, rslt))
338     {
339       debug ("xdr rslt");
340       success = 0;
341     }
342   fclose(frslt);
343
344  wait_again:
345   if (__wait4 (pid, &status, 0, NULL) < 0)
346     {
347       if (errno == EINTR)
348         goto wait_again;
349       debug ("wait4");
350       if (errno == ECHILD || errno == ESRCH)
351         perror ("wait");
352       else
353         success = 0;
354     }
355   else
356     if (status.w_retcode)
357       {
358         debug ("wait4 1");
359         success = 0;
360       }
361   __sigprocmask (SIG_SETMASK, &oldmask, NULL);
362
363   return success;
364 }
365 #endif
366
367 struct  key_call_private {
368   CLIENT  *client;        /* Client handle */
369   pid_t   pid;            /* process-id at moment of creation */
370   uid_t   uid;            /* user-id at last authorization */
371 };
372 #ifdef _RPC_THREAD_SAFE_
373 #define key_call_private_main ((struct  key_call_private *)RPC_THREAD_VARIABLE(key_call_private_s))
374 #else
375 static struct key_call_private *key_call_private_main;
376 #endif
377 __libc_lock_define_initialized (static, keycall_lock)
378
379 /*
380  * Keep the handle cached.  This call may be made quite often.
381  */
382 static CLIENT *
383 getkeyserv_handle (int vers)
384 {
385   struct key_call_private *kcp = key_call_private_main;
386   struct timeval wait_time;
387   int fd;
388   struct sockaddr_un name;
389   int namelen = sizeof(struct sockaddr_un);
390
391 #define TOTAL_TIMEOUT   30      /* total timeout talking to keyserver */
392 #define TOTAL_TRIES     5       /* Number of tries */
393
394   if (kcp == (struct key_call_private *)NULL)
395     {
396       kcp = (struct key_call_private *)malloc (sizeof (*kcp));
397       if (kcp == (struct key_call_private *)NULL)
398         return (CLIENT *) NULL;
399
400       key_call_private_main = kcp;
401       kcp->client = NULL;
402     }
403
404   /* if pid has changed, destroy client and rebuild */
405   if (kcp->client != NULL && kcp->pid != __getpid ())
406     {
407       clnt_destroy (kcp->client);
408       kcp->client = NULL;
409     }
410
411   if (kcp->client != NULL)
412     {
413       /* if other side closed socket, build handle again */
414       clnt_control (kcp->client, CLGET_FD, (char *)&fd);
415       if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1)
416         {
417           auth_destroy (kcp->client->cl_auth);
418           clnt_destroy (kcp->client);
419           kcp->client = NULL;
420         }
421     }
422
423   if (kcp->client != NULL)
424     {
425       /* if uid has changed, build client handle again */
426       if (kcp->uid != __geteuid ())
427         {
428         kcp->uid = __geteuid ();
429         auth_destroy (kcp->client->cl_auth);
430         kcp->client->cl_auth =
431           INTUSE(authunix_create) ((char *)"", kcp->uid, 0, 0, NULL);
432         if (kcp->client->cl_auth == NULL)
433           {
434             clnt_destroy (kcp->client);
435             kcp->client = NULL;
436             return ((CLIENT *) NULL);
437           }
438         }
439       /* Change the version number to the new one */
440       clnt_control (kcp->client, CLSET_VERS, (void *)&vers);
441       return kcp->client;
442     }
443
444   if ((kcp->client == (CLIENT *) NULL))
445     /* Use the AF_UNIX transport */
446     kcp->client = INTUSE(clnt_create) ("/var/run/keyservsock", KEY_PROG, vers,
447                                        "unix");
448
449   if (kcp->client == (CLIENT *) NULL)
450     return (CLIENT *) NULL;
451
452   kcp->uid = __geteuid ();
453   kcp->pid = __getpid ();
454   kcp->client->cl_auth = INTUSE(authunix_create) ((char *)"", kcp->uid, 0, 0,
455                                                   NULL);
456   if (kcp->client->cl_auth == NULL)
457     {
458       clnt_destroy (kcp->client);
459       kcp->client = NULL;
460       return (CLIENT *) NULL;
461     }
462
463   wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
464   wait_time.tv_usec = 0;
465   clnt_control (kcp->client, CLSET_RETRY_TIMEOUT,
466                 (char *)&wait_time);
467   if (clnt_control (kcp->client, CLGET_FD, (char *)&fd))
468     __fcntl (fd, F_SETFD, 1);  /* make it "close on exec" */
469
470   return kcp->client;
471 }
472
473 /* returns  0 on failure, 1 on success */
474 static int
475 internal_function
476 key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg,
477                xdrproc_t xdr_rslt, char *rslt)
478 {
479   CLIENT *clnt;
480   struct timeval wait_time;
481   int result = 0;
482
483   __libc_lock_lock (keycall_lock);
484   if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
485       (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
486       (proc == KEY_GET_CONV))
487     clnt = getkeyserv_handle(2); /* talk to version 2 */
488   else
489     clnt = getkeyserv_handle(1); /* talk to version 1 */
490
491   if (clnt != NULL)
492     {
493       wait_time.tv_sec = TOTAL_TIMEOUT;
494       wait_time.tv_usec = 0;
495
496       if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
497                      wait_time) == RPC_SUCCESS)
498         result = 1;
499     }
500
501   __libc_lock_unlock (keycall_lock);
502
503   return result;
504 }
505
506 #ifdef HAVE_DOORS
507 /* returns 0 on failure, 1 on success */
508 static int
509 internal_function
510 key_call_door (u_long proc, xdrproc_t xdr_arg, char *arg,
511                xdrproc_t xdr_rslt, char *rslt)
512 {
513   XDR xdrs;
514   int fd, ret;
515   door_arg_t args;
516   char *data_ptr;
517   u_long data_len = 0;
518   char res[255];
519
520   if ((fd = open("/var/run/keyservdoor", O_RDONLY)) < 0)
521     return 0;
522   res[0] = 0;
523
524   data_len = xdr_sizeof (xdr_arg, arg);
525   data_ptr = calloc (1, data_len + 2 * sizeof (u_long));
526   if (data_ptr == NULL)
527     return 0;
528
529   INTUSE(xdrmem_create) (&xdrs, &data_ptr[2 * sizeof (u_long)], data_len,
530                          XDR_ENCODE);
531   if (!xdr_arg (&xdrs, arg))
532     {
533       xdr_destroy (&xdrs);
534       free (data_ptr);
535       return 0;
536     }
537   xdr_destroy (&xdrs);
538
539   memcpy (data_ptr, &proc, sizeof (u_long));
540   memcpy (&data_ptr[sizeof (proc)], &data_len, sizeof (u_long));
541
542   args.data_ptr = data_ptr;
543   args.data_size = data_len + 2 * sizeof (u_long);
544   args.desc_ptr = NULL;
545   args.desc_num = 0;
546   args.rbuf = res;
547   args.rsize = sizeof (res);
548
549   ret = __door_call (fd, &args);
550   free (data_ptr);
551   close (fd);
552
553   if (ret < 0)
554     return 0;
555
556   memcpy (&data_len, args.data_ptr, sizeof (u_long));
557   if (data_len != 0)
558     return 0;
559
560   memcpy (&data_len, &args.data_ptr[sizeof (u_long)], sizeof (u_long));
561   INTUSE(xdrmem_create) (&xdrs, &args.data_ptr[2 * sizeof (u_long)],
562                          data_len, XDR_DECODE);
563   if (!xdr_rslt (&xdrs, rslt))
564     {
565       xdr_destroy (&xdrs);
566       return 0;
567     }
568   xdr_destroy (&xdrs);
569
570   return 1;
571 }
572 #endif
573
574 /* returns 0 on failure, 1 on success */
575 static int
576 internal_function
577 key_call (u_long proc, xdrproc_t xdr_arg, char *arg,
578           xdrproc_t xdr_rslt, char *rslt)
579 {
580 #ifndef SO_PASSCRED
581   static int use_keyenvoy;
582 #endif
583 #ifdef HAVE_DOORS
584   static int not_use_doors;
585 #endif
586
587   if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL)
588     {
589       cryptkeyres *res;
590       res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg);
591       *(cryptkeyres *) rslt = *res;
592       return 1;
593     }
594   else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL)
595     {
596       cryptkeyres *res;
597       res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg);
598       *(cryptkeyres *) rslt = *res;
599       return 1;
600     }
601   else if (proc == KEY_GEN && __key_gendes_LOCAL)
602     {
603       des_block *res;
604       res = (*__key_gendes_LOCAL) (__geteuid (), 0);
605       *(des_block *) rslt = *res;
606       return 1;
607     }
608
609 #ifdef HAVE_DOORS
610   if (!not_use_doors)
611     {
612       if (key_call_door (proc, xdr_arg, arg, xdr_rslt, rslt))
613         return 1;
614       not_use_doors = 1;
615     }
616 #endif
617
618 #ifdef SO_PASSCRED
619   return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt);
620 #else
621   if (!use_keyenvoy)
622     {
623       if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt))
624         return 1;
625       use_keyenvoy = 1;
626     }
627   return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt);
628 #endif
629 }
630
631 #ifdef _RPC_THREAD_SAFE_
632 void
633 __rpc_thread_key_cleanup (void)
634 {
635         struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s);
636
637         if (kcp) {
638                 if (kcp->client)
639                         clnt_destroy(kcp->client);
640                 free (kcp);
641         }
642 }
643 #endif /* _RPC_THREAD_SAFE_ */