It would be nice to have the ability to set a user's rtable upon login.
This would be useful both for road warrior VPN setups (put both the VPN
interface and user in an rdomain other than 0) and to differentiate
users in firewall rules on the gateway or unbound views on a resolver.
The below patch adds an rtable capability to login.conf which sets the
user's default rtable on login. 

Since setusercontext will now call setrtable, that syscall needs to be
covered under some pledge promise. Since the wroute promise already
allows programs to set a different rtable on a per socket basis, I don't
think adding the setrtable syscall materially changes the scope or
attack surface. This impacts dhcpleased, iked, rad, and slaacd which
already use the wroute promise. The wroute promise is added to
calendar, skeyaudit, su, cron, and inetd.

chroot, login, ssh, nsd, and unbound are impacted, but don't show up in
the diff because they don't use pledge. ssh continues to work as
expected: the user's session has the login specified default rtable;
however, the socket connecting the user to the session remains in the
original rtable so the connection doesn't break. nsd and unbound open
their listening sockets prior to privdrop, so those sockets are in the
original rtable (which should be set via rcctl). Notifications and XFRs
for nsd and lookups for unbound happen in the login specified rtable.
While this is somewhat surprising, I think most people would set the
rtable for a daemon via rcctl.

doas is a bit of a special case: it explicitly lists the LOGIN_ flags
rather than using LOGIN_SETALL and removing specific flags. I am
inclined to say doas should also set the default rtable, but it's not
a strong stance. I've left it out of this version of the patch pending
feedback on how others feel.

In addition xenodm would need to add wroute to the pledge in dm.c
StartDisplay and to the first pledge in session.c StartClient.

>From a Debian code search the ports which use LOGIN_SETALL appear to be
mail/alpine, security/sudo, and x11/gnome/gdm. None of these ports have
pledge markers in their Makefile.

Finally it may be nicer to drop -a from calendar and skeyaudit. Each
user that desires the service could then create their own cron job.
While it would allow a fair bit of code to be deleted and a tighter
pledge, it would break existing workflows and could be considered as
in a separate thread if desired.

- Matthew Martin



diff --git include/login_cap.h include/login_cap.h
index d9a4c2c349c..1e831b6471a 100644
--- include/login_cap.h
+++ include/login_cap.h
@@ -53,7 +53,8 @@
 #define        LOGIN_SETUMASK          0x0020  /* Set umask */
 #define        LOGIN_SETUSER           0x0040  /* Set user */
 #define        LOGIN_SETENV            0x0080  /* Set environment */
-#define        LOGIN_SETALL            0x00ff  /* Set all. */
+#define        LOGIN_SETRTABLE         0x0100  /* Set rtable */
+#define        LOGIN_SETALL            0x01ff  /* Set all. */
 
 #define        BI_AUTH         "authorize"             /* Accepted 
authentication */
 #define        BI_REJECT       "reject"                /* Rejected 
authentication */
diff --git lib/libc/gen/login_cap.c lib/libc/gen/login_cap.c
index 862f33b2065..65848f09ef6 100644
--- lib/libc/gen/login_cap.c
+++ lib/libc/gen/login_cap.c
@@ -52,6 +52,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/resource.h>
+#include <sys/socket.h>
 
 #include <err.h>
 #include <errno.h>
@@ -574,7 +575,7 @@ int
 setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags)
 {
        login_cap_t *flc;
-       quad_t p;
+       quad_t p, rtable;
        int i;
 
        flc = NULL;
@@ -625,6 +626,14 @@ setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t 
uid, u_int flags)
                umask((mode_t)p);
        }
 
+       if (flags & LOGIN_SETRTABLE) {
+               rtable = login_getcapnum(lc, "rtable", 0, 0);
+
+               if (setrtable((int)rtable) == -1) {
+                       syslog(LOG_ERR, "%s: setrtable: %m", lc->lc_class);
+               }
+       }
+
        if (flags & LOGIN_SETGROUP) {
                if (setresgid(pwd->pw_gid, pwd->pw_gid, pwd->pw_gid) == -1) {
                        syslog(LOG_ERR, "setresgid(%u,%u,%u): %m",
diff --git share/man/man5/login.conf.5 share/man/man5/login.conf.5
index da935fa223e..ffeafc3531c 100644
--- share/man/man5/login.conf.5
+++ share/man/man5/login.conf.5
@@ -276,6 +276,10 @@ Initial priority (nice) level.
 Require home directory to login.
 .\"
 .Pp
+.It rtable Ta number Ta Dv 0 Ta
+Rtable to be set for the class.
+.\"
+.Pp
 .It setenv Ta envlist Ta "" Ta
 A list of environment variables and associated values to be set for the class.
 .\"
diff --git sys/kern/kern_pledge.c sys/kern/kern_pledge.c
index 6687bf91f09..901d41cefb6 100644
--- sys/kern/kern_pledge.c
+++ sys/kern/kern_pledge.c
@@ -373,6 +373,8 @@ const uint64_t pledge_syscalls[SYS_MAXSYSCALL] = {
        [SYS_flock] = PLEDGE_FLOCK | PLEDGE_YPACTIVE,
 
        [SYS_swapctl] = PLEDGE_VMINFO,  /* XXX should limit to "get" operations 
*/
+
+       [SYS_setrtable] = PLEDGE_WROUTE,
 };
 
 static const struct {
diff --git usr.bin/calendar/calendar.c usr.bin/calendar/calendar.c
index ad82a94707c..bda19f4c6a6 100644
--- usr.bin/calendar/calendar.c
+++ usr.bin/calendar/calendar.c
@@ -126,7 +126,7 @@ main(int argc, char *argv[])
                usage();
 
        if (doall) {
-               if (pledge("stdio rpath tmppath fattr getpw id proc exec", NULL)
+               if (pledge("stdio rpath tmppath fattr getpw id proc exec 
wroute", NULL)
                    == -1)
                        err(1, "pledge");
        } else {
diff --git usr.bin/skeyaudit/skeyaudit.c usr.bin/skeyaudit/skeyaudit.c
index 3cad9b2eafa..1462d677650 100644
--- usr.bin/skeyaudit/skeyaudit.c
+++ usr.bin/skeyaudit/skeyaudit.c
@@ -71,7 +71,7 @@ main(int argc, char **argv)
        char *name;
        int ch, left, aflag, iflag, limit;
 
-       if (pledge("stdio rpath wpath flock getpw proc exec id", NULL) == -1)
+       if (pledge("stdio rpath wpath flock getpw proc exec id wroute", NULL) 
== -1)
                err(1, "pledge");
 
        aflag = iflag = 0;
diff --git usr.bin/su/su.c usr.bin/su/su.c
index f9fb2c0ac88..1fc73fa36bd 100644
--- usr.bin/su/su.c
+++ usr.bin/su/su.c
@@ -73,7 +73,7 @@ main(int argc, char **argv)
        uid_t ruid;
        u_int flags;
 
-       if (pledge("stdio unveil rpath getpw proc exec id", NULL) == -1)
+       if (pledge("stdio unveil rpath getpw proc exec id wroute", NULL) == -1)
                err(1, "pledge");
 
        while ((ch = getopt(argc, argv, "a:c:fKLlms:-")) != -1)
@@ -232,7 +232,7 @@ main(int argc, char **argv)
        if (pwd == NULL)
                auth_errx(as, 1, "internal error");
 
-       if (pledge("stdio unveil rpath getpw exec id", NULL) == -1)
+       if (pledge("stdio unveil rpath getpw exec id wroute", NULL) == -1)
                err(1, "pledge");
 
        if (!altshell) {
@@ -309,7 +309,7 @@ main(int argc, char **argv)
                if (setenv("SHELL", shell, 1) == -1)
                        auth_err(as, 1, "unable to set environment");
        }
-       if (pledge("stdio rpath getpw exec id", NULL) == -1)
+       if (pledge("stdio rpath getpw exec id wroute", NULL) == -1)
                err(1, "pledge");
 
        np = *argv ? argv : argv - 1;
diff --git usr.sbin/cron/cron.c usr.sbin/cron/cron.c
index 831a70d5411..033864b1369 100644
--- usr.sbin/cron/cron.c
+++ usr.sbin/cron/cron.c
@@ -99,7 +99,7 @@ main(int argc, char *argv[])
 
        openlog(__progname, LOG_PID, LOG_CRON);
 
-       if (pledge("stdio rpath wpath cpath fattr getpw unix id dns proc exec",
+       if (pledge("stdio rpath wpath cpath fattr getpw unix id dns proc exec 
wroute",
            NULL) == -1) {
                warn("pledge");
                syslog(LOG_ERR, "(CRON) PLEDGE (%m)");
diff --git usr.sbin/inetd/inetd.c usr.sbin/inetd/inetd.c
index 2c623ef66d1..6ad2aec838b 100644
--- usr.sbin/inetd/inetd.c
+++ usr.sbin/inetd/inetd.c
@@ -344,7 +344,7 @@ main(int argc, char *argv[])
                        (void) setlogin("");
        }
 
-       if (pledge("stdio rpath cpath getpw dns inet unix proc exec id", NULL) 
== -1)
+       if (pledge("stdio rpath cpath getpw dns inet unix proc exec id wroute", 
NULL) == -1)
                err(1, "pledge");
 
        if (uid == 0) {

Reply via email to