chiark / gitweb /
server/admin.h: Consolidate address construction during resolution.
[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 -------------------------------------------------*/