]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cmd/lxc_usernsexec.c
Merge pull request #2534 from tcharding/checkpatch
[mirror_lxc.git] / src / lxc / cmd / lxc_usernsexec.c
1 /*
2 * (C) Copyright IBM Corp. 2008
3 * (C) Copyright Canonical, Inc 2010-2013
4 *
5 * Authors:
6 * Serge Hallyn <serge.hallyn@ubuntu.com>
7 * (Once upon a time, this was based on nsexec from the IBM
8 * container tools)
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sched.h>
30 #include <sys/syscall.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <libgen.h>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <sys/mount.h>
39 #include <sys/wait.h>
40 #include <sched.h>
41 #include <pwd.h>
42 #include <grp.h>
43
44 #include "conf.h"
45 #include "namespace.h"
46 #include "utils.h"
47
48 #ifndef MS_REC
49 #define MS_REC 16384
50 #endif
51
52 #ifndef MS_SLAVE
53 #define MS_SLAVE (1 << 19)
54 #endif
55
56 extern int lxc_log_fd;
57
58 int unshare(int flags);
59
60 static void usage(const char *name)
61 {
62 printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
63 printf("\n");
64 printf(" -h this message\n");
65 printf("\n");
66 printf(" -m <uid-maps> uid maps to use\n");
67 printf("\n");
68 printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
69 printf(" [u|g|b]: map user id, group id, or both\n");
70 printf(" ns_id: the base id in the new namespace\n");
71 printf(" host_id: the base id in the parent namespace\n");
72 printf(" range: how many ids to map\n");
73 printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n");
74 printf(" As such, /etc/subuid and /etc/subgid must grant the\n");
75 printf(" calling user permission to use the mapped ranges\n");
76 }
77
78 static void opentty(const char *tty, int which)
79 {
80 int fd, flags;
81
82 if (tty[0] == '\0')
83 return;
84
85 fd = open(tty, O_RDWR | O_NONBLOCK);
86 if (fd < 0) {
87 printf("WARN: could not reopen tty: %s\n", strerror(errno));
88 return;
89 }
90
91 flags = fcntl(fd, F_GETFL);
92 flags &= ~O_NONBLOCK;
93 if (fcntl(fd, F_SETFL, flags) < 0) {
94 printf("WARN: could not set fd flags: %s\n", strerror(errno));
95 close(fd);
96 return;
97 }
98
99 close(which);
100 if (fd != which) {
101 (void)dup2(fd, which);
102 close(fd);
103 }
104 }
105 /* Code copy end */
106
107 static int do_child(void *vargv)
108 {
109 char **argv = (char **)vargv;
110
111 /* Assume we want to become root */
112 if (setgid(0) < 0) {
113 perror("setgid");
114 return -1;
115 }
116 if (setuid(0) < 0) {
117 perror("setuid");
118 return -1;
119 }
120 if (setgroups(0, NULL) < 0) {
121 perror("setgroups");
122 return -1;
123 }
124 if (unshare(CLONE_NEWNS) < 0) {
125 perror("unshare CLONE_NEWNS");
126 return -1;
127 }
128 if (detect_shared_rootfs()) {
129 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
130 printf("Failed to make / rslave\n");
131 return -1;
132 }
133 }
134 execvp(argv[0], argv);
135 perror("execvpe");
136 return -1;
137 }
138
139 static struct lxc_list active_map;
140
141 /*
142 * given a string like "b:0:100000:10", map both uids and gids
143 * 0-10 to 100000 to 100010
144 */
145 static int parse_map(char *map)
146 {
147 struct id_map *newmap;
148 struct lxc_list *tmp = NULL;
149 int ret;
150 int i;
151 char types[2] = {'u', 'g'};
152 char which;
153 long host_id, ns_id, range;
154
155 if (!map)
156 return -1;
157
158 ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
159 if (ret != 4)
160 return -1;
161
162 if (which != 'b' && which != 'u' && which != 'g')
163 return -1;
164
165 for (i = 0; i < 2; i++) {
166 if (which != types[i] && which != 'b')
167 continue;
168
169 newmap = malloc(sizeof(*newmap));
170 if (!newmap)
171 return -1;
172
173 newmap->hostid = host_id;
174 newmap->nsid = ns_id;
175 newmap->range = range;
176
177 if (types[i] == 'u')
178 newmap->idtype = ID_TYPE_UID;
179 else
180 newmap->idtype = ID_TYPE_GID;
181
182 tmp = malloc(sizeof(*tmp));
183 if (!tmp) {
184 free(newmap);
185 return -1;
186 }
187
188 tmp->elem = newmap;
189 lxc_list_add_tail(&active_map, tmp);
190 }
191
192 return 0;
193 }
194
195 /*
196 * This is called if the user did not pass any uid ranges in
197 * through -m flags. It's called once to get the default uid
198 * map, and once for the default gid map.
199 * Go through /etc/subuids and /etc/subgids to find this user's
200 * allowed map. We only use the first one for each of uid and
201 * gid, because otherwise we're not sure which entries the user
202 * wanted.
203 */
204 static int read_default_map(char *fnam, int which, char *username)
205 {
206 FILE *fin;
207 char *line = NULL;
208 size_t sz = 0;
209 struct id_map *newmap;
210 struct lxc_list *tmp = NULL;
211 char *p1, *p2;
212
213 fin = fopen(fnam, "r");
214 if (!fin)
215 return -1;
216 while (getline(&line, &sz, fin) != -1) {
217 if (sz <= strlen(username) ||
218 strncmp(line, username, strlen(username)) != 0 ||
219 line[strlen(username)] != ':')
220 continue;
221 p1 = strchr(line, ':');
222 if (!p1)
223 continue;
224 p2 = strchr(p1+1, ':');
225 if (!p2)
226 continue;
227 newmap = malloc(sizeof(*newmap));
228 if (!newmap) {
229 fclose(fin);
230 free(line);
231 return -1;
232 }
233 newmap->hostid = atol(p1+1);
234 newmap->range = atol(p2+1);
235 newmap->nsid = 0;
236 newmap->idtype = which;
237
238 tmp = malloc(sizeof(*tmp));
239 if (!tmp) {
240 fclose(fin);
241 free(line);
242 free(newmap);
243 return -1;
244 }
245
246 tmp->elem = newmap;
247 lxc_list_add_tail(&active_map, tmp);
248 break;
249 }
250
251 free(line);
252 fclose(fin);
253 return 0;
254 }
255
256 static int find_default_map(void)
257 {
258 struct passwd pwent;
259 struct passwd *pwentp = NULL;
260 char *buf;
261 size_t bufsize;
262 int ret;
263
264 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
265 if (bufsize == -1)
266 bufsize = 1024;
267
268 buf = malloc(bufsize);
269 if (!buf)
270 return -1;
271
272 ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
273 if (!pwentp) {
274 if (ret == 0)
275 printf("WARN: could not find matched password record\n");
276
277 printf("Failed to get password record - %u\n", getuid());
278 free(buf);
279 return -1;
280 }
281
282 if (read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name) < 0) {
283 free(buf);
284 return -1;
285 }
286
287 if (read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name) < 0) {
288 free(buf);
289 return -1;
290 }
291
292 free(buf);
293 return 0;
294 }
295
296 int main(int argc, char *argv[])
297 {
298 int c;
299 unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
300 char ttyname0[256], ttyname1[256], ttyname2[256];
301 int status;
302 int ret;
303 int pid;
304 char *default_args[] = {"/bin/sh", NULL};
305 char buf[1];
306 int pipe_fds1[2], /* child tells parent it has unshared */
307 pipe_fds2[2]; /* parent tells child it is mapped and may proceed */
308
309 lxc_log_fd = STDERR_FILENO;
310
311 memset(ttyname0, '\0', sizeof(ttyname0));
312 memset(ttyname1, '\0', sizeof(ttyname1));
313 memset(ttyname2, '\0', sizeof(ttyname2));
314 if (isatty(0)) {
315 ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
316 if (ret < 0) {
317 perror("unable to open stdin.");
318 exit(EXIT_FAILURE);
319 }
320 ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
321 if (ret < 0) {
322 printf("Warning: unable to open stdout, continuing.\n");
323 memset(ttyname1, '\0', sizeof(ttyname1));
324 }
325 ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
326 if (ret < 0) {
327 printf("Warning: unable to open stderr, continuing.\n");
328 memset(ttyname2, '\0', sizeof(ttyname2));
329 }
330 }
331
332 lxc_list_init(&active_map);
333
334 while ((c = getopt(argc, argv, "m:h")) != EOF) {
335 switch (c) {
336 case 'm':
337 if (parse_map(optarg)) {
338 usage(argv[0]);
339 exit(EXIT_FAILURE);
340 }
341 break;
342 case 'h':
343 usage(argv[0]);
344 exit(EXIT_SUCCESS);
345 default:
346 usage(argv[0]);
347 exit(EXIT_FAILURE);
348 }
349 };
350
351 if (lxc_list_empty(&active_map)) {
352 if (find_default_map()) {
353 fprintf(stderr, "You have no allocated subuids or subgids\n");
354 exit(EXIT_FAILURE);
355 }
356 }
357
358 argv = &argv[optind];
359 argc = argc - optind;
360 if (argc < 1)
361 argv = default_args;
362
363 if (pipe2(pipe_fds1, O_CLOEXEC) < 0 || pipe2(pipe_fds2, O_CLOEXEC) < 0) {
364 perror("pipe");
365 exit(EXIT_FAILURE);
366 }
367 pid = fork();
368 if (pid == 0) { /* Child. */
369 close(pipe_fds1[0]);
370 close(pipe_fds2[1]);
371 opentty(ttyname0, 0);
372 opentty(ttyname1, 1);
373 opentty(ttyname2, 2);
374
375 ret = unshare(flags);
376 if (ret < 0) {
377 fprintf(stderr,
378 "Failed to unshare mount and user namespace\n");
379 return 1;
380 }
381 buf[0] = '1';
382 if (lxc_write_nointr(pipe_fds1[1], buf, 1) < 1) {
383 perror("write pipe");
384 exit(EXIT_FAILURE);
385 }
386 if (lxc_read_nointr(pipe_fds2[0], buf, 1) < 1) {
387 perror("read pipe");
388 exit(EXIT_FAILURE);
389 }
390 if (buf[0] != '1') {
391 fprintf(stderr, "parent had an error, child exiting\n");
392 exit(EXIT_FAILURE);
393 }
394
395 close(pipe_fds1[1]);
396 close(pipe_fds2[0]);
397 return do_child((void *)argv);
398 }
399
400 close(pipe_fds1[1]);
401 close(pipe_fds2[0]);
402 ret = lxc_read_nointr(pipe_fds1[0], buf, 1);
403 if (ret < 0) {
404 perror("read pipe");
405 exit(EXIT_FAILURE);
406 } else if (ret == 0) {
407 fprintf(stderr, "Failed to read from pipe\n");
408 exit(EXIT_FAILURE);
409 }
410
411 buf[0] = '1';
412
413 if (lxc_map_ids(&active_map, pid))
414 fprintf(stderr, "error mapping child\n");
415
416 if (lxc_write_nointr(pipe_fds2[1], buf, 1) < 0) {
417 perror("write to pipe");
418 exit(EXIT_FAILURE);
419 }
420 ret = waitpid(pid, &status, __WALL);
421 if (ret < 0) {
422 printf("waitpid() returns %d, errno %d\n", ret, errno);
423 exit(EXIT_FAILURE);
424 }
425
426 exit(WEXITSTATUS(status));
427 }