server/admin.c: Remove spurious `ping' in usage message.
[tripe] / priv / helper.c
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 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.
16 *
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.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
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
41 static void NORETURN lose(const char *excuse)
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
51 /* --- @itrace@ --- *
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
64 static void PRINTF_LIKE(2, 3) itrace(unsigned mask, const char *fmt, ...)
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
94 static void EXECL_LIKE(0) IGNORABLE warn(const char *fmt, ...)
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 -------------------------------------------------*/