/src/radvd/privsep-linux.c
Line | Count | Source |
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 | | |
28 | 0 | 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 */ |
46 | | static void privsep_read_loop(void) |
47 | 0 | { |
48 | 0 | while (1) { |
49 | 0 | struct privsep_command cmd; |
50 | 0 | int ret = readn(pfd, &cmd, sizeof(cmd)); |
51 | 0 | if (ret <= 0) { |
52 | | /* Error or EOF, give up */ |
53 | 0 | if (ret < 0) { |
54 | 0 | flog(LOG_ERR, "Exiting, privsep_read_loop had readn error: %s", strerror(errno)); |
55 | 0 | } else { |
56 | 0 | flog(LOG_ERR, "Exiting, privsep_read_loop had readn return 0 bytes"); |
57 | 0 | } |
58 | 0 | } |
59 | 0 | if (ret != sizeof(cmd)) { |
60 | | /* Short read, ignore */ |
61 | 0 | return; |
62 | 0 | } |
63 | | |
64 | 0 | cmd.iface[IFNAMSIZ - 1] = '\0'; |
65 | |
|
66 | 0 | switch (cmd.type) { |
67 | | |
68 | 0 | case SET_INTERFACE_LINKMTU: |
69 | 0 | if (cmd.val < MIN_AdvLinkMTU || cmd.val > MAX_AdvLinkMTU) { |
70 | 0 | flog(LOG_ERR, "(privsep) %s: LinkMTU (%u) is not within the defined bounds, ignoring", cmd.iface, |
71 | 0 | cmd.val); |
72 | 0 | break; |
73 | 0 | } |
74 | 0 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", cmd.val); |
75 | 0 | break; |
76 | | |
77 | 0 | case SET_INTERFACE_CURHLIM: |
78 | 0 | if (cmd.val < MIN_AdvCurHopLimit || cmd.val > MAX_AdvCurHopLimit) { |
79 | 0 | flog(LOG_ERR, "(privsep) %s: CurHopLimit (%u) is not within the defined bounds, ignoring", |
80 | 0 | cmd.iface, cmd.val); |
81 | 0 | break; |
82 | 0 | } |
83 | 0 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", cmd.val); |
84 | 0 | break; |
85 | | |
86 | 0 | case SET_INTERFACE_REACHTIME: |
87 | 0 | if (cmd.val < MIN_AdvReachableTime || cmd.val > MAX_AdvReachableTime) { |
88 | 0 | flog(LOG_ERR, "(privsep) %s: BaseReachableTimer (%u) is not within the defined bounds, ignoring", |
89 | 0 | cmd.iface, cmd.val); |
90 | 0 | break; |
91 | 0 | } |
92 | 0 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME_MS, "BaseReachableTimer (ms)", cmd.val); |
93 | 0 | if (ret == 0) |
94 | 0 | break; |
95 | 0 | set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", cmd.val / 1000); |
96 | 0 | break; |
97 | | |
98 | 0 | case SET_INTERFACE_RETRANSTIMER: |
99 | 0 | if (cmd.val < MIN_AdvRetransTimer || cmd.val > MAX_AdvRetransTimer) { |
100 | 0 | flog(LOG_ERR, "(privsep) %s: RetransTimer (%u) is not within the defined bounds, ignoring", |
101 | 0 | cmd.iface, cmd.val); |
102 | 0 | break; |
103 | 0 | } |
104 | 0 | ret = set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER_MS, "RetransTimer (ms)", cmd.val); |
105 | 0 | if (ret == 0) |
106 | 0 | break; |
107 | 0 | set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer", |
108 | 0 | cmd.val / 1000 * USER_HZ); /* XXX user_hz */ |
109 | 0 | break; |
110 | | |
111 | 0 | default: |
112 | | /* Bad command */ |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | void privsep_init(int fd) |
119 | 0 | { |
120 | | /* This will be the privileged child */ |
121 | 0 | pfd = fd; |
122 | 0 | privsep_read_loop(); |
123 | 0 | close(pfd); |
124 | 0 | flog(LOG_ERR, "Exiting, privsep_read_loop is complete."); |
125 | 0 | } |
126 | | |
127 | | /* Interface calls for the unprivileged process */ |
128 | | int privsep_interface_linkmtu(const char *iface, uint32_t mtu) |
129 | 0 | { |
130 | 0 | struct privsep_command cmd; |
131 | 0 | cmd.type = SET_INTERFACE_LINKMTU; |
132 | 0 | memset(&cmd.iface, 0, sizeof(cmd.iface)); |
133 | 0 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); |
134 | 0 | cmd.val = mtu; |
135 | |
|
136 | 0 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) |
137 | 0 | return -1; |
138 | 0 | return 0; |
139 | 0 | } |
140 | | |
141 | | int privsep_interface_curhlim(const char *iface, uint32_t hlim) |
142 | 0 | { |
143 | 0 | struct privsep_command cmd; |
144 | 0 | cmd.type = SET_INTERFACE_CURHLIM; |
145 | 0 | memset(&cmd.iface, 0, sizeof(cmd.iface)); |
146 | 0 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); |
147 | 0 | cmd.val = hlim; |
148 | 0 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) |
149 | 0 | return -1; |
150 | 0 | return 0; |
151 | 0 | } |
152 | | |
153 | | int privsep_interface_reachtime(const char *iface, uint32_t rtime) |
154 | 0 | { |
155 | 0 | struct privsep_command cmd; |
156 | 0 | cmd.type = SET_INTERFACE_REACHTIME; |
157 | 0 | memset(&cmd.iface, 0, sizeof(cmd.iface)); |
158 | 0 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); |
159 | 0 | cmd.val = rtime; |
160 | 0 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) |
161 | 0 | return -1; |
162 | 0 | return 0; |
163 | 0 | } |
164 | | |
165 | | int privsep_interface_retranstimer(const char *iface, uint32_t rettimer) |
166 | 0 | { |
167 | 0 | struct privsep_command cmd; |
168 | 0 | cmd.type = SET_INTERFACE_RETRANSTIMER; |
169 | 0 | memset(&cmd.iface, 0, sizeof(cmd.iface)); |
170 | 0 | strlcpy(cmd.iface, iface, sizeof(cmd.iface)); |
171 | 0 | cmd.val = rettimer; |
172 | 0 | if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) |
173 | 0 | return -1; |
174 | 0 | return 0; |
175 | 0 | } |
176 | | |
177 | | /* note: also called from the root context */ |
178 | | static int set_interface_var(const char *iface, const char *var, const char *name, uint32_t val) |
179 | 0 | { |
180 | 0 | int retval = -1; |
181 | 0 | FILE *fp = 0; |
182 | 0 | char *spath = strdupf(var, iface); |
183 | 0 | int fd = -1 ; |
184 | | |
185 | | /* No path traversal */ |
186 | | // TODO: if interface names contain '/' in future, this may break. |
187 | 0 | if (!iface[0] || !strcmp(iface, ".") || !strcmp(iface, "..") || strchr(iface, '/')) |
188 | 0 | goto errmsg; |
189 | | |
190 | | // Open the file for writing, ONLY if it already exists. |
191 | | // explicitly ensure that O_CREAT is *NOT* set. |
192 | 0 | fd = open(spath, O_WRONLY & (~O_CREAT), 0); |
193 | 0 | if (fd == -1) { |
194 | | // If the file does NOT exist, this is non-fatal; as we fallback to a |
195 | | // different filename at a higher level. |
196 | | // ensure we cleanup and return -1 to the higher level |
197 | 0 | if (errno == ENOENT) |
198 | 0 | goto cleanup; |
199 | | // If we got some other error, e.g. ENOACCESS |
200 | 0 | goto errmsg; |
201 | 0 | } |
202 | | |
203 | | // We know the file exists now, write to it. |
204 | 0 | fp = fdopen(fd, "w"); |
205 | 0 | if (!fp) |
206 | 0 | goto errmsg; |
207 | | |
208 | 0 | if (fprintf(fp, "%u", val) > 0) { |
209 | 0 | retval = 0; |
210 | 0 | } |
211 | |
|
212 | 0 | errmsg: |
213 | 0 | if (name && retval != 0) |
214 | 0 | flog(LOG_ERR, "failed to set %s (%u) for %s: %s", name, val, iface, strerror(errno)); |
215 | 0 | cleanup: |
216 | |
|
217 | 0 | if (fp) |
218 | 0 | fclose(fp); |
219 | 0 | if (fd != -1) |
220 | 0 | close(fd); |
221 | |
|
222 | 0 | free(spath); |
223 | |
|
224 | 0 | return retval; |
225 | 0 | } |