github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/stage1/enterexec/enterexec.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 "diagnostic-util.h" 31 32 /* Create keep_env file from keep_env, if they're present in 33 * current environment and file doesn't exist */ 34 void initialize_keep_env(const char *keep_env_file, const char **keep_env) 35 { 36 FILE *f; 37 const char **p; 38 char *v; 39 char nul = '\0'; 40 41 if (!access(keep_env_file, F_OK)) return; 42 pexit_if((f = fopen(keep_env_file, "a")) == NULL, 43 "Unable to fopen \"%s\"", keep_env_file); 44 45 p = keep_env; 46 while (*p) { 47 v = getenv(*p); 48 if (v) { 49 pexit_if(fprintf(f, "%s=%s%c", *p, v, nul) != (strlen(*p) + strlen(v) + 2), 50 "Unable to write to \"%s\"", keep_env_file); 51 } 52 p++; 53 } 54 55 pexit_if(fclose(f) == EOF, 56 "Unable to fclose \"%s\"", keep_env_file); 57 } 58 59 /* Try to set current env from keep_env and env file. */ 60 static void set_env(const char *env_file) 61 { 62 FILE *f; 63 char *line = NULL; 64 size_t len = 0; 65 ssize_t read; 66 char *v, *nl; 67 68 pexit_if((f = fopen(env_file, "r")) == NULL, 69 "Unable to fopen \"%s\"", env_file); 70 while ((read = getline(&line, &len, f)) != -1) { 71 pexit_if((v = strchr(line, '=')) == NULL, 72 "Malformed environment entry: \"%s\"", line); 73 *v = '\0'; 74 v++; 75 /* remove new line character */ 76 if ((nl = strchr(v, '\n')) != NULL) 77 *nl = '\0'; 78 pexit_if(setenv(line, v, 1) == -1, 79 "Unable to set env variable: \"%s\"=\"%s\"", line, v); 80 } 81 free(line); 82 pexit_if(fclose(f) == EOF, 83 "Unable to fclose \"%s\"", env_file); 84 } 85 86 /* Read environment from env and keep_env files make it our own, keeping the env variables in 87 * if they're present in the current environment. 88 * The environment files must exist, may be empty, and are expected to be of the format: 89 * key=value\nkey=value\n... 90 */ 91 static void load_env(const char *env_file, const char *keep_env_file, int entering) 92 { 93 char *term = getenv("TERM"); /* useful to keep during entering. */ 94 pexit_if(clearenv() != 0, 95 "Unable to clear environment"); 96 97 set_env(env_file); 98 set_env(keep_env_file); 99 if (entering) { 100 // enter is typically interactive; ensure we always have a sane enough term 101 // variable. 102 if (term == NULL) { 103 setenv("TERM", "vt100", 1); 104 } else { 105 setenv("TERM", term, 1); 106 } 107 } 108 } 109 110 /* Parse a comma-separated list of numeric gids from str, returns an malloc'd 111 * array of gids in *gids_p with the number of elements in *n_gids_p. 112 */ 113 static void parse_gids(const char *str, size_t *n_gids_p, gid_t **gids_p) 114 { 115 char c = ',', last_c; 116 int i, n_gids = 0, done = 0; 117 gid_t gid = 0; 118 gid_t *gids = NULL; 119 120 for(i = 0; !done; i++) { 121 last_c = c; 122 switch(c = str[i]) { 123 case '0' ... '9': 124 gid *= 10; 125 gid += c - '0'; 126 break; 127 128 case '\0': 129 done = 1; 130 /* fallthrough */ 131 case ',': 132 exit_if(last_c == ',', 133 "Gids contains an empty gid: \"%s\"", str); 134 pexit_if((gids = realloc(gids, sizeof(*gids) * (n_gids + 1))) == NULL, 135 "Unable to allocate gids: \"%s\"", str); 136 gids[n_gids++] = gid; 137 gid = 0; 138 break; 139 140 default: 141 exit_if(1, 142 "Gids contains invalid input (%c): \"%s\"", 143 c, str); 144 } 145 } 146 147 exit_if(!n_gids, "At least one gid is required, got: \"%s\"", str); 148 149 *gids_p = gids; 150 *n_gids_p = n_gids; 151 } 152 153 int main(int argc, char *argv[]) 154 { 155 int entering = 0; 156 157 /* '-e' optional flag passed only during 'entering' phase from stage1. 158 */ 159 int c; 160 while ((c = getopt(argc, argv, "e")) != -1) 161 switch (c) { 162 case 'e': 163 entering = 1; 164 break; 165 } 166 167 /* We need to keep these env variables since systemd uses them for socket 168 * activation 169 */ 170 static const char *keep_env[] = { 171 "LISTEN_FDS", 172 "LISTEN_PID", 173 NULL 174 }; 175 176 const char *keep_env_file = "/rkt/env/keep_env"; 177 const char *root, *cwd, *env_file, *uid_str, *gid_str, *exe; 178 179 char **args; 180 uid_t uid; 181 gid_t *gids; 182 size_t n_gids; 183 184 exit_if(argc < 7, 185 "Usage: %s /path/to/root /work/directory /env/file uid gid[,gid...] [-e] /to/exec [args ...]", argv[0]); 186 187 root = argv[optind]; 188 cwd = argv[optind+1]; 189 env_file = argv[optind+2]; 190 uid_str = argv[optind+3]; 191 uid = atoi(uid_str); 192 gid_str = argv[optind+4]; 193 args = &argv[optind+5]; 194 exe = args[0]; 195 196 parse_gids(gid_str, &n_gids, &gids); 197 198 initialize_keep_env(keep_env_file, keep_env); 199 load_env(env_file, keep_env_file, entering); 200 201 pexit_if(chroot(root) == -1, "Chroot \"%s\" failed", root); 202 pexit_if(chdir(cwd) == -1, "Chdir \"%s\" failed", cwd); 203 pexit_if(gids[0] > 0 && setresgid(gids[0], gids[0], gids[0]) == -1, 204 "Setresgid \"%s\" failed", gid_str); 205 pexit_if(n_gids > 1 && setgroups(n_gids - 1, &gids[1]) == -1, 206 "Setgroups \"%s\" failed", gid_str); 207 pexit_if(uid > 0 && setresuid(uid, uid, uid) == -1, 208 "Setresuid \"%s\" failed", uid_str); 209 210 /* XXX(vc): note that since execvp() is happening post-chroot, the 211 * app's environment settings correctly affect the PATH search. 212 * This is why execvpe() isn't being used, we manipulate the environment 213 * manually then let it potentially affect execvp(). execvpe() simply 214 * passes the environment to execve() _after_ performing the search, not 215 * what we want here. */ 216 pexit_if(execvp(exe, args) == -1 && 217 errno != ENOENT && errno != EACCES, 218 "Exec of \"%s\" failed", exe); 219 diag(exe); 220 221 return EXIT_FAILURE; 222 }