Imported Upstream version 0.2.5
[platform/upstream/libtirpc.git] / src / key_call.c
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
33 #include <sys/cdefs.h>
34
35 /*
36  * key_call.c, Interface to keyserver
37  *
38  * setsecretkey(key) - set your secret key
39  * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent
40  * decryptsessionkey(agent, deskey) - decrypt ditto
41  * gendeskey(deskey) - generate a secure des key
42  */
43  
44 #include <pthread.h>
45 #include <reentrant.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <errno.h>
50 #include <rpc/rpc.h>
51 #include <rpc/auth.h>
52 #include <rpc/auth_unix.h>
53 #include <rpc/key_prot.h>
54 #include <string.h>
55 #include <netconfig.h>
56 #include <sys/utsname.h>
57 #include <stdlib.h>
58 #include <signal.h>
59 #include <sys/wait.h>
60 #include <sys/fcntl.h>
61
62 #include "dump.h"
63
64 #define KEY_TIMEOUT     5       /* per-try timeout in seconds */
65 #define KEY_NRETRY      12      /* number of retries */
66
67 /*
68  * Hack to allow the keyserver to use AUTH_DES (for authenticated
69  * NIS+ calls, for example).  The only functions that get called
70  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
71  *
72  * The approach is to have the keyserver fill in pointers to local
73  * implementations of these functions, and to call those in key_call().
74  */
75
76 cryptkeyres *(*__key_encryptsession_pk_LOCAL)() = 0;
77 cryptkeyres *(*__key_decryptsession_pk_LOCAL)() = 0;
78 des_block *(*__key_gendes_LOCAL)() = 0;
79
80 static int key_call( u_long, xdrproc_t, void *, xdrproc_t, void *);
81
82 int
83 key_setsecret(secretkey)
84         const char *secretkey;
85 {
86         keystatus status;
87
88         if (!key_call((u_long) KEY_SET, (xdrproc_t)xdr_keybuf,
89                         (void *)secretkey,
90                         (xdrproc_t)xdr_keystatus, &status)) {
91                 return (-1);
92         }
93         if (status != KEY_SUCCESS) {
94                 LIBTIRPC_DEBUG(1, ("key_setsecret: set status is nonzero"));
95                 return (-1);
96         }
97         return (0);
98 }
99
100
101 /* key_secretkey_is_set() returns 1 if the keyserver has a secret key
102  * stored for the caller's effective uid; it returns 0 otherwise
103  *
104  * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
105  * be using it, because it allows them to get the user's secret key.
106  */
107
108 int
109 key_secretkey_is_set(void)
110 {
111         struct key_netstres     kres;
112
113         memset((void*)&kres, 0, sizeof (kres));
114         if (key_call((u_long) KEY_NET_GET, (xdrproc_t)xdr_void, NULL,
115                         (xdrproc_t)xdr_key_netstres, &kres) &&
116             (kres.status == KEY_SUCCESS) &&
117             (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
118                 /* avoid leaving secret key in memory */
119                 memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
120                 return (1);
121         }
122         return (0);
123 }
124
125 int
126 key_encryptsession_pk(remotename, remotekey, deskey)
127         char *remotename;
128         netobj *remotekey;
129         des_block *deskey;
130 {
131         cryptkeyarg2 arg;
132         cryptkeyres res;
133
134         arg.remotename = remotename;
135         arg.remotekey = *remotekey;
136         arg.deskey = *deskey;
137         if (!key_call((u_long)KEY_ENCRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
138                         (xdrproc_t)xdr_cryptkeyres, &res)) {
139                 return (-1);
140         }
141         if (res.status != KEY_SUCCESS) {
142                 LIBTIRPC_DEBUG(1, ("key_encryptsession_pk: encrypt status is nonzero"));
143                 return (-1);
144         }
145         *deskey = res.cryptkeyres_u.deskey;
146         return (0);
147 }
148
149 int
150 key_decryptsession_pk(remotename, remotekey, deskey)
151         char *remotename;
152         netobj *remotekey;
153         des_block *deskey;
154 {
155         cryptkeyarg2 arg;
156         cryptkeyres res;
157
158         arg.remotename = remotename;
159         arg.remotekey = *remotekey;
160         arg.deskey = *deskey;
161         if (!key_call((u_long)KEY_DECRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
162                         (xdrproc_t)xdr_cryptkeyres, &res)) {
163                 return (-1);
164         }
165         if (res.status != KEY_SUCCESS) {
166                 LIBTIRPC_DEBUG(1, ("key_decryptsession_pk: decrypt status is nonzero"));
167                 return (-1);
168         }
169         *deskey = res.cryptkeyres_u.deskey;
170         return (0);
171 }
172
173 int
174 key_encryptsession(remotename, deskey)
175         const char *remotename;
176         des_block *deskey;
177 {
178         cryptkeyarg arg;
179         cryptkeyres res;
180
181         arg.remotename = (char *) remotename;
182         arg.deskey = *deskey;
183         if (!key_call((u_long)KEY_ENCRYPT, (xdrproc_t)xdr_cryptkeyarg, &arg,
184                         (xdrproc_t)xdr_cryptkeyres, &res)) {
185                 return (-1);
186         }
187         if (res.status != KEY_SUCCESS) {
188                 LIBTIRPC_DEBUG(1, ("key_encryptsession: encrypt status is nonzero"));
189                 return (-1);
190         }
191         *deskey = res.cryptkeyres_u.deskey;
192         return (0);
193 }
194
195 int
196 key_decryptsession(remotename, deskey)
197         const char *remotename;
198         des_block *deskey;
199 {
200         cryptkeyarg arg;
201         cryptkeyres res;
202
203         arg.remotename = (char *) remotename;
204         arg.deskey = *deskey;
205         if (!key_call((u_long)KEY_DECRYPT, (xdrproc_t)xdr_cryptkeyarg, &arg,
206                         (xdrproc_t)xdr_cryptkeyres, &res)) {
207                 return (-1);
208         }
209         if (res.status != KEY_SUCCESS) {
210                 LIBTIRPC_DEBUG(1, ("key_decryptsession: decrypt status is nonzero"));
211                 return (-1);
212         }
213         *deskey = res.cryptkeyres_u.deskey;
214         return (0);
215 }
216
217 int
218 key_gendes(key)
219         des_block *key;
220 {
221         if (!key_call((u_long)KEY_GEN, (xdrproc_t)xdr_void, NULL,
222                         (xdrproc_t)xdr_des_block, key)) {
223                 return (-1);
224         }
225         return (0);
226 }
227
228 int
229 key_setnet(arg)
230 struct key_netstarg *arg;
231 {
232         keystatus status;
233
234
235         if (!key_call((u_long) KEY_NET_PUT, (xdrproc_t)xdr_key_netstarg, arg,
236                         (xdrproc_t)xdr_keystatus, &status)){
237                 return (-1);
238         }
239
240         if (status != KEY_SUCCESS) {
241                 LIBTIRPC_DEBUG(1, ("key_setnet: key_setnet status is nonzero"));
242                 return (-1);
243         }
244         return (1);
245 }
246
247
248 int
249 key_get_conv(pkey, deskey)
250         char *pkey;
251         des_block *deskey;
252 {
253         cryptkeyres res;
254
255         if (!key_call((u_long) KEY_GET_CONV, (xdrproc_t)xdr_keybuf, pkey,
256                         (xdrproc_t)xdr_cryptkeyres, &res)) {
257                 return (-1);
258         }
259         if (res.status != KEY_SUCCESS) {
260                 LIBTIRPC_DEBUG(1, ("key_get_conv: get_conv status is nonzero"));
261                 return (-1);
262         }
263         *deskey = res.cryptkeyres_u.deskey;
264         return (0);
265 }
266
267 struct  key_call_private {
268         CLIENT  *client;        /* Client handle */
269         pid_t   pid;            /* process-id at moment of creation */
270         uid_t   uid;            /* user-id at last authorization */
271 };
272 static struct key_call_private *key_call_private_main = NULL;
273
274 static void
275 key_call_destroy(void *vp)
276 {
277         struct key_call_private *kcp = (struct key_call_private *)vp;
278
279         if (kcp) {
280                 if (kcp->client)
281                         clnt_destroy(kcp->client);
282                 free(kcp);
283         }
284 }
285
286 /*
287  * Keep the handle cached.  This call may be made quite often.
288  */
289 static CLIENT *
290 getkeyserv_handle(vers)
291 int     vers;
292 {
293         void *localhandle;
294         struct netconfig *nconf;
295         struct netconfig *tpconf;
296         struct key_call_private *kcp = key_call_private_main;
297         struct timeval wait_time;
298         struct utsname u;
299         int fd;
300         extern thread_key_t key_call_key;
301         extern mutex_t tsd_lock;
302
303 #define TOTAL_TIMEOUT   30      /* total timeout talking to keyserver */
304 #define TOTAL_TRIES     5       /* Number of tries */
305
306         if (key_call_key == -1) {
307                 mutex_lock(&tsd_lock);
308                 if (key_call_key == -1)
309                         thr_keycreate(&key_call_key, key_call_destroy);
310                 mutex_unlock(&tsd_lock);
311         }
312         kcp = (struct key_call_private *)thr_getspecific(key_call_key);
313         if (kcp == (struct key_call_private *)NULL) {
314                 kcp = (struct key_call_private *)malloc(sizeof (*kcp));
315                 if (kcp == (struct key_call_private *)NULL) {
316                         return ((CLIENT *) NULL);
317                 }
318                 thr_setspecific(key_call_key, (void *) kcp);
319                 kcp->client = NULL;
320         }
321
322         /* if pid has changed, destroy client and rebuild */
323         if (kcp->client != NULL && kcp->pid != getpid()) {
324                 clnt_destroy(kcp->client);
325                 kcp->client = NULL;
326         }
327
328         if (kcp->client != NULL) {
329                 /* if uid has changed, build client handle again */
330                 if (kcp->uid != geteuid()) {
331                         kcp->uid = geteuid();
332                         auth_destroy(kcp->client->cl_auth);
333                         kcp->client->cl_auth =
334                                 authsys_create("", kcp->uid, 0, 0, NULL);
335                         if (kcp->client->cl_auth == NULL) {
336                                 clnt_destroy(kcp->client);
337                                 kcp->client = NULL;
338                                 return ((CLIENT *) NULL);
339                         }
340                 }
341                 /* Change the version number to the new one */
342                 clnt_control(kcp->client, CLSET_VERS, (void *)&vers);
343                 return (kcp->client);
344         }
345         if (!(localhandle = setnetconfig())) {
346                 return ((CLIENT *) NULL);
347         }
348         tpconf = NULL;
349         if (uname(&u) == -1) {
350                 endnetconfig(localhandle);
351                 return ((CLIENT *) NULL);
352         }
353         while ((nconf = getnetconfig(localhandle)) != NULL) {
354                 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
355                         /*
356                          * We use COTS_ORD here so that the caller can
357                          * find out immediately if the server is dead.
358                          */
359                         if (nconf->nc_semantics == NC_TPI_COTS_ORD) {
360                                 kcp->client = clnt_tp_create(u.nodename,
361                                         KEY_PROG, vers, nconf);
362                                 if (kcp->client)
363                                         break;
364                         } else {
365                                 tpconf = nconf;
366                         }
367                 }
368         }
369         if ((kcp->client == (CLIENT *) NULL) && (tpconf))
370                 /* Now, try the CLTS or COTS loopback transport */
371                 kcp->client = clnt_tp_create(u.nodename,
372                         KEY_PROG, vers, tpconf);
373         endnetconfig(localhandle);
374
375         if (kcp->client == (CLIENT *) NULL) {
376                 return ((CLIENT *) NULL);
377         }
378         kcp->uid = geteuid();
379         kcp->pid = getpid();
380         kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL);
381         if (kcp->client->cl_auth == NULL) {
382                 clnt_destroy(kcp->client);
383                 kcp->client = NULL;
384                 return ((CLIENT *) NULL);
385         }
386
387         wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
388         wait_time.tv_usec = 0;
389         (void) clnt_control(kcp->client, CLSET_RETRY_TIMEOUT,
390                 (char *)&wait_time);
391         if (clnt_control(kcp->client, CLGET_FD, (char *)&fd))
392                 fcntl(fd, F_SETFD, 1);  /* make it "close on exec" */
393
394         return (kcp->client);
395 }
396
397 /* returns  0 on failure, 1 on success */
398
399 static int
400 key_call(proc, xdr_arg, arg, xdr_rslt, rslt)
401         u_long proc;
402         xdrproc_t xdr_arg;
403         void *arg;
404         xdrproc_t xdr_rslt;
405         void *rslt;
406 {
407         CLIENT *clnt;
408         struct timeval wait_time;
409
410         if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
411                 cryptkeyres *res;
412                 res = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg);
413                 *(cryptkeyres*)rslt = *res;
414                 return (1);
415         } else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
416                 cryptkeyres *res;
417                 res = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg);
418                 *(cryptkeyres*)rslt = *res;
419                 return (1);
420         } else if (proc == KEY_GEN && __key_gendes_LOCAL) {
421                 des_block *res;
422                 res = (*__key_gendes_LOCAL)(geteuid(), 0);
423                 *(des_block*)rslt = *res;
424                 return (1);
425         }
426
427         if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
428             (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
429             (proc == KEY_GET_CONV))
430                 clnt = getkeyserv_handle(2); /* talk to version 2 */
431         else
432                 clnt = getkeyserv_handle(1); /* talk to version 1 */
433
434         if (clnt == NULL) {
435                 return (0);
436         }
437
438         wait_time.tv_sec = TOTAL_TIMEOUT;
439         wait_time.tv_usec = 0;
440
441         if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
442                 wait_time) == RPC_SUCCESS) {
443                 return (1);
444         } else {
445                 return (0);
446         }
447 }