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