diff --git a/conf.c b/conf.c index 8477a6e..7f32859 100644 --- a/conf.c +++ b/conf.c @@ -1043,6 +1043,7 @@ static int conf_ugid(const char *runas, uid_t *uid, gid_t *gid) */ void conf(struct ctx *c, int argc, char **argv) { + int netns_only = 0; struct option options[] = { {"debug", no_argument, NULL, 'd' }, {"quiet", no_argument, NULL, 'q' }, @@ -1077,7 +1078,7 @@ void conf(struct ctx *c, int argc, char **argv) {"udp-ns", required_argument, NULL, 'U' }, {"userns", required_argument, NULL, 2 }, {"netns", required_argument, NULL, 3 }, - {"netns-only", no_argument, &c->netns_only, 1 }, + {"netns-only", no_argument, &netns_only, 1 }, {"config-net", no_argument, &c->pasta_conf_ns, 1 }, {"ns-mac-addr", required_argument, NULL, 4 }, {"dhcp-dns", no_argument, NULL, 5 }, @@ -1515,22 +1516,22 @@ void conf(struct ctx *c, int argc, char **argv) if (ret) usage(argv[0]); - drop_root(uid, gid); - if (c->mode == MODE_PASTA) { - if (conf_pasta_ns(&c->netns_only, userns, netns, + if (conf_pasta_ns(&netns_only, userns, netns, optind, argc, argv) < 0) usage(argv[0]); } else if (optind != argc) { usage(argv[0]); } + isolate_user(uid, gid, !netns_only, userns); + if (c->pasta_conf_ns) c->no_ra = 1; if (c->mode == MODE_PASTA) { if (*netns) { - pasta_open_ns(c, userns, netns); + pasta_open_ns(c, netns); } else { pasta_start_ns(c, argc - optind, argv + optind); } diff --git a/isolation.c b/isolation.c index 41ca888..124dea4 100644 --- a/isolation.c +++ b/isolation.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -59,14 +60,19 @@ void drop_caps(void) } /** - * drop_root() - Switch to given UID and GID - * @uid: User ID to switch to - * @gid: Group ID to switch to + * isolate_user() - Switch to final UID/GID and move into userns + * @uid: User ID to run as (in original userns) + * @gid: Group ID to run as (in original userns) + * @use_userns: Whether to join or create a userns + * @userns: userns path to enter, may be empty */ -void drop_root(uid_t uid, gid_t gid) +void isolate_user(uid_t uid, gid_t gid, bool use_userns, const char *userns) { + char nsmap[BUFSIZ]; + + /* First set our UID & GID in the original namespace */ if (setgroups(0, NULL)) { - /* If we don't start with CAP_SETGID, this will EPERM */ + /* If we don't have CAP_SETGID, this will EPERM */ if (errno != EPERM) { err("Can't drop supplementary groups: %s", strerror(errno)); @@ -74,11 +80,57 @@ void drop_root(uid_t uid, gid_t gid) } } - if (!setgid(gid) && !setuid(uid)) + if (setgid(gid) != 0) { + err("Can't set GID to %u: %s", gid, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (setuid(uid) != 0) { + err("Can't set UID to %u: %s", uid, strerror(errno)); + exit(EXIT_FAILURE); + } + + /* If we're told not to use a userns, nothing more to do */ + if (!use_userns) return; - err("Can't change user/group, exiting"); - exit(EXIT_FAILURE); + /* Otherwise, if given a userns, join it */ + if (*userns) { + int ufd; + + ufd = open(userns, O_RDONLY | O_CLOEXEC); + if (ufd < 0) { + err("Couldn't open user namespace %s: %s", + userns, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (setns(ufd, CLONE_NEWUSER) != 0) { + err("Couldn't enter user namespace %s: %s", + userns, strerror(errno)); + exit(EXIT_FAILURE); + } + + close(ufd); + + return; + } + + /* Otherwise, create our own userns */ + if (unshare(CLONE_NEWUSER) != 0) { + err("Couldn't create user namespace: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + /* Configure user and group mappings */ + snprintf(nsmap, BUFSIZ, "0 %u 1", uid); + FWRITE("/proc/self/uid_map", nsmap, "Cannot set uid_map in namespace"); + + FWRITE("/proc/self/setgroups", "deny", + "Cannot write to setgroups in namespace"); + + snprintf(nsmap, BUFSIZ, "0 %u 1", gid); + FWRITE("/proc/self/gid_map", nsmap, "Cannot set gid_map in namespace"); } /** @@ -90,15 +142,6 @@ int sandbox(struct ctx *c) { int flags = CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWUTS; - if (!c->netns_only) { - if (c->pasta_userns_fd == -1) - flags |= CLONE_NEWUSER; - else - setns(c->pasta_userns_fd, CLONE_NEWUSER); - } - - c->pasta_userns_fd = -1; - /* If we run in foreground, we have no chance to actually move to a new * PID namespace. For passt, use CLONE_NEWPID anyway, in case somebody * ever gets around seccomp profiles -- there's no harm in passing it. diff --git a/isolation.h b/isolation.h index 2540a35..2c73df7 100644 --- a/isolation.h +++ b/isolation.h @@ -8,7 +8,7 @@ #define ISOLATION_H void drop_caps(void); -void drop_root(uid_t uid, gid_t gid); +void isolate_user(uid_t uid, gid_t gid, bool use_userns, const char *userns); int sandbox(struct ctx *c); void seccomp(const struct ctx *c); diff --git a/passt.c b/passt.c index 2a8314c..f0ed897 100644 --- a/passt.c +++ b/passt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -185,7 +186,7 @@ int main(int argc, char **argv) drop_caps(); - c.pasta_userns_fd = c.pasta_netns_fd = c.fd_tap = c.fd_tap_listen = -1; + c.pasta_netns_fd = c.fd_tap = c.fd_tap_listen = -1; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; diff --git a/passt.h b/passt.h index 3035430..8c8d710 100644 --- a/passt.h +++ b/passt.h @@ -145,8 +145,6 @@ struct ip6_ctx { * @pcap: Path for packet capture file * @pid_file: Path to PID file, empty string if not configured * @pasta_netns_fd: File descriptor for network namespace in pasta mode - * @pasta_userns_fd: Descriptor for user namespace to join, -1 once joined - * @netns_only: In pasta mode, don't join or create a user namespace * @no_netns_quit: In pasta mode, don't exit if fs-bound namespace is gone * @netns_base: Base name for fs-bound namespace, if any, in pasta mode * @netns_dir: Directory of fs-bound namespace, if any, in pasta mode @@ -197,8 +195,6 @@ struct ctx { char pid_file[PATH_MAX]; int pasta_netns_fd; - int pasta_userns_fd; - int netns_only; int no_netns_quit; char netns_base[PATH_MAX]; diff --git a/pasta.c b/pasta.c index 7eac8e9..e233762 100644 --- a/pasta.c +++ b/pasta.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -83,16 +84,6 @@ static int pasta_wait_for_ns(void *arg) int flags = O_RDONLY | O_CLOEXEC; char ns[PATH_MAX]; - if (c->netns_only) - goto netns; - - snprintf(ns, PATH_MAX, "/proc/%i/ns/user", pasta_child_pid); - do - while ((c->pasta_userns_fd = open(ns, flags)) < 0); - while (setns(c->pasta_userns_fd, CLONE_NEWUSER) && - !close(c->pasta_userns_fd)); - -netns: snprintf(ns, PATH_MAX, "/proc/%i/ns/net", pasta_child_pid); do while ((c->pasta_netns_fd = open(ns, flags)) < 0); @@ -112,25 +103,23 @@ static int ns_check(void *arg) { struct ctx *c = (struct ctx *)arg; - if ((!c->netns_only && setns(c->pasta_userns_fd, CLONE_NEWUSER)) || - setns(c->pasta_netns_fd, CLONE_NEWNET)) - c->pasta_userns_fd = c->pasta_netns_fd = -1; + if (setns(c->pasta_netns_fd, CLONE_NEWNET)) + c->pasta_netns_fd = -1; return 0; } /** - * pasta_open_ns() - Open network, user namespaces descriptors + * pasta_open_ns() - Open network namespace descriptors * @c: Execution context - * @userns: --userns argument, can be an empty string * @netns: network namespace path * * Return: 0 on success, negative error code otherwise */ -void pasta_open_ns(struct ctx *c, const char *userns, const char *netns) +void pasta_open_ns(struct ctx *c, const char *netns) { - int ufd = -1, nfd = -1; + int nfd = -1; nfd = open(netns, O_RDONLY | O_CLOEXEC); if (nfd < 0) { @@ -138,17 +127,7 @@ void pasta_open_ns(struct ctx *c, const char *userns, const char *netns) exit(EXIT_FAILURE); } - if (!c->netns_only && *userns) { - ufd = open(userns, O_RDONLY | O_CLOEXEC); - if (ufd < 0) { - close(nfd); - err("Couldn't open user namespace %s", userns); - exit(EXIT_FAILURE); - } - } - c->pasta_netns_fd = nfd; - c->pasta_userns_fd = ufd; NS_CALL(ns_check, c); @@ -169,12 +148,9 @@ void pasta_open_ns(struct ctx *c, const char *userns, const char *netns) /** * struct pasta_setup_ns_arg - Argument for pasta_setup_ns() - * @c: Execution context - * @euid: Effective UID of caller + * @argv: Command and arguments to run */ struct pasta_setup_ns_arg { - struct ctx *c; - int euid; char **argv; }; @@ -188,21 +164,6 @@ static int pasta_setup_ns(void *arg) { struct pasta_setup_ns_arg *a = (struct pasta_setup_ns_arg *)arg; - if (!a->c->netns_only) { - char buf[BUFSIZ]; - - snprintf(buf, BUFSIZ, "%i %i %i", 0, a->euid, 1); - - FWRITE("/proc/self/uid_map", buf, - "Cannot set uid_map in namespace"); - - FWRITE("/proc/self/setgroups", "deny", - "Cannot write to setgroups in namespace"); - - FWRITE("/proc/self/gid_map", buf, - "Cannot set gid_map in namespace"); - } - FWRITE("/proc/sys/net/ipv4/ping_group_range", "0 0", "Cannot set ping_group_range, ICMP requests might fail"); @@ -221,8 +182,6 @@ static int pasta_setup_ns(void *arg) void pasta_start_ns(struct ctx *c, int argc, char *argv[]) { struct pasta_setup_ns_arg arg = { - .c = c, - .euid = geteuid(), .argv = argv, }; char *shell = getenv("SHELL") ? getenv("SHELL") : "/bin/sh"; @@ -244,7 +203,6 @@ void pasta_start_ns(struct ctx *c, int argc, char *argv[]) pasta_child_pid = clone(pasta_setup_ns, ns_fn_stack + sizeof(ns_fn_stack) / 2, - (c->netns_only ? 0 : CLONE_NEWUSER) | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWUTS, (void *)&arg); diff --git a/pasta.h b/pasta.h index a1937b2..02df1f6 100644 --- a/pasta.h +++ b/pasta.h @@ -6,7 +6,7 @@ #ifndef PASTA_H #define PASTA_H -void pasta_open_ns(struct ctx *c, const char *userns, const char *netns); +void pasta_open_ns(struct ctx *c, const char *netns); void pasta_start_ns(struct ctx *c, int argc, char *argv[]); void pasta_ns_conf(struct ctx *c); void pasta_child_handler(int signal); diff --git a/util.c b/util.c index f709838..8d26561 100644 --- a/util.c +++ b/util.c @@ -464,11 +464,6 @@ void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version, int ns, */ int ns_enter(const struct ctx *c) { - if (!c->netns_only && - c->pasta_userns_fd != -1 && - setns(c->pasta_userns_fd, CLONE_NEWUSER)) - exit(EXIT_FAILURE); - if (setns(c->pasta_netns_fd, CLONE_NEWNET)) exit(EXIT_FAILURE);