1 | /* |
2 | * Copyright (c) 2010, Oracle America, Inc. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions are |
6 | * met: |
7 | * |
8 | * * Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * * Redistributions in binary form must reproduce the above |
11 | * copyright notice, this list of conditions and the following |
12 | * disclaimer in the documentation and/or other materials |
13 | * provided with the distribution. |
14 | * * Neither the name of the "Oracle America, Inc." nor the names of its |
15 | * contributors may be used to endorse or promote products derived |
16 | * from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
22 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
25 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
27 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
28 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | /* |
32 | * The original source is from the RPCSRC 4.0 package from Sun Microsystems. |
33 | * The Interface to keyserver protocol 2, RPC over AF_UNIX and Linux/doors |
34 | * was added by Thorsten Kukuk <kukuk@suse.de> |
35 | * Since the Linux/doors project was stopped, I doubt that this code will |
36 | * ever be useful <kukuk@suse.de>. |
37 | */ |
38 | |
39 | #include <stdio.h> |
40 | #include <errno.h> |
41 | #include <fcntl.h> |
42 | #include <signal.h> |
43 | #include <unistd.h> |
44 | #include <string.h> |
45 | #include <rpc/rpc.h> |
46 | #include <rpc/auth.h> |
47 | #include <sys/wait.h> |
48 | #include <sys/param.h> |
49 | #include <sys/socket.h> |
50 | #include <rpc/key_prot.h> |
51 | #include <libc-lock.h> |
52 | #include <shlib-compat.h> |
53 | |
54 | #define KEY_TIMEOUT 5 /* per-try timeout in seconds */ |
55 | #define KEY_NRETRY 12 /* number of retries */ |
56 | |
57 | #define debug(msg) /* turn off debugging */ |
58 | |
59 | #ifndef SO_PASSCRED |
60 | extern int _openchild (const char *command, FILE **fto, FILE **ffrom); |
61 | #endif |
62 | |
63 | static int key_call (u_long, xdrproc_t xdr_arg, char *, |
64 | xdrproc_t xdr_rslt, char *); |
65 | |
66 | static const struct timeval trytimeout = {KEY_TIMEOUT, 0}; |
67 | static const struct timeval tottimeout = {KEY_TIMEOUT *KEY_NRETRY, 0}; |
68 | |
69 | int |
70 | key_setsecret (char *secretkey) |
71 | { |
72 | keystatus status; |
73 | |
74 | if (!key_call ((u_long) KEY_SET, (xdrproc_t) xdr_keybuf, secretkey, |
75 | (xdrproc_t) xdr_keystatus, (char *) &status)) |
76 | return -1; |
77 | if (status != KEY_SUCCESS) |
78 | { |
79 | debug ("set status is nonzero" ); |
80 | return -1; |
81 | } |
82 | return 0; |
83 | } |
84 | libc_hidden_nolink_sunrpc (key_setsecret, GLIBC_2_1) |
85 | |
86 | /* key_secretkey_is_set() returns 1 if the keyserver has a secret key |
87 | * stored for the caller's effective uid; it returns 0 otherwise |
88 | * |
89 | * N.B.: The KEY_NET_GET key call is undocumented. Applications shouldn't |
90 | * be using it, because it allows them to get the user's secret key. |
91 | */ |
92 | int |
93 | key_secretkey_is_set (void) |
94 | { |
95 | struct key_netstres kres; |
96 | |
97 | memset (&kres, 0, sizeof (kres)); |
98 | if (key_call ((u_long) KEY_NET_GET, (xdrproc_t) xdr_void, |
99 | (char *) NULL, (xdrproc_t) xdr_key_netstres, |
100 | (char *) &kres) && |
101 | (kres.status == KEY_SUCCESS) && |
102 | (kres.key_netstres_u.knet.st_priv_key[0] != 0)) |
103 | { |
104 | /* avoid leaving secret key in memory */ |
105 | memset (kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES); |
106 | return 1; |
107 | } |
108 | return 0; |
109 | } |
110 | #ifdef EXPORT_RPC_SYMBOLS |
111 | libc_hidden_def (key_secretkey_is_set) |
112 | #else |
113 | libc_hidden_nolink_sunrpc (key_secretkey_is_set, GLIBC_2_1) |
114 | #endif |
115 | |
116 | int |
117 | key_encryptsession (char *remotename, des_block *deskey) |
118 | { |
119 | cryptkeyarg arg; |
120 | cryptkeyres res; |
121 | |
122 | arg.remotename = remotename; |
123 | arg.deskey = *deskey; |
124 | if (!key_call ((u_long) KEY_ENCRYPT, (xdrproc_t) xdr_cryptkeyarg, |
125 | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, |
126 | (char *) &res)) |
127 | return -1; |
128 | |
129 | if (res.status != KEY_SUCCESS) |
130 | { |
131 | debug ("encrypt status is nonzero" ); |
132 | return -1; |
133 | } |
134 | *deskey = res.cryptkeyres_u.deskey; |
135 | return 0; |
136 | } |
137 | libc_hidden_nolink_sunrpc (key_encryptsession, GLIBC_2_1) |
138 | |
139 | int |
140 | key_decryptsession (char *remotename, des_block *deskey) |
141 | { |
142 | cryptkeyarg arg; |
143 | cryptkeyres res; |
144 | |
145 | arg.remotename = remotename; |
146 | arg.deskey = *deskey; |
147 | if (!key_call ((u_long) KEY_DECRYPT, (xdrproc_t) xdr_cryptkeyarg, |
148 | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, |
149 | (char *) &res)) |
150 | return -1; |
151 | if (res.status != KEY_SUCCESS) |
152 | { |
153 | debug ("decrypt status is nonzero" ); |
154 | return -1; |
155 | } |
156 | *deskey = res.cryptkeyres_u.deskey; |
157 | return 0; |
158 | } |
159 | libc_hidden_nolink_sunrpc (key_decryptsession, GLIBC_2_1) |
160 | |
161 | int |
162 | key_encryptsession_pk (char *remotename, netobj *remotekey, |
163 | des_block *deskey) |
164 | { |
165 | cryptkeyarg2 arg; |
166 | cryptkeyres res; |
167 | |
168 | arg.remotename = remotename; |
169 | arg.remotekey = *remotekey; |
170 | arg.deskey = *deskey; |
171 | if (!key_call ((u_long) KEY_ENCRYPT_PK, (xdrproc_t) xdr_cryptkeyarg2, |
172 | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, |
173 | (char *) &res)) |
174 | return -1; |
175 | |
176 | if (res.status != KEY_SUCCESS) |
177 | { |
178 | debug ("encrypt status is nonzero" ); |
179 | return -1; |
180 | } |
181 | *deskey = res.cryptkeyres_u.deskey; |
182 | return 0; |
183 | } |
184 | libc_hidden_nolink_sunrpc (key_encryptsession_pk, GLIBC_2_1) |
185 | |
186 | int |
187 | key_decryptsession_pk (char *remotename, netobj *remotekey, |
188 | des_block *deskey) |
189 | { |
190 | cryptkeyarg2 arg; |
191 | cryptkeyres res; |
192 | |
193 | arg.remotename = remotename; |
194 | arg.remotekey = *remotekey; |
195 | arg.deskey = *deskey; |
196 | if (!key_call ((u_long) KEY_DECRYPT_PK, (xdrproc_t) xdr_cryptkeyarg2, |
197 | (char *) &arg, (xdrproc_t) xdr_cryptkeyres, |
198 | (char *) &res)) |
199 | return -1; |
200 | |
201 | if (res.status != KEY_SUCCESS) |
202 | { |
203 | debug ("decrypt status is nonzero" ); |
204 | return -1; |
205 | } |
206 | *deskey = res.cryptkeyres_u.deskey; |
207 | return 0; |
208 | } |
209 | libc_hidden_nolink_sunrpc (key_decryptsession_pk, GLIBC_2_1) |
210 | |
211 | int |
212 | key_gendes (des_block *key) |
213 | { |
214 | struct sockaddr_in sin; |
215 | CLIENT *client; |
216 | int socket; |
217 | enum clnt_stat stat; |
218 | |
219 | sin.sin_family = AF_INET; |
220 | sin.sin_port = 0; |
221 | sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
222 | memset (sin.sin_zero, 0, sizeof (sin.sin_zero)); |
223 | socket = RPC_ANYSOCK; |
224 | client = clntudp_bufcreate (&sin, (u_long) KEY_PROG, (u_long) KEY_VERS, |
225 | trytimeout, &socket, RPCSMALLMSGSIZE, |
226 | RPCSMALLMSGSIZE); |
227 | if (client == NULL) |
228 | return -1; |
229 | |
230 | stat = clnt_call (client, KEY_GEN, (xdrproc_t) xdr_void, NULL, |
231 | (xdrproc_t) xdr_des_block, (caddr_t) key, |
232 | tottimeout); |
233 | clnt_destroy (client); |
234 | __close (socket); |
235 | if (stat != RPC_SUCCESS) |
236 | return -1; |
237 | |
238 | return 0; |
239 | } |
240 | #ifdef EXPORT_RPC_SYMBOLS |
241 | libc_hidden_def (key_gendes) |
242 | #else |
243 | libc_hidden_nolink_sunrpc (key_gendes, GLIBC_2_1) |
244 | #endif |
245 | |
246 | int |
247 | key_setnet (struct key_netstarg *arg) |
248 | { |
249 | keystatus status; |
250 | |
251 | if (!key_call ((u_long) KEY_NET_PUT, (xdrproc_t) xdr_key_netstarg, |
252 | (char *) arg,(xdrproc_t) xdr_keystatus, |
253 | (char *) &status)) |
254 | return -1; |
255 | |
256 | if (status != KEY_SUCCESS) |
257 | { |
258 | debug ("key_setnet status is nonzero" ); |
259 | return -1; |
260 | } |
261 | return 1; |
262 | } |
263 | libc_hidden_nolink_sunrpc (key_setnet, GLIBC_2_1) |
264 | |
265 | int |
266 | key_get_conv (char *pkey, des_block *deskey) |
267 | { |
268 | cryptkeyres res; |
269 | |
270 | if (!key_call ((u_long) KEY_GET_CONV, (xdrproc_t) xdr_keybuf, pkey, |
271 | (xdrproc_t) xdr_cryptkeyres, (char *) &res)) |
272 | return -1; |
273 | |
274 | if (res.status != KEY_SUCCESS) |
275 | { |
276 | debug ("get_conv status is nonzero" ); |
277 | return -1; |
278 | } |
279 | *deskey = res.cryptkeyres_u.deskey; |
280 | return 0; |
281 | } |
282 | libc_hidden_nolink_sunrpc (key_get_conv, GLIBC_2_1) |
283 | |
284 | /* |
285 | * Hack to allow the keyserver to use AUTH_DES (for authenticated |
286 | * NIS+ calls, for example). The only functions that get called |
287 | * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes. |
288 | * |
289 | * The approach is to have the keyserver fill in pointers to local |
290 | * implementations of these functions, and to call those in key_call(). |
291 | */ |
292 | |
293 | cryptkeyres *(*__key_encryptsession_pk_LOCAL) (uid_t, char *); |
294 | cryptkeyres *(*__key_decryptsession_pk_LOCAL) (uid_t, char *); |
295 | des_block *(*__key_gendes_LOCAL) (uid_t, char *); |
296 | #ifdef SHARED |
297 | # ifndef EXPORT_RPC_SYMBOLS |
298 | compat_symbol (libc, __key_encryptsession_pk_LOCAL, |
299 | __key_encryptsession_pk_LOCAL, GLIBC_2_1); |
300 | compat_symbol (libc, __key_decryptsession_pk_LOCAL, |
301 | __key_decryptsession_pk_LOCAL, GLIBC_2_1); |
302 | compat_symbol (libc, __key_gendes_LOCAL, __key_gendes_LOCAL, GLIBC_2_1); |
303 | # endif |
304 | #endif |
305 | |
306 | #ifndef SO_PASSCRED |
307 | static int |
308 | key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg, |
309 | xdrproc_t xdr_rslt, char *rslt) |
310 | { |
311 | XDR xdrargs; |
312 | XDR xdrrslt; |
313 | FILE *fargs; |
314 | FILE *frslt; |
315 | sigset_t oldmask, mask; |
316 | int status; |
317 | int pid; |
318 | int success; |
319 | uid_t ruid; |
320 | uid_t euid; |
321 | static const char MESSENGER[] = "/usr/etc/keyenvoy" ; |
322 | |
323 | success = 1; |
324 | sigemptyset (&mask); |
325 | sigaddset (&mask, SIGCHLD); |
326 | __sigprocmask (SIG_BLOCK, &mask, &oldmask); |
327 | |
328 | /* |
329 | * We are going to exec a set-uid program which makes our effective uid |
330 | * zero, and authenticates us with our real uid. We need to make the |
331 | * effective uid be the real uid for the setuid program, and |
332 | * the real uid be the effective uid so that we can change things back. |
333 | */ |
334 | euid = __geteuid (); |
335 | ruid = __getuid (); |
336 | __setreuid (euid, ruid); |
337 | pid = _openchild (MESSENGER, &fargs, &frslt); |
338 | __setreuid (ruid, euid); |
339 | if (pid < 0) |
340 | { |
341 | debug ("open_streams" ); |
342 | __sigprocmask (SIG_SETMASK, &oldmask, NULL); |
343 | return (0); |
344 | } |
345 | xdrstdio_create (&xdrargs, fargs, XDR_ENCODE); |
346 | xdrstdio_create (&xdrrslt, frslt, XDR_DECODE); |
347 | |
348 | if (!xdr_u_long (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg)) |
349 | { |
350 | debug ("xdr args" ); |
351 | success = 0; |
352 | } |
353 | fclose (fargs); |
354 | |
355 | if (success && !(*xdr_rslt) (&xdrrslt, rslt)) |
356 | { |
357 | debug ("xdr rslt" ); |
358 | success = 0; |
359 | } |
360 | fclose(frslt); |
361 | |
362 | wait_again: |
363 | if (__wait4 (pid, &status, 0, NULL) < 0) |
364 | { |
365 | if (errno == EINTR) |
366 | goto wait_again; |
367 | debug ("wait4" ); |
368 | if (errno == ECHILD || errno == ESRCH) |
369 | perror ("wait" ); |
370 | else |
371 | success = 0; |
372 | } |
373 | else |
374 | if (status != 0) |
375 | { |
376 | debug ("wait4 1" ); |
377 | success = 0; |
378 | } |
379 | __sigprocmask (SIG_SETMASK, &oldmask, NULL); |
380 | |
381 | return success; |
382 | } |
383 | #endif |
384 | |
385 | struct key_call_private { |
386 | CLIENT *client; /* Client handle */ |
387 | pid_t pid; /* process-id at moment of creation */ |
388 | uid_t uid; /* user-id at last authorization */ |
389 | }; |
390 | #define key_call_private_main RPC_THREAD_VARIABLE(key_call_private_s) |
391 | __libc_lock_define_initialized (static, keycall_lock) |
392 | |
393 | /* |
394 | * Keep the handle cached. This call may be made quite often. |
395 | */ |
396 | static CLIENT * |
397 | getkeyserv_handle (int vers) |
398 | { |
399 | struct key_call_private *kcp = key_call_private_main; |
400 | struct timeval wait_time; |
401 | int fd; |
402 | struct sockaddr_un name; |
403 | socklen_t namelen = sizeof(struct sockaddr_un); |
404 | |
405 | #define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */ |
406 | #define TOTAL_TRIES 5 /* Number of tries */ |
407 | |
408 | if (kcp == (struct key_call_private *)NULL) |
409 | { |
410 | kcp = (struct key_call_private *)malloc (sizeof (*kcp)); |
411 | if (kcp == (struct key_call_private *)NULL) |
412 | return (CLIENT *) NULL; |
413 | |
414 | key_call_private_main = kcp; |
415 | kcp->client = NULL; |
416 | } |
417 | |
418 | /* if pid has changed, destroy client and rebuild */ |
419 | if (kcp->client != NULL && kcp->pid != __getpid ()) |
420 | { |
421 | auth_destroy (kcp->client->cl_auth); |
422 | clnt_destroy (kcp->client); |
423 | kcp->client = NULL; |
424 | } |
425 | |
426 | if (kcp->client != NULL) |
427 | { |
428 | /* if other side closed socket, build handle again */ |
429 | clnt_control (kcp->client, CLGET_FD, (char *)&fd); |
430 | if (__getpeername (fd,(struct sockaddr *)&name,&namelen) == -1) |
431 | { |
432 | auth_destroy (kcp->client->cl_auth); |
433 | clnt_destroy (kcp->client); |
434 | kcp->client = NULL; |
435 | } |
436 | } |
437 | |
438 | if (kcp->client != NULL) |
439 | { |
440 | /* if uid has changed, build client handle again */ |
441 | if (kcp->uid != __geteuid ()) |
442 | { |
443 | kcp->uid = __geteuid (); |
444 | auth_destroy (kcp->client->cl_auth); |
445 | kcp->client->cl_auth = |
446 | authunix_create ((char *)"" , kcp->uid, 0, 0, NULL); |
447 | if (kcp->client->cl_auth == NULL) |
448 | { |
449 | clnt_destroy (kcp->client); |
450 | kcp->client = NULL; |
451 | return ((CLIENT *) NULL); |
452 | } |
453 | } |
454 | /* Change the version number to the new one */ |
455 | clnt_control (kcp->client, CLSET_VERS, (void *)&vers); |
456 | return kcp->client; |
457 | } |
458 | |
459 | if ((kcp->client == (CLIENT *) NULL)) |
460 | /* Use the AF_UNIX transport */ |
461 | kcp->client = clnt_create ("/var/run/keyservsock" , KEY_PROG, vers, "unix" ); |
462 | |
463 | if (kcp->client == (CLIENT *) NULL) |
464 | return (CLIENT *) NULL; |
465 | |
466 | kcp->uid = __geteuid (); |
467 | kcp->pid = __getpid (); |
468 | kcp->client->cl_auth = authunix_create ((char *)"" , kcp->uid, 0, 0, NULL); |
469 | if (kcp->client->cl_auth == NULL) |
470 | { |
471 | clnt_destroy (kcp->client); |
472 | kcp->client = NULL; |
473 | return (CLIENT *) NULL; |
474 | } |
475 | |
476 | wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES; |
477 | wait_time.tv_usec = 0; |
478 | clnt_control (kcp->client, CLSET_RETRY_TIMEOUT, |
479 | (char *)&wait_time); |
480 | if (clnt_control (kcp->client, CLGET_FD, (char *)&fd)) |
481 | __fcntl (fd, F_SETFD, FD_CLOEXEC); /* make it "close on exec" */ |
482 | |
483 | return kcp->client; |
484 | } |
485 | |
486 | /* returns 0 on failure, 1 on success */ |
487 | static int |
488 | key_call_socket (u_long proc, xdrproc_t xdr_arg, char *arg, |
489 | xdrproc_t xdr_rslt, char *rslt) |
490 | { |
491 | CLIENT *clnt; |
492 | struct timeval wait_time; |
493 | int result = 0; |
494 | |
495 | __libc_lock_lock (keycall_lock); |
496 | if ((proc == KEY_ENCRYPT_PK) || (proc == KEY_DECRYPT_PK) || |
497 | (proc == KEY_NET_GET) || (proc == KEY_NET_PUT) || |
498 | (proc == KEY_GET_CONV)) |
499 | clnt = getkeyserv_handle(2); /* talk to version 2 */ |
500 | else |
501 | clnt = getkeyserv_handle(1); /* talk to version 1 */ |
502 | |
503 | if (clnt != NULL) |
504 | { |
505 | wait_time.tv_sec = TOTAL_TIMEOUT; |
506 | wait_time.tv_usec = 0; |
507 | |
508 | if (clnt_call (clnt, proc, xdr_arg, arg, xdr_rslt, rslt, |
509 | wait_time) == RPC_SUCCESS) |
510 | result = 1; |
511 | } |
512 | |
513 | __libc_lock_unlock (keycall_lock); |
514 | |
515 | return result; |
516 | } |
517 | |
518 | |
519 | /* returns 0 on failure, 1 on success */ |
520 | static int |
521 | key_call (u_long proc, xdrproc_t xdr_arg, char *arg, |
522 | xdrproc_t xdr_rslt, char *rslt) |
523 | { |
524 | #ifndef SO_PASSCRED |
525 | static int use_keyenvoy; |
526 | #endif |
527 | |
528 | if (proc == KEY_ENCRYPT_PK && __key_encryptsession_pk_LOCAL) |
529 | { |
530 | cryptkeyres *res; |
531 | res = (*__key_encryptsession_pk_LOCAL) (__geteuid (), arg); |
532 | *(cryptkeyres *) rslt = *res; |
533 | return 1; |
534 | } |
535 | else if (proc == KEY_DECRYPT_PK && __key_decryptsession_pk_LOCAL) |
536 | { |
537 | cryptkeyres *res; |
538 | res = (*__key_decryptsession_pk_LOCAL) (__geteuid (), arg); |
539 | *(cryptkeyres *) rslt = *res; |
540 | return 1; |
541 | } |
542 | else if (proc == KEY_GEN && __key_gendes_LOCAL) |
543 | { |
544 | des_block *res; |
545 | res = (*__key_gendes_LOCAL) (__geteuid (), 0); |
546 | *(des_block *) rslt = *res; |
547 | return 1; |
548 | } |
549 | |
550 | #ifdef SO_PASSCRED |
551 | return key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt); |
552 | #else |
553 | if (!use_keyenvoy) |
554 | { |
555 | if (key_call_socket (proc, xdr_arg, arg, xdr_rslt, rslt)) |
556 | return 1; |
557 | use_keyenvoy = 1; |
558 | } |
559 | return key_call_keyenvoy (proc, xdr_arg, arg, xdr_rslt, rslt); |
560 | #endif |
561 | } |
562 | |
563 | void |
564 | __rpc_thread_key_cleanup (void) |
565 | { |
566 | struct key_call_private *kcp = RPC_THREAD_VARIABLE(key_call_private_s); |
567 | |
568 | if (kcp) { |
569 | if (kcp->client) { |
570 | if (kcp->client->cl_auth) |
571 | auth_destroy (kcp->client->cl_auth); |
572 | clnt_destroy(kcp->client); |
573 | } |
574 | free (kcp); |
575 | } |
576 | } |
577 | |