SUBDIRS += common
SUBDIRS += uslip
SUBDIRS += client
+SUBDIRS += priv
SUBDIRS += server
SUBDIRS += proxy
SUBDIRS += pkstream
\&
.RB [ \-p
.IR path ]
+.RB [ \-U
+.IR user ]
+.RB [ \-G
+.IR group ]
.RB [ \-S
.IB arg , arg ,\fR...]
.
(e.g.,
.BR \-a ).
.TP
+.BI "\-U, \-\-setuid=" user
+Set uid to that of
+.I user
+(either a user name or integer uid) after initialization. Also set gid
+to
+.IR user 's
+primary group, unless overridden by a
+.B \-G
+option. If a new
+.BR tripe (8)
+server is going to be spawned then it is also passed corresponding
+.B \-U
+and
+.B \-G
+options.
+.TP
+.BI "\-G, \-\-setgid=" group
+Set gid to that of
+.I group
+(either a group name or integer gid) after initialization. If a new
+.BR tripe (8)
+server is going to be spawned then it is also passed a corresponding
+.B \-G
+option.
+.TP
.B "\-l, \-\-syslog"
Send warnings and trace messages to the
.BR syslog (8)
/*----- Static variables --------------------------------------------------*/
+static sel_state sel;
static const char *pidfile = 0;
static const char *logname = 0;
static FILE *logfp = 0;
die(EXIT_FAILURE, "unexpected background tag `%s'", q);
}
+static void dolog(int prio, const char *msg, ...)
+{
+ va_list ap;
+ dstr d = DSTR_INIT;
+ const char *cat;
+
+ va_start(ap, msg);
+ dstr_vputf(&d, msg, &ap);
+ va_end(ap);
+ if (f & f_syslog) syslog(prio, "%s", d.buf);
+ if (logfp) {
+ switch (prio) {
+ case LOG_WARNING: cat = "warning"; break;
+ case LOG_DEBUG: cat = "debug"; break;
+ case LOG_ERR: cat = "error"; break;
+ default: cat = "message"; break;
+ }
+ writelog(cat, d.buf);
+ }
+ if (prio == LOG_WARNING && (f & f_warn))
+ fprintf(stderr, "Warning: %s\n", d.buf);
+ dstr_destroy(&d);
+}
+
static void checkfg(void)
{ if (bgtag) die(EXIT_FAILURE, "unexpected foreground response"); }
q = str_getword(&p);
if (!q)
return;
- if (strcmp(q, "WARN") == 0) {
- if (f & f_syslog)
- syslog(LOG_WARNING, "%s", p);
- if (logfp)
- writelog("warning", p);
- if (f & f_warn)
- fprintf(stderr, "Warning: %s\n", p);
- } else if (strcmp(q, "TRACE") == 0) {
- if (f & f_syslog)
- syslog(LOG_DEBUG, "%s", p);
- if (logfp)
- writelog("debug", p);
- } else if (!(f & f_command)) {
- if (f & f_syslog)
- syslog(LOG_ERR, "unexpected output `%s %s'", q, p);
- if (logfp) {
- dstr d = DSTR_INIT;
- dstr_putf(&d, "unexpected output `%s %s'", q, p);
- writelog("error", d.buf);
- dstr_destroy(&d);
- }
- } else if (strcmp(q, "FAIL") == 0) {
+ if (strcmp(q, "WARN") == 0)
+ dolog(LOG_WARNING, p);
+ else if (strcmp(q, "TRACE") == 0)
+ dolog(LOG_DEBUG, p);
+ else if (!(f & f_command))
+ dolog(LOG_ERR, "unexpected output `%s %s'", q, p);
+ else if (strcmp(q, "FAIL") == 0) {
checkfg();
die(EXIT_FAILURE, "%s", p);
} else if (strcmp(q, "INFO") == 0) {
}
}
+static void eline(char *p, size_t len, void *b)
+{
+ if (p)
+ dolog(LOG_WARNING, "(stderr): %s", p);
+ else {
+ selbuf_destroy(b);
+ close(fd);
+ }
+}
+
static void setup(const char *cmd)
{
dstr d = DSTR_INIT;
static void sigdie(int sig)
{ cleanup(); signal(sig, SIG_DFL); raise(sig); }
+static void putarg(string_v *av, const char *fmt, ...)
+{
+ va_list ap;
+ dstr d = DSTR_INIT;
+
+ va_start(ap, fmt);
+ dstr_vputf(&d, fmt, &ap);
+ dstr_putz(&d);
+ va_end(ap);
+ DA_UNSHIFT(av, xstrdup(d.buf));
+ dstr_destroy(&d);
+}
+
static void version(FILE *fp)
{ pquis(fp, "$, TrIPE version " VERSION "\n"); }
\n\
-D, --daemon Become a background task after connecting.\n\
-d, --directory=DIR Select current directory [default " CONFIGDIR "].\n\
+-U, --setuid=USER Set uid to USER after initialization.\n\
+-G, --setgid=GROUP Set gid to GROUP after initialization.\n\
-a, --admin-socket=FILE Select socket to connect to\n\
[default " SOCKETDIR "/tripesock].\n\
-P, --pidfile=FILE Write process-id to FILE.\n\
string_v spawnopts = DA_INIT;
char *p;
FILE *pidfp = 0;
+ int i;
+ size_t sz;
+ uid_t u = -1;
+ gid_t g = -1;
+ int pfd[2], efd[2];
+ pid_t kid;
+ struct sigaction sa;
+ sigset_t newmask, oldmask;
+ struct sockaddr_un sun;
+ selbuf bu, bs, be;
+ dstr d = DSTR_INIT;
+ sig hup;
ego(argv[0]);
{ "version", 0, 0, 'v' },
{ "usage", 0, 0, 'u' },
{ "daemon", 0, 0, 'D' },
+ { "uid", OPTF_ARGREQ, 0, 'U' },
+ { "setuid", OPTF_ARGREQ, 0, 'U' },
+ { "gid", OPTF_ARGREQ, 0, 'G' },
+ { "setgid", OPTF_ARGREQ, 0, 'G' },
{ "directory", OPTF_ARGREQ, 0, 'd' },
{ "admin-socket", OPTF_ARGREQ, 0, 'a' },
{ "spawn", 0, 0, 's' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+hvuDd:a:sp:S:lwf:nP:", opts, 0, 0, 0);
+ i = mdwopt(argc, argv, "+hvuDU:G:d:a:sp:S:lwf:nP:", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case 'D':
f |= f_daemon | f_noinput;
break;
+ case 'U':
+ u = u_getuser(optarg, &g);
+ break;
+ case 'G':
+ g = u_getgroup(optarg);
+ break;
case 'd':
dir = optarg;
break;
die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
pidfile, strerror(errno));
}
+ sel_init(&sel);
+ sig_init(&sel);
signal(SIGINT, sigdie);
signal(SIGQUIT, sigdie);
signal(SIGTERM, sigdie);
/* --- Connect to the server --- */
if (f & f_spawn) {
- int pfd[2];
- pid_t kid;
- struct sigaction sa;
- sigset_t newmask, oldmask;
-
sa.sa_handler = reap;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP;
#endif
sigaction(SIGCHLD, &sa, 0);
- DA_UNSHIFT(&spawnopts, (char *)sock);
- DA_UNSHIFT(&spawnopts, "-a");
- DA_UNSHIFT(&spawnopts, "-d.");
- DA_UNSHIFT(&spawnopts, "-F");
- DA_UNSHIFT(&spawnopts, (char *)spawnpath);
DA_PUSH(&spawnopts, 0);
- if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd))
+ if (g != (gid_t)-1) putarg(&spawnopts, "-G%lu", (unsigned long)g);
+ if (u != (uid_t)-1) putarg(&spawnopts, "-U%lu", (unsigned long)u);
+ putarg(&spawnopts, "-a%s", sock);
+ putarg(&spawnopts, "-d.");
+ putarg(&spawnopts, "-F");
+ putarg(&spawnopts, "%s", spawnpath);
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd) || pipe(efd))
die(EXIT_FAILURE, "error from socketpair: %s", strerror(errno));
sigemptyset(&newmask);
sigaddset(&newmask, SIGCHLD);
if (!kid) {
dup2(pfd[1], STDIN_FILENO);
dup2(pfd[1], STDOUT_FILENO);
- close(pfd[0]);
- close(pfd[1]);
+ dup2(efd[1], STDERR_FILENO);
+ close(pfd[0]); close(pfd[1]);
+ close(efd[0]); close(efd[1]);
if (logfp) fclose(logfp);
if (pidfp) fclose(pidfp);
closelog();
}
sigprocmask(SIG_SETMASK, &oldmask, 0);
fd = pfd[0];
- close(pfd[1]);
+ close(pfd[1]); close(efd[1]);
+ selbuf_init(&be, &sel, efd[0], eline, &be);
} else {
- struct sockaddr_un sun;
- size_t sz = strlen(sock) + 1;
+ sz = strlen(sock) + 1;
if (sz > sizeof(sun.sun_path))
die(EXIT_FAILURE, "socket name `%s' too long", sock);
memset(&sun, 0, sizeof(sun));
}
}
+ u_setugid(u, g);
if (f & f_daemon) {
if (daemonize())
die(EXIT_FAILURE, "error becoming daemon: %s", strerror(errno));
if (optind == argc)
setup("WATCH -A+tw");
if (!(f & f_noinput) && optind == argc) {
- sel_state sel;
- selbuf bu, bs;
-
- sel_init(&sel);
selbuf_init(&bu, &sel, STDIN_FILENO, uline, &bu);
selbuf_init(&bs, &sel, fd, sline, &bs);
for (;;) {
/* --- If there's a command, submit it --- */
if (optind < argc) {
- dstr d = DSTR_INIT;
setup((f & f_warn) ? "WATCH -A+w" : "WATCH -A");
while (optind < argc)
u_quotify(&d, argv[optind++]);
/* --- Pull everything else out of the box --- */
- {
- sel_state sel;
- selbuf b;
- sig hup;
-
- sel_init(&sel);
- selbuf_init(&b, &sel, fd, cline, 0);
+ selbuf_init(&bs, &sel, fd, cline, 0);
- if (f & f_syslog)
- openlog(QUIS, 0, LOG_DAEMON);
- if (logfp) {
- sig_init(&sel);
- sig_add(&hup, SIGHUP, sighup, 0);
- }
- for (;;) {
- if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
- die(EXIT_FAILURE, "select failed: %s", strerror(errno));
- }
+ if (f & f_syslog)
+ openlog(QUIS, 0, LOG_DAEMON);
+ if (logfp)
+ sig_add(&hup, SIGHUP, sighup, 0);
+ for (;;) {
+ if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
+ die(EXIT_FAILURE, "select failed: %s", strerror(errno));
}
return (0);
TRIPE_DEFINE_PATH(
[logfile], [FILE], [logging output [[./tripe.log]]], [tripe.log])
+dnl--------------------------------------------------------------------------
+dnl Privilege-separation helper.
+
+mdw_DEFINE_PATHS([
+ AC_DEFINE_UNQUOTED([PRIVSEP_HELPER],
+ ["mdw_PATH([$libexecdir])/mdw_PROG([tripe-privhelper])"],
+ [Pathname of privilege-separation helper.])
+])
+
dnl--------------------------------------------------------------------------
dnl Other options.
[uslip/Makefile]
[pathmtu/Makefile]
[client/Makefile]
+ [priv/Makefile]
[server/Makefile]
[proxy/Makefile]
[pkstream/Makefile]
debian/tmp/usr/share/man/man1/tripectl.1
debian/tmp/usr/share/man/man5/tripe-admin.5
debian/tmp/usr/share/man/man8/tripe.8
+debian/tmp/usr/lib/tripe/tripe-privhelper
+debian/tmp/usr/share/man/man8/tripe-privhelper.8
debian/tmp/usr/lib/pkgconfig/tripe.pc
${keytag+-S-t}$keytag \
${addr+-S-b}$addr \
${port+-S-p}${port} \
- ${user+-S-U}${user} \
- ${group+-S-G}${group} \
+ ${user+-U}${user} \
+ ${group+-G}${group} \
${trace+-S-T}${trace} \
${tunnel+-S-n}${tunnel} \
${miscopts}
--- /dev/null
+### -*-makefile-*-
+###
+### Makefile for privilege separation
+###
+### (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.
+
+include $(top_srcdir)/vars.am
+
+noinst_LIBRARIES = libpriv.a
+libexec_PROGRAMS = tripe-privhelper
+man_MANS =
+
+###--------------------------------------------------------------------------
+### Library.
+
+libpriv_a_SOURCES =
+
+## Header.
+libpriv_a_SOURCES += priv.h
+
+## Communications.
+libpriv_a_SOURCES += comm.c
+
+###--------------------------------------------------------------------------
+### Helper.
+
+## The progam itself.
+tripe_privhelper_SOURCES = helper.c
+tripe_privhelper_LDADD = $(libpriv) $(libtripe)
+
+## Manual page.
+man_MANS += tripe-privhelper.8
+CLEANFILES += tripe-privhelper.8
+EXTRA_DIST += tripe-privhelper.8.in
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * Communication between server and 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"
+
+/*----- Global variables --------------------------------------------------*/
+
+int pc_fd = 0; /* File descriptor for comms */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @pc_put@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to buffer
+ * @size_t sz@ = size of the buffer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Writes a buffer, handling short writes and other bogosity.
+ */
+
+int pc_put(const void *p, size_t sz)
+{
+ ssize_t n;
+ const unsigned char *pp = p;
+
+ while (sz) {
+ n = write(pc_fd, pp, sz);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ return (-1);
+ }
+ if (n == 0) {
+ errno = EIO;
+ return (-1);
+ }
+ pp += n; sz -= n;
+ }
+ return (0);
+}
+
+/* --- @pc_puterr@, @pc_putuint@, @pc_putsz@, @pc_puttops@ --- *
+ *
+ * Arguments: @int err@ = error number to write
+ * @uint u@ = unsigned integer to write
+ * @size_t sz@ = size to write
+ * @const tunnel_ops *tops@ = tunnel pointer to write
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Sends an error/integer/size/tunnel-ops pointer.
+ */
+
+#define PUT(abbr, type) \
+ int pc_put##abbr(type x) { return (pc_put(&x, sizeof(x))); }
+COMM_TYPES(PUT)
+
+/* --- @pc_putstring@ --- *
+ *
+ * Arguments: @const char *s@ = pointer to string to write
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Sends a string/error/integer/tunnel-ops pointer.
+ */
+
+int pc_putstring(const char *s)
+{
+ size_t sz = strlen(s);
+
+ if (pc_putsz(sz) || pc_put(s, sz))
+ return (-1);
+ return (0);
+}
+
+/* --- @pc_get@ --- *
+ *
+ * Arguments: @void *p@ = pointer to buffer
+ * @size_t sz@ = size of the buffer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives a buffer, handling short reads and other bogosity.
+ */
+
+int pc_get(void *p, size_t sz)
+{
+ ssize_t n;
+ unsigned char *pp = p;
+
+ while (sz) {
+ n = read(pc_fd, pp, sz);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (errno == ECONNRESET)
+ errno = -1;
+ return (-1);
+ }
+ if (n == 0) {
+ errno = -1;
+ return (-1);
+ }
+ pp += n; sz -= n;
+ }
+ return (0);
+}
+
+/* --- @pc_geterr@, @pc_getuint@, @pc_getsz@, @pc_getops@ --- *
+ *
+ * Arguments: @int *err@ = where to put the error number
+ * @uint *u@ = where to put the unsigned integer
+ * @size_t *sz@ = where to put the size
+ * @const tunnel_ops **tops@ = where to put the tunnel pointer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives an error/integer/size/tunnel-ops pointer.
+ */
+
+#define GET(abbr, type) \
+ int pc_get##abbr(type *x) { return (pc_get(x, sizeof(*x))); }
+COMM_TYPES(GET)
+
+/* --- @pc_gettring@ --- *
+ *
+ * Arguments: @dstr *d@ = where to pc_put the string
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives a string.
+ */
+
+int pc_getstring(dstr *d)
+{
+ size_t sz;
+
+ if (pc_getsz(&sz))
+ return (-1);
+ DENSURE(d, sz + 1);
+ if (pc_get(d->buf + d->len, sz))
+ return (-1);
+ d->len += sz;
+ d->buf[d->len] = 0;
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Privilege separation definitions
+ *
+ * (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.
+ */
+
+#ifndef PRIV_H
+#define PRIV_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <mLib/dstr.h>
+#include <mLib/fdpass.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/trace.h>
+
+#include "util.h"
+
+#undef sun
+
+/*----- Protocol ----------------------------------------------------------*/
+
+/* --- Notes --- *
+ *
+ * The protocol is synchronous. The socket is not marked as nonblocking;
+ * instead we just trust the helper to respond in good time; this is
+ * reasonable since it's not doing anything complicated. The helper is
+ * completely trusted.
+ *
+ * The protocol works like this. Messages begin with a request code which is
+ * a single @unsigned int@. The server sends a request @PS_TUNRQ@ to the
+ * helper, followed by a @const tunnel_ops *@ referring to the tunnel driver
+ * of interest. The server responds with a sequence of @PS_TRACE@ and/or
+ * @PS_WARN@ messages, followed by either a @PS_TUNFD@ carrying a file
+ * descriptor, or a @PS_TUNERR@ followed by an integer @errno@ code.
+ *
+ * If all else fails, the helper process will just quit.
+ */
+
+enum {
+ PS_TUNRQ, /* Request (@tunnel_ops *@) */
+ PS_TUNFD, /* Tunnel descriptor (string) */
+ PS_TUNERR, /* Error (@int errno@) */
+#ifndef NTRACE
+ PS_TRACE, /* Trace (@unsigned mask@, string) */
+#endif
+ PS_WARN, /* Warning (string) */
+};
+
+/*----- Tracing definitions -----------------------------------------------*/
+
+#define T_PRIVSEP 512u
+
+/*----- Global variables --------------------------------------------------*/
+
+extern int pc_fd; /* File descriptor for comms */
+
+/*----- Functions provided ------------------------------------------------*/
+
+#define COMM_TYPES(_) \
+ _(err, int) \
+ _(uint, unsigned int) \
+ _(sz, size_t)
+
+/* --- @put@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to buffer
+ * @size_t sz@ = size of the buffer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Writes a buffer, handling short writes and other bogosity.
+ */
+
+extern int pc_put(const void */*p*/, size_t /*sz*/);
+
+/* --- @puterr@, @putuint@, @putsz@, @puttops@ --- *
+ *
+ * Arguments: @int err@ = error number to write
+ * @uint u@ = unsigned integer to write
+ * @size_t sz@ = size to write
+ * @const tunnel_ops *tops@ = tunnel pointer to write
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Sends an error/integer/size/tunnel-ops pointer.
+ */
+
+#define DECL(abbr, type) extern int pc_put##abbr(type /*x*/);
+COMM_TYPES(DECL)
+#undef DECL
+
+/* --- @putstring@ --- *
+ *
+ * Arguments: @const char *s@ = pointer to string to write
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Sends a string/error/integer/tunnel-ops pointer.
+ */
+
+extern int pc_putstring(const char */*s*/);
+
+/* --- @get@ --- *
+ *
+ * Arguments: @void *p@ = pointer to buffer
+ * @size_t sz@ = size of the buffer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives a buffer, handling short reads and other bogosity.
+ */
+
+extern int pc_get(void */*p*/, size_t /*sz*/);
+
+/* --- @geterr@, @getuint@, @getsz@, @getops@ --- *
+ *
+ * Arguments: @int *err@ = where to put the error number
+ * @uint *u@ = where to put the unsigned integer
+ * @size_t *sz@ = where to put the size
+ * @const tunnel_ops **tops@ = where to put the tunnel pointer
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives an error/integer/size/tunnel-ops pointer.
+ */
+
+#define DECL(abbr, type) extern int pc_get##abbr(type */*x*/);
+COMM_TYPES(DECL)
+#undef DECL
+
+/* --- @gettring@ --- *
+ *
+ * Arguments: @dstr *d@ = where to put the string
+ *
+ * Returns: Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use: Receives a string.
+ */
+
+extern int pc_getstring(dstr */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+.\" -*-nroff-*-
+.\".
+.\" Manual for the server
+.\"
+.\" (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.
+.
+.\"--------------------------------------------------------------------------
+.so ../defs.man.in \" @@@PRE@@@
+.
+.\"--------------------------------------------------------------------------
+.TH tripe-privhelper 8 "28 April 2008" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption"
+.
+.\"--------------------------------------------------------------------------
+.SH "NAME"
+.
+tripe-privhelper \- privilege-separation helper program
+.
+.\"--------------------------------------------------------------------------
+.SH "SYNOPSIS"
+.
+This program communicates using a binary protocol over a Unix-domain
+socket on file descriptor 0. It is not intended to be run
+interactively.
+.
+.\"--------------------------------------------------------------------------
+.SH "DESCRIPTION"
+.
+The
+.BR tripe (8)
+server usually needs superuser privileges in order to open new tunnel
+devices, through which it collects and emits network packets. In order
+to prevent the whole system needing to be run as root, the server splits
+off a child process and then drops its privileges; the child process
+runs this program.
+.PP
+The
+.B tripe-privhelper
+program reads requests for tunnel devices on file descriptor 0 and
+responds with appropriate file descriptors (using Unix-domain socket
+file descriptor passing: see
+.BR unix (7))
+for correctly configured tunnel devices.
+.
+.\"--------------------------------------------------------------------------
+.SH "BUGS"
+.
+The objective of the privilege separation model is to reduce the attack
+surface for the code running with superuser privileges down to a simple
+binary protocol. There may still be bugs in the small program which
+runs as root.
+.PP
+The `unprivileged' portion of the server still runs with the ability to
+read and write arbitrary data on tunnel devices. In particular, if
+compromised, it can inject arbitrary packets into the network. This is
+unfortunately inherent in the nature of a VPN server.
+.
+.\"--------------------------------------------------------------------------
+.SH "SEE ALSO"
+.
+.BR tripe (8).
+.PP
+.IR "The Trivial IP Encryption Protocol" ,
+.IR "The Wrestlers Protocol" .
+.
+.\"--------------------------------------------------------------------------
+.SH "AUTHOR"
+.
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
sbin_PROGRAMS =
man_MANS =
-LDADD = $(libtripe) $(catacomb_LIBS)
+LDADD = $(libtripe) $(libpriv) $(catacomb_LIBS)
###--------------------------------------------------------------------------
### The main server.
tripe_SOURCES += keyexch.c
tripe_SOURCES += chal.c
tripe_SOURCES += peer.c
+tripe_SOURCES += privsep.c
tripe_SOURCES += admin.c
tripe_SOURCES += tripe.c
{ 'x', T_KEYEXCH, "key exchange" },
{ 'm', T_KEYMGMT, "key management" },
{ 'l', T_CHAL, "challenge management" },
+ { 'v', T_PRIVSEP, "privilege separation" },
{ 'p', T_PACKET, "packet contents" },
{ 'c', T_CRYPTO, "crypto details" },
{ 'A', T_ALL, "all of the above" },
*
* Returns: ---
*
- * Use: Main message token formatting driver.
+ * Use: Main message token formatting driver. The arguments are
+ * interleaved formatting tokens and their parameters, finally
+ * terminated by an entry @A_END@.
+ *
+ * Tokens recognized:
+ *
+ * * "*..." ... -- pretokenized @dstr_putf@-like string
+ *
+ * * "?ADDR" SOCKADDR -- a socket address, to be converted
+ *
+ * * "?B64" BUFFER SIZE -- binary data to be base64-encoded
+ *
+ * * "?TOKENS" VECTOR -- null-terminated vector of tokens
+ *
+ * * "?PEER" PEER -- peer's name
+ *
+ * * "?ERRNO" ERRNO -- system error code
+ *
+ * * "[!]..." ... -- @dstr_putf@-like string as single token
*/
-static void a_vformat(dstr *d, const char *fmt, va_list ap)
+void a_vformat(dstr *d, const char *fmt, va_list ap)
{
dstr dd = DSTR_INIT;
while (fmt) {
if (*fmt == '*') {
- dstr_putc(d, ' ');
+ if (d->len) dstr_putc(d, ' ');
dstr_vputf(d, fmt + 1, &ap);
} else if (*fmt == '?') {
if (strcmp(fmt, "?ADDR") == 0) {
close(sock.fd);
unlink(sockname);
FOREACH_PEER(p, { p_destroy(p); });
+ ps_quit();
exit(0);
}
/* --- @a_init@ --- *
*
* Arguments: @const char *name@ = socket name to create
+ * @uid_t u@ = user to own the socket
+ * @gid_t g@ = group to own the socket
*
* Returns: ---
*
* Use: Creates the admin listening socket.
*/
-void a_init(const char *name)
+void a_init(const char *name, uid_t u, gid_t g)
{
int fd;
int n = 5;
goto again;
}
chmod(sun.sun_path, 0600);
+ if (chown(sun.sun_path, u, g)) {
+ T( trace(T_ADMIN,
+ "admin: failed to give away socket: %s",
+ strerror(errno)); )
+ }
fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
if (listen(fd, 5))
die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
peer *p_create(peerspec *spec)
{
peer *p = CREATE(peer);
+ const tunnel_ops *tops = spec->tops;
+ int fd;
unsigned f;
p->byname = sym_find(&byname, spec->name, -1, sizeof(peer_byname), &f);
p->ifname = 0;
memset(&p->st, 0, sizeof(stats));
p->st.t_start = time(0);
- if ((p->t = spec->tops->create(p, &p->ifname)) == 0)
+ if (!(tops->flags & TUNF_PRIVOPEN))
+ fd = -1;
+ else if ((fd = ps_tunfd(tops, &p->ifname)) < 0)
goto tidy_2;
+ if ((p->t = tops->create(p, fd, &p->ifname)) == 0)
+ goto tidy_3;
+ T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'",
+ p->ifname, p_name(p)); )
p_setkatimer(p);
if (kx_init(&p->kx, p, &p->ks, p->spec.kxf))
- goto tidy_3;
+ goto tidy_4;
a_notify("ADD",
"?PEER", p,
"%s", p->ifname,
}
return (p);
-tidy_3:
+tidy_4:
if (spec->t_ka)
sel_rmtimer(&p->tka);
xfree(p->ifname);
p->t->ops->destroy(p->t);
+tidy_3:
+ if (fd >= 0) close(fd);
tidy_2:
am_remove(&byaddr, p->byaddr);
tidy_1:
--- /dev/null
+/* -*-c-*-
+ *
+ * Privilege separation communication protocol
+ *
+ * (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 "tripe.h"
+#include "priv.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static pid_t kid = -1;
+
+/*----- Fetching a tunnel file descriptor ---------------------------------*/
+
+/* --- @ps_tunfd@ --- *
+ *
+ * Arguments: @const tunnel_ops *tops@ = pointer to tunnel operations
+ * @char **ifn@ = where to put the interface name
+ *
+ * Returns: The file descriptor, or @-1@ on error.
+ *
+ * Use: Fetches a file descriptor for a tunnel driver.
+ */
+
+int ps_tunfd(const tunnel_ops *tops, char **ifn)
+{
+ unsigned code;
+ ssize_t n;
+ dstr d = DSTR_INIT;
+ int fd;
+
+ if (pc_fd == -1) {
+ a_warn("PRIVSEP", "helper-died", A_END);
+ return (-1);
+ }
+ T( trace(T_PRIVSEP,
+ "privsep: requesting descriptor for %s tunnel",
+ tops->name); )
+ if (pc_putuint(PS_TUNRQ) || pc_putstring(tops->name)) {
+ a_warn("PRIVSEP", "helper-write-error", "?ERRNO", A_END);
+ goto lose;
+ }
+ for (;;) {
+ n = fdpass_recv(pc_fd, &fd, &code, sizeof(code));
+ if (n < 0) goto readlose;
+ if (n < sizeof(code)) {
+ a_warn("PRIVSEP", "helper-short-read", A_END);
+ goto lose;
+ }
+ switch (code) {
+ case PS_TUNFD:
+ if (fd == -1) {
+ a_warn("PRIVSEP", "no-fd-from-helper", A_END);
+ goto lose;
+ }
+ if (pc_getstring(&d)) { close(fd); goto readlose; }
+ *ifn = xstrdup(d.buf);
+ T( trace(T_PRIVSEP,
+ "privsep: received winning descriptor for %s",
+ *ifn); )
+ goto done;
+ case PS_TUNERR:
+ if (pc_geterr(&errno)) goto readlose;
+ T( trace(T_PRIVSEP, "privsep: helper lost: %s", strerror(errno)); )
+ fd = -1;
+ goto done;
+#ifndef NTRACE
+ case PS_TRACE:
+ if (pc_getuint(&code) || pc_getstring(&d)) goto readlose;
+ trace(code, "%s", d.buf);
+ DRESET(&d);
+ break;
+#endif
+ case PS_WARN:
+ if (pc_getstring(&d)) goto readlose;
+ a_warn("*%s", d.buf, A_END);
+ DRESET(&d);
+ break;
+ default:
+ a_warn("PRIVSEP", "unknown-response-code", "%u", code, A_END);
+ goto lose;
+ }
+ }
+done:
+ dstr_destroy(&d);
+ return (fd);
+
+readlose:
+ a_warn("PRIVSEP", "helper-read-error", "?ERRNO", A_END);
+lose:
+ dstr_destroy(&d);
+ close(pc_fd);
+ pc_fd = -1;
+ return (-1);
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @reap@ --- *
+ *
+ * Arguments: @int sig@ = signal number (always @SIGCHLD@; ignored)
+ *
+ * Returns: ---
+ *
+ * Use: Notices and reports child process death.
+ */
+
+static void reap(int sig)
+{
+ pid_t k;
+ int st;
+
+ for (;;) {
+ k = waitpid(-1, &st, WNOHANG);
+ if (k < 0) {
+ switch (errno) {
+ case EINTR:
+ break;
+ default:
+ a_warn("SERVER", "waitpid-error", "?ERRNO", A_END);
+ case ECHILD:
+ return;
+ }
+ }
+ if (!k)
+ return;
+ if (k == kid) {
+ if (WIFEXITED(st))
+ a_warn("PRIVSEP", "child-exited", "%d", WEXITSTATUS(st), A_END);
+ else if (WIFSIGNALED(st))
+ a_warn("PRIVSEP", "child-killed", "%d", WTERMSIG(st), A_END);
+ else
+ a_warn("PRIVSEP", "child-died", "%d", st, A_END);
+ kid = -1;
+ }
+ }
+}
+
+/* --- @ps_split@ --- *
+ *
+ * Arguments: @int detachp@ = whether to detach the child from its terminal
+ *
+ * Returns: ---
+ *
+ * Use: Separates off the privileged tunnel-opening service from the
+ * rest of the server.
+ */
+
+void ps_split(int detachp)
+{
+ pid_t kid;
+ int fd[2];
+ const char *helper;
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
+ die(EXIT_FAILURE,
+ "failed to create socket pair for privilege separation: %s",
+ strerror(errno));
+ }
+ helper = getenv("TRIPE_PRIVHELPER");
+ if (!helper) helper = PRIVSEP_HELPER;
+ fdflags(fd[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
+ fdflags(fd[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
+ signal(SIGCHLD, reap);
+ kid = fork();
+ if (kid == 0) {
+ signal(SIGCHLD, SIG_DFL);
+ if (detachp) detachtty();
+ if (dup2(fd[0], 0) < 0) goto lose;
+ close(fd[0]); close(fd[1]);
+ execl(helper, helper, (char *)0);
+ lose:
+ fprintf(stderr, "helper: failed to run helper: %s\n", strerror(errno));
+ _exit(127);
+ }
+ T( trace(T_PRIVSEP, "privsep: forked child successfully"); )
+ close(fd[0]);
+ pc_fd = fd[1];
+}
+
+/* --- @ps_quit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Detaches from the helper process.
+ */
+
+void ps_quit(void) { if (pc_fd != -1) close(pc_fd); }
+
+/*----- That's all, folks -------------------------------------------------*/
## Running standard programs with useful options.
m4_define([TRIPE],
- [$abs_top_builddir/server/tripe -F -d. -aadmin -p0 -b127.0.0.1 -talice])
+ [env TRIPE_PRIVHELPER=$abs_top_builddir/priv/tripe-privhelper \
+ $abs_top_builddir/server/tripe -F -d. -aadmin -p0 -b127.0.0.1 -talice])
m4_define([TRIPECTL], [$abs_top_builddir/client/tripectl -d. -aadmin])
m4_define([USLIP], [$abs_top_builddir/uslip/tripe-uslip])
.IR user 's
primary group, unless overridden by a
.B \-G
-option.
+option. The selected user (and group) will also be the owner of the
+administration socket.
.TP
.BI "\-G, \-\-setgid=" group
Set gid to that of
af |= AF_FOREGROUND;
a_create(STDIN_FILENO, STDOUT_FILENO, af);
}
+ ps_split(f & f_daemon);
+ a_init(csock, u, g);
u_setugid(u, g);
km_init(kr_priv, kr_pub, tag_priv);
- a_init(csock);
if (f & f_daemon) {
if (daemonize())
die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno));
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <mLib/dstr.h>
#include <mLib/env.h>
#include <mLib/fdflags.h>
+#include <mLib/fdpass.h>
#include <mLib/fwatch.h>
#include <mLib/hash.h>
#include <mLib/macros.h>
#include <catacomb/ec-keys.h>
#include <catacomb/group.h>
+#include "priv.h"
#include "protocol.h"
#include "slip.h"
#include "util.h"
#define T_KEYEXCH 64u
#define T_KEYMGMT 128u
#define T_CHAL 256u
+/* T_PRIVSEP in priv.h */
-#define T_ALL 511u
+#define T_ALL 1023u
/* --- Units --- */
typedef struct tunnel_ops {
const char *name; /* Name of this tunnel driver */
+ unsigned flags; /* Various interesting flags */
+#define TUNF_PRIVOPEN 1u /* Need helper to open file */
void (*init)(void); /* Initializes the system */
- tunnel *(*create)(struct peer */*p*/, char **/*ifn*/);
+ tunnel *(*create)(struct peer */*p*/, int /*fd*/, char **/*ifn*/);
/* Initializes a new tunnel */
void (*setifname)(tunnel */*t*/, const char */*ifn*/);
/* Notifies ifname change */
#define A_END ((char *)0)
+/* --- @a_vformat@ --- *
+ *
+ * Arguments: @dstr *d@ = where to leave the formatted message
+ * @const char *fmt@ = pointer to format string
+ * @va_list ap@ = arguments in list
+ *
+ * Returns: ---
+ *
+ * Use: Main message token formatting driver. The arguments are
+ * interleaved formatting tokens and their parameters, finally
+ * terminated by an entry @A_END@.
+ *
+ * Tokens recognized:
+ *
+ * * "*..." ... -- pretokenized @dstr_putf@-like string
+ *
+ * * "?ADDR" SOCKADDR -- a socket address, to be converted
+ *
+ * * "?B64" BUFFER SIZE -- binary data to be base64-encoded
+ *
+ * * "?TOKENS" VECTOR -- null-terminated vector of tokens
+ *
+ * * "?PEER" PEER -- peer's name
+ *
+ * * "?ERRNO" ERRNO -- system error code
+ *
+ * * "[!]..." ... -- @dstr_putf@-like string as single token
+ */
+
+extern void a_vformat(dstr */*d*/, const char */*fmt*/, va_list /*ap*/);
+
/* --- @a_warn@ --- *
*
* Arguments: @const char *fmt@ = pointer to format string
/* --- @a_init@ --- *
*
* Arguments: @const char *sock@ = socket name to create
+ * @uid_t u@ = user to own the socket
+ * @gid_t g@ = group to own the socket
*
* Returns: ---
*
* Use: Creates the admin listening socket.
*/
-extern void a_init(const char */*sock*/);
+extern void a_init(const char */*sock*/, uid_t /*u*/, gid_t /*g*/);
/*----- Mapping with addresses as keys ------------------------------------*/
extern void am_remove(addrmap */*m*/, void */*i*/);
+/*----- Privilege separation ----------------------------------------------*/
+
+/* --- @ps_trace@ --- *
+ *
+ * Arguments: @unsigned mask@ = trace mask to check
+ * @const char *fmt@ = message format
+ * @...@ = values for placeholders
+ *
+ * Returns: ---
+ *
+ * Use: Writes a trace message.
+ */
+
+T( extern void ps_trace(unsigned /*mask*/, const char */*fmt*/, ...); )
+
+/* --- @ps_warn@ --- *
+ *
+ * Arguments: @const char *fmt@ = message format
+ * @...@ = values for placeholders
+ *
+ * Returns: ---
+ *
+ * Use: Writes a warning message.
+ */
+
+extern void ps_warn(const char */*fmt*/, ...);
+
+/* --- @ps_tunfd@ --- *
+ *
+ * Arguments: @const tunnel_ops *tops@ = pointer to tunnel operations
+ * @char **ifn@ = where to put the interface name
+ *
+ * Returns: The file descriptor, or @-1@ on error.
+ *
+ * Use: Fetches a file descriptor for a tunnel driver.
+ */
+
+extern int ps_tunfd(const tunnel_ops */*tops*/, char **/*ifn*/);
+
+/* --- @ps_split@ --- *
+ *
+ * Arguments: @int detachp@ = whether to detach the child from its terminal
+ *
+ * Returns: ---
+ *
+ * Use: Separates off the privileged tunnel-opening service from the
+ * rest of the server.
+ */
+
+extern void ps_split(int /*detachp*/);
+
+/* --- @ps_quit@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Detaches from the helper process.
+ */
+
+extern void ps_quit(void);
+
/*----- Peer management ---------------------------------------------------*/
/* --- @p_txstart@ --- *
const tunnel_ops *ops; /* Pointer to operations */
sel_file f; /* Selector for tunnel device */
struct peer *p; /* Pointer to my peer */
- unsigned n; /* Number of my tunnel device */
};
/* --- @t_read@ --- *
/* --- @t_create@ --- *
*
* Arguments: @peer *p@ = pointer to peer block
+ * @int fd@ = file descriptor of tunnel device
* @char **ifn@ = where to put the interface name
*
* Returns: A tunnel block if it worked, or null on failure.
* Use: Initializes a new tunnel.
*/
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
{
- int fd;
- unsigned n;
tunnel *t;
- 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( trace(T_TUNNEL, "tunnel device %u busy: skipping", n); )
- break;
- case ENOENT:
- a_warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
- return (0);
- default:
- a_warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
- break;
- }
- n++;
- }
+ fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
t = CREATE(tunnel);
t->ops = &tun_bsd;
- fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
t->p = p;
- t->n = n;
sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
sel_addfile(&t->f);
- *ifn = xstrdup(buf + 5);
- T( trace(T_TUNNEL, "tun-bsd: attached interface %s to peer `%s'",
- *ifn, p_name(p)); )
return (t);
}
const tunnel_ops tun_bsd = {
"bsd",
+ TUNF_PRIVOPEN,
t_init,
t_create,
0,
/* --- @t_create@ --- *
*
* Arguments: @peer *p@ = pointer to peer block
+ * @int fd@ = file descriptor of tunnel device
* @char **ifn@ = where to put the interface name
*
* Returns: A tunnel block if it worked, or null on failure.
* Use: Initializes a new tunnel.
*/
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
{
- int fd;
- int f;
- struct ifreq iff;
tunnel *t;
- if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
- a_warn("TUN", "-", "linux",
- "open-error", "/dev/net/tun", "?ERRNO",
- A_END);
- return (0);
- }
fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
- memset(&iff, 0, sizeof(iff));
- iff.ifr_name[0] = 0;
- iff.ifr_flags = IFF_TUN | IFF_NO_PI;
- if ((f = ioctl(fd, TUNSETIFF, &iff)) < 0) {
- a_warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
- close(fd);
- return (0);
- }
t = CREATE(tunnel);
t->ops = &tun_linux;
t->p = p;
sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
sel_addfile(&t->f);
- iff.ifr_name[IFNAMSIZ - 1] = 0;
- *ifn = xstrdup(iff.ifr_name);
- T( trace(T_TUNNEL, "tun-linux: attached interface %s to peer `%s'",
- *ifn, p_name(p)); )
return (t);
}
const tunnel_ops tun_linux = {
"linux",
+ TUNF_PRIVOPEN,
t_init,
t_create,
0,
/* --- @t_create@ --- *
*
* Arguments: @peer *p@ = pointer to peer block
+ * @int fd@ = file descriptor of tunnel device (unused)
* @char **ifn@ = where to put the interface name
*
* Returns: A tunnel block if it worked, or null on failure.
* Use: Initializes a new tunnel.
*/
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
{
slipif *sl = 0;
int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
const tunnel_ops tun_slip = {
"slip",
+ 0,
t_init,
t_create,
t_setifname,
#ifdef TUN_UNET
# include <sys/ioctl.h>
-# include <net/if.h>
+# include <linux/if.h>
# include <unet.h>
#endif
/* --- @t_create@ --- *
*
- * Arguments: @tunnel *t@ = pointer to tunnel block
- * @peer *p@ = pointer to peer block
- * @char *ifn@ = where to put the interface name
+ * Arguments: @peer *p@ = pointer to peer block
+ * @int fd@ = file descriptor of tunnel device
+ * @char **ifn@ = where to put the interface name
*
* Returns: A tunnel block if it worked, or null on failure.
*
* Use: Initializes a new tunnel.
*/
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
{
- int fd;
tunnel *t;
- int f;
- struct unet_info uni;
- if ((fd = open("/dev/unet", O_RDWR)) < 0) {
- a_warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END);
- return (0);
- }
fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
- if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
- ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
- a_warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
- close(fd);
- return (0);
- }
t = CREATE(tunnel);
t->ops = &tun_unet;
t->p = p;
sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
sel_addfile(&t->f);
-
- if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
- a_warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
- return (0);
- }
- *ifn = xstrdup(uni.uni_ifname);
- T( trace(T_TUNNEL, "tun-unet: attached interface %s to peer `%s'",
- *ifn, p_name(p)); )
return (t);
}
const tunnel_ops tun_unet = {
"unet",
+ TUNF_PRIVOPEN,
t_init,
t_create,
0,
TRIPE_INCLUDES = \
-I$(top_builddir)/config.h \
- -I$(top_srcdir)/common
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/priv
CPPFLAGS += $(TRIPE_INCLUDES)
###--------------------------------------------------------------------------
### Miscellanous useful definitions.
-## Library of common code.
+## Libraries of common code.
libtripe = $(top_builddir)/common/libtripe.a
+libpriv = $(top_builddir)/priv/libpriv.a
## Create a directory if it doesn't exist.
mkdir_p = $(top_srcdir)/config/install-sh -d