summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-10-20 23:41:21 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-10-26 20:12:51 -0400
commit299a34c11a4241f8c5832ccd5a7bc13263f8488b (patch)
treef67f997d142debec99297a095f4687f65bf7c00e /src/basic
parent24597ee0e626b61f134e09b4e871449ef86b1343 (diff)
detect-virt: add --private-users switch to check if a userns is active
Various things don't work when we're running in a user namespace, but it's pretty hard to reliably detect if that is true. A function is added which looks at /proc/self/uid_map and returns false if the default "0 0 UINT32_MAX" is found, and true if it finds anything else. This misses the case where an 1:1 mapping with the full range was used, but I don't know how to distinguish this case. 'systemd-detect-virt --private-users' is very similar to 'systemd-detect-virt --chroot', but we check for a user namespace instead.
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/virt.c70
-rw-r--r--src/basic/virt.h1
2 files changed, 71 insertions, 0 deletions
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 41012d52a0..69b0f96183 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -485,6 +485,76 @@ int detect_virtualization(void) {
return r;
}
+static int userns_has_mapping(const char *name) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *buf = NULL;
+ size_t n_allocated = 0;
+ ssize_t n;
+ uint32_t a, b, c;
+ int r;
+
+ f = fopen(name, "re");
+ if (!f) {
+ log_debug_errno(errno, "Failed to open %s: %m", name);
+ return errno == -ENOENT ? false : -errno;
+ }
+
+ n = getline(&buf, &n_allocated, f);
+ if (n < 0) {
+ if (feof(f)) {
+ log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ return true;
+ }
+
+ return log_debug_errno(errno, "Failed to read %s: %m", name);
+ }
+
+ r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
+ if (r < 3)
+ return log_debug_errno(errno, "Failed to parse %s: %m", name);
+
+ if (a == 0 && b == 0 && c == UINT32_MAX) {
+ /* The kernel calls mappings_overlap() and does not allow overlaps */
+ log_debug("%s has a full 1:1 mapping", name);
+ return false;
+ }
+
+ /* Anything else implies that we are in a user namespace */
+ log_debug("Mapping found in %s, we're in a user namespace", name);
+ return true;
+}
+
+int running_in_userns(void) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ r = userns_has_mapping("/proc/self/uid_map");
+ if (r != 0)
+ return r;
+
+ r = userns_has_mapping("/proc/self/gid_map");
+ if (r != 0)
+ return r;
+
+ /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
+ * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
+ * also does not exist. We cannot distinguish those two cases, so assume that
+ * we're running on a stripped-down recent kernel, rather than on an old one,
+ * and if the file is not found, return false.
+ */
+ r = read_one_line_file("/proc/self/setgroups", &line);
+ if (r < 0) {
+ log_debug_errno(r, "/proc/self/setgroups: %m");
+ return r == -ENOENT ? false : r;
+ }
+
+ truncate_nl(line);
+ r = streq(line, "deny");
+ /* See user_namespaces(7) for a description of this "setgroups" contents. */
+ log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
+ return r;
+}
+
int running_in_chroot(void) {
int ret;
diff --git a/src/basic/virt.h b/src/basic/virt.h
index bc5b3ae94d..7d15169112 100644
--- a/src/basic/virt.h
+++ b/src/basic/virt.h
@@ -67,6 +67,7 @@ int detect_vm(void);
int detect_container(void);
int detect_virtualization(void);
+int running_in_userns(void);
int running_in_chroot(void);
const char *virtualization_to_string(int v) _const_;