Back to index

glibc  2.9
key_call.c
Go to the documentation of this file.
00001 /*
00002  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
00003  * unrestricted use provided that this legend is included on all tape
00004  * media and as a part of the software program in whole or part.  Users
00005  * may copy or modify Sun RPC without charge, but are not authorized
00006  * to license or distribute it to anyone else except as part of a product or
00007  * program developed by the user.
00008  *
00009  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
00010  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
00011  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
00012  *
00013  * Sun RPC is provided with no support and without any obligation on the
00014  * part of Sun Microsystems, Inc. to assist in its use, correction,
00015  * modification or enhancement.
00016  *
00017  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
00018  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
00019  * OR ANY PART THEREOF.
00020  *
00021  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
00022  * or profits or other special, indirect and consequential damages, even if
00023  * Sun has been advised of the possibility of such damages.
00024  *
00025  * Sun Microsystems, Inc.
00026  * 2550 Garcia Avenue
00027  * Mountain View, California  94043
00028  */
00029 /*
00030  * Copyright (c) 1988 by Sun Microsystems, Inc.
00031  */
00032 /*
00033  * The original source is from the RPCSRC 4.0 package from Sun Microsystems.
00034  * The Interface to keyserver protocoll 2, RPC over AF_UNIX and Linux/doors
00035  * was added by Thorsten Kukuk <kukuk@suse.de>
00036  * Since the Linux/doors project was stopped, I doubt that this code will
00037  * ever be useful <kukuk@suse.de>.
00038  */
00039 
00040 #include <stdio.h>
00041 #include <errno.h>
00042 #include <fcntl.h>
00043 #include <signal.h>
00044 #include <unistd.h>
00045 #include <string.h>
00046 #include <rpc/rpc.h>
00047 #include <rpc/auth.h>
00048 #include <sys/wait.h>
00049 #include <sys/param.h>
00050 #include <sys/socket.h>
00051 #include <rpc/key_prot.h>
00052 #include <bits/libc-lock.h>
00053 
00054 #define KEY_TIMEOUT  5      /* per-try timeout in seconds */
00055 #define KEY_NRETRY   12     /* number of retries */
00056 
00057 #define debug(msg)          /* turn off debugging */
00058 
00059 #ifndef SO_PASSCRED
00060 extern int _openchild (const char *command, FILE **fto, FILE **ffrom);
00061 #endif
00062 
00063 static int key_call (u_long, xdrproc_t xdr_arg, char *,
00064                    xdrproc_t xdr_rslt, char *) internal_function;
00065 
00066 static const struct timeval trytimeout = {KEY_TIMEOUT, 0};
00067 static const struct timeval tottimeout = {KEY_TIMEOUT *KEY_NRETRY, 0};
00068 
00069 int
00070 key_setsecret (char *secretkey)
00071 {
00072   keystatus status;
00073 
00074   if (!key_call ((u_long) KEY_SET, (xdrproc_t) INTUSE(xdr_keybuf), secretkey,
00075                (xdrproc_t) INTUSE(xdr_keystatus), (char *) &status))
00076     return -1;
00077   if (status != KEY_SUCCESS)
00078     {
00079       debug ("set status is nonzero");
00080       return -1;
00081     }
00082   return 0;
00083 }
00084 
00085 /* key_secretkey_is_set() returns 1 if the keyserver has a secret key
00086  * stored for the caller's effective uid; it returns 0 otherwise
00087  *
00088  * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
00089  * be using it, because it allows them to get the user's secret key.
00090  */
00091 int
00092 key_secretkey_is_set (void)
00093 {
00094   struct key_netstres kres;
00095 
00096   memset (&kres, 0, sizeof (kres));
00097   if (key_call ((u_long) KEY_NET_GET, (xdrproc_t) INTUSE(xdr_void),
00098               (char *) NULL, (xdrproc_t) INTUSE(xdr_key_netstres),
00099               (char *) &kres) &&
00100       (kres.status == KEY_SUCCESS) &&
00101       (kres.key_netstres_u.knet.st_priv_key[0] != 0))
00102     {
00103       /* avoid leaving secret key in memory */
00104       memset (kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
00105       return 1;
00106     }
00107   return 0;
00108 }
00109 
00110 int
00111 key_encryptsession (char *remotename, des_block *deskey)
00112 {
00113   cryptkeyarg arg;
00114   cryptkeyres res;
00115 
00116   arg.remotename = remotename;
00117   arg.deskey = *deskey;
00118   if (!key_call ((u_long) KEY_ENCRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
00119                (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
00120                (char *) &res))
00121     return -1;
00122 
00123   if (res.status != KEY_SUCCESS)
00124     {
00125       debug ("encrypt status is nonzero");
00126       return -1;
00127     }
00128   *deskey = res.cryptkeyres_u.deskey;
00129   return 0;
00130 }
00131 
00132 int
00133 key_decryptsession (char *remotename, des_block *deskey)
00134 {
00135   cryptkeyarg arg;
00136   cryptkeyres res;
00137 
00138   arg.remotename = remotename;
00139   arg.deskey = *deskey;
00140   if (!key_call ((u_long) KEY_DECRYPT, (xdrproc_t) INTUSE(xdr_cryptkeyarg),
00141                (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
00142                (char *) &res))
00143     return -1;
00144   if (res.status != KEY_SUCCESS)
00145     {
00146       debug ("decrypt status is nonzero");
00147       return -1;
00148     }
00149   *deskey = res.cryptkeyres_u.deskey;
00150   return 0;
00151 }
00152 
00153 int
00154 key_encryptsession_pk (char *remotename, netobj *remotekey,
00155                      des_block *deskey)
00156 {
00157   cryptkeyarg2 arg;
00158   cryptkeyres res;
00159 
00160   arg.remotename = remotename;
00161   arg.remotekey = *remotekey;
00162   arg.deskey = *deskey;
00163   if (!key_call ((u_long) KEY_ENCRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
00164                (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
00165                (char *) &res))
00166     return -1;
00167 
00168   if (res.status != KEY_SUCCESS)
00169     {
00170       debug ("encrypt status is nonzero");
00171       return -1;
00172     }
00173   *deskey = res.cryptkeyres_u.deskey;
00174   return 0;
00175 }
00176 libc_hidden_def (key_encryptsession_pk)
00177 
00178 int
00179 key_decryptsession_pk (char *remotename, netobj *remotekey,
00180                      des_block *deskey)
00181 {
00182   cryptkeyarg2 arg;
00183   cryptkeyres res;
00184 
00185   arg.remotename = remotename;
00186   arg.remotekey = *remotekey;
00187   arg.deskey = *deskey;
00188   if (!key_call ((u_long) KEY_DECRYPT_PK, (xdrproc_t) INTUSE(xdr_cryptkeyarg2),
00189                (char *) &arg, (xdrproc_t) INTUSE(xdr_cryptkeyres),
00190                (char *) &res))
00191     return -1;
00192 
00193   if (res.status != KEY_SUCCESS)
00194     {
00195       debug ("decrypt status is nonzero");
00196       return -1;
00197     }
00198   *deskey = res.cryptkeyres_u.deskey;
00199   return 0;
00200 }
00201 libc_hidden_def (key_decryptsession_pk)
00202 
00203 int
00204 key_gendes (des_block *key)
00205 {
00206   struct sockaddr_in sin;
00207   CLIENT *client;
00208   int socket;
00209   enum clnt_stat stat;
00210 
00211   sin.sin_family = AF_INET;
00212   sin.sin_port = 0;
00213   sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
00214   __bzero (sin.sin_zero, sizeof (sin.sin_zero));
00215   socket = RPC_ANYSOCK;
00216   client = INTUSE(clntudp_bufcreate) (&sin, (u_long) KEY_PROG,
00217                                   (u_long) KEY_VERS, trytimeout, &socket,
00218                                   RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
00219   if (client == NULL)
00220     return -1;
00221 
00222   stat = clnt_call (client, KEY_GEN, (xdrproc_t) INTUSE(xdr_void), NULL,
00223                   (xdrproc_t) INTUSE(xdr_des_block), (caddr_t) key,
00224                   tottimeout);
00225   clnt_destroy (client);
00226   __close (socket);
00227   if (stat != RPC_SUCCESS)
00228     return -1;
00229 
00230   return 0;
00231 }
00232 libc_hidden_def (key_gendes)
00233 
00234 int
00235 key_setnet (struct key_netstarg *arg)
00236 {
00237   keystatus status;
00238 
00239   if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) INTUSE(xdr_key_netstarg),
00240                (char *) arg,(xdrproc_t) INTUSE(xdr_keystatus),
00241                (char *) &status))
00242     return -1;
00243 
00244   if (status != KEY_SUCCESS)
00245     {
00246       debug ("key_setnet status is nonzero");
00247       return -1;
00248     }
00249   return 1;
00250 }
00251 
00252 int
00253 key_get_conv (char *pkey, des_block *deskey)
00254 {
00255   cryptkeyres res;
00256 
00257   if (!key_call ((u_long) KEY_GET_CONV, (xdrproc_t) INTUSE(xdr_keybuf), pkey,
00258                (xdrproc_t) INTUSE(xdr_cryptkeyres), (char *) &res))
00259     return -1;
00260 
00261   if (res.status != KEY_SUCCESS)
00262     {
00263       debug ("get_conv status is nonzero");
00264       return -1;
00265     }
00266   *deskey = res.cryptkeyres_u.deskey;
00267   return 0;
00268 }
00269 
00270 /*
00271  * Hack to allow the keyserver to use AUTH_DES (for authenticated
00272  * NIS+ calls, for example).  The only functions that get called
00273  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
00274  *
00275  * The approach is to have the keyserver fill in pointers to local
00276  * implementations of these functions, and to call those in key_call().
00277  */
00278 
00279 cryptkeyres *(*__key_encryptsession_pk_LOCAL) (uid_t, char *);
00280 cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *);
00281 des_block *(*__key_gendes_LOCAL) (uid_t, char *);
00282 
00283 #ifndef SO_PASSCRED
00284 static int
00285 internal_function
00286 key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg,
00287                  xdrproc_t xdr_rslt, char *rslt)
00288 {
00289   XDR xdrargs;
00290   XDR xdrrslt;
00291   FILE *fargs;
00292   FILE *frslt;
00293   sigset_t oldmask, mask;
00294   union wait status;
00295   int pid;
00296   int success;
00297   uid_t ruid;
00298   uid_t euid;
00299   static const char MESSENGER[] = "/usr/etc/keyenvoy";
00300 
00301   success = 1;
00302   sigemptyset (&mask);
00303   sigaddset (&mask, SIGCHLD);
00304   __sigprocmask (SIG_BLOCK, &mask, &oldmask);
00305 
00306   /*
00307    * We are going to exec a set-uid program which makes our effective uid
00308    * zero, and authenticates us with our real uid. We need to make the
00309    * effective uid be the real uid for the setuid program, and
00310    * the real uid be the effective uid so that we can change things back.
00311    */
00312   euid = __geteuid ();
00313   ruid = __getuid ();
00314   __setreuid (euid, ruid);
00315   pid = _openchild (MESSENGER, &fargs, &frslt);
00316   __setreuid (ruid, euid);
00317   if (pid < 0)
00318     {
00319       debug ("open_streams");
00320       __sigprocmask (SIG_SETMASK, &oldmask, NULL);
00321       return (0);
00322     }
00323   xdrstdio_create (&xdrargs, fargs, XDR_ENCODE);
00324   xdrstdio_create (&xdrrslt, frslt, XDR_DECODE);
00325 
00326   if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg))
00327     {
00328       debug ("xdr args");
00329       success = 0;
00330     }
00331   fclose (fargs);
00332 
00333   if (success && !(*xdr_rslt) (&xdrrslt, rslt))
00334     {
00335       debug ("xdr rslt");
00336       success = 0;
00337     }
00338   fclose(frslt);
00339 
00340  wait_again:
00341   if (__wait4 (pid, &status, 0, NULL) < 0)
00342     {
00343       if (errno == EINTR)
00344        goto wait_again;
00345       debug ("wait4");
00346       if (errno == ECHILD || errno == ESRCH)
00347        perror ("wait");
00348       else
00349        success = 0;
00350     }
00351   else
00352     if (status.w_retcode)
00353       {
00354        debug ("wait4 1");
00355        success = 0;
00356       }
00357   __sigprocmask (SIG_SETMASK, &oldmask, NULL);
00358 
00359   return success;
00360 }
00361 #endif
00362 
00363 struct  key_call_private {
00364   CLIENT  *client;        /* Client handle */
00365   pid_t   pid;            /* process-id at moment of creation */
00366   uid_t   uid;            /* user-id at last authorization */
00367 };
00368 #ifdef _RPC_THREAD_SAFE_
00369 #define key_call_private_main RPC_THREAD_VARIABLE(key_call_private_s)
00370 #else
00371 static struct key_call_private *key_call_private_main;
00372 #endif
00373 __libc_lock_define_initialized (static, keycall_lock)
00374 
00375 /*
00376  * Keep the handle cached.  This call may be made quite often.
00377  */
00378 static CLIENT *
00379 getkeyserv_handle (int vers)
00380 {
00381   struct key_call_private *kcp = key_call_private_main;
00382   struct timeval wait_time;
00383   int fd;
00384   struct sockaddr_un name;
00385   socklen_t namelen = sizeof(struct sockaddr_un);
00386 
00387 #define TOTAL_TIMEOUT   30      /* total timeout talking to keyserver */
00388 #define TOTAL_TRIES     5       /* Number of tries */
00389 
00390   if (kcp == (struct key_call_private *)NULL)
00391     {
00392       kcp = (struct key_call_private *)malloc (sizeof (*kcp));
00393       if (kcp == (struct key_call_private *)NULL)
00394        return (CLIENT *) NULL;
00395 
00396       key_call_private_main = kcp;
00397       kcp->client = NULL;
00398     }
00399 
00400   /* if pid has changed, destroy client and rebuild */
00401   if (kcp->client != NULL && kcp->pid != __getpid ())
00402     {
00403       auth_destroy (kcp->client->cl_auth);
00404       clnt_destroy (kcp->client);
00405       kcp->client = NULL;
00406     }
00407 
00408   if (kcp->client != NULL)
00409     {
00410       /* if other side closed socket, build handle again */
00411       clnt_control (kcp->client, CLGET_FD, (char *)&fd);
00412       if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1)
00413        {
00414          auth_destroy (kcp->client->cl_auth);
00415          clnt_destroy (kcp->client);
00416          kcp->client = NULL;
00417        }
00418     }
00419 
00420   if (kcp->client != NULL)
00421     {
00422       /* if uid has changed, build client handle again */
00423       if (kcp->uid != __geteuid ())
00424        {
00425         kcp->uid = __geteuid ();
00426         auth_destroy (kcp->client->cl_auth);
00427         kcp->client->cl_auth =
00428           INTUSE(authunix_create) ((char *)"", kcp->uid, 0, 0, NULL);
00429         if (kcp->client->cl_auth == NULL)
00430           {
00431             clnt_destroy (kcp->client);
00432             kcp->client = NULL;
00433             return ((CLIENT *) NULL);
00434           }
00435        }
00436       /* Change the version number to the new one */
00437       clnt_control (kcp->client, CLSET_VERS, (void *)&vers);
00438       return kcp->client;
00439     }
00440 
00441   if ((kcp->client == (CLIENT *) NULL))
00442     /* Use the AF_UNIX transport */
00443     kcp->client = INTUSE(clnt_create) ("/var/run/keyservsock", KEY_PROG, vers,
00444                                    "unix");
00445 
00446   if (kcp->client == (CLIENT *) NULL)
00447     return (CLIENT *) NULL;
00448 
00449   kcp->uid = __geteuid ();
00450   kcp->pid = __getpid ();
00451   kcp->client->cl_auth = INTUSE(authunix_create) ((char *)"", kcp->uid, 0, 0,
00452                                             NULL);
00453   if (kcp->client->cl_auth == NULL)
00454     {
00455       clnt_destroy (kcp->client);
00456       kcp->client = NULL;
00457       return (CLIENT *) NULL;
00458     }
00459 
00460   wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
00461   wait_time.tv_usec = 0;
00462   clnt_control (kcp->client, CLSET_RETRY_TIMEOUT,
00463               (char *)&wait_time);
00464   if (clnt_control (kcp->client, CLGET_FD, (char *)&fd))
00465     __fcntl (fd, F_SETFD, FD_CLOEXEC);  /* make it "close on exec" */
00466 
00467   return kcp->client;
00468 }
00469 
00470 /* returns  0 on failure, 1 on success */
00471 static int
00472 internal_function
00473 key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg,
00474                xdrproc_t xdr_rslt, char *rslt)
00475 {
00476   CLIENT *clnt;
00477   struct timeval wait_time;
00478   int result = 0;
00479 
00480   __libc_lock_lock (keycall_lock);
00481   if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
00482       (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
00483       (proc == KEY_GET_CONV))
00484     clnt = getkeyserv_handle(2); /* talk to version 2 */
00485   else
00486     clnt = getkeyserv_handle(1); /* talk to version 1 */
00487 
00488   if (clnt != NULL)
00489     {
00490       wait_time.tv_sec = TOTAL_TIMEOUT;
00491       wait_time.tv_usec = 0;
00492 
00493       if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
00494                    wait_time) == RPC_SUCCESS)
00495        result = 1;
00496     }
00497 
00498   __libc_lock_unlock (keycall_lock);
00499 
00500   return result;
00501 }
00502 
00503 
00504 /* returns 0 on failure, 1 on success */
00505 static int
00506 internal_function
00507 key_call (u_long proc, xdrproc_t xdr_arg, char *arg,
00508          xdrproc_t xdr_rslt, char *rslt)
00509 {
00510 #ifndef SO_PASSCRED
00511   static int use_keyenvoy;
00512 #endif
00513 
00514   if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL)
00515     {
00516       cryptkeyres *res;
00517       res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg);
00518       *(cryptkeyres *) rslt = *res;
00519       return 1;
00520     }
00521   else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL)
00522     {
00523       cryptkeyres *res;
00524       res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg);
00525       *(cryptkeyres *) rslt = *res;
00526       return 1;
00527     }
00528   else if (proc == KEY_GEN && __key_gendes_LOCAL)
00529     {
00530       des_block *res;
00531       res = (*__key_gendes_LOCAL) (__geteuid (), 0);
00532       *(des_block *) rslt = *res;
00533       return 1;
00534     }
00535 
00536 #ifdef SO_PASSCRED
00537   return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt);
00538 #else
00539   if (!use_keyenvoy)
00540     {
00541       if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt))
00542        return 1;
00543       use_keyenvoy = 1;
00544     }
00545   return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt);
00546 #endif
00547 }
00548 
00549 #ifdef _RPC_THREAD_SAFE_
00550 void
00551 __rpc_thread_key_cleanup (void)
00552 {
00553        struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s);
00554 
00555        if (kcp) {
00556               if (kcp->client) {
00557                      if (kcp->client->cl_auth)
00558                             auth_destroy (kcp->client->cl_auth);
00559                      clnt_destroy(kcp->client);
00560               }
00561               free (kcp);
00562        }
00563 }
00564 #endif /* _RPC_THREAD_SAFE_ */