]> git.saurik.com Git - apple/ipsec.git/blob - ipsec-tools/racoon/privsep.c
ipsec-93.8.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / privsep.c
1 /* $NetBSD: privsep.c,v 1.6 2006/09/09 16:22:10 manu Exp $ */
2
3 /* Id: privsep.c,v 1.15 2005/08/08 11:23:44 vanhu Exp */
4
5 /*
6 * Copyright (C) 2004 Emmanuel Dreyfus
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include "config.h"
35
36 #include <unistd.h>
37 #include <string.h>
38 #ifdef __NetBSD__
39 #include <stdlib.h> /* for setproctitle */
40 #endif
41 #include <errno.h>
42 #include <signal.h>
43 #include <pwd.h>
44
45 #include <sys/socket.h>
46 #include <sys/param.h>
47
48 #include "gcmalloc.h"
49 #include "vmbuf.h"
50 #include "misc.h"
51 #include "plog.h"
52 #include "var.h"
53 #include "libpfkey.h"
54
55 #include "crypto_openssl.h"
56 #include "isakmp_var.h"
57 #include "isakmp.h"
58 #ifdef ENABLE_HYBRID
59 #include "resolv.h"
60 #include "isakmp_xauth.h"
61 #include "isakmp_cfg.h"
62 #endif
63 #include "localconf.h"
64 #include "remoteconf.h"
65 #include "admin.h"
66 #include "sockmisc.h"
67 #include "privsep.h"
68
69 static int privsep_sock[2] = { -1, -1 };
70
71 static int privsep_recv(int, struct privsep_com_msg **, size_t *);
72 static int privsep_send(int, struct privsep_com_msg *, size_t);
73 static int safety_check(struct privsep_com_msg *, int i);
74 static int port_check(int);
75 static int unsafe_env(char *const *);
76 static int unknown_name(int);
77 static int unsafe_path(char *, int);
78
79 static int
80 privsep_send(sock, buf, len)
81 int sock;
82 struct privsep_com_msg *buf;
83 size_t len;
84 {
85 if (buf == NULL)
86 return 0;
87
88 if (sendto(sock, (char *)buf, len, 0, NULL, 0) == -1) {
89 plog(LLV_ERROR, LOCATION, NULL,
90 "privsep_send failed: %s\n",
91 strerror(errno));
92 return -1;
93 }
94
95 racoon_free((char *)buf);
96
97 return 0;
98 }
99
100
101 static int
102 privsep_recv(sock, bufp, lenp)
103 int sock;
104 struct privsep_com_msg **bufp;
105 size_t *lenp;
106 {
107 struct admin_com com;
108 struct admin_com *combuf;
109 size_t len;
110
111 *bufp = NULL;
112 *lenp = 0;
113
114 /* Get the header */
115 while ((len = recvfrom(sock, (char *)&com,
116 sizeof(com), MSG_PEEK, NULL, NULL)) == -1) {
117 if (errno == EINTR)
118 continue;
119
120 plog(LLV_ERROR, LOCATION, NULL,
121 "privsep_recv failed: %s\n",
122 strerror(errno));
123 return -1;
124 }
125
126 /* Check for short packets */
127 if (len < sizeof(com)) {
128 plog(LLV_ERROR, LOCATION, NULL,
129 "corrupted privsep message (short header)\n");
130 return -1;
131 }
132
133 /* Allocate buffer for the whole message */
134 if ((combuf = (struct admin_com *)racoon_malloc(com.ac_len)) == NULL) {
135 plog(LLV_ERROR, LOCATION, NULL,
136 "failed to allocate memory: %s\n", strerror(errno));
137 return -1;
138 }
139
140 /* Get the whole buffer */
141 while ((len = recvfrom(sock, (char *)combuf,
142 com.ac_len, 0, NULL, NULL)) == -1) {
143 if (errno == EINTR)
144 continue;
145 plog(LLV_ERROR, LOCATION, NULL,
146 "failed to recv privsep command: %s\n",
147 strerror(errno));
148 return -1;
149 }
150
151 /* We expect len to match */
152 if (len != com.ac_len) {
153 plog(LLV_ERROR, LOCATION, NULL,
154 "corrupted privsep message (short packet)\n");
155 return -1;
156 }
157
158 *bufp = (struct privsep_com_msg *)combuf;
159 *lenp = len;
160
161 return 0;
162 }
163
164 int
165 privsep_init(void)
166 {
167 int i;
168 pid_t child_pid;
169
170 /* If running as root, we don't use the privsep code path */
171 if (lcconf->uid == 0)
172 return 0;
173
174 /*
175 * When running privsep, certificate and script paths
176 * are mandatory, as they enable us to check path safety
177 * in the privilegied instance
178 */
179 if ((lcconf->pathinfo[LC_PATHTYPE_CERT] == NULL) ||
180 (lcconf->pathinfo[LC_PATHTYPE_SCRIPT] == NULL)) {
181 plog(LLV_ERROR, LOCATION, NULL, "privilege separation "
182 "require path cert and path script in the config file\n");
183 return -1;
184 }
185
186 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, privsep_sock) != 0) {
187 plog(LLV_ERROR, LOCATION, NULL,
188 "Cannot allocate privsep_sock: %s\n", strerror(errno));
189 return -1;
190 }
191
192 switch (child_pid = fork()) {
193 case -1:
194 plog(LLV_ERROR, LOCATION, NULL, "Cannot fork privsep: %s\n",
195 strerror(errno));
196 return -1;
197 break;
198
199 case 0: /* Child: drop privileges */
200 if (lcconf->chroot != NULL) {
201 if (chdir(lcconf->chroot) != 0) {
202 plog(LLV_ERROR, LOCATION, NULL,
203 "Cannot chdir(%s): %s\n", lcconf->chroot,
204 strerror(errno));
205 return -1;
206 }
207 if (chroot(lcconf->chroot) != 0) {
208 plog(LLV_ERROR, LOCATION, NULL,
209 "Cannot chroot(%s): %s\n", lcconf->chroot,
210 strerror(errno));
211 return -1;
212 }
213 }
214
215 if (setgid(lcconf->gid) != 0) {
216 plog(LLV_ERROR, LOCATION, NULL,
217 "Cannot setgid(%d): %s\n", lcconf->gid,
218 strerror(errno));
219 return -1;
220 }
221
222 if (setegid(lcconf->gid) != 0) {
223 plog(LLV_ERROR, LOCATION, NULL,
224 "Cannot setegid(%d): %s\n", lcconf->gid,
225 strerror(errno));
226 return -1;
227 }
228
229 if (setuid(lcconf->uid) != 0) {
230 plog(LLV_ERROR, LOCATION, NULL,
231 "Cannot setuid(%d): %s\n", lcconf->uid,
232 strerror(errno));
233 return -1;
234 }
235
236 if (seteuid(lcconf->uid) != 0) {
237 plog(LLV_ERROR, LOCATION, NULL,
238 "Cannot seteuid(%d): %s\n", lcconf->uid,
239 strerror(errno));
240 return -1;
241 }
242
243 return 0;
244 break;
245
246 default: /* Parent: privilegied process */
247 break;
248 }
249
250 /*
251 * Close everything except the socketpair,
252 * and stdout if running in the forground.
253 */
254 for (i = sysconf(_SC_OPEN_MAX); i > 0; i--) {
255 if (i == privsep_sock[0])
256 continue;
257 if (i == privsep_sock[1])
258 continue;
259 if ((f_foreground) && (i == 1))
260 continue;
261 (void)close(i);
262 }
263
264 /* Above trickery closed the log file, reopen it */
265 ploginit();
266
267 plog(LLV_INFO, LOCATION, NULL,
268 "racoon privilegied process running with PID %d\n", getpid());
269
270 #ifdef __NetBSD__
271 setproctitle("[priv]");
272 #endif
273
274 /*
275 * Don't catch any signal
276 * This duplicate session:signals[], which is static...
277 */
278 signal(SIGHUP, SIG_DFL);
279 signal(SIGINT, SIG_DFL);
280 signal(SIGTERM, SIG_DFL);
281 signal(SIGUSR1, SIG_DFL);
282 signal(SIGUSR2, SIG_DFL);
283 signal(SIGCHLD, SIG_DFL);
284
285 while (1) {
286 size_t len;
287 struct privsep_com_msg *combuf;
288 struct privsep_com_msg *reply;
289 char *data;
290 size_t *buflen;
291 size_t totallen;
292 char *bufs[PRIVSEP_NBUF_MAX];
293 int i;
294
295 if (privsep_recv(privsep_sock[0], &combuf, &len) != 0)
296 goto out;
297
298 /* Safety checks and gather the data */
299 if (len < sizeof(*combuf)) {
300 plog(LLV_ERROR, LOCATION, NULL,
301 "corrupted privsep message (short buflen)\n");
302 goto out;
303 }
304
305 data = (char *)(combuf + 1);
306 totallen = sizeof(*combuf);
307 for (i = 0; i < PRIVSEP_NBUF_MAX; i++) {
308 bufs[i] = (char *)data;
309 data += combuf->bufs.buflen[i];
310 totallen += combuf->bufs.buflen[i];
311 }
312
313 if (totallen > len) {
314 plog(LLV_ERROR, LOCATION, NULL,
315 "corrupted privsep message (bufs too big)\n");
316 goto out;
317 }
318
319 /* Prepare the reply buffer */
320 if ((reply = racoon_malloc(sizeof(*reply))) == NULL) {
321 plog(LLV_ERROR, LOCATION, NULL,
322 "Cannot allocate reply buffer: %s\n",
323 strerror(errno));
324 goto out;
325 }
326 bzero(reply, sizeof(*reply));
327 reply->hdr.ac_cmd = combuf->hdr.ac_cmd;
328 reply->hdr.ac_len = sizeof(*reply);
329
330 switch(combuf->hdr.ac_cmd) {
331 /*
332 * XXX Improvement: instead of returning the key,
333 * stuff eay_get_pkcs1privkey and eay_get_x509sign
334 * together and sign the hash in the privilegied
335 * instance?
336 * pro: the key remains inaccessible to unpriv
337 * con: a compromised unpriv racoon can still sign anything
338 */
339 case PRIVSEP_EAY_GET_PKCS1PRIVKEY: {
340 vchar_t *privkey;
341
342 /* Make sure the string is NULL terminated */
343 if (safety_check(combuf, 0) != 0)
344 break;
345 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
346
347 if (unsafe_path(bufs[0], LC_PATHTYPE_CERT) != 0) {
348 plog(LLV_ERROR, LOCATION, NULL,
349 "privsep_eay_get_pkcs1privkey: "
350 "unsafe cert \"%s\"\n", bufs[0]);
351 }
352
353 plog(LLV_DEBUG, LOCATION, NULL,
354 "eay_get_pkcs1privkey(\"%s\")\n", bufs[0]);
355
356 if ((privkey = eay_get_pkcs1privkey(bufs[0])) == NULL){
357 reply->hdr.ac_errno = errno;
358 break;
359 }
360
361 reply->bufs.buflen[0] = privkey->l;
362 reply->hdr.ac_len = sizeof(*reply) + privkey->l;
363 reply = racoon_realloc(reply, reply->hdr.ac_len);
364 if (reply == NULL) {
365 plog(LLV_ERROR, LOCATION, NULL,
366 "Cannot allocate reply buffer: %s\n",
367 strerror(errno));
368 goto out;
369 }
370
371 memcpy(reply + 1, privkey->v, privkey->l);
372 vfree(privkey);
373 break;
374 }
375
376 case PRIVSEP_SCRIPT_EXEC: {
377 char *script;
378 int name;
379 char **envp = NULL;
380 int envc = 0;
381 int count = 0;
382 int i;
383
384 /*
385 * First count the bufs, and make sure strings
386 * are NULL terminated.
387 *
388 * We expect: script, name, envp[], void
389 */
390 if (safety_check(combuf, 0) != 0)
391 break;
392 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
393 count++; /* script */
394 count++; /* name */
395
396 for (; count < PRIVSEP_NBUF_MAX; count++) {
397 if (combuf->bufs.buflen[count] == 0)
398 break;
399 bufs[count]
400 [combuf->bufs.buflen[count] - 1] = '\0';
401 envc++;
402 }
403
404 /* count a void buf and perform safety check */
405 count++;
406 if (count >= PRIVSEP_NBUF_MAX) {
407 plog(LLV_ERROR, LOCATION, NULL,
408 "privsep_script_exec: too many args\n");
409 goto out;
410 }
411
412
413 /*
414 * Allocate the arrays for envp
415 */
416 envp = racoon_malloc((envc + 1) * sizeof(char *));
417 if (envp == NULL) {
418 plog(LLV_ERROR, LOCATION, NULL,
419 "cannot allocate memory: %s\n",
420 strerror(errno));
421 goto out;
422 }
423 bzero(envp, (envc + 1) * sizeof(char *));
424
425
426 /*
427 * Populate script, name and envp
428 */
429 count = 0;
430 script = bufs[count++];
431
432 if (combuf->bufs.buflen[count] != sizeof(name)) {
433 plog(LLV_ERROR, LOCATION, NULL,
434 "privsep_script_exec: corrupted message\n");
435 goto out;
436 }
437 memcpy((char *)&name, bufs[count++], sizeof(name));
438
439 for (i = 0; combuf->bufs.buflen[count]; count++)
440 envp[i++] = bufs[count];
441
442 count++; /* void */
443
444 plog(LLV_DEBUG, LOCATION, NULL,
445 "script_exec(\"%s\", %d, %p)\n",
446 script, name, envp);
447
448 /*
449 * Check env for dangerous variables
450 * Check script path and name
451 * Perform fork and execve
452 */
453 if ((unsafe_env(envp) == 0) &&
454 (unknown_name(name) == 0) &&
455 (unsafe_path(script, LC_PATHTYPE_SCRIPT) == 0))
456 (void)script_exec(script, name, envp);
457 else
458 plog(LLV_ERROR, LOCATION, NULL,
459 "privsep_script_exec: "
460 "unsafe script \"%s\"\n", script);
461
462 racoon_free(envp);
463 break;
464 }
465
466 case PRIVSEP_GETPSK: {
467 vchar_t *psk;
468 int keylen;
469
470 /* Make sure the string is NULL terminated */
471 if (safety_check(combuf, 0) != 0)
472 break;
473 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
474
475 if (combuf->bufs.buflen[1] != sizeof(keylen)) {
476 plog(LLV_ERROR, LOCATION, NULL,
477 "privsep_getpsk: corrupted message\n");
478 goto out;
479 }
480 memcpy(&keylen, bufs[1], sizeof(keylen));
481
482 plog(LLV_DEBUG, LOCATION, NULL,
483 "getpsk(\"%s\", %d)\n", bufs[0], keylen);
484
485 if ((psk = getpsk(bufs[0], keylen)) == NULL) {
486 reply->hdr.ac_errno = errno;
487 break;
488 }
489
490 reply->bufs.buflen[0] = psk->l;
491 reply->hdr.ac_len = sizeof(*reply) + psk->l;
492 reply = racoon_realloc(reply, reply->hdr.ac_len);
493 if (reply == NULL) {
494 plog(LLV_ERROR, LOCATION, NULL,
495 "Cannot allocate reply buffer: %s\n",
496 strerror(errno));
497 goto out;
498 }
499
500 memcpy(reply + 1, psk->v, psk->l);
501 vfree(psk);
502 break;
503 }
504
505 #ifdef ENABLE_HYBRID
506 case PRIVSEP_ACCOUNTING_SYSTEM: {
507 int pool_size;
508 int port;
509 int inout;
510 struct sockaddr *raddr;
511
512 if (safety_check(combuf, 0) != 0)
513 break;
514 if (safety_check(combuf, 1) != 0)
515 break;
516 if (safety_check(combuf, 2) != 0)
517 break;
518 if (safety_check(combuf, 3) != 0)
519 break;
520
521 memcpy(&port, bufs[0], sizeof(port));
522 raddr = (struct sockaddr *)bufs[1];
523
524 bufs[2][combuf->bufs.buflen[2] - 1] = '\0';
525 memcpy(&inout, bufs[3], sizeof(port));
526
527 if (port_check(port) != 0)
528 break;
529
530 plog(LLV_DEBUG, LOCATION, NULL,
531 "accounting_system(%d, %s, %s)\n",
532 port, saddr2str(raddr), bufs[2]);
533
534 errno = 0;
535 if (isakmp_cfg_accounting_system(port,
536 raddr, bufs[2], inout) != 0) {
537 if (errno == 0)
538 reply->hdr.ac_errno = EINVAL;
539 else
540 reply->hdr.ac_errno = errno;
541 }
542 break;
543 }
544 case PRIVSEP_XAUTH_LOGIN_SYSTEM: {
545 if (safety_check(combuf, 0) != 0)
546 break;
547 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
548
549 if (safety_check(combuf, 1) != 0)
550 break;
551 bufs[1][combuf->bufs.buflen[1] - 1] = '\0';
552
553 plog(LLV_DEBUG, LOCATION, NULL,
554 "xauth_login_system(\"%s\", <password>)\n",
555 bufs[0]);
556
557 errno = 0;
558 if (xauth_login_system(bufs[0], bufs[1]) != 0) {
559 if (errno == 0)
560 reply->hdr.ac_errno = EINVAL;
561 else
562 reply->hdr.ac_errno = errno;
563 }
564 break;
565 }
566 #ifdef HAVE_LIBPAM
567 case PRIVSEP_ACCOUNTING_PAM: {
568 int port;
569 int inout;
570 int pool_size;
571
572 if (safety_check(combuf, 0) != 0)
573 break;
574 if (safety_check(combuf, 1) != 0)
575 break;
576 if (safety_check(combuf, 2) != 0)
577 break;
578
579 memcpy(&port, bufs[0], sizeof(port));
580 memcpy(&inout, bufs[1], sizeof(inout));
581 memcpy(&pool_size, bufs[2], sizeof(pool_size));
582
583 if (pool_size != isakmp_cfg_config.pool_size)
584 if (isakmp_cfg_resize_pool(pool_size) != 0)
585 break;
586
587 if (port_check(port) != 0)
588 break;
589
590 plog(LLV_DEBUG, LOCATION, NULL,
591 "isakmp_cfg_accounting_pam(%d, %d)\n",
592 port, inout);
593
594 errno = 0;
595 if (isakmp_cfg_accounting_pam(port, inout) != 0) {
596 if (errno == 0)
597 reply->hdr.ac_errno = EINVAL;
598 else
599 reply->hdr.ac_errno = errno;
600 }
601 break;
602 }
603
604 case PRIVSEP_XAUTH_LOGIN_PAM: {
605 int port;
606 int pool_size;
607 struct sockaddr *raddr;
608
609 if (safety_check(combuf, 0) != 0)
610 break;
611 if (safety_check(combuf, 1) != 0)
612 break;
613 if (safety_check(combuf, 2) != 0)
614 break;
615 if (safety_check(combuf, 3) != 0)
616 break;
617 if (safety_check(combuf, 4) != 0)
618 break;
619
620 memcpy(&port, bufs[0], sizeof(port));
621 memcpy(&pool_size, bufs[1], sizeof(pool_size));
622 raddr = (struct sockaddr *)bufs[2];
623
624 bufs[3][combuf->bufs.buflen[3] - 1] = '\0';
625 bufs[4][combuf->bufs.buflen[4] - 1] = '\0';
626
627 if (pool_size != isakmp_cfg_config.pool_size)
628 if (isakmp_cfg_resize_pool(pool_size) != 0)
629 break;
630
631 if (port_check(port) != 0)
632 break;
633
634 plog(LLV_DEBUG, LOCATION, NULL,
635 "xauth_login_pam(%d, %s, \"%s\", <password>)\n",
636 port, saddr2str(raddr), bufs[3]);
637
638 errno = 0;
639 if (xauth_login_pam(port,
640 raddr, bufs[3], bufs[4]) != 0) {
641 if (errno == 0)
642 reply->hdr.ac_errno = EINVAL;
643 else
644 reply->hdr.ac_errno = errno;
645 }
646 break;
647 }
648
649 case PRIVSEP_CLEANUP_PAM: {
650 int port;
651 int pool_size;
652
653 if (safety_check(combuf, 0) != 0)
654 break;
655 if (safety_check(combuf, 1) != 0)
656 break;
657
658 memcpy(&port, bufs[0], sizeof(port));
659 memcpy(&pool_size, bufs[1], sizeof(pool_size));
660
661 if (pool_size != isakmp_cfg_config.pool_size)
662 if (isakmp_cfg_resize_pool(pool_size) != 0)
663 break;
664
665 if (port_check(port) != 0)
666 break;
667
668 plog(LLV_DEBUG, LOCATION, NULL,
669 "cleanup_pam(%d)\n", port);
670
671 cleanup_pam(port);
672 reply->hdr.ac_errno = 0;
673
674 break;
675 }
676 #endif /* HAVE_LIBPAM */
677 #endif /* ENABLE_HYBRID */
678
679 default:
680 plog(LLV_ERROR, LOCATION, NULL,
681 "unexpected privsep command %d\n",
682 combuf->hdr.ac_cmd);
683 goto out;
684 break;
685 }
686
687 /* This frees reply */
688 if (privsep_send(privsep_sock[0],
689 reply, reply->hdr.ac_len) != 0)
690 goto out;
691
692 racoon_free(combuf);
693 }
694
695 out:
696 plog(LLV_INFO, LOCATION, NULL, "privsep exit\n");
697 _exit(0);
698 }
699
700
701 vchar_t *
702 privsep_eay_get_pkcs1privkey(path)
703 char *path;
704 {
705 vchar_t *privkey;
706 struct privsep_com_msg *msg;
707 size_t len;
708
709 if (geteuid() == 0)
710 return eay_get_pkcs1privkey(path);
711
712 len = sizeof(*msg) + strlen(path) + 1;
713 if ((msg = racoon_malloc(len)) == NULL) {
714 plog(LLV_ERROR, LOCATION, NULL,
715 "Cannot allocate memory: %s\n", strerror(errno));
716 return NULL;
717 }
718 bzero(msg, len);
719 msg->hdr.ac_cmd = PRIVSEP_EAY_GET_PKCS1PRIVKEY;
720 msg->hdr.ac_len = len;
721 msg->bufs.buflen[0] = len - sizeof(*msg);
722 memcpy(msg + 1, path, msg->bufs.buflen[0]);
723
724 if (privsep_send(privsep_sock[1], msg, len) != 0)
725 return NULL;
726
727 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
728 return NULL;
729
730 if (msg->hdr.ac_errno != 0) {
731 errno = msg->hdr.ac_errno;
732 goto out;
733 }
734
735 if ((privkey = vmalloc(len - sizeof(*msg))) == NULL)
736 goto out;
737
738 memcpy(privkey->v, msg + 1, privkey->l);
739 racoon_free(msg);
740 return privkey;
741
742 out:
743 racoon_free(msg);
744 return NULL;
745 }
746
747 /*
748 * No prigilege separation trick here, we just open PFKEY before
749 * dropping root privs and we remember it later.
750 */
751 static int pfkey_socket = -1;
752 int
753 privsep_pfkey_open(void)
754 {
755 int ps;
756
757 if (pfkey_socket != -1)
758 return pfkey_socket;
759
760 ps = pfkey_open();
761 if (ps != -1)
762 pfkey_socket = ps;
763
764 return ps;
765 }
766
767 /*
768 * Consequence of the above trickery: don't
769 * really close PFKEY as we never re-open it.
770 */
771 void
772 privsep_pfkey_close(ps)
773 int ps;
774 {
775 return;
776 }
777
778 int
779 privsep_script_exec(script, name, envp)
780 char *script;
781 int name;
782 char *const envp[];
783 {
784 int count = 0;
785 char *const *c;
786 char *data;
787 size_t len;
788 struct privsep_com_msg *msg;
789
790 if (geteuid() == 0)
791 return script_exec(script, name, envp);
792
793 if ((msg = racoon_malloc(sizeof(*msg))) == NULL) {
794 plog(LLV_ERROR, LOCATION, NULL,
795 "Cannot allocate memory: %s\n", strerror(errno));
796 return -1;
797 }
798
799 bzero(msg, sizeof(*msg));
800 msg->hdr.ac_cmd = PRIVSEP_SCRIPT_EXEC;
801 msg->hdr.ac_len = sizeof(*msg);
802
803 /*
804 * We send:
805 * script, name, envp[0], ... envp[N], void
806 */
807
808 /*
809 * Safety check on the counts: PRIVSEP_NBUF_MAX max
810 */
811 count = 0;
812 count++; /* script */
813 count++; /* name */
814 for (c = envp; *c; c++) /* envp */
815 count++;
816 count++; /* void */
817
818 if (count > PRIVSEP_NBUF_MAX) {
819 plog(LLV_ERROR, LOCATION, NULL, "Unexpected error: "
820 "privsep_script_exec count > PRIVSEP_NBUF_MAX\n");
821 racoon_free(msg);
822 return -1;
823 }
824
825
826 /*
827 * Compute the length
828 */
829 count = 0;
830 msg->bufs.buflen[count] = strlen(script) + 1; /* script */
831 msg->hdr.ac_len += msg->bufs.buflen[count++];
832
833 msg->bufs.buflen[count] = sizeof(name); /* name */
834 msg->hdr.ac_len += msg->bufs.buflen[count++];
835
836 for (c = envp; *c; c++) { /* envp */
837 msg->bufs.buflen[count] = strlen(*c) + 1;
838 msg->hdr.ac_len += msg->bufs.buflen[count++];
839 }
840
841 msg->bufs.buflen[count] = 0; /* void */
842 msg->hdr.ac_len += msg->bufs.buflen[count++];
843
844 if ((msg = racoon_realloc(msg, msg->hdr.ac_len)) == NULL) {
845 plog(LLV_ERROR, LOCATION, NULL,
846 "Cannot allocate memory: %s\n", strerror(errno));
847 return -1;
848 }
849
850 /*
851 * Now copy the data
852 */
853 data = (char *)(msg + 1);
854 count = 0;
855
856 memcpy(data, (char *)script, msg->bufs.buflen[count]); /* script */
857 data += msg->bufs.buflen[count++];
858
859 memcpy(data, (char *)&name, msg->bufs.buflen[count]); /* name */
860 data += msg->bufs.buflen[count++];
861
862 for (c = envp; *c; c++) { /* envp */
863 memcpy(data, *c, msg->bufs.buflen[count]);
864 data += msg->bufs.buflen[count++];
865 }
866
867 count++; /* void */
868
869 /*
870 * And send it!
871 */
872 if (privsep_send(privsep_sock[1], msg, msg->hdr.ac_len) != 0)
873 return -1;
874
875 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
876 return -1;
877
878 if (msg->hdr.ac_errno != 0) {
879 errno = msg->hdr.ac_errno;
880 racoon_free(msg);
881 return -1;
882 }
883
884 racoon_free(msg);
885 return 0;
886 }
887
888 vchar_t *
889 privsep_getpsk(str, keylen)
890 const char *str;
891 int keylen;
892 {
893 vchar_t *psk;
894 struct privsep_com_msg *msg;
895 size_t len;
896 int *keylenp;
897 char *data;
898
899 if (geteuid() == 0)
900 return getpsk(str, keylen);
901
902 len = sizeof(*msg) + strlen(str) + 1 + sizeof(keylen);
903 if ((msg = racoon_malloc(len)) == NULL) {
904 plog(LLV_ERROR, LOCATION, NULL,
905 "Cannot allocate memory: %s\n", strerror(errno));
906 return NULL;
907 }
908 bzero(msg, len);
909 msg->hdr.ac_cmd = PRIVSEP_GETPSK;
910 msg->hdr.ac_len = len;
911
912 data = (char *)(msg + 1);
913 msg->bufs.buflen[0] = strlen(str) + 1;
914 memcpy(data, str, msg->bufs.buflen[0]);
915
916 data += msg->bufs.buflen[0];
917 msg->bufs.buflen[1] = sizeof(keylen);
918 memcpy(data, &keylen, sizeof(keylen));
919
920 if (privsep_send(privsep_sock[1], msg, len) != 0)
921 return NULL;
922
923 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
924 return NULL;
925
926 if (msg->hdr.ac_errno != 0) {
927 errno = msg->hdr.ac_errno;
928 goto out;
929 }
930
931 if ((psk = vmalloc(len - sizeof(*msg))) == NULL)
932 goto out;
933
934 memcpy(psk->v, msg + 1, psk->l);
935 racoon_free(msg);
936 return psk;
937
938 out:
939 racoon_free(msg);
940 return NULL;
941 }
942
943 #ifdef ENABLE_HYBRID
944 int
945 privsep_xauth_login_system(usr, pwd)
946 char *usr;
947 char *pwd;
948 {
949 struct privsep_com_msg *msg;
950 size_t len;
951 char *data;
952
953 if (geteuid() == 0)
954 return xauth_login_system(usr, pwd);
955
956 len = sizeof(*msg) + strlen(usr) + 1 + strlen(pwd) + 1;
957 if ((msg = racoon_malloc(len)) == NULL) {
958 plog(LLV_ERROR, LOCATION, NULL,
959 "Cannot allocate memory: %s\n", strerror(errno));
960 return -1;
961 }
962 bzero(msg, len);
963 msg->hdr.ac_cmd = PRIVSEP_XAUTH_LOGIN_SYSTEM;
964 msg->hdr.ac_len = len;
965
966 data = (char *)(msg + 1);
967 msg->bufs.buflen[0] = strlen(usr) + 1;
968 memcpy(data, usr, msg->bufs.buflen[0]);
969 data += msg->bufs.buflen[0];
970
971 msg->bufs.buflen[1] = strlen(pwd) + 1;
972 memcpy(data, pwd, msg->bufs.buflen[1]);
973
974 if (privsep_send(privsep_sock[1], msg, len) != 0)
975 return -1;
976
977 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
978 return -1;
979
980 if (msg->hdr.ac_errno != 0) {
981 racoon_free(msg);
982 return -1;
983 }
984
985 racoon_free(msg);
986 return 0;
987 }
988
989 int
990 privsep_accounting_system(port, raddr, usr, inout)
991 int port;
992 struct sockaddr *raddr;
993 char *usr;
994 int inout;
995 {
996 struct privsep_com_msg *msg;
997 size_t len;
998 char *data;
999 int result;
1000
1001 if (geteuid() == 0)
1002 return isakmp_cfg_accounting_system(port, raddr,
1003 usr, inout);
1004
1005 len = sizeof(*msg)
1006 + sizeof(port)
1007 + sysdep_sa_len(raddr)
1008 + strlen(usr) + 1
1009 + sizeof(inout);
1010
1011 if ((msg = racoon_malloc(len)) == NULL) {
1012 plog(LLV_ERROR, LOCATION, NULL,
1013 "Cannot allocate memory: %s\n", strerror(errno));
1014 return -1;
1015 }
1016 bzero(msg, len);
1017 msg->hdr.ac_cmd = PRIVSEP_ACCOUNTING_SYSTEM;
1018 msg->hdr.ac_len = len;
1019 msg->bufs.buflen[0] = sizeof(port);
1020 msg->bufs.buflen[1] = sysdep_sa_len(raddr);
1021 msg->bufs.buflen[2] = strlen(usr) + 1;
1022 msg->bufs.buflen[3] = sizeof(inout);
1023
1024 data = (char *)(msg + 1);
1025 memcpy(data, &port, msg->bufs.buflen[0]);
1026
1027 data += msg->bufs.buflen[0];
1028 memcpy(data, raddr, msg->bufs.buflen[1]);
1029
1030 data += msg->bufs.buflen[1];
1031 memcpy(data, usr, msg->bufs.buflen[2]);
1032
1033 data += msg->bufs.buflen[2];
1034 memcpy(data, &inout, msg->bufs.buflen[3]);
1035
1036 if (privsep_send(privsep_sock[1], msg, len) != 0)
1037 return -1;
1038
1039 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1040 return -1;
1041
1042 if (msg->hdr.ac_errno != 0) {
1043 errno = msg->hdr.ac_errno;
1044 goto out;
1045 }
1046
1047 racoon_free(msg);
1048 return 0;
1049
1050 out:
1051 racoon_free(msg);
1052 return -1;
1053 }
1054
1055 static int
1056 port_check(port)
1057 int port;
1058 {
1059 if ((port < 0) || (port >= isakmp_cfg_config.pool_size)) {
1060 plog(LLV_ERROR, LOCATION, NULL,
1061 "privsep: port %d outside of allowed range [0,%zu]\n",
1062 port, isakmp_cfg_config.pool_size - 1);
1063 return -1;
1064 }
1065
1066 return 0;
1067 }
1068 #endif
1069
1070 static int
1071 safety_check(msg, index)
1072 struct privsep_com_msg *msg;
1073 int index;
1074 {
1075 if (index >= PRIVSEP_NBUF_MAX) {
1076 plog(LLV_ERROR, LOCATION, NULL,
1077 "privsep: Corrupted message, too many buffers\n");
1078 return -1;
1079 }
1080
1081 if (msg->bufs.buflen[index] == 0) {
1082 plog(LLV_ERROR, LOCATION, NULL,
1083 "privsep: Corrupted message, unexpected void buffer\n");
1084 return -1;
1085 }
1086
1087 return 0;
1088 }
1089
1090 /*
1091 * Filter unsafe environement variables
1092 */
1093 static int
1094 unsafe_env(envp)
1095 char *const *envp;
1096 {
1097 char *const *e;
1098 char *const *be;
1099 char *const bad_env[] = { "PATH=", "LD_LIBRARY_PATH=", "IFS=", NULL };
1100
1101 for (e = envp; *e; e++) {
1102 for (be = bad_env; *be; be++) {
1103 if (strncmp(*e, *be, strlen(*be)) == 0) {
1104 goto found;
1105 }
1106 }
1107 }
1108
1109 return 0;
1110 found:
1111 plog(LLV_ERROR, LOCATION, NULL,
1112 "privsep_script_exec: unsafe environement variable\n");
1113 return -1;
1114 }
1115
1116 /*
1117 * Check path safety
1118 */
1119 static int
1120 unsafe_path(script, pathtype)
1121 char *script;
1122 int pathtype;
1123 {
1124 char *path;
1125 char rpath[MAXPATHLEN + 1];
1126 size_t len;
1127
1128 if (script == NULL)
1129 return -1;
1130
1131 path = lcconf->pathinfo[pathtype];
1132
1133 /* No path was given for scripts: skip the check */
1134 if (path == NULL)
1135 return 0;
1136
1137 if (realpath(script, rpath) == NULL) {
1138 plog(LLV_ERROR, LOCATION, NULL,
1139 "script path \"%s\" is invalid\n", script);
1140 return -1;
1141 }
1142
1143 len = strlen(path);
1144 if (strncmp(path, rpath, len) != 0)
1145 return -1;
1146
1147 return 0;
1148 }
1149
1150 static int
1151 unknown_name(name)
1152 int name;
1153 {
1154 if ((name < 0) || (name > SCRIPT_MAX)) {
1155 plog(LLV_ERROR, LOCATION, NULL,
1156 "privsep_script_exec: unsafe name index\n");
1157 return -1;
1158 }
1159
1160 return 0;
1161 }
1162
1163 #ifdef HAVE_LIBPAM
1164 int
1165 privsep_accounting_pam(port, inout)
1166 int port;
1167 int inout;
1168 {
1169 struct privsep_com_msg *msg;
1170 size_t len;
1171 int *port_data;
1172 int *inout_data;
1173 int *pool_size_data;
1174 int result;
1175
1176 if (geteuid() == 0)
1177 return isakmp_cfg_accounting_pam(port, inout);
1178
1179 len = sizeof(*msg)
1180 + sizeof(port)
1181 + sizeof(inout)
1182 + sizeof(isakmp_cfg_config.pool_size);
1183
1184 if ((msg = racoon_malloc(len)) == NULL) {
1185 plog(LLV_ERROR, LOCATION, NULL,
1186 "Cannot allocate memory: %s\n", strerror(errno));
1187 return -1;
1188 }
1189 bzero(msg, len);
1190 msg->hdr.ac_cmd = PRIVSEP_ACCOUNTING_PAM;
1191 msg->hdr.ac_len = len;
1192 msg->bufs.buflen[0] = sizeof(port);
1193 msg->bufs.buflen[1] = sizeof(inout);
1194 msg->bufs.buflen[2] = sizeof(isakmp_cfg_config.pool_size);
1195
1196 port_data = (int *)(msg + 1);
1197 inout_data = (int *)(port_data + 1);
1198 pool_size_data = (int *)(inout_data + 1);
1199
1200 *port_data = port;
1201 *inout_data = inout;
1202 *pool_size_data = isakmp_cfg_config.pool_size;
1203
1204 if (privsep_send(privsep_sock[1], msg, len) != 0)
1205 return -1;
1206
1207 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1208 return -1;
1209
1210 if (msg->hdr.ac_errno != 0) {
1211 errno = msg->hdr.ac_errno;
1212 goto out;
1213 }
1214
1215 racoon_free(msg);
1216 return 0;
1217
1218 out:
1219 racoon_free(msg);
1220 return -1;
1221 }
1222
1223 int
1224 privsep_xauth_login_pam(port, raddr, usr, pwd)
1225 int port;
1226 struct sockaddr *raddr;
1227 char *usr;
1228 char *pwd;
1229 {
1230 struct privsep_com_msg *msg;
1231 size_t len;
1232 char *data;
1233 int result;
1234
1235 if (geteuid() == 0)
1236 return xauth_login_pam(port, raddr, usr, pwd);
1237
1238 len = sizeof(*msg)
1239 + sizeof(port)
1240 + sizeof(isakmp_cfg_config.pool_size)
1241 + sysdep_sa_len(raddr)
1242 + strlen(usr) + 1
1243 + strlen(pwd) + 1;
1244
1245 if ((msg = racoon_malloc(len)) == NULL) {
1246 plog(LLV_ERROR, LOCATION, NULL,
1247 "Cannot allocate memory: %s\n", strerror(errno));
1248 return -1;
1249 }
1250 bzero(msg, len);
1251 msg->hdr.ac_cmd = PRIVSEP_XAUTH_LOGIN_PAM;
1252 msg->hdr.ac_len = len;
1253 msg->bufs.buflen[0] = sizeof(port);
1254 msg->bufs.buflen[1] = sizeof(isakmp_cfg_config.pool_size);
1255 msg->bufs.buflen[2] = sysdep_sa_len(raddr);
1256 msg->bufs.buflen[3] = strlen(usr) + 1;
1257 msg->bufs.buflen[4] = strlen(pwd) + 1;
1258
1259 data = (char *)(msg + 1);
1260 memcpy(data, &port, msg->bufs.buflen[0]);
1261
1262 data += msg->bufs.buflen[0];
1263 memcpy(data, &isakmp_cfg_config.pool_size, msg->bufs.buflen[1]);
1264
1265 data += msg->bufs.buflen[1];
1266 memcpy(data, raddr, msg->bufs.buflen[2]);
1267
1268 data += msg->bufs.buflen[2];
1269 memcpy(data, usr, msg->bufs.buflen[3]);
1270
1271 data += msg->bufs.buflen[3];
1272 memcpy(data, pwd, msg->bufs.buflen[4]);
1273
1274 if (privsep_send(privsep_sock[1], msg, len) != 0)
1275 return -1;
1276
1277 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1278 return -1;
1279
1280 if (msg->hdr.ac_errno != 0) {
1281 errno = msg->hdr.ac_errno;
1282 goto out;
1283 }
1284
1285 racoon_free(msg);
1286 return 0;
1287
1288 out:
1289 racoon_free(msg);
1290 return -1;
1291 }
1292
1293 void
1294 privsep_cleanup_pam(port)
1295 int port;
1296 {
1297 struct privsep_com_msg *msg;
1298 size_t len;
1299 char *data;
1300 int result;
1301
1302 if (geteuid() == 0)
1303 return cleanup_pam(port);
1304
1305 len = sizeof(*msg)
1306 + sizeof(port)
1307 + sizeof(isakmp_cfg_config.pool_size);
1308
1309 if ((msg = racoon_malloc(len)) == NULL) {
1310 plog(LLV_ERROR, LOCATION, NULL,
1311 "Cannot allocate memory: %s\n", strerror(errno));
1312 return;
1313 }
1314 bzero(msg, len);
1315 msg->hdr.ac_cmd = PRIVSEP_CLEANUP_PAM;
1316 msg->hdr.ac_len = len;
1317 msg->bufs.buflen[0] = sizeof(port);
1318 msg->bufs.buflen[1] = sizeof(isakmp_cfg_config.pool_size);
1319
1320 data = (char *)(msg + 1);
1321 memcpy(data, &port, msg->bufs.buflen[0]);
1322
1323 data += msg->bufs.buflen[0];
1324 memcpy(data, &isakmp_cfg_config.pool_size, msg->bufs.buflen[1]);
1325
1326 if (privsep_send(privsep_sock[1], msg, len) != 0)
1327 return;
1328
1329 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1330 return;
1331
1332 if (msg->hdr.ac_errno != 0)
1333 errno = msg->hdr.ac_errno;
1334
1335 racoon_free(msg);
1336 return;
1337 }
1338 #endif