X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/blobdiff_plain/6ebbf072683ac7ea09a0aaafa0bb12fc0678d068..388e0319a0faf48193658c82228133bd1ea24eb6:/priv/helper.c diff --git a/priv/helper.c b/priv/helper.c new file mode 100644 index 00000000..c1401f12 --- /dev/null +++ b/priv/helper.c @@ -0,0 +1,307 @@ +/* -*-c-*- + * + * Privilege-separated helper + * + * (c) 2008 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Trivial IP Encryption (TrIPE). + * + * TrIPE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "priv.h" + +/*----- Helper-side utilities ---------------------------------------------*/ + +/* --- @lose@ --- * + * + * Arguments: @const char *excuse@ = what went wrong + * + * Returns: Doesn't. + * + * Use: Reports a fatal error and quits. + */ + +static void lose(const char *excuse) +{ + moan("helper process bailing out: %s; error: %s", + excuse, + errno == -1 ? "Unexpected EOF" : strerror(errno)); + _exit(127); +} + +/*----- Diagnostic functions ----------------------------------------------*/ + +/* --- @trace@ --- * + * + * Arguments: @unsigned mask@ = trace mask to check + * @const char *fmt@ = message format + * @...@ = values for placeholders + * + * Returns: --- + * + * Use: Writes a trace message. + */ + +#ifndef NTRACE + +static void itrace(unsigned mask, const char *fmt, ...) +{ + va_list ap; + dstr d = DSTR_INIT; + + va_start(ap, fmt); + dstr_vputf(&d, fmt, &ap); + if (pc_putuint(PS_TRACE) || + pc_putuint(mask) || + pc_putsz(d.len) || + pc_put(d.buf, d.len)) + lose("write (trace)"); + va_end(ap); + dstr_destroy(&d); +} + +#endif + +/* --- @warn@ --- * + * + * Arguments: @const char *fmt@ = message format + * @...@ = values for placeholders + * + * Returns: --- + * + * Use: Writes a warning message. + */ + +#define A_END ((char *)0) + +static void warn(const char *fmt, ...) +{ + va_list ap; + dstr d = DSTR_INIT, dd = DSTR_INIT; + + va_start(ap, fmt); + while (fmt) { + if (*fmt == '?') { + if (strcmp(fmt, "?ERRNO") == 0) { + dstr_putf(&d, " E%d", errno); + u_quotify(&d, strerror(errno)); + } else + abort(); + } else { + DRESET(&dd); + dstr_vputf(&dd, fmt, &ap); + u_quotify(&d, dd.buf); + } + fmt = va_arg(ap, const char *); + } + va_end(ap); + + if (pc_putuint(PS_WARN) || + pc_putsz(d.len) || + pc_put(d.buf, d.len)) + lose("write (warn)"); + + dstr_destroy(&d); + dstr_destroy(&dd); +} + +/*----- Tunnel drivers ----------------------------------------------------*/ + +/* --- @topen_DRIVER@ --- * + * + * Arguments: @char **ifn@ = where to put the interface name + * + * Returns: A file descriptor, or @-1@ on failure. + * + * Use: Opens a tunnel device. + */ + +#ifdef TUN_LINUX + +#include +#include +#include + +static int topen_linux(char **ifn) +{ + int fd; + struct ifreq iff; + + if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { + warn("TUN", "-", "linux", + "open-error", "/dev/net/tun", "?ERRNO", + A_END); + return (-1); + } + memset(&iff, 0, sizeof(iff)); + iff.ifr_name[0] = 0; + iff.ifr_flags = IFF_TUN | IFF_NO_PI; + if (ioctl(fd, TUNSETIFF, &iff) < 0) { + warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END); + close(fd); + return (-1); + } + iff.ifr_name[IFNAMSIZ - 1] = 0; + *ifn = xstrdup(iff.ifr_name); + return (fd); +} + +#endif + +#ifdef TUN_BSD + +static int topen_bsd(char **ifn) +{ + int fd; + unsigned n; + char buf[16]; + + n = 0; + for (;;) { + sprintf(buf, "/dev/tun%u", n); + if ((fd = open(buf, O_RDWR)) >= 0) + break; + switch (errno) { + case EBUSY: + T( itrace(T_PRIVSEP, "tunnel device %u busy: skipping", n); ) + break; + case ENOENT: + warn("TUN", "-", "bsd", "no-tunnel-devices", A_END); + return (-1); + default: + warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END); + break; + } + n++; + } + return (fd); +} + +#endif + +#ifdef TUN_UNET + +#include +#include +#include + +static int topen_unet(char **ifn) +{ + int fd; + int f; + struct unet_info uni; + + if ((fd = open("/dev/unet", O_RDWR)) < 0) { + warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END); + goto fail_0; + } + if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 || + ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) { + warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END); + goto fail_1; + } + if (ioctl(fd, UNIOCGINFO, &uni)) { + warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END); + goto fail_1; + } + *ifn = xstrdup(uni.uni_ifname); + return (fd); + +fail_1: + close(fd); +fail_0: + return (-1); +} + +#endif + +static const struct tunnel { + const char *name; + int (*open)(char **); +} tunnels[] = { +#ifdef TUN_LINUX + { "linux", topen_linux }, +#endif +#ifdef TUN_BSD + { "bsd", topen_bsd }, +#endif +#ifdef TUN_UNET + { "unet", topen_unet }, +#endif + { 0, 0 } +}; + +/*----- Helper process core -----------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + struct sockaddr_un sun; + socklen_t slen = sizeof(sun); + unsigned rq; + dstr d = DSTR_INIT; + const struct tunnel *t; + char *ifn = 0; + int fd; + ssize_t n; + + ego(argv[0]); + if (argc != 1 || + getpeername(0, (struct sockaddr *)&sun, &slen) || + sun.sun_family != AF_UNIX) + die(EXIT_FAILURE, "please do not run this program again."); + + for (;;) { + if (pc_getuint(&rq)) { + if (errno == -1) break; + else lose("read (main)"); + } + switch (rq) { + case PS_TUNRQ: + DRESET(&d); + if (pc_getstring(&d)) lose("read (tunnel)"); + for (t = tunnels;; t++) { + if (!t->name) lose("unknown tunnel"); + if (strcmp(d.buf, t->name) == 0) break; + } + T( itrace(T_PRIVSEP, + "privsep: received request for %s tunnel", + t->name); ) + if ((fd = t->open(&ifn)) < 0) + goto err; + rq = PS_TUNFD; + n = fdpass_send(pc_fd, fd, &rq, sizeof(rq)); close(fd); + if (n < 0) { xfree(ifn); goto err; } + else if (n < sizeof(rq)) lose("partial write (fd-pass)"); + if (pc_putstring(ifn)) lose("write (ifname)"); + xfree(ifn); + break; + err: + if (pc_putuint(PS_TUNERR) || pc_puterr(errno)) lose("write (error)"); + break; + default: + lose("bad request"); + break; + } + } + _exit(0); +} + +/*----- That's all, folks -------------------------------------------------*/