On Mon, Jul 22, 2013 at 03:23:58PM -0500, Serge Hallyn wrote:
> It uses the newuidmap and newgidmap program to start a shell in
> a mapped user namespace.  While newuidmap and newgidmap are
> setuid-root, lxc-usernsexec is not.
> 
> If new{ug}idmap are not available, then this program is not
> built or installed.  Otherwise, it will be used to support creating,
> starting, destroying, etc containers by unprivileged users using
> their authorized subuids and subgids.
> 
> Example:
>       usernsexec -m u:0:100000:1 -- /bin/bash
> 
> will, if the user is authorized to use subuid 100000, start a
> bash shell in a user namespace where 100000 on the host is
> mapped to root in the namespace, and the shell is running as
> (privileged) root.
> 
> Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com>

I'm assuming this is a copy of the code you had in the separate package, if so:

Acked-by: Stéphane Graber <stgra...@ubuntu.com>

> ---
>  configure.ac             |   3 +
>  src/lxc/Makefile.am      |   9 +
>  src/lxc/lxc_usernsexec.c | 417 
> +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 429 insertions(+)
>  create mode 100644 src/lxc/lxc_usernsexec.c
> 
> diff --git a/configure.ac b/configure.ac
> index 56638d4..1131f8b 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -52,6 +52,9 @@ esac
>  AC_MSG_RESULT([$with_distro])
>  AM_CONDITIONAL([HAVE_DEBIAN], [test x"$with_distro" = "xdebian" -o 
> x"$with_distro" = "xubuntu"])
>  
> +AC_CHECK_PROG([NEWUIDMAP], [newuidmap], [newuidmap])
> +AM_CONDITIONAL([HAVE_NEWUIDMAP], [test -n "$NEWUIDMAP"])
> +
>  # Allow disabling rpath
>  AC_ARG_ENABLE([rpath],
>       [AC_HELP_STRING([--disable-rpath], [do not set rpath in executables])],
> diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
> index 18469a1..1421251 100644
> --- a/src/lxc/Makefile.am
> +++ b/src/lxc/Makefile.am
> @@ -101,6 +101,10 @@ if ENABLE_APPARMOR
>  AM_CFLAGS += -DHAVE_APPARMOR
>  endif
>  
> +if HAVE_NEWUIDMAP
> +AM_CFLAGS += -DHAVE_NEWUIDMAP
> +endif
> +
>  if USE_CONFIGPATH_LOGS
>  AM_CFLAGS += -DUSE_CONFIGPATH_LOGS
>  endif
> @@ -163,6 +167,10 @@ bin_PROGRAMS = \
>       lxc-destroy \
>      lxc-create
>  
> +if HAVE_NEWUIDMAP
> +bin_PROGRAMS += lxc-usernsexec
> +endif
> +
>  pkglibexec_PROGRAMS = \
>       lxc-init
>  
> @@ -196,6 +204,7 @@ lxc_unshare_SOURCES = lxc_unshare.c
>  lxc_wait_SOURCES = lxc_wait.c
>  lxc_kill_SOURCES = lxc_kill.c
>  lxc_create_SOURCES = lxc_create.c
> +lxc_usernsexec_SOURCES = lxc_usernsexec.c
>  
>  install-exec-local: install-soPROGRAMS
>       mkdir -p $(DESTDIR)$(datadir)/lxc
> diff --git a/src/lxc/lxc_usernsexec.c b/src/lxc/lxc_usernsexec.c
> new file mode 100644
> index 0000000..afc3bd7
> --- /dev/null
> +++ b/src/lxc/lxc_usernsexec.c
> @@ -0,0 +1,417 @@
> +/*
> + * (C) Copyright IBM Corp. 2008
> + * (C) Copyright Canonical, Inc 2010-2013
> + *
> + * Authors:
> + * Serge Hallyn <serge.hal...@ubuntu.com>
> + * (Once upon a time, this was based on nsexec from the IBM
> + *  container tools)
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <sched.h>
> +#include <sys/syscall.h>
> +#include <signal.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <libgen.h>
> +#include <fcntl.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <sched.h>
> +#include <pwd.h>
> +#include <grp.h>
> +#include "namespace.h"
> +
> +int unshare(int flags);
> +
> +static void usage(const char *name)
> +{
> +     printf("usage: %s [-h] [-c] [-mnuUip] [-P <pid-file>]"
> +                     "[command [arg ..]]\n", name);
> +     printf("\n");
> +     printf("  -h            this message\n");
> +     printf("\n");
> +     printf("  -m <uid-maps> uid maps to use\n");
> +     printf("\n");
> +     printf("  uid-maps: [u|g|b]:ns_id:host_id:range\n");
> +     printf("            [u|g|b]: map user id, group id, or both\n");
> +     printf("            ns_id: the base id in the new namespace\n");
> +     printf("            host_id: the base id in the parent namespace\n");
> +     printf("            range: how many ids to map\n");
> +     printf("  Note: This program uses newuidmap(2) and newgidmap(2).\n");
> +     printf("        As such, /etc/subuid and /etc/subgid must grant the\n");
> +     printf("        calling user permission to use the mapped ranges\n");
> +     exit(1);
> +}
> +
> +static void opentty(const char * tty) {
> +     int i, fd, flags;
> +
> +     fd = open(tty, O_RDWR | O_NONBLOCK);
> +     if (fd == -1) {
> +             printf("FATAL: can't reopen tty: %s", strerror(errno));
> +             sleep(1);
> +             exit(1);
> +     }
> +
> +     flags = fcntl(fd, F_GETFL);
> +     flags &= ~O_NONBLOCK;
> +     fcntl(fd, F_SETFL, flags);
> +
> +     for (i = 0; i < fd; i++)
> +             close(i);
> +     for (i = 0; i < 3; i++)
> +             if (fd != i)
> +                     dup2(fd, i);
> +     if (fd >= 3)
> +             close(fd);
> +}
> +// Code copy end
> +
> +static int do_child(void *vargv)
> +{
> +     char **argv = (char **)vargv;
> +
> +     // Assume we want to become root
> +     if (setgid(0) < 0) {
> +             perror("setgid");
> +             return -1;
> +     }
> +     if (setuid(0) < 0) {
> +             perror("setuid");
> +             return -1;
> +     }
> +     if (setgroups(0, NULL) < 0) {
> +             perror("setgroups");
> +             return -1;
> +     }
> +     if (unshare(CLONE_NEWNS) < 0) {
> +             perror("unshare CLONE_NEWNS");
> +             return -1;
> +     }
> +     execvp(argv[0], argv);
> +     perror("execvpe");
> +     return -1;
> +}
> +
> +struct id_map {
> +     char which; // b or u or g
> +     long host_id, ns_id, range;
> +     struct id_map *next;
> +};
> +
> +struct id_map default_map = {
> +     .which = 'b',
> +     .host_id = 100000,
> +     .ns_id = 0,
> +     .range = 10000,
> +};
> +static struct id_map *active_map = &default_map;
> +
> +/*
> + * given a string like "b:0:100000:10", map both uids and gids
> + * 0-10 to 100000 to 100010
> + */
> +static int parse_map(char *map)
> +{
> +     struct id_map *newmap;
> +    int ret;
> +
> +     if (!map)
> +             return -1;
> +     newmap = malloc(sizeof(*newmap));
> +     if (!newmap)
> +             return -1;
> +     ret = sscanf(map, "%c:%ld:%ld:%ld", &newmap->which, &newmap->ns_id, 
> &newmap->host_id, &newmap->range);
> +     if (ret != 4)
> +             goto out_free_map;
> +     if (newmap->which != 'b' && newmap->which != 'u' && newmap->which != 
> 'g')
> +             goto out_free_map;
> +     if (active_map != &default_map)
> +             newmap->next = active_map;
> +     else
> +             newmap->next = NULL;
> +     active_map = newmap;
> +     return 0;
> +
> +out_free_map:
> +     free(newmap);
> +     return -1;
> +}
> +
> +/*
> + * go through /etc/subuids and /etc/subgids to find this user's
> + * allowed map.  We only use the first one (bc otherwise we're
> + * not sure which ns ids he wants to use).
> + */
> +static int read_default_map(char *fnam, char which, char *username)
> +{
> +     FILE *fin;
> +     char *line = NULL;
> +     size_t sz = 0;
> +     struct id_map *newmap;
> +    char *p1, *p2;
> +
> +     fin = fopen(fnam, "r");
> +     if (!fin)
> +             return -1;
> +     while (getline(&line, &sz, fin) != -1) {
> +             if (sz <= strlen(username) ||
> +                 strncmp(line, username, strlen(username)) != 0 ||
> +                 line[strlen(username)] != ':')
> +                     continue;
> +             p1 = index(line, ':');
> +             if (!p1)
> +                     continue;
> +             p2 = index(p1+1, ':');
> +             if (!p2)
> +                     continue;
> +             newmap = malloc(sizeof(*newmap));
> +             if (!newmap)
> +                     return -1;
> +             newmap->host_id = atol(p1+1);
> +             newmap->range = atol(p2+1);
> +             newmap->ns_id = 0;
> +             newmap->which = which;
> +             if (active_map != &default_map)
> +                     newmap->next = active_map;
> +             else
> +                     newmap->next = NULL;
> +             break;
> +     }
> +
> +     if (line)
> +             free(line);
> +     fclose(fin);
> +     return 0;
> +}
> +
> +#define subuidfile "/etc/subuid"
> +#define subgidfile "/etc/subgid"
> +static int find_default_map(void)
> +{
> +     struct passwd *p = getpwuid(getuid());
> +     if (!p)
> +             return -1;
> +     if (read_default_map(subuidfile, 'u', p->pw_name) < 0)
> +             return -1;
> +     if (read_default_map(subgidfile, 'g', p->pw_name) < 0)
> +             return -1;
> +    return 0;
> +}
> +
> +static int run_cmd(char **argv)
> +{
> +    int status;
> +     pid_t pid = fork();
> +
> +     if (pid < 0)
> +             return pid;
> +     if (pid == 0) {
> +             execvp(argv[0], argv);
> +             perror("exec failed");
> +             exit(1);
> +     }
> +     if (waitpid(pid, &status, __WALL) < 0) {
> +        perror("waitpid");
> +             return -1;
> +     }
> +
> +     return WEXITSTATUS(status);
> +}
> +
> +static int map_child_uids(int pid, struct id_map *map)
> +{
> +     char **uidargs = NULL, **gidargs = NULL;
> +     int i, nuargs = 2, ngargs = 2;
> +     struct id_map *m;
> +
> +     uidargs = malloc(3 * sizeof(*uidargs));
> +     gidargs = malloc(3 * sizeof(*gidargs));
> +     if (uidargs == NULL || gidargs == NULL)
> +             return -1;
> +     uidargs[0] = malloc(10);
> +     gidargs[0] = malloc(10);
> +     uidargs[1] = malloc(21);
> +     gidargs[1] = malloc(21);
> +     uidargs[2] = NULL;
> +     gidargs[2] = NULL;
> +     if (!uidargs[0] || !uidargs[1] || !gidargs[0] || !gidargs[1])
> +             return -1;
> +     sprintf(uidargs[0], "newuidmap");
> +     sprintf(gidargs[0], "newgidmap");
> +     sprintf(uidargs[1], "%d", pid);
> +     sprintf(gidargs[1], "%d", pid);
> +     for (m=map; m; m = m->next) {
> +             if (m->which == 'b' || m->which == 'u') {
> +                     nuargs += 3;
> +                     uidargs = realloc(uidargs, (nuargs+1) * 
> sizeof(*uidargs));
> +                     if (!uidargs)
> +                             return -1;
> +                     uidargs[nuargs - 3] = malloc(21);
> +                     uidargs[nuargs - 2] = malloc(21);
> +                     uidargs[nuargs - 1] = malloc(21);
> +                     if (!uidargs[nuargs-3] || !uidargs[nuargs-2] || 
> !uidargs[nuargs-1])
> +                             return -1;
> +                     sprintf(uidargs[nuargs - 3], "%ld", m->ns_id);
> +                     sprintf(uidargs[nuargs - 2], "%ld", m->host_id);
> +                     sprintf(uidargs[nuargs - 1], "%ld", m->range);
> +                     uidargs[nuargs] = NULL;
> +             }
> +             if (m->which == 'b' || m->which == 'g') {
> +                     ngargs += 3;
> +                     gidargs = realloc(gidargs, (ngargs+1) * 
> sizeof(*gidargs));
> +                     if (!gidargs)
> +                             return -1;
> +                     gidargs[ngargs - 3] = malloc(21);
> +                     gidargs[ngargs - 2] = malloc(21);
> +                     gidargs[ngargs - 1] = malloc(21);
> +                     if (!gidargs[ngargs-3] || !gidargs[ngargs-2] || 
> !gidargs[ngargs-1])
> +                             return -1;
> +                     sprintf(gidargs[ngargs - 3], "%ld", m->ns_id);
> +                     sprintf(gidargs[ngargs - 2], "%ld", m->host_id);
> +                     sprintf(gidargs[ngargs - 1], "%ld", m->range);
> +                     gidargs[ngargs] = NULL;
> +             }
> +     }
> +
> +     // exec newuidmap
> +     if (nuargs > 2 && run_cmd(uidargs) != 0) {
> +             fprintf(stderr, "Error mapping uids\n");
> +             return -2;
> +     }
> +     // exec newgidmap
> +     if (ngargs > 2 && run_cmd(gidargs) != 0) {
> +             fprintf(stderr, "Error mapping gids\n");
> +             return -2;
> +     }
> +
> +     for (i=0; i<nuargs; i++)
> +             free(uidargs[i]);
> +     for (i=0; i<ngargs; i++)
> +             free(gidargs[i]);
> +     free(uidargs);
> +     free(gidargs);
> +
> +    return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{    
> +     int c;
> +     unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
> +     char ttyname[256];
> +     int status;
> +     int ret;
> +     int pid;
> +     char *default_args[] = {"/bin/sh", NULL};
> +     int pipe1[2],  // child tells parent it has unshared
> +         pipe2[2];  // parent tells child it is mapped and may proceed
> +
> +     memset(ttyname, '\0', sizeof(ttyname));
> +     ret = readlink("/proc/self/fd/0", ttyname, sizeof(ttyname));
> +     if (ret < 0) {
> +             perror("readlink on fd 0");
> +             exit(1);
> +     }
> +
> +     while ((c = getopt(argc, argv, "m:h")) != EOF) {
> +             switch (c) {
> +                     case 'm': if (parse_map(optarg)) usage(argv[0]); break;
> +                     case 'h':
> +                     default:
> +                               usage(argv[0]);
> +             }
> +     };
> +
> +     if (active_map == &default_map) {
> +             if (find_default_map()) {
> +                     fprintf(stderr, "You have no allocated subuids or 
> subgids\n");
> +                     exit(1);
> +             }
> +     }
> +
> +     argv = &argv[optind];
> +     argc = argc - optind;   
> +     if (argc < 1) {
> +             argv = default_args;
> +             argc = 1;
> +     }
> +
> +     if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
> +             perror("pipe");
> +             exit(1);
> +     }
> +     if ((pid = fork()) == 0) {
> +             // Child.
> +
> +             close(pipe1[0]);
> +             close(pipe2[1]);
> +             opentty(ttyname);
> +
> +             ret = unshare(flags);
> +             if (ret < 0) {
> +                     perror("unshare");
> +                     return 1;
> +             }
> +             ret = 1;
> +             if (write(pipe1[1], &ret, 1) < 1) {
> +                     perror("write pipe");
> +                     exit(1);
> +             }
> +             if (read(pipe2[0], &ret, 1) < 1) {
> +                     perror("read pipe");
> +                     exit(1);
> +             }
> +             if (ret != 1) {
> +                     fprintf(stderr, "parent had an error, child exiting\n");
> +                     exit(1);
> +             }
> +
> +             close(pipe1[1]);
> +             close(pipe2[0]);
> +             return do_child((void*)argv);
> +     }
> +
> +     close(pipe1[1]);
> +     close(pipe2[0]);
> +     if (read(pipe1[0], &ret, 1) < 1) {
> +             perror("read pipe");
> +             exit(1);
> +     }
> +
> +     ret = 1;
> +     if (map_child_uids(pid, active_map)) {
> +             fprintf(stderr, "error mapping child\n");
> +             ret = 0;
> +     }
> +     if (write(pipe2[1], &ret, 1) < 0) {
> +             perror("write to pipe");
> +             exit(1);
> +     }
> +
> +     if ((ret = waitpid(pid, &status, __WALL)) < 0) {
> +             printf("waitpid() returns %d, errno %d\n", ret, errno);
> +             exit(ret);
> +     }
> +
> +     exit(WEXITSTATUS(status));
> +}
> -- 
> 1.8.3.2
> 
> 
> ------------------------------------------------------------------------------
> See everything from the browser to the database with AppDynamics
> Get end-to-end visibility with application monitoring from AppDynamics
> Isolate bottlenecks and diagnose root cause in seconds.
> Start your free trial of AppDynamics Pro today!
> http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
> _______________________________________________
> Lxc-devel mailing list
> Lxc-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com

Attachment: signature.asc
Description: Digital signature

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to