Commit | Line | Data |
---|---|---|
388e0319 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Privilege-separated helper | |
4 | * | |
5 | * (c) 2008 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Trivial IP Encryption (TrIPE). | |
11 | * | |
11ad66c2 MW |
12 | * TrIPE is free software: you can redistribute it and/or modify it under |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
388e0319 | 16 | * |
11ad66c2 MW |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
388e0319 MW |
21 | * |
22 | * You should have received a copy of the GNU General Public License | |
11ad66c2 | 23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. |
388e0319 MW |
24 | */ |
25 | ||
26 | /*----- Header files ------------------------------------------------------*/ | |
27 | ||
28 | #include "priv.h" | |
29 | ||
30 | /*----- Helper-side utilities ---------------------------------------------*/ | |
31 | ||
32 | /* --- @lose@ --- * | |
33 | * | |
34 | * Arguments: @const char *excuse@ = what went wrong | |
35 | * | |
36 | * Returns: Doesn't. | |
37 | * | |
38 | * Use: Reports a fatal error and quits. | |
39 | */ | |
40 | ||
21f48320 | 41 | static void NORETURN lose(const char *excuse) |
388e0319 MW |
42 | { |
43 | moan("helper process bailing out: %s; error: %s", | |
44 | excuse, | |
45 | errno == -1 ? "Unexpected EOF" : strerror(errno)); | |
46 | _exit(127); | |
47 | } | |
48 | ||
49 | /*----- Diagnostic functions ----------------------------------------------*/ | |
50 | ||
dd68bf2e | 51 | /* --- @itrace@ --- * |
388e0319 MW |
52 | * |
53 | * Arguments: @unsigned mask@ = trace mask to check | |
54 | * @const char *fmt@ = message format | |
55 | * @...@ = values for placeholders | |
56 | * | |
57 | * Returns: --- | |
58 | * | |
59 | * Use: Writes a trace message. | |
60 | */ | |
61 | ||
62 | #ifndef NTRACE | |
63 | ||
ddb384f1 | 64 | static void PRINTF_LIKE(2, 3) itrace(unsigned mask, const char *fmt, ...) |
388e0319 MW |
65 | { |
66 | va_list ap; | |
67 | dstr d = DSTR_INIT; | |
68 | ||
69 | va_start(ap, fmt); | |
70 | dstr_vputf(&d, fmt, &ap); | |
71 | if (pc_putuint(PS_TRACE) || | |
72 | pc_putuint(mask) || | |
73 | pc_putsz(d.len) || | |
74 | pc_put(d.buf, d.len)) | |
75 | lose("write (trace)"); | |
76 | va_end(ap); | |
77 | dstr_destroy(&d); | |
78 | } | |
79 | ||
80 | #endif | |
81 | ||
82 | /* --- @warn@ --- * | |
83 | * | |
84 | * Arguments: @const char *fmt@ = message format | |
85 | * @...@ = values for placeholders | |
86 | * | |
87 | * Returns: --- | |
88 | * | |
89 | * Use: Writes a warning message. | |
90 | */ | |
91 | ||
92 | #define A_END ((char *)0) | |
93 | ||
ddb384f1 | 94 | static void EXECL_LIKE(0) IGNORABLE warn(const char *fmt, ...) |
388e0319 MW |
95 | { |
96 | va_list ap; | |
97 | dstr d = DSTR_INIT, dd = DSTR_INIT; | |
98 | ||
99 | va_start(ap, fmt); | |
100 | while (fmt) { | |
101 | if (*fmt == '?') { | |
102 | if (strcmp(fmt, "?ERRNO") == 0) { | |
103 | dstr_putf(&d, " E%d", errno); | |
104 | u_quotify(&d, strerror(errno)); | |
105 | } else | |
106 | abort(); | |
107 | } else { | |
108 | DRESET(&dd); | |
109 | dstr_vputf(&dd, fmt, &ap); | |
110 | u_quotify(&d, dd.buf); | |
111 | } | |
112 | fmt = va_arg(ap, const char *); | |
113 | } | |
114 | va_end(ap); | |
115 | ||
116 | if (pc_putuint(PS_WARN) || | |
117 | pc_putsz(d.len) || | |
118 | pc_put(d.buf, d.len)) | |
119 | lose("write (warn)"); | |
120 | ||
121 | dstr_destroy(&d); | |
122 | dstr_destroy(&dd); | |
123 | } | |
124 | ||
125 | /*----- Tunnel drivers ----------------------------------------------------*/ | |
126 | ||
127 | /* --- @topen_DRIVER@ --- * | |
128 | * | |
129 | * Arguments: @char **ifn@ = where to put the interface name | |
130 | * | |
131 | * Returns: A file descriptor, or @-1@ on failure. | |
132 | * | |
133 | * Use: Opens a tunnel device. | |
134 | */ | |
135 | ||
136 | #ifdef TUN_LINUX | |
137 | ||
138 | #include <sys/ioctl.h> | |
139 | #include <linux/if.h> | |
140 | #include <linux/if_tun.h> | |
141 | ||
142 | static int topen_linux(char **ifn) | |
143 | { | |
144 | int fd; | |
145 | struct ifreq iff; | |
146 | ||
147 | if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { | |
148 | warn("TUN", "-", "linux", | |
149 | "open-error", "/dev/net/tun", "?ERRNO", | |
150 | A_END); | |
151 | return (-1); | |
152 | } | |
153 | memset(&iff, 0, sizeof(iff)); | |
154 | iff.ifr_name[0] = 0; | |
155 | iff.ifr_flags = IFF_TUN | IFF_NO_PI; | |
156 | if (ioctl(fd, TUNSETIFF, &iff) < 0) { | |
157 | warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END); | |
158 | close(fd); | |
159 | return (-1); | |
160 | } | |
161 | iff.ifr_name[IFNAMSIZ - 1] = 0; | |
162 | *ifn = xstrdup(iff.ifr_name); | |
163 | return (fd); | |
164 | } | |
165 | ||
166 | #endif | |
167 | ||
168 | #ifdef TUN_BSD | |
169 | ||
170 | static int topen_bsd(char **ifn) | |
171 | { | |
172 | int fd; | |
173 | unsigned n; | |
174 | char buf[16]; | |
175 | ||
176 | n = 0; | |
177 | for (;;) { | |
178 | sprintf(buf, "/dev/tun%u", n); | |
179 | if ((fd = open(buf, O_RDWR)) >= 0) | |
180 | break; | |
181 | switch (errno) { | |
182 | case EBUSY: | |
183 | T( itrace(T_PRIVSEP, "tunnel device %u busy: skipping", n); ) | |
184 | break; | |
185 | case ENOENT: | |
186 | warn("TUN", "-", "bsd", "no-tunnel-devices", A_END); | |
187 | return (-1); | |
188 | default: | |
189 | warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END); | |
190 | break; | |
191 | } | |
192 | n++; | |
193 | } | |
194 | return (fd); | |
195 | } | |
196 | ||
197 | #endif | |
198 | ||
199 | #ifdef TUN_UNET | |
200 | ||
201 | #include <sys/ioctl.h> | |
202 | #include <linux/if.h> | |
203 | #include <unet.h> | |
204 | ||
205 | static int topen_unet(char **ifn) | |
206 | { | |
207 | int fd; | |
208 | int f; | |
209 | struct unet_info uni; | |
210 | ||
211 | if ((fd = open("/dev/unet", O_RDWR)) < 0) { | |
212 | warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END); | |
213 | goto fail_0; | |
214 | } | |
215 | if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 || | |
216 | ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) { | |
217 | warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END); | |
218 | goto fail_1; | |
219 | } | |
220 | if (ioctl(fd, UNIOCGINFO, &uni)) { | |
221 | warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END); | |
222 | goto fail_1; | |
223 | } | |
224 | *ifn = xstrdup(uni.uni_ifname); | |
225 | return (fd); | |
226 | ||
227 | fail_1: | |
228 | close(fd); | |
229 | fail_0: | |
230 | return (-1); | |
231 | } | |
232 | ||
233 | #endif | |
234 | ||
235 | static const struct tunnel { | |
236 | const char *name; | |
237 | int (*open)(char **); | |
238 | } tunnels[] = { | |
239 | #ifdef TUN_LINUX | |
240 | { "linux", topen_linux }, | |
241 | #endif | |
242 | #ifdef TUN_BSD | |
243 | { "bsd", topen_bsd }, | |
244 | #endif | |
245 | #ifdef TUN_UNET | |
246 | { "unet", topen_unet }, | |
247 | #endif | |
248 | { 0, 0 } | |
249 | }; | |
250 | ||
251 | /*----- Helper process core -----------------------------------------------*/ | |
252 | ||
253 | int main(int argc, char *argv[]) | |
254 | { | |
255 | struct sockaddr_un sun; | |
256 | socklen_t slen = sizeof(sun); | |
257 | unsigned rq; | |
258 | dstr d = DSTR_INIT; | |
259 | const struct tunnel *t; | |
260 | char *ifn = 0; | |
261 | int fd; | |
262 | ssize_t n; | |
263 | ||
264 | ego(argv[0]); | |
265 | if (argc != 1 || | |
266 | getpeername(0, (struct sockaddr *)&sun, &slen) || | |
267 | sun.sun_family != AF_UNIX) | |
268 | die(EXIT_FAILURE, "please do not run this program again."); | |
269 | ||
270 | for (;;) { | |
271 | if (pc_getuint(&rq)) { | |
272 | if (errno == -1) break; | |
273 | else lose("read (main)"); | |
274 | } | |
275 | switch (rq) { | |
276 | case PS_TUNRQ: | |
277 | DRESET(&d); | |
278 | if (pc_getstring(&d)) lose("read (tunnel)"); | |
279 | for (t = tunnels;; t++) { | |
280 | if (!t->name) lose("unknown tunnel"); | |
281 | if (strcmp(d.buf, t->name) == 0) break; | |
282 | } | |
283 | T( itrace(T_PRIVSEP, | |
284 | "privsep: received request for %s tunnel", | |
285 | t->name); ) | |
286 | if ((fd = t->open(&ifn)) < 0) | |
287 | goto err; | |
288 | rq = PS_TUNFD; | |
289 | n = fdpass_send(pc_fd, fd, &rq, sizeof(rq)); close(fd); | |
290 | if (n < 0) { xfree(ifn); goto err; } | |
291 | else if (n < sizeof(rq)) lose("partial write (fd-pass)"); | |
292 | if (pc_putstring(ifn)) lose("write (ifname)"); | |
293 | xfree(ifn); | |
294 | break; | |
295 | err: | |
296 | if (pc_putuint(PS_TUNERR) || pc_puterr(errno)) lose("write (error)"); | |
297 | break; | |
298 | default: | |
299 | lose("bad request"); | |
300 | break; | |
301 | } | |
302 | } | |
303 | _exit(0); | |
304 | } | |
305 | ||
306 | /*----- That's all, folks -------------------------------------------------*/ |