summaryrefslogtreecommitdiff
path: root/src/basic/virt.c
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2016-10-20 23:41:21 -0400
committerSven Eden <yamakuzure@gmx.net>2017-07-05 08:50:55 +0200
commit70cec624a0e54f3c35aff781645b07452fc32596 (patch)
treea088cea6dbdd3125fcc1070f1bc4c478534f69d5 /src/basic/virt.c
parentfb3aeda07e139f87c787fab2e2d0bd8bf97aa3a5 (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. 'elogind-detect-virt --private-users' is very similar to 'elogind-detect-virt --chroot', but we check for a user namespace instead.
Diffstat (limited to 'src/basic/virt.c')
-rw-r--r--src/basic/virt.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 3e8e1805f..7a2b9e9bd 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -488,6 +488,76 @@ int detect_virtualization(void) {
}
#endif // 0
+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;