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