From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751283AbdAYCC7 (ORCPT ); Tue, 24 Jan 2017 21:02:59 -0500 Received: from out03.mta.xmission.com ([166.70.13.233]:41194 "EHLO out03.mta.xmission.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751113AbdAYCCz (ORCPT ); Tue, 24 Jan 2017 21:02:55 -0500 From: ebiederm@xmission.com (Eric W. Biederman) To: "Michael Kerrisk \(man-pages\)" Cc: "Serge E. Hallyn" , linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, Andrey Vagin , James Bottomley , "W. Trevor King" , Alexander Viro References: <93e5c7f9-9dc1-6c93-ad20-0ba053d8bfef@gmail.com> <963f76f9-3ae5-b316-e688-00d3e59cad30@gmail.com> Date: Wed, 25 Jan 2017 14:58:35 +1300 In-Reply-To: <963f76f9-3ae5-b316-e688-00d3e59cad30@gmail.com> (Michael Kerrisk's message of "Wed, 25 Jan 2017 14:03:22 +1300") Message-ID: <87d1fblx2s.fsf@xmission.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-XM-SPF: eid=1cWCvB-0008HV-4b;;;mid=<87d1fblx2s.fsf@xmission.com>;;;hst=in01.mta.xmission.com;;;ip=101.100.131.98;;;frm=ebiederm@xmission.com;;;spf=neutral X-XM-AID: U2FsdGVkX1/m57UxxQtr9hB2NqAHapP9EyMjwYWFXio= X-SA-Exim-Connect-IP: 101.100.131.98 X-SA-Exim-Mail-From: ebiederm@xmission.com X-Spam-Report: * -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP * 0.7 XMSubLong Long Subject * 1.0 XM_Body_Obfu01 BODY: Obfuscations * 0.0 T_TM2_M_HEADER_IN_MSG BODY: No description available. * 0.8 BAYES_50 BODY: Bayes spam probability is 40 to 60% * [score: 0.4973] * -0.0 DCC_CHECK_NEGATIVE Not listed in DCC * [sa06 1397; Body=1 Fuz1=1 Fuz2=1] * 0.0 T_TooManySym_02 5+ unique symbols in subject * 0.0 T_TooManySym_01 4+ unique symbols in subject X-Spam-DCC: XMission; sa06 1397; Body=1 Fuz1=1 Fuz2=1 X-Spam-Combo: *;"Michael Kerrisk \(man-pages\)" X-Spam-Relay-Country: X-Spam-Timing: total 1441 ms - load_scoreonly_sql: 0.03 (0.0%), signal_user_changed: 2.8 (0.2%), b_tie_ro: 2.1 (0.1%), parse: 0.95 (0.1%), extract_message_metadata: 5 (0.4%), get_uri_detail_list: 3.8 (0.3%), tests_pri_-1000: 3.6 (0.2%), tests_pri_-950: 1.13 (0.1%), tests_pri_-900: 0.95 (0.1%), tests_pri_-400: 36 (2.5%), check_bayes: 35 (2.4%), b_tokenize: 14 (1.0%), b_tok_get_all: 11 (0.8%), b_comp_prob: 3.0 (0.2%), b_tok_touch_all: 4.9 (0.3%), b_finish: 0.61 (0.0%), tests_pri_0: 1376 (95.5%), check_dkim_signature: 0.59 (0.0%), check_dkim_adsp: 2.7 (0.2%), tests_pri_500: 5 (0.4%), rewrite_mail: 0.00 (0.0%) Subject: Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery X-Spam-Flag: No X-SA-Exim-Version: 4.2.1 (built Thu, 05 May 2016 13:38:54 -0600) X-SA-Exim-Scanned: Yes (on in01.mta.xmission.com) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org "Michael Kerrisk (man-pages)" 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? 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, > > 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 > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include > > #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