Hi Eric, On 25 January 2017 at 14:58, Eric W. Biederman <ebiederm@xxxxxxxxxxxx> wrote: > "Michael Kerrisk (man-pages)" <mtk.manpages@xxxxxxxxx> writes: > >> I would like to write code that discovers the namespace setup on a live >> system. The NS_GET_PARENT and NS_GET_USERNS ioctl() operations added in >> Linux 4.9 provide much of what I want, but there are still a couple of >> small pieces missing. Those pieces are added with this patch series. > > So it looks like the -EOVERFLOW change broke your example program. > Causing it to abort if -EOVERFLOW is hit. Do we really want to return > -EOVERFLOW? Or do you want to fix your program? Bother! Yes, I should have kept the example program in sync. (I overlooked that it was not any more in sync.) So, I want to make sure I understand correctly, before I aswer your question. Suppose we have 1. Outer namespace owned by UID 0 2. Inner namespace owned by UID 1000 3. A UID mapping in the inner namespace that maps '0 1000 1' 4. A processs, X, in the outer namespace with UID 0 (and all caps). That's the case you're meaning, right? So, UID 0 doesn't have a mapping into the inner namespace, but does have all capabilities in that inner namespace, right? Cheers, Michael > Eric > > >> Here's an example program that makes use of the new ioctl() operations. >> >> 8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x--- >> /* ns_capable.c >> >> (C) 2016 Michael Kerrisk, <mtk.manpages@xxxxxxxxx> >> >> Licensed under the GNU General Public License v2 or later. >> >> Test whether a process (identified by PID) might (subject to LSM checks) >> have capabilities in a namespace (identified by a /proc/PID/ns/xxx file). >> */ >> #define _GNU_SOURCE >> #include <sched.h> >> #include <stdlib.h> >> #include <unistd.h> >> #include <stdio.h> >> #include <errno.h> >> #include <fcntl.h> >> #include <string.h> >> #include <sys/stat.h> >> #include <sys/ioctl.h> >> #include <limits.h> >> #include <sys/capability.h> >> >> #ifndef NS_GET_USERNS >> #define NSIO 0xb7 >> #define NS_GET_USERNS _IO(NSIO, 0x1) >> #define NS_GET_PARENT _IO(NSIO, 0x2) >> #define NS_GET_NSTYPE _IO(NSIO, 0x3) >> #define NS_GET_OWNER_UID _IO(NSIO, 0x4) >> #endif >> >> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ >> } while (0) >> >> #define fatal(msg) do { fprintf(stderr, "%s\n", msg); \ >> exit(EXIT_FAILURE); } while (0) >> >> /* Display capabilities sets of process with specified PID */ >> >> static void >> show_cap(pid_t pid) >> { >> cap_t caps; >> char *cap_string; >> >> caps = cap_get_pid(pid); >> if (caps == NULL) >> errExit("cap_get_proc"); >> >> cap_string = cap_to_text(caps, NULL); >> if (cap_string == NULL) >> errExit("cap_to_text"); >> >> printf("Capabilities: %s\n", cap_string); >> } >> >> /* Obtain the effective UID pf the process 'pid' by >> scanning its /proc/PID/file */ >> >> static uid_t >> get_euid_of_process(pid_t pid) >> { >> char path[PATH_MAX]; >> char line[1024]; >> int uid; >> >> snprintf(path, sizeof(path), "/proc/%ld/status", (long) pid); >> >> FILE *fp; >> fp = fopen(path, "r"); >> if (fp == NULL) >> errExit("fopen-/proc/PID/status"); >> >> for (;;) { >> if (fgets(line, sizeof(line), fp) == NULL) { >> >> /* Should never happen... */ >> >> fprintf(stderr, "Failure scanning %s\n", path); >> exit(EXIT_FAILURE); >> } >> >> if (strstr(line, "Uid:") == line) { >> sscanf(line, "Uid: %*d %d %*d %*d", &uid); >> return uid; >> } >> } >> } >> >> int >> main(int argc, char *argv[]) >> { >> int ns_fd, userns_fd, pid_userns_fd; >> int nstype; >> int next_fd; >> struct stat pid_stat; >> struct stat target_stat; >> char *pid_str; >> pid_t pid; >> char path[PATH_MAX]; >> >> if (argc < 2) { >> fprintf(stderr, "Usage: %s PID [ns-file]\n", argv[0]); >> fprintf(stderr, "\t'ns-file' is a /proc/PID/ns/xxxx file; " >> "if omitted, use the namespace\n" >> "\treferred to by standard input " >> "(file descriptor 0)\n"); >> exit(EXIT_FAILURE); >> } >> >> pid_str = argv[1]; >> pid = atoi(pid_str); >> >> if (argc <= 2) { >> ns_fd = STDIN_FILENO; >> } else { >> ns_fd = open(argv[2], O_RDONLY); >> if (ns_fd == -1) >> errExit("open-ns-file"); >> } >> >> /* Get the relevant user namespace FD, which is 'ns_fd' if 'ns_fd' refers >> to a user namespace, otherwise the user namespace that owns 'ns_fd' */ >> >> nstype = ioctl(ns_fd, NS_GET_NSTYPE); >> if (nstype == -1) >> errExit("ioctl-NS_GET_NSTYPE"); >> >> if (nstype == CLONE_NEWUSER) { >> userns_fd = ns_fd; >> } else { >> userns_fd = ioctl(ns_fd, NS_GET_USERNS); >> if (userns_fd == -1) >> errExit("ioctl-NS_GET_USERNS"); >> } >> >> /* Obtain 'stat' info for the user namespace of the specified PID */ >> >> snprintf(path, sizeof(path), "/proc/%s/ns/user", pid_str); >> >> pid_userns_fd = open(path, O_RDONLY); >> if (pid_userns_fd == -1) >> errExit("open-PID"); >> >> if (fstat(pid_userns_fd, &pid_stat) == -1) >> errExit("fstat-PID"); >> >> /* Get 'stat' info for the target user namesapce */ >> >> if (fstat(userns_fd, &target_stat) == -1) >> errExit("fstat-PID"); >> >> /* If the PID is in the target user namespace, then it has >> whatever capabilities are in its sets. */ >> >> if (pid_stat.st_dev == target_stat.st_dev && >> pid_stat.st_ino == target_stat.st_ino) { >> printf("PID is in target namespace\n"); >> printf("Subject to LSM checks, it has the following capabilities\n"); >> >> show_cap(pid); >> >> exit(EXIT_SUCCESS); >> } >> >> /* Otherwise, we need to walk through the ancestors of the target >> user namespace to see if PID is in an ancestor namespace */ >> >> for (;;) { >> int f; >> >> next_fd = ioctl(userns_fd, NS_GET_PARENT); >> >> if (next_fd == -1) { >> >> /* The error here should be EPERM... */ >> >> if (errno != EPERM) >> errExit("ioctl-NS_GET_PARENT"); >> >> printf("PID is not in an ancestor namespace\n"); >> printf("It has no capabilities in the target namespace\n"); >> >> exit(EXIT_SUCCESS); >> } >> >> if (fstat(next_fd, &target_stat) == -1) >> errExit("fstat-PID"); >> >> /* If the 'stat' info for this user namespace matches the 'stat' >> * info for 'next_fd', then the PID is in an ancestor namespace */ >> >> if (pid_stat.st_dev == target_stat.st_dev && >> pid_stat.st_ino == target_stat.st_ino) >> break; >> >> /* Next time round, get the next parent */ >> >> f = userns_fd; >> userns_fd = next_fd; >> close(f); >> } >> >> /* At this point, we found that PID is in an ancestor of the target >> user namespace, and 'userns_fd' refers to the immediate descendant >> user namespace of PID in the chain of user namespaces from PID to >> the target user namespace. If the effective UID of PID matches the >> owner UID of descendant user namespace, then PID has all >> capabilities in the descendant namespace(s); otherwise, it just has >> the capabilities that are in its sets. */ >> >> uid_t owner_uid, uid; >> if (ioctl(userns_fd, NS_GET_OWNER_UID, &owner_uid) == -1) { >> perror("ioctl-NS_GET_OWNER_UID"); >> exit(EXIT_FAILURE); >> } >> >> uid = get_euid_of_process(pid); >> >> printf("PID is in an ancestor namespace\n"); >> if (owner_uid == uid) { >> printf("And its effective UID matches the owner " >> "of the namespace\n"); >> printf("Subject to LSM checks, PID has all capabilities in " >> "that namespace!\n"); >> } else { >> printf("But its effective UID does not match the owner " >> "of the namespace\n"); >> printf("Subject to LSM checks, it has the following capabilities\n"); >> show_cap(pid); >> } >> >> exit(EXIT_SUCCESS); >> } >> 8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x---8x--- >> >> Michael Kerrisk (2): >> nsfs: Add an ioctl() to return the namespace type >> nsfs: Add an ioctl() to return owner UID of a userns >> >> fs/nsfs.c | 15 +++++++++++++++ >> include/uapi/linux/nsfs.h | 9 +++++++-- >> 2 files changed, 22 insertions(+), 2 deletions(-) >> >> -- >> 2.5.5 -- Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Linux/UNIX System Programming Training: http://man7.org/training/ -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html