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 }