• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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