Re: [RCF] [PATCH] unprivileged mount/umount

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> > I'm not sure passing directory file descriptors is the right semantic
> > we want - but at least it provides a point of explicit control (in
> > much the same way as a bind).  Are you sure the clone + open("/") +
> > pass-to-parent scenario you allows the parent to traverse the child's
> > private name space through that fd?
> 
> Pretty sure.

Yup.  Attached a little program that can be used to try this out.  It
creates a new namespace in the child, does a bind mount (so the
namespaces can be differentiated), then sends the file descriptor of
"/" to the parent.  The parent does fchdir(fd), then starts a shell.

So the result is that CWD is under the child namespace, while root is
under the initial namespace.

I also tried bind mounting from the child's namespace to the parent's,
and that works too.  But the new mount's mnt_namespace is copied from
the old, which makes the mount un-removable.  This is most likely not
intentional, IOW a bug.

Miklos

=== newns.c =========================================================
#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>

static int socks[2];

static int send_fd(int sock_fd, int fd)
{
    int retval;
    struct msghdr msg;
    struct cmsghdr *p_cmsg;
    struct iovec vec;
    char cmsgbuf[CMSG_SPACE(sizeof(fd))];
    int *p_fds;
    char sendchar = 0;

    msg.msg_control = cmsgbuf;
    msg.msg_controllen = sizeof(cmsgbuf);
    p_cmsg = CMSG_FIRSTHDR(&msg);
    p_cmsg->cmsg_level = SOL_SOCKET;
    p_cmsg->cmsg_type = SCM_RIGHTS;
    p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
    p_fds = (int *) CMSG_DATA(p_cmsg);
    *p_fds = fd;
    msg.msg_controllen = p_cmsg->cmsg_len;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = &vec;
    msg.msg_iovlen = 1;
    msg.msg_flags = 0;
    /* "To pass file descriptors or credentials you need to send/read at
     * least one byte" (man 7 unix) */
    vec.iov_base = &sendchar;
    vec.iov_len = sizeof(sendchar);
    while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
    if (retval != 1) {
        perror("sending file descriptor");
        return -1;
    }
    return 0;
}

static int receive_fd(int fd)
{
    struct msghdr msg;
    struct iovec iov;
    char buf[1];
    int rv;
    int connfd = -1;
    char ccmsg[CMSG_SPACE(sizeof(connfd))];
    struct cmsghdr *cmsg;

    iov.iov_base = buf;
    iov.iov_len = 1;

    msg.msg_name = 0;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    /* old BSD implementations should use msg_accrights instead of
     * msg_control; the interface is different. */
    msg.msg_control = ccmsg;
    msg.msg_controllen = sizeof(ccmsg);

    while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
    if (rv == -1) {
        perror("recvmsg");
        return -1;
    }
    if(!rv) {
        /* EOF */
        return -1;
    }

    cmsg = CMSG_FIRSTHDR(&msg);
    if (!cmsg->cmsg_type == SCM_RIGHTS) {
        fprintf(stderr, "got control message of unknown type %d\n",
                cmsg->cmsg_type);
        return -1;
    }
    return *(int*)CMSG_DATA(cmsg);
}

int childfn(void *p)
{
    int fd;

    (void) p;
    mkdir("/tmp/clonetest", 755);
    mkdir("/tmp/clonetest/dir1", 755);
    mkdir("/tmp/clonetest/dir1/subdir1", 755);
    mkdir("/tmp/clonetest/mnt", 755);
    system("mount --bind /tmp/clonetest/dir1 /tmp/clonetest/mnt");
    fd = open("/", O_RDONLY | O_DIRECTORY);
    send_fd(socks[0], fd);
    sleep(1000);
    return 1;
}

int main()
{
    char buf[10000];
    pid_t pid;
    int res;
    int childfd;

    res = socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
    if (res == -1) {
        perror("socketpair");
        return 1;
    }

    pid = clone(childfn, buf+5000, CLONE_NEWNS | SIGCHLD, NULL);
    if ((int) pid == -1) {
        perror("clone");
        exit(1);
    }

    childfd = receive_fd(socks[1]);
    res = fchdir(childfd);
    if (res == -1) {
        perror("fchdir");
        return 1;
    }
    execl("/bin/bash", "/bin/bash", NULL);
    
    return 0;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux