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
privsep_send(sock,buf,len)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
privsep_recv(sock,bufp,lenp)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
privsep_init(void)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
395 count++; /* name */
396
397 for (; count < PRIVSEP_NBUF_MAX; count++) {
398 if (combuf->bufs.buflen[count] == 0)
399 break;
400 bufs[count]
401 [combuf->bufs.buflen[count] - 1] = '\0';
402 envc++;
403 }
404
405 /* count a void buf and perform safety check */
406 count++;
407 if (count >= PRIVSEP_NBUF_MAX) {
408 plog(LLV_ERROR, LOCATION, NULL,
409 "privsep_script_exec: too many args\n");
410 goto out;
411 }
412
413
414 /*
415 * Allocate the arrays for envp
416 */
417 envp = racoon_malloc((envc + 1) * sizeof(char *));
418 if (envp == NULL) {
419 plog(LLV_ERROR, LOCATION, NULL,
420 "cannot allocate memory: %s\n",
421 strerror(errno));
422 goto out;
423 }
424 bzero(envp, (envc + 1) * sizeof(char *));
425
426
427 /*
428 * Populate script, name and envp
429 */
430 count = 0;
431 script = bufs[count++];
432
433 if (combuf->bufs.buflen[count] != sizeof(name)) {
434 plog(LLV_ERROR, LOCATION, NULL,
435 "privsep_script_exec: corrupted message\n");
436 goto out;
437 }
438 memcpy((char *)&name, bufs[count++], sizeof(name));
439
440 for (i = 0; combuf->bufs.buflen[count]; count++)
441 envp[i++] = bufs[count];
442
443 count++; /* void */
444
445 plog(LLV_DEBUG, LOCATION, NULL,
446 "script_exec(\"%s\", %d, %p)\n",
447 script, name, envp);
448
449 /*
450 * Check env for dangerous variables
451 * Check script path and name
452 * Perform fork and execve
453 */
454 if ((unsafe_env(envp) == 0) &&
455 (unknown_name(name) == 0) &&
456 (unsafe_path(script, LC_PATHTYPE_SCRIPT) == 0))
457 (void)script_exec(script, name, envp);
458 else
459 plog(LLV_ERROR, LOCATION, NULL,
460 "privsep_script_exec: "
461 "unsafe script \"%s\"\n", script);
462
463 racoon_free(envp);
464 break;
465 }
466
467 case PRIVSEP_GETPSK: {
468 vchar_t *psk;
469 int keylen;
470
471 /* Make sure the string is NULL terminated */
472 if (safety_check(combuf, 0) != 0)
473 break;
474 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
475
476 if (combuf->bufs.buflen[1] != sizeof(keylen)) {
477 plog(LLV_ERROR, LOCATION, NULL,
478 "privsep_getpsk: corrupted message\n");
479 goto out;
480 }
481 memcpy(&keylen, bufs[1], sizeof(keylen));
482
483 plog(LLV_DEBUG, LOCATION, NULL,
484 "getpsk(\"%s\", %d)\n", bufs[0], keylen);
485
486 if ((psk = getpsk(bufs[0], keylen)) == NULL) {
487 reply->hdr.ac_errno = errno;
488 break;
489 }
490
491 reply->bufs.buflen[0] = psk->l;
492 reply->hdr.ac_len = sizeof(*reply) + psk->l;
493 reply = racoon_realloc(reply, reply->hdr.ac_len);
494 if (reply == NULL) {
495 plog(LLV_ERROR, LOCATION, NULL,
496 "Cannot allocate reply buffer: %s\n",
497 strerror(errno));
498 goto out;
499 }
500
501 memcpy(reply + 1, psk->v, psk->l);
502 vfree(psk);
503 break;
504 }
505
506 #ifdef ENABLE_HYBRID
507 case PRIVSEP_ACCOUNTING_SYSTEM: {
508 int pool_size;
509 int port;
510 int inout;
511 struct sockaddr *raddr;
512
513 if (safety_check(combuf, 0) != 0)
514 break;
515 if (safety_check(combuf, 1) != 0)
516 break;
517 if (safety_check(combuf, 2) != 0)
518 break;
519 if (safety_check(combuf, 3) != 0)
520 break;
521
522 memcpy(&port, bufs[0], sizeof(port));
523 raddr = (struct sockaddr *)bufs[1];
524
525 bufs[2][combuf->bufs.buflen[2] - 1] = '\0';
526 memcpy(&inout, bufs[3], sizeof(port));
527
528 if (port_check(port) != 0)
529 break;
530
531 plog(LLV_DEBUG, LOCATION, NULL,
532 "accounting_system(%d, %s, %s)\n",
533 port, saddr2str(raddr), bufs[2]);
534
535 errno = 0;
536 if (isakmp_cfg_accounting_system(port,
537 raddr, bufs[2], inout) != 0) {
538 if (errno == 0)
539 reply->hdr.ac_errno = EINVAL;
540 else
541 reply->hdr.ac_errno = errno;
542 }
543 break;
544 }
545 case PRIVSEP_XAUTH_LOGIN_SYSTEM: {
546 if (safety_check(combuf, 0) != 0)
547 break;
548 bufs[0][combuf->bufs.buflen[0] - 1] = '\0';
549
550 if (safety_check(combuf, 1) != 0)
551 break;
552 bufs[1][combuf->bufs.buflen[1] - 1] = '\0';
553
554 plog(LLV_DEBUG, LOCATION, NULL,
555 "xauth_login_system(\"%s\", <password>)\n",
556 bufs[0]);
557
558 errno = 0;
559 if (xauth_login_system(bufs[0], bufs[1]) != 0) {
560 if (errno == 0)
561 reply->hdr.ac_errno = EINVAL;
562 else
563 reply->hdr.ac_errno = errno;
564 }
565 break;
566 }
567 #ifdef HAVE_LIBPAM
568 case PRIVSEP_ACCOUNTING_PAM: {
569 int port;
570 int inout;
571 int pool_size;
572
573 if (safety_check(combuf, 0) != 0)
574 break;
575 if (safety_check(combuf, 1) != 0)
576 break;
577 if (safety_check(combuf, 2) != 0)
578 break;
579
580 memcpy(&port, bufs[0], sizeof(port));
581 memcpy(&inout, bufs[1], sizeof(inout));
582 memcpy(&pool_size, bufs[2], sizeof(pool_size));
583
584 if (pool_size != isakmp_cfg_config.pool_size)
585 if (isakmp_cfg_resize_pool(pool_size) != 0)
586 break;
587
588 if (port_check(port) != 0)
589 break;
590
591 plog(LLV_DEBUG, LOCATION, NULL,
592 "isakmp_cfg_accounting_pam(%d, %d)\n",
593 port, inout);
594
595 errno = 0;
596 if (isakmp_cfg_accounting_pam(port, inout) != 0) {
597 if (errno == 0)
598 reply->hdr.ac_errno = EINVAL;
599 else
600 reply->hdr.ac_errno = errno;
601 }
602 break;
603 }
604
605 case PRIVSEP_XAUTH_LOGIN_PAM: {
606 int port;
607 int pool_size;
608 struct sockaddr *raddr;
609
610 if (safety_check(combuf, 0) != 0)
611 break;
612 if (safety_check(combuf, 1) != 0)
613 break;
614 if (safety_check(combuf, 2) != 0)
615 break;
616 if (safety_check(combuf, 3) != 0)
617 break;
618 if (safety_check(combuf, 4) != 0)
619 break;
620
621 memcpy(&port, bufs[0], sizeof(port));
622 memcpy(&pool_size, bufs[1], sizeof(pool_size));
623 raddr = (struct sockaddr *)bufs[2];
624
625 bufs[3][combuf->bufs.buflen[3] - 1] = '\0';
626 bufs[4][combuf->bufs.buflen[4] - 1] = '\0';
627
628 if (pool_size != isakmp_cfg_config.pool_size)
629 if (isakmp_cfg_resize_pool(pool_size) != 0)
630 break;
631
632 if (port_check(port) != 0)
633 break;
634
635 plog(LLV_DEBUG, LOCATION, NULL,
636 "xauth_login_pam(%d, %s, \"%s\", <password>)\n",
637 port, saddr2str(raddr), bufs[3]);
638
639 errno = 0;
640 if (xauth_login_pam(port,
641 raddr, bufs[3], bufs[4]) != 0) {
642 if (errno == 0)
643 reply->hdr.ac_errno = EINVAL;
644 else
645 reply->hdr.ac_errno = errno;
646 }
647 break;
648 }
649
650 case PRIVSEP_CLEANUP_PAM: {
651 int port;
652 int pool_size;
653
654 if (safety_check(combuf, 0) != 0)
655 break;
656 if (safety_check(combuf, 1) != 0)
657 break;
658
659 memcpy(&port, bufs[0], sizeof(port));
660 memcpy(&pool_size, bufs[1], sizeof(pool_size));
661
662 if (pool_size != isakmp_cfg_config.pool_size)
663 if (isakmp_cfg_resize_pool(pool_size) != 0)
664 break;
665
666 if (port_check(port) != 0)
667 break;
668
669 plog(LLV_DEBUG, LOCATION, NULL,
670 "cleanup_pam(%d)\n", port);
671
672 cleanup_pam(port);
673 reply->hdr.ac_errno = 0;
674
675 break;
676 }
677 #endif /* HAVE_LIBPAM */
678 #endif /* ENABLE_HYBRID */
679
680 default:
681 plog(LLV_ERROR, LOCATION, NULL,
682 "unexpected privsep command %d\n",
683 combuf->hdr.ac_cmd);
684 goto out;
685 break;
686 }
687
688 /* This frees reply */
689 if (privsep_send(privsep_sock[0],
690 reply, reply->hdr.ac_len) != 0)
691 goto out;
692
693 racoon_free(combuf);
694 }
695
696 out:
697 plog(LLV_INFO, LOCATION, NULL, "privsep exit\n");
698 _exit(0);
699 }
700
701
702 vchar_t *
privsep_eay_get_pkcs1privkey(path)703 privsep_eay_get_pkcs1privkey(path)
704 char *path;
705 {
706 vchar_t *privkey;
707 struct privsep_com_msg *msg;
708 size_t len;
709
710 if (geteuid() == 0)
711 return eay_get_pkcs1privkey(path);
712
713 len = sizeof(*msg) + strlen(path) + 1;
714 if ((msg = racoon_malloc(len)) == NULL) {
715 plog(LLV_ERROR, LOCATION, NULL,
716 "Cannot allocate memory: %s\n", strerror(errno));
717 return NULL;
718 }
719 bzero(msg, len);
720 msg->hdr.ac_cmd = PRIVSEP_EAY_GET_PKCS1PRIVKEY;
721 msg->hdr.ac_len = len;
722 msg->bufs.buflen[0] = len - sizeof(*msg);
723 memcpy(msg + 1, path, msg->bufs.buflen[0]);
724
725 if (privsep_send(privsep_sock[1], msg, len) != 0)
726 return NULL;
727
728 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
729 return NULL;
730
731 if (msg->hdr.ac_errno != 0) {
732 errno = msg->hdr.ac_errno;
733 goto out;
734 }
735
736 if ((privkey = vmalloc(len - sizeof(*msg))) == NULL)
737 goto out;
738
739 memcpy(privkey->v, msg + 1, privkey->l);
740 racoon_free(msg);
741 return privkey;
742
743 out:
744 racoon_free(msg);
745 return NULL;
746 }
747
748 /*
749 * No prigilege separation trick here, we just open PFKEY before
750 * dropping root privs and we remember it later.
751 */
752 static int pfkey_socket = -1;
753 int
privsep_pfkey_open(void)754 privsep_pfkey_open(void)
755 {
756 int ps;
757
758 if (pfkey_socket != -1)
759 return pfkey_socket;
760
761 ps = pfkey_open();
762 if (ps != -1)
763 pfkey_socket = ps;
764
765 return ps;
766 }
767
768 /*
769 * Consequence of the above trickery: don't
770 * really close PFKEY as we never re-open it.
771 */
772 void
privsep_pfkey_close(ps)773 privsep_pfkey_close(ps)
774 int ps;
775 {
776 return;
777 }
778
779 int
privsep_script_exec(script,name,envp)780 privsep_script_exec(script, name, envp)
781 char *script;
782 int name;
783 char *const envp[];
784 {
785 int count = 0;
786 char *const *c;
787 char *data;
788 size_t len;
789 struct privsep_com_msg *msg;
790
791 if (geteuid() == 0)
792 return script_exec(script, name, envp);
793
794 if ((msg = racoon_malloc(sizeof(*msg))) == NULL) {
795 plog(LLV_ERROR, LOCATION, NULL,
796 "Cannot allocate memory: %s\n", strerror(errno));
797 return -1;
798 }
799
800 bzero(msg, sizeof(*msg));
801 msg->hdr.ac_cmd = PRIVSEP_SCRIPT_EXEC;
802 msg->hdr.ac_len = sizeof(*msg);
803
804 /*
805 * We send:
806 * script, name, envp[0], ... envp[N], void
807 */
808
809 /*
810 * Safety check on the counts: PRIVSEP_NBUF_MAX max
811 */
812 count = 0;
813 count++; /* script */
814 count++; /* name */
815 for (c = envp; *c; c++) /* envp */
816 count++;
817 count++; /* void */
818
819 if (count > PRIVSEP_NBUF_MAX) {
820 plog(LLV_ERROR, LOCATION, NULL, "Unexpected error: "
821 "privsep_script_exec count > PRIVSEP_NBUF_MAX\n");
822 racoon_free(msg);
823 return -1;
824 }
825
826
827 /*
828 * Compute the length
829 */
830 count = 0;
831 msg->bufs.buflen[count] = strlen(script) + 1; /* script */
832 msg->hdr.ac_len += msg->bufs.buflen[count++];
833
834 msg->bufs.buflen[count] = sizeof(name); /* name */
835 msg->hdr.ac_len += msg->bufs.buflen[count++];
836
837 for (c = envp; *c; c++) { /* envp */
838 msg->bufs.buflen[count] = strlen(*c) + 1;
839 msg->hdr.ac_len += msg->bufs.buflen[count++];
840 }
841
842 msg->bufs.buflen[count] = 0; /* void */
843 msg->hdr.ac_len += msg->bufs.buflen[count++];
844
845 if ((msg = racoon_realloc(msg, msg->hdr.ac_len)) == NULL) {
846 plog(LLV_ERROR, LOCATION, NULL,
847 "Cannot allocate memory: %s\n", strerror(errno));
848 return -1;
849 }
850
851 /*
852 * Now copy the data
853 */
854 data = (char *)(msg + 1);
855 count = 0;
856
857 memcpy(data, (char *)script, msg->bufs.buflen[count]); /* script */
858 data += msg->bufs.buflen[count++];
859
860 memcpy(data, (char *)&name, msg->bufs.buflen[count]); /* name */
861 data += msg->bufs.buflen[count++];
862
863 for (c = envp; *c; c++) { /* envp */
864 memcpy(data, *c, msg->bufs.buflen[count]);
865 data += msg->bufs.buflen[count++];
866 }
867
868 count++; /* void */
869
870 /*
871 * And send it!
872 */
873 if (privsep_send(privsep_sock[1], msg, msg->hdr.ac_len) != 0)
874 return -1;
875
876 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
877 return -1;
878
879 if (msg->hdr.ac_errno != 0) {
880 errno = msg->hdr.ac_errno;
881 racoon_free(msg);
882 return -1;
883 }
884
885 racoon_free(msg);
886 return 0;
887 }
888
889 vchar_t *
privsep_getpsk(str,keylen)890 privsep_getpsk(str, keylen)
891 const char *str;
892 int keylen;
893 {
894 vchar_t *psk;
895 struct privsep_com_msg *msg;
896 size_t len;
897 int *keylenp;
898 char *data;
899
900 if (geteuid() == 0)
901 return getpsk(str, keylen);
902
903 len = sizeof(*msg) + strlen(str) + 1 + sizeof(keylen);
904 if ((msg = racoon_malloc(len)) == NULL) {
905 plog(LLV_ERROR, LOCATION, NULL,
906 "Cannot allocate memory: %s\n", strerror(errno));
907 return NULL;
908 }
909 bzero(msg, len);
910 msg->hdr.ac_cmd = PRIVSEP_GETPSK;
911 msg->hdr.ac_len = len;
912
913 data = (char *)(msg + 1);
914 msg->bufs.buflen[0] = strlen(str) + 1;
915 memcpy(data, str, msg->bufs.buflen[0]);
916
917 data += msg->bufs.buflen[0];
918 msg->bufs.buflen[1] = sizeof(keylen);
919 memcpy(data, &keylen, sizeof(keylen));
920
921 if (privsep_send(privsep_sock[1], msg, len) != 0)
922 return NULL;
923
924 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
925 return NULL;
926
927 if (msg->hdr.ac_errno != 0) {
928 errno = msg->hdr.ac_errno;
929 goto out;
930 }
931
932 if ((psk = vmalloc(len - sizeof(*msg))) == NULL)
933 goto out;
934
935 memcpy(psk->v, msg + 1, psk->l);
936 racoon_free(msg);
937 return psk;
938
939 out:
940 racoon_free(msg);
941 return NULL;
942 }
943
944 #ifdef ENABLE_HYBRID
945 int
privsep_xauth_login_system(usr,pwd)946 privsep_xauth_login_system(usr, pwd)
947 char *usr;
948 char *pwd;
949 {
950 struct privsep_com_msg *msg;
951 size_t len;
952 char *data;
953
954 if (geteuid() == 0)
955 return xauth_login_system(usr, pwd);
956
957 len = sizeof(*msg) + strlen(usr) + 1 + strlen(pwd) + 1;
958 if ((msg = racoon_malloc(len)) == NULL) {
959 plog(LLV_ERROR, LOCATION, NULL,
960 "Cannot allocate memory: %s\n", strerror(errno));
961 return -1;
962 }
963 bzero(msg, len);
964 msg->hdr.ac_cmd = PRIVSEP_XAUTH_LOGIN_SYSTEM;
965 msg->hdr.ac_len = len;
966
967 data = (char *)(msg + 1);
968 msg->bufs.buflen[0] = strlen(usr) + 1;
969 memcpy(data, usr, msg->bufs.buflen[0]);
970 data += msg->bufs.buflen[0];
971
972 msg->bufs.buflen[1] = strlen(pwd) + 1;
973 memcpy(data, pwd, msg->bufs.buflen[1]);
974
975 if (privsep_send(privsep_sock[1], msg, len) != 0)
976 return -1;
977
978 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
979 return -1;
980
981 if (msg->hdr.ac_errno != 0) {
982 racoon_free(msg);
983 return -1;
984 }
985
986 racoon_free(msg);
987 return 0;
988 }
989
990 int
privsep_accounting_system(port,raddr,usr,inout)991 privsep_accounting_system(port, raddr, usr, inout)
992 int port;
993 struct sockaddr *raddr;
994 char *usr;
995 int inout;
996 {
997 struct privsep_com_msg *msg;
998 size_t len;
999 char *data;
1000 int result;
1001
1002 if (geteuid() == 0)
1003 return isakmp_cfg_accounting_system(port, raddr,
1004 usr, inout);
1005
1006 len = sizeof(*msg)
1007 + sizeof(port)
1008 + sysdep_sa_len(raddr)
1009 + strlen(usr) + 1
1010 + sizeof(inout);
1011
1012 if ((msg = racoon_malloc(len)) == NULL) {
1013 plog(LLV_ERROR, LOCATION, NULL,
1014 "Cannot allocate memory: %s\n", strerror(errno));
1015 return -1;
1016 }
1017 bzero(msg, len);
1018 msg->hdr.ac_cmd = PRIVSEP_ACCOUNTING_SYSTEM;
1019 msg->hdr.ac_len = len;
1020 msg->bufs.buflen[0] = sizeof(port);
1021 msg->bufs.buflen[1] = sysdep_sa_len(raddr);
1022 msg->bufs.buflen[2] = strlen(usr) + 1;
1023 msg->bufs.buflen[3] = sizeof(inout);
1024
1025 data = (char *)(msg + 1);
1026 memcpy(data, &port, msg->bufs.buflen[0]);
1027
1028 data += msg->bufs.buflen[0];
1029 memcpy(data, raddr, msg->bufs.buflen[1]);
1030
1031 data += msg->bufs.buflen[1];
1032 memcpy(data, usr, msg->bufs.buflen[2]);
1033
1034 data += msg->bufs.buflen[2];
1035 memcpy(data, &inout, msg->bufs.buflen[3]);
1036
1037 if (privsep_send(privsep_sock[1], msg, len) != 0)
1038 return -1;
1039
1040 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1041 return -1;
1042
1043 if (msg->hdr.ac_errno != 0) {
1044 errno = msg->hdr.ac_errno;
1045 goto out;
1046 }
1047
1048 racoon_free(msg);
1049 return 0;
1050
1051 out:
1052 racoon_free(msg);
1053 return -1;
1054 }
1055
1056 static int
port_check(port)1057 port_check(port)
1058 int port;
1059 {
1060 if ((port < 0) || (port >= isakmp_cfg_config.pool_size)) {
1061 plog(LLV_ERROR, LOCATION, NULL,
1062 "privsep: port %d outside of allowed range [0,%zu]\n",
1063 port, isakmp_cfg_config.pool_size - 1);
1064 return -1;
1065 }
1066
1067 return 0;
1068 }
1069 #endif
1070
1071 static int
safety_check(msg,index)1072 safety_check(msg, index)
1073 struct privsep_com_msg *msg;
1074 int index;
1075 {
1076 if (index >= PRIVSEP_NBUF_MAX) {
1077 plog(LLV_ERROR, LOCATION, NULL,
1078 "privsep: Corrupted message, too many buffers\n");
1079 return -1;
1080 }
1081
1082 if (msg->bufs.buflen[index] == 0) {
1083 plog(LLV_ERROR, LOCATION, NULL,
1084 "privsep: Corrupted message, unexpected void buffer\n");
1085 return -1;
1086 }
1087
1088 return 0;
1089 }
1090
1091 /*
1092 * Filter unsafe environement variables
1093 */
1094 static int
unsafe_env(envp)1095 unsafe_env(envp)
1096 char *const *envp;
1097 {
1098 char *const *e;
1099 char *const *be;
1100 char *const bad_env[] = { "PATH=", "LD_LIBRARY_PATH=", "IFS=", NULL };
1101
1102 for (e = envp; *e; e++) {
1103 for (be = bad_env; *be; be++) {
1104 if (strncmp(*e, *be, strlen(*be)) == 0) {
1105 goto found;
1106 }
1107 }
1108 }
1109
1110 return 0;
1111 found:
1112 plog(LLV_ERROR, LOCATION, NULL,
1113 "privsep_script_exec: unsafe environement variable\n");
1114 return -1;
1115 }
1116
1117 /*
1118 * Check path safety
1119 */
1120 static int
unsafe_path(script,pathtype)1121 unsafe_path(script, pathtype)
1122 char *script;
1123 int pathtype;
1124 {
1125 char *path;
1126 char rpath[MAXPATHLEN + 1];
1127 size_t len;
1128
1129 if (script == NULL)
1130 return -1;
1131
1132 path = lcconf->pathinfo[pathtype];
1133
1134 /* No path was given for scripts: skip the check */
1135 if (path == NULL)
1136 return 0;
1137
1138 if (realpath(script, rpath) == NULL) {
1139 plog(LLV_ERROR, LOCATION, NULL,
1140 "script path \"%s\" is invalid\n", script);
1141 return -1;
1142 }
1143
1144 len = strlen(path);
1145 if (strncmp(path, rpath, len) != 0)
1146 return -1;
1147
1148 return 0;
1149 }
1150
1151 static int
unknown_name(name)1152 unknown_name(name)
1153 int name;
1154 {
1155 if ((name < 0) || (name > SCRIPT_MAX)) {
1156 plog(LLV_ERROR, LOCATION, NULL,
1157 "privsep_script_exec: unsafe name index\n");
1158 return -1;
1159 }
1160
1161 return 0;
1162 }
1163
1164 #ifdef HAVE_LIBPAM
1165 int
privsep_accounting_pam(port,inout)1166 privsep_accounting_pam(port, inout)
1167 int port;
1168 int inout;
1169 {
1170 struct privsep_com_msg *msg;
1171 size_t len;
1172 int *port_data;
1173 int *inout_data;
1174 int *pool_size_data;
1175 int result;
1176
1177 if (geteuid() == 0)
1178 return isakmp_cfg_accounting_pam(port, inout);
1179
1180 len = sizeof(*msg)
1181 + sizeof(port)
1182 + sizeof(inout)
1183 + sizeof(isakmp_cfg_config.pool_size);
1184
1185 if ((msg = racoon_malloc(len)) == NULL) {
1186 plog(LLV_ERROR, LOCATION, NULL,
1187 "Cannot allocate memory: %s\n", strerror(errno));
1188 return -1;
1189 }
1190 bzero(msg, len);
1191 msg->hdr.ac_cmd = PRIVSEP_ACCOUNTING_PAM;
1192 msg->hdr.ac_len = len;
1193 msg->bufs.buflen[0] = sizeof(port);
1194 msg->bufs.buflen[1] = sizeof(inout);
1195 msg->bufs.buflen[2] = sizeof(isakmp_cfg_config.pool_size);
1196
1197 port_data = (int *)(msg + 1);
1198 inout_data = (int *)(port_data + 1);
1199 pool_size_data = (int *)(inout_data + 1);
1200
1201 *port_data = port;
1202 *inout_data = inout;
1203 *pool_size_data = isakmp_cfg_config.pool_size;
1204
1205 if (privsep_send(privsep_sock[1], msg, len) != 0)
1206 return -1;
1207
1208 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1209 return -1;
1210
1211 if (msg->hdr.ac_errno != 0) {
1212 errno = msg->hdr.ac_errno;
1213 goto out;
1214 }
1215
1216 racoon_free(msg);
1217 return 0;
1218
1219 out:
1220 racoon_free(msg);
1221 return -1;
1222 }
1223
1224 int
privsep_xauth_login_pam(port,raddr,usr,pwd)1225 privsep_xauth_login_pam(port, raddr, usr, pwd)
1226 int port;
1227 struct sockaddr *raddr;
1228 char *usr;
1229 char *pwd;
1230 {
1231 struct privsep_com_msg *msg;
1232 size_t len;
1233 char *data;
1234 int result;
1235
1236 if (geteuid() == 0)
1237 return xauth_login_pam(port, raddr, usr, pwd);
1238
1239 len = sizeof(*msg)
1240 + sizeof(port)
1241 + sizeof(isakmp_cfg_config.pool_size)
1242 + sysdep_sa_len(raddr)
1243 + strlen(usr) + 1
1244 + strlen(pwd) + 1;
1245
1246 if ((msg = racoon_malloc(len)) == NULL) {
1247 plog(LLV_ERROR, LOCATION, NULL,
1248 "Cannot allocate memory: %s\n", strerror(errno));
1249 return -1;
1250 }
1251 bzero(msg, len);
1252 msg->hdr.ac_cmd = PRIVSEP_XAUTH_LOGIN_PAM;
1253 msg->hdr.ac_len = len;
1254 msg->bufs.buflen[0] = sizeof(port);
1255 msg->bufs.buflen[1] = sizeof(isakmp_cfg_config.pool_size);
1256 msg->bufs.buflen[2] = sysdep_sa_len(raddr);
1257 msg->bufs.buflen[3] = strlen(usr) + 1;
1258 msg->bufs.buflen[4] = strlen(pwd) + 1;
1259
1260 data = (char *)(msg + 1);
1261 memcpy(data, &port, msg->bufs.buflen[0]);
1262
1263 data += msg->bufs.buflen[0];
1264 memcpy(data, &isakmp_cfg_config.pool_size, msg->bufs.buflen[1]);
1265
1266 data += msg->bufs.buflen[1];
1267 memcpy(data, raddr, msg->bufs.buflen[2]);
1268
1269 data += msg->bufs.buflen[2];
1270 memcpy(data, usr, msg->bufs.buflen[3]);
1271
1272 data += msg->bufs.buflen[3];
1273 memcpy(data, pwd, msg->bufs.buflen[4]);
1274
1275 if (privsep_send(privsep_sock[1], msg, len) != 0)
1276 return -1;
1277
1278 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1279 return -1;
1280
1281 if (msg->hdr.ac_errno != 0) {
1282 errno = msg->hdr.ac_errno;
1283 goto out;
1284 }
1285
1286 racoon_free(msg);
1287 return 0;
1288
1289 out:
1290 racoon_free(msg);
1291 return -1;
1292 }
1293
1294 void
privsep_cleanup_pam(port)1295 privsep_cleanup_pam(port)
1296 int port;
1297 {
1298 struct privsep_com_msg *msg;
1299 size_t len;
1300 char *data;
1301 int result;
1302
1303 if (geteuid() == 0)
1304 return cleanup_pam(port);
1305
1306 len = sizeof(*msg)
1307 + sizeof(port)
1308 + sizeof(isakmp_cfg_config.pool_size);
1309
1310 if ((msg = racoon_malloc(len)) == NULL) {
1311 plog(LLV_ERROR, LOCATION, NULL,
1312 "Cannot allocate memory: %s\n", strerror(errno));
1313 return;
1314 }
1315 bzero(msg, len);
1316 msg->hdr.ac_cmd = PRIVSEP_CLEANUP_PAM;
1317 msg->hdr.ac_len = len;
1318 msg->bufs.buflen[0] = sizeof(port);
1319 msg->bufs.buflen[1] = sizeof(isakmp_cfg_config.pool_size);
1320
1321 data = (char *)(msg + 1);
1322 memcpy(data, &port, msg->bufs.buflen[0]);
1323
1324 data += msg->bufs.buflen[0];
1325 memcpy(data, &isakmp_cfg_config.pool_size, msg->bufs.buflen[1]);
1326
1327 if (privsep_send(privsep_sock[1], msg, len) != 0)
1328 return;
1329
1330 if (privsep_recv(privsep_sock[1], &msg, &len) != 0)
1331 return;
1332
1333 if (msg->hdr.ac_errno != 0)
1334 errno = msg->hdr.ac_errno;
1335
1336 racoon_free(msg);
1337 return;
1338 }
1339 #endif
1340