Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1566257 > unrolled thread

[PATCH v4 0/2] Add further ioctl() operations for namespace discovery

Started by"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
First post2017-01-25 02:10 +0100
Last post2017-01-25 03:40 +0100
Articles 7 — 2 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  [PATCH v4 0/2] Add further ioctl() operations for namespace discovery "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> - 2017-01-25 02:10 +0100
    Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery ebiederm@xmission.com (Eric W. Biederman) - 2017-01-25 03:10 +0100
      Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery ebiederm@xmission.com (Eric W. Biederman) - 2017-01-25 03:40 +0100
        Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery ebiederm@xmission.com (Eric W. Biederman) - 2017-01-25 03:40 +0100
          Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> - 2017-01-25 05:00 +0100
            Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery ebiederm@xmission.com (Eric W. Biederman) - 2017-01-26 05:30 +0100
      Re: [PATCH v4 0/2] Add further ioctl() operations for namespace discovery "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> - 2017-01-25 03:40 +0100

#1566257 — [PATCH v4 0/2] Add further ioctl() operations for namespace discovery

From"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
Date2017-01-25 02:10 +0100
Subject[PATCH v4 0/2] Add further ioctl() operations for namespace discovery
Message-ID<t3kg2-3qv-15@gated-at.bofh.it>
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.

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@gmail.com>

   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/

[toc] | [next] | [standalone]


#1566274

Fromebiederm@xmission.com (Eric W. Biederman)
Date2017-01-25 03:10 +0100
Message-ID<t3lc5-401-9@gated-at.bofh.it>
In reply to#1566257
"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> 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, <mtk.manpages@gmail.com>
>
>    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

[toc] | [prev] | [next] | [standalone]


#1566286

Fromebiederm@xmission.com (Eric W. Biederman)
Date2017-01-25 03:40 +0100
Message-ID<t3lF7-4at-3@gated-at.bofh.it>
In reply to#1566274
"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:

> Hi Eric,
>
> On 25 January 2017 at 14:58, Eric W. Biederman <ebiederm@xmission.com> wrote:
>> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> 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?

I think so I just noticed you did not handle -EOVERFLOW in
the one NS_GET_OWNER_UID call.

> So, UID 0 doesn't have a
> mapping into the inner namespace, but does have all capabilities in
> that inner namespace, right?

That is correct.


Eric

[toc] | [prev] | [next] | [standalone]


#1566288

Fromebiederm@xmission.com (Eric W. Biederman)
Date2017-01-25 03:40 +0100
Message-ID<t3lF8-4at-13@gated-at.bofh.it>
In reply to#1566286
ebiederm@xmission.com (Eric W. Biederman) writes:

> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:
>
>> Hi Eric,
>>
>> On 25 January 2017 at 14:58, Eric W. Biederman <ebiederm@xmission.com> wrote:
>>> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> 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?
>
> I think so I just noticed you did not handle -EOVERFLOW in
> the one NS_GET_OWNER_UID call.
>
>> So, UID 0 doesn't have a
>> mapping into the inner namespace, but does have all capabilities in
>> that inner namespace, right?
>
> That is correct.

My concern is that the difference between returning -EOVERFLOW and
overflow_uid is primarily about usability.  If you haven't played with
the usability I don't trust that we have made the proper trade off.


Eric

[toc] | [prev] | [next] | [standalone]


#1566311

From"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
Date2017-01-25 05:00 +0100
Message-ID<t3mUx-4XL-3@gated-at.bofh.it>
In reply to#1566288
Hi Eric,

On 25 January 2017 at 15:28, Eric W. Biederman <ebiederm@xmission.com> wrote:
> ebiederm@xmission.com (Eric W. Biederman) writes:
>
>> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:
>>
>>> Hi Eric,
>>>
>>> On 25 January 2017 at 14:58, Eric W. Biederman <ebiederm@xmission.com> wrote:
>>>> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> 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?
>>
>> I think so I just noticed you did not handle -EOVERFLOW in
>> the one NS_GET_OWNER_UID call.
>>
>>> So, UID 0 doesn't have a
>>> mapping into the inner namespace, but does have all capabilities in
>>> that inner namespace, right?
>>
>> That is correct.
>
> My concern is that the difference between returning -EOVERFLOW and
> overflow_uid is primarily about usability.  If you haven't played with
> the usability I don't trust that we have made the proper trade off.

So, I had not initially included the no-UID-mapping case, and when you
proposed -EOVERFLOW for that case, it seemed better.

On reflection, mapping to the overflow_uid seems simpler. Taking the
example shown in my other mail a short time ago, the unmapped UID 0
from the outer namespace would map to the overflow_uid (which UID my
program would print), but my program would still correctly report that
the UID 0 process in the outer namespace might (subject to LSM checks)
have capabilities in the inner namespace.

So, it seems that reverting the EOVERFLOW change is in order (and my
example program thus needs no changes). Does that sound reasonable to
you?

Cheers,

Michael

-- 
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/

[toc] | [prev] | [next] | [standalone]


#1567112

Fromebiederm@xmission.com (Eric W. Biederman)
Date2017-01-26 05:30 +0100
Message-ID<t3JR7-2Lo-1@gated-at.bofh.it>
In reply to#1566311
"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> writes:


> On 25 January 2017 at 15:28, Eric W. Biederman <ebiederm@xmission.com> wrote:

>> My concern is that the difference between returning -EOVERFLOW and
>> overflow_uid is primarily about usability.  If you haven't played with
>> the usability I don't trust that we have made the proper trade off.
>
> So, I had not initially included the no-UID-mapping case, and when you
> proposed -EOVERFLOW for that case, it seemed better.
>
> On reflection, mapping to the overflow_uid seems simpler. Taking the
> example shown in my other mail a short time ago, the unmapped UID 0
> from the outer namespace would map to the overflow_uid (which UID my
> program would print), but my program would still correctly report that
> the UID 0 process in the outer namespace might (subject to LSM checks)
> have capabilities in the inner namespace.
>
> So, it seems that reverting the EOVERFLOW change is in order (and my
> example program thus needs no changes). Does that sound reasonable to
> you?

It does.  I just care that you have thought through the tradeoffs of
that corner of the interface design.

Eric

[toc] | [prev] | [next] | [standalone]


#1566287

From"Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com>
Date2017-01-25 03:40 +0100
Message-ID<t3lF7-4at-5@gated-at.bofh.it>
In reply to#1566274
Hi Eric,

On 25 January 2017 at 14:58, Eric W. Biederman <ebiederm@xmission.com> wrote:
> "Michael Kerrisk (man-pages)" <mtk.manpages@gmail.com> 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@gmail.com>
>>
>>    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/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web