github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/stage1/appexec/appexec.c (about) 1 // Copyright 2014 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #define _GNU_SOURCE 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <grp.h> 19 #include <inttypes.h> 20 #include <limits.h> 21 #include <stdint.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/mman.h> 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 #include <unistd.h> 29 30 #include "elf.h" 31 32 static int exit_err; 33 #define exit_if(_cond, _fmt, _args...) \ 34 exit_err++; \ 35 if(_cond) { \ 36 fprintf(stderr, "Error: " _fmt "\n", ##_args); \ 37 exit(exit_err); \ 38 } 39 #define pexit_if(_cond, _fmt, _args...) \ 40 exit_if(_cond, _fmt ": %s", ##_args, strerror(errno)) 41 42 #define MAX_DIAG_DEPTH 10 43 #define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) 44 45 static void map_file(const char *path, int prot, int flags, struct stat *st, void **map) 46 { 47 int fd; 48 49 pexit_if((fd = open(path, O_RDONLY)) == -1, 50 "Unable to open \"%s\"", path); 51 pexit_if(fstat(fd, st) == -1, 52 "Cannot stat \"%s\"", path); 53 exit_if(!S_ISREG(st->st_mode), "\"%s\" is not a regular file", path); 54 pexit_if(!(*map = mmap(NULL, st->st_size, prot, flags, fd, 0)), 55 "Mmap of \"%s\" failed", path); 56 pexit_if(close(fd) == -1, 57 "Close of %i [%s] failed", fd, path); 58 } 59 60 static void diag(const char *exe) 61 { 62 static const uint8_t elf[] = {0x7f, 'E', 'L', 'F'}; 63 static const uint8_t shebang[] = {'#','!'}; 64 static int diag_depth; 65 struct stat st; 66 const uint8_t *mm; 67 const char *itrp = NULL; 68 69 map_file(exe, PROT_READ, MAP_SHARED, &st, (void **)&mm); 70 exit_if(!((S_IXUSR|S_IXGRP|S_IXOTH) & st.st_mode), 71 "\"%s\" is not executable", exe) 72 73 if(st.st_size >= sizeof(shebang) && 74 !memcmp(mm, shebang, sizeof(shebang))) { 75 const uint8_t *nl; 76 int maxlen = MIN(PATH_MAX, st.st_size - sizeof(shebang)); 77 /* TODO(vc): EOF-terminated shebang lines are technically possible */ 78 exit_if(!(nl = memchr(&mm[sizeof(shebang)], '\n', maxlen)), 79 "Shebang line too long"); 80 pexit_if(!(itrp = strndup((char *)&mm[sizeof(shebang)], (nl - mm) - 2)), 81 "Failed to dup interpreter path"); 82 } else if(st.st_size >= sizeof(elf) && 83 !memcmp(mm, elf, sizeof(elf))) { 84 uint64_t (*lget)(const uint8_t *) = NULL; 85 uint32_t (*iget)(const uint8_t *) = NULL; 86 uint16_t (*sget)(const uint8_t *) = NULL; 87 const void *phoff = NULL, *phesz = NULL, *phecnt = NULL; 88 const uint8_t *ph = NULL; 89 int i, phreloff, phrelsz; 90 91 exit_if(mm[ELF_VERSION] != 1, 92 "Unsupported ELF version: %hhx", mm[ELF_VERSION]); 93 94 /* determine which accessors to use and where */ 95 if(mm[ELF_BITS] == ELF_BITS_32) { 96 if(mm[ELF_ENDIAN] == ELF_ENDIAN_LITL) { 97 lget = le32_lget; 98 sget = le_sget; 99 iget = le_iget; 100 } else if(mm[ELF_ENDIAN] == ELF_ENDIAN_BIG) { 101 lget = be32_lget; 102 sget = be_sget; 103 iget = be_iget; 104 } 105 phoff = &mm[ELF32_PHT_OFF]; 106 phesz = &mm[ELF32_PHTE_SIZE]; 107 phecnt = &mm[ELF32_PHTE_CNT]; 108 phreloff = ELF32_PHE_OFF; 109 phrelsz = ELF32_PHE_SIZE; 110 } else if(mm[ELF_BITS] == ELF_BITS_64) { 111 if(mm[ELF_ENDIAN] == ELF_ENDIAN_LITL) { 112 lget = le64_lget; 113 sget = le_sget; 114 iget = le_iget; 115 } else if(mm[ELF_ENDIAN] == ELF_ENDIAN_BIG) { 116 lget = be64_lget; 117 sget = be_sget; 118 iget = be_iget; 119 } 120 phoff = &mm[ELF64_PHT_OFF]; 121 phesz = &mm[ELF64_PHTE_SIZE]; 122 phecnt = &mm[ELF64_PHTE_CNT]; 123 phreloff = ELF64_PHE_OFF; 124 phrelsz = ELF64_PHE_SIZE; 125 } 126 127 exit_if(!lget, "Unsupported ELF format"); 128 129 if(!phoff) /* program header may be absent, don't make it an error */ 130 return; 131 132 /* TODO(vc): sanity checks on values before using them */ 133 for(ph = &mm[lget(phoff)], i = 0; i < sget(phecnt); i++, ph += sget(phesz)) { 134 if(iget(ph) == ELF_PT_INTERP) { 135 itrp = strndup((char *)&mm[lget(&ph[phreloff])], lget(&ph[phrelsz])); 136 break; 137 } 138 } 139 } else { 140 exit_if(1, "Unsupported file type"); 141 } 142 143 exit_if(!itrp, "Unable to determine interpreter for \"%s\"", exe); 144 exit_if(*itrp != '/', "Path must be absolute: \"%s\"", itrp); 145 exit_if(++diag_depth > MAX_DIAG_DEPTH, 146 "Excessive interpreter recursion, giving up"); 147 diag(itrp); 148 } 149 150 /* Append env variables listed in keep_env to env_file if they're present in 151 * current environment */ 152 void append_env(const char *env_file, const char **keep_env) 153 { 154 FILE *f; 155 const char **p; 156 char *v; 157 char nul = '\0'; 158 159 pexit_if((f = fopen(env_file, "a")) == NULL, 160 "Unable to fopen \"%s\"", env_file); 161 162 p = keep_env; 163 while (*p) { 164 v = getenv(*p); 165 if (v) { 166 pexit_if(fprintf(f, "%s=%s%c", *p, v, nul) != (strlen(*p) + strlen(v) + 2), 167 "Unable to write to \"%s\"", env_file); 168 } 169 p++; 170 } 171 172 pexit_if(fclose(f) == EOF, 173 "Unable to fclose \"%s\"", env_file); 174 175 return; 176 } 177 178 /* Read environment from env and make it our own keeping the env variables in 179 * keep_env if they're present in the current environment. 180 * The environment file must exist, may be empty, and is expected to be of the format: 181 * key=value\0key=value\0... 182 */ 183 static void load_env(const char *env, const char **keep_env) 184 { 185 struct stat st; 186 char *map, *k, *v; 187 typeof(st.st_size) i; 188 189 append_env(env, keep_env); 190 191 map_file(env, PROT_READ|PROT_WRITE, MAP_PRIVATE, &st, (void **)&map); 192 193 pexit_if(clearenv() != 0, 194 "Unable to clear environment"); 195 196 if(!st.st_size) 197 return; 198 199 map[st.st_size - 1] = '\0'; /* ensure the mapping is null-terminated */ 200 201 for(i = 0; i < st.st_size;) { 202 k = &map[i]; 203 i += strlen(k) + 1; 204 exit_if((v = strchr(k, '=')) == NULL, 205 "Malformed environment entry: \"%s\"", k); 206 /* a private writable map is used permitting s/=/\0/ */ 207 *v = '\0'; 208 v++; 209 pexit_if(setenv(k, v, 1) == -1, 210 "Unable to set env variable: \"%s\"=\"%s\"", k, v); 211 } 212 } 213 214 /* Parse a comma-separated list of numeric gids from str, returns an malloc'd 215 * array of gids in *gids_p with the number of elements in *n_gids_p. 216 */ 217 static void parse_gids(const char *str, size_t *n_gids_p, gid_t **gids_p) 218 { 219 char c = ',', last_c; 220 int i, n_gids = 0, done = 0; 221 gid_t gid = 0; 222 gid_t *gids = NULL; 223 224 for(i = 0; !done; i++) { 225 last_c = c; 226 switch(c = str[i]) { 227 case '0' ... '9': 228 gid *= 10; 229 gid += c - '0'; 230 break; 231 232 case '\0': 233 done = 1; 234 /* fallthrough */ 235 case ',': 236 exit_if(last_c == ',', 237 "Gids contains an empty gid: \"%s\"", str); 238 pexit_if((gids = realloc(gids, sizeof(*gids) * (n_gids + 1))) == NULL, 239 "Unable to allocate gids: \"%s\"", str); 240 gids[n_gids++] = gid; 241 gid = 0; 242 break; 243 244 default: 245 exit_if(1, 246 "Gids contains invalid input (%c): \"%s\"", 247 c, str); 248 } 249 } 250 251 exit_if(!n_gids, "At least one gid is required, got: \"%s\"", str); 252 253 *gids_p = gids; 254 *n_gids_p = n_gids; 255 } 256 257 int main(int argc, char *argv[]) 258 { 259 /* We need to keep these env variables since systemd uses them for socket 260 * activation 261 */ 262 static const char *keep_env[] = { 263 "LISTEN_FDS", 264 "LISTEN_PID", 265 NULL 266 }; 267 268 const char *root, *cwd, *env, *uid_str, *gid_str, *exe; 269 char **args; 270 uid_t uid; 271 gid_t *gids; 272 size_t n_gids; 273 274 exit_if(argc < 7, 275 "Usage: %s /path/to/root /work/directory /env/file uid gid[,gid...] /to/exec [args ...]", argv[0]); 276 277 root = argv[1]; 278 cwd = argv[2]; 279 env = argv[3]; 280 uid_str = argv[4]; 281 uid = atoi(uid_str); 282 gid_str = argv[5]; 283 args = &argv[6]; 284 exe = args[0]; 285 286 parse_gids(gid_str, &n_gids, &gids); 287 load_env(env, keep_env); 288 289 pexit_if(chroot(root) == -1, "Chroot \"%s\" failed", root); 290 pexit_if(chdir(cwd) == -1, "Chdir \"%s\" failed", cwd); 291 pexit_if(gids[0] > 0 && setresgid(gids[0], gids[0], gids[0]) == -1, 292 "Setresgid \"%s\" failed", gid_str); 293 pexit_if(n_gids > 1 && setgroups(n_gids - 1, &gids[1]) == -1, 294 "Setgroups \"%s\" failed", gid_str); 295 pexit_if(uid > 0 && setresuid(uid, uid, uid) == -1, 296 "Setresuid \"%s\" failed", uid_str); 297 298 /* XXX(vc): note that since execvp() is happening post-chroot, the 299 * app's environment settings correctly affect the PATH search. 300 * This is why execvpe() isn't being used, we manipulate the environment 301 * manually then let it potentially affect execvp(). execvpe() simply 302 * passes the environment to execve() _after_ performing the search, not 303 * what we want here. */ 304 pexit_if(execvp(exe, args) == -1 && 305 errno != ENOENT && errno != EACCES, 306 "Exec of \"%s\" failed", exe); 307 diag(exe); 308 309 return EXIT_FAILURE; 310 }