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
69static int privsep_sock[2] = { -1, -1 };
70
71static int privsep_recv(int, struct privsep_com_msg **, size_t *);
72static int privsep_send(int, struct privsep_com_msg *, size_t);
73static int safety_check(struct privsep_com_msg *, int i);
74static int port_check(int);
75static int unsafe_env(char *const *);
76static int unknown_name(int);
77static int unsafe_path(char *, int);
78
79static int
80privsep_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
101static int
102privsep_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
164int
165privsep_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
696out:
697	plog(LLV_INFO, LOCATION, NULL, "privsep exit\n");
698	_exit(0);
699}
700
701
702vchar_t *
703privsep_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
743out:
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 */
752static int  pfkey_socket = -1;
753int
754privsep_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 */
772void
773privsep_pfkey_close(ps)
774	int ps;
775{
776	return;
777}
778
779int
780privsep_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
889vchar_t *
890privsep_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
939out:
940	racoon_free(msg);
941	return NULL;
942}
943
944#ifdef ENABLE_HYBRID
945int
946privsep_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
990int
991privsep_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
1051out:
1052	racoon_free(msg);
1053	return -1;
1054}
1055
1056static int
1057port_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
1071static int
1072safety_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 */
1094static int
1095unsafe_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;
1111found:
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 */
1120static int
1121unsafe_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
1151static int
1152unknown_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
1165int
1166privsep_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
1219out:
1220	racoon_free(msg);
1221	return -1;
1222}
1223
1224int
1225privsep_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
1289out:
1290	racoon_free(msg);
1291	return -1;
1292}
1293
1294void
1295privsep_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