github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/old/linux_backend/src/nstar/nstar.c (about)

     1  /*
     2   * This executable passes through to the host's tar, extracting into a
     3   * directory in the container.
     4   *
     5   * It does this with a funky dance involving switching to the container's mount
     6   * namespace, creating the destination and saving off its fd, and then
     7   * switching back to the host's rootfs (but the container's destination) for
     8   * the actual untarring.
     9   */
    10  
    11  #include <stdio.h>
    12  #include <errno.h>
    13  #include <fcntl.h>
    14  #include <linux/sched.h>
    15  #include <string.h>
    16  #include <sys/param.h>
    17  #include <sys/stat.h>
    18  #include <sys/types.h>
    19  #include <unistd.h>
    20  
    21   #include "pwd.h"
    22  
    23  /* create a directory; chown only if newly created */
    24  int mkdir_as(const char *dir, uid_t uid, gid_t gid) {
    25    int rv;
    26  
    27    rv = mkdir(dir, 0755);
    28    if(rv == 0) {
    29      /* new directory; set ownership */
    30      return chown(dir, uid, gid);
    31    } else {
    32      if(errno == EEXIST) {
    33        /* if directory already exists, leave ownership as-is */
    34        return 0;
    35      } else {
    36        /* if any other error, abort */
    37        return rv;
    38      }
    39    }
    40  
    41    /* unreachable */
    42    return -1;
    43  }
    44  
    45  /* recursively mkdir with directories owned by a given user */
    46  int mkdir_p_as(const char *dir, uid_t uid, gid_t gid) {
    47    char tmp[PATH_MAX];
    48    char *p = NULL;
    49    size_t len;
    50    int rv;
    51  
    52    /* copy the given dir as it'll be mutated */
    53    snprintf(tmp, sizeof(tmp), "%s", dir);
    54    len = strlen(tmp);
    55  
    56    /* strip trailing slash */
    57    if(tmp[len - 1] == '/')
    58      tmp[len - 1] = 0;
    59  
    60    for(p = tmp + 1; *p; p++) {
    61      if(*p == '/') {
    62        /* temporarily null-terminte the string so that mkdir only creates this
    63         * path segment */
    64        *p = 0;
    65  
    66        /* mkdir with truncated path segment */
    67        rv = mkdir_as(tmp, uid, gid);
    68        if(rv == -1) {
    69          return rv;
    70        }
    71  
    72        /* restore path separator */
    73        *p = '/';
    74      }
    75    }
    76  
    77    /* create final destination */
    78    return mkdir_as(tmp, uid, gid);
    79  }
    80  
    81  /* nothing seems to define this... */
    82  int setns(int fd, int nstype);
    83  
    84  int main(int argc, char **argv) {
    85    int rv;
    86    int mntnsfd;
    87    int usrnsfd;
    88    char *user = NULL;
    89    char *destination = NULL;
    90    int tpid;
    91    int hostrootfd;
    92    int containerworkdir;
    93    char *compress = NULL;
    94    struct passwd *pw;
    95  
    96    if(argc < 4) {
    97      fprintf(stderr, "Usage: %s <wshd pid> <user> <destination> [files to compress]\n", argv[0]);
    98      return 1;
    99    }
   100  
   101    rv = sscanf(argv[1], "%d", &tpid);
   102    if(rv != 1) {
   103      fprintf(stderr, "invalid pid\n");
   104      return 1;
   105    }
   106  
   107    user = argv[2];
   108    destination = argv[3];
   109  
   110    if(argc > 4) {
   111      compress = argv[4];
   112    }
   113  
   114    char mntnspath[PATH_MAX];
   115    rv = snprintf(mntnspath, sizeof(mntnspath), "/proc/%u/ns/mnt", tpid);
   116    if(rv == -1) {
   117      perror("snprintf ns mnt path");
   118      return 1;
   119    }
   120  
   121    mntnsfd = open(mntnspath, O_RDONLY);
   122    if(mntnsfd == -1) {
   123      perror("open mnt namespace");
   124      return 1;
   125    }
   126  
   127    hostrootfd = open("/", O_RDONLY);
   128    if(hostrootfd == -1) {
   129      perror("open host rootfs");
   130      return 1;
   131    }
   132  
   133    char usrnspath[PATH_MAX];
   134    rv = snprintf(usrnspath, sizeof(usrnspath), "/proc/%u/ns/user", tpid);
   135    if(rv == -1) {
   136      perror("snprintf ns user path");
   137      return 1;
   138    }
   139  
   140    usrnsfd = open(usrnspath, O_RDONLY);
   141    if(usrnsfd == -1) {
   142      perror("open user namespace");
   143      return 1;
   144    }
   145  
   146    /* switch to container's mount namespace/rootfs */
   147    rv = setns(mntnsfd, CLONE_NEWNS);
   148    if(rv == -1) {
   149      perror("setns");
   150      return 1;
   151    }
   152    close(mntnsfd);
   153  
   154    /* switch to container's user namespace so that user lookup returns correct uids */
   155    /* we allow this to fail if the container isn't user-namespaced */
   156    setns(usrnsfd, CLONE_NEWUSER);
   157  
   158    pw = getpwnam(user);
   159    if(pw == NULL) {
   160      perror("getpwnam");
   161      return 1;
   162    }
   163  
   164    rv = chdir(pw->pw_dir);
   165    if(rv == -1) {
   166      perror("chdir to user home");
   167      return 1;
   168    }
   169  
   170    rv = setgid(0);
   171    if(rv == -1) {
   172      perror("setgid");
   173      return 1;
   174    }
   175  
   176    rv = setuid(0);
   177    if(rv == -1) {
   178      perror("setuid");
   179      return 1;
   180    }
   181  
   182    /* create destination directory */
   183    rv = mkdir_p_as(destination, pw->pw_uid, pw->pw_gid);
   184    if(rv == -1) {
   185      char msg[1024];
   186      sprintf(msg, "mkdir_p_as %d %d", pw->pw_uid, pw->pw_gid);
   187      perror(msg);
   188      return 1;
   189    }
   190  
   191    /* save off destination dir for switching back to it later */
   192    containerworkdir = open(destination, O_RDONLY);
   193    if(containerworkdir == -1) {
   194      perror("open container destination");
   195      return 1;
   196    }
   197  
   198    /* switch to original host rootfs */
   199    rv = fchdir(hostrootfd);
   200    if(rv == -1) {
   201      perror("fchdir to host rootfs");
   202      return 1;
   203    }
   204  
   205    rv = chroot(".");
   206    if(rv == -1) {
   207      perror("failed to chroot to host rootfs");
   208      return 1;
   209    }
   210  
   211    rv = close(hostrootfd);
   212    if(rv == -1) {
   213      perror("close host destination");
   214      return 1;
   215    }
   216  
   217    /* switch to container's destination directory, with host still as rootfs */
   218    rv = fchdir(containerworkdir);
   219    if(rv == -1) {
   220      perror("fchdir to container destination");
   221      return 1;
   222    }
   223  
   224    rv = close(containerworkdir);
   225    if(rv == -1) {
   226      perror("close container destination");
   227      return 1;
   228    }
   229  
   230    rv = setgid(pw->pw_gid);
   231    if(rv == -1) {
   232      perror("setgid");
   233      return 1;
   234    }
   235  
   236    rv = setuid(pw->pw_uid);
   237    if(rv == -1) {
   238      perror("setuid");
   239      return 1;
   240    }
   241  
   242    if(compress != NULL) {
   243      rv = execl("/bin/tar", "tar", "cf", "-", compress, NULL);
   244      if(rv == -1) {
   245        perror("execl");
   246        return 1;
   247      }
   248    } else {
   249      rv = execl("/bin/tar", "tar", "xf", "-", NULL);
   250      if(rv == -1) {
   251        perror("execl");
   252        return 1;
   253      }
   254    }
   255  
   256    // unreachable
   257    return 2;
   258  }