+/* -*-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 <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+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 <sys/ioctl.h>
+#include <linux/if.h>
+#include <unet.h>
+
+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 -------------------------------------------------*/