1 /*
2  *
3  *   Authors:
4  *    Jim Paris			<jim@jtan.com>
5  *    Pedro Roque		<roque@di.fc.ul.pt>
6  *    Lars Fenneberg		<lf@elemental.net>
7  *
8  *   This software is Copyright 1996,1997,2008 by the above mentioned author(s),
9  *   All Rights Reserved.
10  *
11  *   The license which is distributed with this software in the file COPYRIGHT
12  *   applies to this software. If your distribution is missing this file, you
13  *   may request it from <reubenhwk@gmail.com>.
14  *
15  */
16 
17 #include "config.h"
18 #include "includes.h"
19 #include "pathnames.h"
20 #include "radvd.h"
21 
22 static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val);
23 static void privsep_read_loop(void);
24 
25 /* For reading or writing, depending on process */
26 static int pfd = -1;
27 
privsep_set_write_fd(int fd)28 void privsep_set_write_fd(int fd) { pfd = fd; }
29 
30 /* Command types */
31 enum privsep_type {
32 	SET_INTERFACE_LINKMTU,
33 	SET_INTERFACE_CURHLIM,
34 	SET_INTERFACE_REACHTIME,
35 	SET_INTERFACE_RETRANSTIMER,
36 };
37 
38 /* Command sent over pipe is a fixed size binary structure. */
39 struct privsep_command {
40 	int type;
41 	char iface[IFNAMSIZ];
42 	uint32_t val;
43 };
44 
45 /* Privileged read loop */
privsep_read_loop(void)46 static void privsep_read_loop(void)
47 {
48 	while (1) {
49 		struct privsep_command cmd;
50 		int ret = readn(pfd, &cmd, sizeof(cmd));
51 		if (ret <= 0) {
52 			/* Error or EOF, give up */
53 			if (ret < 0) {
54 				flog(LOG_ERR, "Exiting, privsep_read_loop had readn error: %s", strerror(errno));
55 			} else {
56 				flog(LOG_ERR, "Exiting, privsep_read_loop had readn return 0 bytes");
57 			}
58 		}
59 		if (ret != sizeof(cmd)) {
60 			/* Short read, ignore */
61 			return;
62 		}
63 
64 		cmd.iface[IFNAMSIZ - 1] = '\0';
65 
66 		switch (cmd.type) {
67 
68 		case SET_INTERFACE_LINKMTU:
69 			if (cmd.val < MIN_AdvLinkMTU || cmd.val > MAX_AdvLinkMTU) {
70 				flog(LOG_ERR, "(privsep) %s: LinkMTU (%u) is not within the defined bounds, ignoring", cmd.iface,
71 				     cmd.val);
72 				break;
73 			}
74 			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", cmd.val);
75 			break;
76 
77 		case SET_INTERFACE_CURHLIM:
78 			if (cmd.val < MIN_AdvCurHopLimit || cmd.val > MAX_AdvCurHopLimit) {
79 				flog(LOG_ERR, "(privsep) %s: CurHopLimit (%u) is not within the defined bounds, ignoring",
80 				     cmd.iface, cmd.val);
81 				break;
82 			}
83 			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", cmd.val);
84 			break;
85 
86 		case SET_INTERFACE_REACHTIME:
87 			if (cmd.val < MIN_AdvReachableTime || cmd.val > MAX_AdvReachableTime) {
88 				flog(LOG_ERR, "(privsep) %s: BaseReachableTimer (%u) is not within the defined bounds, ignoring",
89 				     cmd.iface, cmd.val);
90 				break;
91 			}
92 			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME_MS, "BaseReachableTimer (ms)", cmd.val);
93 			if (ret == 0)
94 				break;
95 			set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", cmd.val / 1000);
96 			break;
97 
98 		case SET_INTERFACE_RETRANSTIMER:
99 			if (cmd.val < MIN_AdvRetransTimer || cmd.val > MAX_AdvRetransTimer) {
100 				flog(LOG_ERR, "(privsep) %s: RetransTimer (%u) is not within the defined bounds, ignoring",
101 				     cmd.iface, cmd.val);
102 				break;
103 			}
104 			ret = set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER_MS, "RetransTimer (ms)", cmd.val);
105 			if (ret == 0)
106 				break;
107 			set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer",
108 					  cmd.val / 1000 * USER_HZ); /* XXX user_hz */
109 			break;
110 
111 		default:
112 			/* Bad command */
113 			break;
114 		}
115 	}
116 }
117 
privsep_init(int fd)118 void privsep_init(int fd)
119 {
120 	/* This will be the privileged child */
121 	pfd = fd;
122 	privsep_read_loop();
123 	close(pfd);
124 	flog(LOG_ERR, "Exiting, privsep_read_loop is complete.");
125 }
126 
127 /* Interface calls for the unprivileged process */
privsep_interface_linkmtu(const char * iface,uint32_t mtu)128 int privsep_interface_linkmtu(const char *iface, uint32_t mtu)
129 {
130 	struct privsep_command cmd;
131 	cmd.type = SET_INTERFACE_LINKMTU;
132 	strncpy(cmd.iface, iface, sizeof(cmd.iface));
133 	cmd.val = mtu;
134 
135 	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
136 		return -1;
137 	return 0;
138 }
139 
privsep_interface_curhlim(const char * iface,uint32_t hlim)140 int privsep_interface_curhlim(const char *iface, uint32_t hlim)
141 {
142 	struct privsep_command cmd;
143 	cmd.type = SET_INTERFACE_CURHLIM;
144 	strncpy(cmd.iface, iface, sizeof(cmd.iface));
145 	cmd.val = hlim;
146 	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
147 		return -1;
148 	return 0;
149 }
150 
privsep_interface_reachtime(const char * iface,uint32_t rtime)151 int privsep_interface_reachtime(const char *iface, uint32_t rtime)
152 {
153 	struct privsep_command cmd;
154 	cmd.type = SET_INTERFACE_REACHTIME;
155 	strncpy(cmd.iface, iface, sizeof(cmd.iface));
156 	cmd.val = rtime;
157 	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
158 		return -1;
159 	return 0;
160 }
161 
privsep_interface_retranstimer(const char * iface,uint32_t rettimer)162 int privsep_interface_retranstimer(const char *iface, uint32_t rettimer)
163 {
164 	struct privsep_command cmd;
165 	cmd.type = SET_INTERFACE_RETRANSTIMER;
166 	strncpy(cmd.iface, iface, sizeof(cmd.iface));
167 	cmd.val = rettimer;
168 	if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd))
169 		return -1;
170 	return 0;
171 }
172 
173 /* note: also called from the root context */
set_interface_var(const char * iface,const char * var,const char * name,uint32_t val)174 static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val)
175 {
176 	int retval = -1;
177 	FILE *fp = 0;
178 	char *spath = strdupf(var, iface);
179 
180 	/* No path traversal */
181 	if (!iface[0] || !strcmp(iface, ".") || !strcmp(iface, "..") || strchr(iface, '/'))
182 		goto cleanup;
183 
184 	if (access(spath, F_OK) != 0)
185 		goto cleanup;
186 
187 	fp = fopen(spath, "w");
188 	if (!fp) {
189 		if (name)
190 			flog(LOG_ERR, "failed to set %s (%u) for %s: %s", name, val, iface, strerror(errno));
191 		goto cleanup;
192 	}
193 
194 	if (0 > fprintf(fp, "%u", val)) {
195 		goto cleanup;
196 	}
197 
198 	retval = 0;
199 
200 cleanup:
201 	if (fp)
202 		fclose(fp);
203 
204 	free(spath);
205 
206 	return retval;
207 }
208