Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libc / rpc / 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) 1986-1991 by Sun Microsystems Inc. 
31  *
32  * $FreeBSD: src/lib/libc/rpc/key_call.c,v 1.3 2000/01/27 23:06:39 jasone Exp $
33  */
34
35 #ident  "@(#)key_call.c 1.25    94/04/24 SMI"
36
37 /*
38  * key_call.c, Interface to keyserver
39  *
40  * setsecretkey(key) - set your secret key
41  * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent
42  * decryptsessionkey(agent, deskey) - decrypt ditto
43  * gendeskey(deskey) - generate a secure des key
44  */
45
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 <sys/utsname.h>
56 #include <stdlib.h>
57 #include <signal.h>
58 #include <sys/wait.h>
59 #include <sys/fcntl.h>
60
61
62 #define KEY_TIMEOUT     5       /* per-try timeout in seconds */
63 #define KEY_NRETRY      12      /* number of retries */
64
65 #ifdef DEBUG
66 #define debug(msg)      (void) fprintf(stderr, "%s\n", msg);
67 #else
68 #define debug(msg)
69 #endif /* DEBUG */
70
71 /*
72  * Hack to allow the keyserver to use AUTH_DES (for authenticated
73  * NIS+ calls, for example).  The only functions that get called
74  * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
75  *
76  * The approach is to have the keyserver fill in pointers to local
77  * implementations of these functions, and to call those in key_call().
78  */
79
80 cryptkeyres *(*__key_encryptsession_pk_LOCAL)() = 0;
81 cryptkeyres *(*__key_decryptsession_pk_LOCAL)() = 0;
82 des_block *(*__key_gendes_LOCAL)() = 0;
83
84 static int key_call __P(( u_long, xdrproc_t, char *, xdrproc_t, char * ));
85
86 int
87 key_setsecret(secretkey)
88         const char *secretkey;
89 {
90         keystatus status;
91
92         if (!key_call((u_long) KEY_SET, xdr_keybuf, (char *) secretkey,
93                         xdr_keystatus, (char *)&status)) {
94                 return (-1);
95         }
96         if (status != KEY_SUCCESS) {
97                 debug("set status is nonzero");
98                 return (-1);
99         }
100         return (0);
101 }
102
103
104 /* key_secretkey_is_set() returns 1 if the keyserver has a secret key
105  * stored for the caller's effective uid; it returns 0 otherwise
106  *
107  * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
108  * be using it, because it allows them to get the user's secret key.
109  */
110
111 int
112 key_secretkey_is_set(void)
113 {
114         struct key_netstres     kres;
115
116         memset((void*)&kres, 0, sizeof (kres));
117         if (key_call((u_long) KEY_NET_GET, xdr_void, (char *)NULL,
118                         xdr_key_netstres, (char *) &kres) &&
119             (kres.status == KEY_SUCCESS) &&
120             (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
121                 /* avoid leaving secret key in memory */
122                 memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
123                 return (1);
124         }
125         return (0);
126 }
127
128 int
129 key_encryptsession_pk(remotename, remotekey, deskey)
130         char *remotename;
131         netobj *remotekey;
132         des_block *deskey;
133 {
134         cryptkeyarg2 arg;
135         cryptkeyres res;
136
137         arg.remotename = remotename;
138         arg.remotekey = *remotekey;
139         arg.deskey = *deskey;
140         if (!key_call((u_long)KEY_ENCRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
141                         xdr_cryptkeyres, (char *)&res)) {
142                 return (-1);
143         }
144         if (res.status != KEY_SUCCESS) {
145                 debug("encrypt status is nonzero");
146                 return (-1);
147         }
148         *deskey = res.cryptkeyres_u.deskey;
149         return (0);
150 }
151
152 int
153 key_decryptsession_pk(remotename, remotekey, deskey)
154         char *remotename;
155         netobj *remotekey;
156         des_block *deskey;
157 {
158         cryptkeyarg2 arg;
159         cryptkeyres res;
160
161         arg.remotename = remotename;
162         arg.remotekey = *remotekey;
163         arg.deskey = *deskey;
164         if (!key_call((u_long)KEY_DECRYPT_PK, xdr_cryptkeyarg2, (char *)&arg,
165                         xdr_cryptkeyres, (char *)&res)) {
166                 return (-1);
167         }
168         if (res.status != KEY_SUCCESS) {
169                 debug("decrypt status is nonzero");
170                 return (-1);
171         }
172         *deskey = res.cryptkeyres_u.deskey;
173         return (0);
174 }
175
176 int
177 key_encryptsession(remotename, deskey)
178         const char *remotename;
179         des_block *deskey;
180 {
181         cryptkeyarg arg;
182         cryptkeyres res;
183
184         arg.remotename = (char *) remotename;
185         arg.deskey = *deskey;
186         if (!key_call((u_long)KEY_ENCRYPT, xdr_cryptkeyarg, (char *)&arg,
187                         xdr_cryptkeyres, (char *)&res)) {
188                 return (-1);
189         }
190         if (res.status != KEY_SUCCESS) {
191                 debug("encrypt status is nonzero");
192                 return (-1);
193         }
194         *deskey = res.cryptkeyres_u.deskey;
195         return (0);
196 }
197
198 int
199 key_decryptsession(remotename, deskey)
200         const char *remotename;
201         des_block *deskey;
202 {
203         cryptkeyarg arg;
204         cryptkeyres res;
205
206         arg.remotename = (char *) remotename;
207         arg.deskey = *deskey;
208         if (!key_call((u_long)KEY_DECRYPT, xdr_cryptkeyarg, (char *)&arg,
209                         xdr_cryptkeyres, (char *)&res)) {
210                 return (-1);
211         }
212         if (res.status != KEY_SUCCESS) {
213                 debug("decrypt status is nonzero");
214                 return (-1);
215         }
216         *deskey = res.cryptkeyres_u.deskey;
217         return (0);
218 }
219
220 int
221 key_gendes(key)
222         des_block *key;
223 {
224         if (!key_call((u_long)KEY_GEN, xdr_void, (char *)NULL,
225                         xdr_des_block, (char *)key)) {
226                 return (-1);
227         }
228         return (0);
229 }
230
231 int
232 key_setnet(arg)
233 struct netstarg *arg;
234 {
235         keystatus status;
236
237
238         if (!key_call((u_long) KEY_NET_PUT, xdr_key_netstarg, (char *) arg,
239                 xdr_keystatus, (char *) &status)){
240                 return (-1);
241         }
242
243         if (status != KEY_SUCCESS) {
244                 debug("key_setnet status is nonzero");
245                 return (-1);
246         }
247         return (1);
248 }
249
250
251 int
252 key_get_conv(pkey, deskey)
253         char *pkey;
254         des_block *deskey;
255 {
256         cryptkeyres res;
257
258         if (!key_call((u_long) KEY_GET_CONV, xdr_keybuf, pkey,
259                 xdr_cryptkeyres, (char *)&res)) {
260                 return (-1);
261         }
262         if (res.status != KEY_SUCCESS) {
263                 debug("get_conv status is nonzero");
264                 return (-1);
265         }
266         *deskey = res.cryptkeyres_u.deskey;
267         return (0);
268 }
269
270 struct  key_call_private {
271         CLIENT  *client;        /* Client handle */
272         pid_t   pid;            /* process-id at moment of creation */
273         uid_t   uid;            /* user-id at last authorization */
274 };
275 static struct key_call_private *key_call_private_main = NULL;
276
277 #ifdef foo
278 static void
279 key_call_destroy(void *vp)
280 {
281         register struct key_call_private *kcp = (struct key_call_private *)vp;
282
283         if (kcp) {
284                 if (kcp->client)
285                         clnt_destroy(kcp->client);
286                 free(kcp);
287         }
288 }
289 #endif
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         struct key_call_private *kcp = key_call_private_main;
299         struct timeval wait_time;
300         int fd;
301         struct sockaddr_un name;
302         int namelen = sizeof(struct sockaddr_un);
303
304 #define TOTAL_TIMEOUT   30      /* total timeout talking to keyserver */
305 #define TOTAL_TRIES     5       /* Number of tries */
306
307         if (kcp == (struct key_call_private *)NULL) {
308                 kcp = (struct key_call_private *)malloc(sizeof (*kcp));
309                 if (kcp == (struct key_call_private *)NULL) {
310                         return ((CLIENT *) NULL);
311                 }
312                 key_call_private_main = kcp;
313                 kcp->client = NULL;
314         }
315
316         /* if pid has changed, destroy client and rebuild */
317         if (kcp->client != NULL && kcp->pid != getpid()) {
318                 clnt_destroy(kcp->client);
319                 kcp->client = NULL;
320         }
321
322         if (kcp->client != NULL) {
323                 /* if other side closed socket, build handle again */
324                 clnt_control(kcp->client, CLGET_FD, (char *)&fd);
325                 if (getpeername(fd,(struct sockaddr *)&name,&namelen) == -1) {
326                         auth_destroy(kcp->client->cl_auth);
327                         clnt_destroy(kcp->client);
328                         kcp->client = NULL;
329                 }
330         }
331
332         if (kcp->client != NULL) {
333                 /* if uid has changed, build client handle again */
334                 if (kcp->uid != geteuid()) {
335                         kcp->uid = geteuid();
336                         auth_destroy(kcp->client->cl_auth);
337                         kcp->client->cl_auth =
338                                 authsys_create("", kcp->uid, 0, 0, NULL);
339                         if (kcp->client->cl_auth == NULL) {
340                                 clnt_destroy(kcp->client);
341                                 kcp->client = NULL;
342                                 return ((CLIENT *) NULL);
343                         }
344                 }
345                 /* Change the version number to the new one */
346                 clnt_control(kcp->client, CLSET_VERS, (void *)&vers);
347                 return (kcp->client);
348         }
349
350         if ((kcp->client == (CLIENT *) NULL))
351                 /* Use the AF_UNIX transport */
352                 kcp->client = clnt_create("/var/run/keyservsock", KEY_PROG,
353                                                         vers, "unix");
354
355         if (kcp->client == (CLIENT *) NULL) {
356                 return ((CLIENT *) NULL);
357         }
358         kcp->uid = geteuid();
359         kcp->pid = getpid();
360         kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL);
361         if (kcp->client->cl_auth == NULL) {
362                 clnt_destroy(kcp->client);
363                 kcp->client = NULL;
364                 return ((CLIENT *) NULL);
365         }
366
367         wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES;
368         wait_time.tv_usec = 0;
369         (void) clnt_control(kcp->client, CLSET_RETRY_TIMEOUT,
370                 (char *)&wait_time);
371         if (clnt_control(kcp->client, CLGET_FD, (char *)&fd))
372                 _fcntl(fd, F_SETFD, 1); /* make it "close on exec" */
373
374         return (kcp->client);
375 }
376
377 /* returns  0 on failure, 1 on success */
378
379 static int
380 key_call(proc, xdr_arg, arg, xdr_rslt, rslt)
381         u_long proc;
382         xdrproc_t xdr_arg;
383         char *arg;
384         xdrproc_t xdr_rslt;
385         char *rslt;
386 {
387         CLIENT *clnt;
388         struct timeval wait_time;
389
390         if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) {
391                 cryptkeyres *res;
392                 res = (*__key_encryptsession_pk_LOCAL)(geteuid(), arg);
393                 *(cryptkeyres*)rslt = *res;
394                 return (1);
395         } else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) {
396                 cryptkeyres *res;
397                 res = (*__key_decryptsession_pk_LOCAL)(geteuid(), arg);
398                 *(cryptkeyres*)rslt = *res;
399                 return (1);
400         } else if (proc == KEY_GEN && __key_gendes_LOCAL) {
401                 des_block *res;
402                 res = (*__key_gendes_LOCAL)(geteuid(), 0);
403                 *(des_block*)rslt = *res;
404                 return (1);
405         }
406
407         if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) ||
408             (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) ||
409             (proc == KEY_GET_CONV))
410                 clnt = getkeyserv_handle(2); /* talk to version 2 */
411         else
412                 clnt = getkeyserv_handle(1); /* talk to version 1 */
413
414         if (clnt == NULL) {
415                 return (0);
416         }
417
418         wait_time.tv_sec = TOTAL_TIMEOUT;
419         wait_time.tv_usec = 0;
420
421         if (clnt_call(clnt, proc, xdr_arg, arg, xdr_rslt, rslt,
422                 wait_time) == RPC_SUCCESS) {
423                 return (1);
424         } else {
425                 return (0);
426         }
427 }