1/*
2 *   $Id: privsep-linux.c,v 1.3 2008/01/24 17:08:46 psavola Exp $
3 *
4 *   Authors:
5 *    Jim Paris			<jim@jtan.com>
6 *    Pedro Roque		<roque@di.fc.ul.pt>
7 *    Lars Fenneberg		<lf@elemental.net>
8 *
9 *   This software is Copyright 1996,1997,2008 by the above mentioned author(s),
10 *   All Rights Reserved.
11 *
12 *   The license which is distributed with this software in the file COPYRIGHT
13 *   applies to this software. If your distribution is missing this file, you
14 *   may request it from <pekkas@netcore.fi>.
15 *
16 */
17
18#include <config.h>
19#include <includes.h>
20#include <radvd.h>
21#include <pathnames.h>
22
23int privsep_set(const char *iface, const char *var, uint32_t val);
24void privsep_read_loop(void);
25
26/* For reading or writing, depending on process */
27static int pfd = -1;
28
29/* Command types */
30enum privsep_type {
31	SET_INTERFACE_LINKMTU,
32	SET_INTERFACE_CURHLIM,
33	SET_INTERFACE_REACHTIME,
34	SET_INTERFACE_RETRANSTIMER,
35};
36
37/* Command sent over pipe is a fixed size binary structure. */
38struct privsep_command {
39	int type;
40	char iface[IFNAMSIZ];
41	uint32_t val;
42};
43
44/* Privileged read loop */
45void
46privsep_read_loop(void)
47{
48	struct privsep_command cmd;
49	int ret;
50
51	while (1) {
52		ret = readn(pfd, &cmd, sizeof(cmd));
53		if (ret <= 0) {
54			/* Error or EOF, give up */
55			close(pfd);
56			_exit(0);
57		}
58		if (ret != sizeof(cmd)) {
59			/* Short read, ignore */
60			continue;
61		}
62
63		cmd.iface[IFNAMSIZ-1] = '\0';
64
65		switch(cmd.type) {
66
67		case SET_INTERFACE_LINKMTU:
68			if (cmd.val < MIN_AdvLinkMTU || cmd.val > MAX_AdvLinkMTU) {
69				flog(LOG_ERR, "(privsep) %s: LinkMTU (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val);
70				break;
71			}
72			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", cmd.val);
73			break;
74
75		case SET_INTERFACE_CURHLIM:
76			if (cmd.val < MIN_AdvCurHopLimit || cmd.val > MAX_AdvCurHopLimit) {
77				flog(LOG_ERR, "(privsep) %s: CurHopLimit (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val);
78				break;
79			}
80			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", cmd.val);
81			break;
82
83		case SET_INTERFACE_REACHTIME:
84			if (cmd.val < MIN_AdvReachableTime || cmd.val > MAX_AdvReachableTime) {
85				flog(LOG_ERR, "(privsep) %s: BaseReachableTimer (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val);
86				break;
87			}
88			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME_MS, "BaseReachableTimer (ms)", cmd.val);
89			if (ret == 0)
90				break;
91			set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", cmd.val / 1000);
92			break;
93
94		case SET_INTERFACE_RETRANSTIMER:
95			if (cmd.val < MIN_AdvRetransTimer || cmd.val > MAX_AdvRetransTimer) {
96				flog(LOG_ERR, "(privsep) %s: RetransTimer (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val);
97				break;
98			}
99			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER_MS, "RetransTimer (ms)", cmd.val);
100			if (ret == 0)
101				break;
102			set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer", cmd.val / 1000 * USER_HZ); /* XXX user_hz */
103			break;
104
105		default:
106			/* Bad command */
107			break;
108		}
109	}
110}
111
112/* Return 1 if privsep is currently enabled */
113int
114privsep_enabled(void)
115{
116	if (pfd < 0)
117		return 0;
118	return 1;
119}
120
121/* Fork to create privileged process connected by a pipe */
122int
123privsep_init(void)
124{
125	int pipefds[2];
126	pid_t pid;
127
128	if (privsep_enabled())
129		return 0;
130
131	if (pipe(pipefds) != 0) {
132		flog(LOG_ERR, "Couldn't create privsep pipe.");
133		return (-1);
134	}
135
136	pid = fork();
137	if (pid == -1) {
138		flog(LOG_ERR, "Couldn't fork for privsep.");
139		return (-1);
140	}
141
142	if (pid == 0) {
143		int nullfd;
144
145		/* This will be the privileged child */
146		close(pipefds[1]);
147		pfd = pipefds[0];
148
149		/* Detach from stdio */
150		nullfd = open("/dev/null", O_RDONLY);
151		if (nullfd < 0) {
152			perror("/dev/null");
153			close(pfd);
154			_exit(1);
155		}
156		dup2(nullfd, 0);
157		dup2(nullfd, 1);
158		/* XXX: we'll keep stderr open in debug mode for better logging */
159		if (get_debuglevel() == 0)
160			dup2(nullfd, 2);
161
162		privsep_read_loop();
163		close(pfd);
164		_exit(0);
165	}
166
167	/* Continue execution (will drop privileges soon) */
168	close(pipefds[0]);
169	pfd = pipefds[1];
170
171	return 0;
172}
173
174/* Interface calls for the unprivileged process */
175int
176privsep_interface_linkmtu(const char *iface, uint32_t mtu)
177{
178	struct privsep_command cmd;
179	cmd.type = SET_INTERFACE_LINKMTU;
180	strncpy(cmd.iface, iface, sizeof(cmd.iface));
181	cmd.val = mtu;
182
183	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
184		return (-1);
185	return 0;
186}
187
188int
189privsep_interface_curhlim(const char *iface, uint32_t hlim)
190{
191	struct privsep_command cmd;
192	cmd.type = SET_INTERFACE_CURHLIM;
193	strncpy(cmd.iface, iface, sizeof(cmd.iface));
194	cmd.val = hlim;
195	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
196		return (-1);
197	return 0;
198}
199
200int
201privsep_interface_reachtime(const char *iface, uint32_t rtime)
202{
203	struct privsep_command cmd;
204	cmd.type = SET_INTERFACE_REACHTIME;
205	strncpy(cmd.iface, iface, sizeof(cmd.iface));
206	cmd.val = rtime;
207	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
208		return (-1);
209	return 0;
210}
211
212int
213privsep_interface_retranstimer(const char *iface, uint32_t rettimer)
214{
215	struct privsep_command cmd;
216	cmd.type = SET_INTERFACE_RETRANSTIMER;
217	strncpy(cmd.iface, iface, sizeof(cmd.iface));
218	cmd.val = rettimer;
219	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
220		return (-1);
221	return 0;
222}
223