]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cmd/lxc_usernsexec.c
utils: make lxc_setgroups() return bool
[mirror_lxc.git] / src / lxc / cmd / lxc_usernsexec.c
CommitLineData
d155b47d
SH
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
250b1eec 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
d155b47d 23 */
72a50694 24
7a435f9a 25#include "config.h"
d155b47d 26
72a50694
CB
27#include <errno.h>
28#include <fcntl.h>
29#include <grp.h>
30#include <libgen.h>
31#include <pwd.h>
d155b47d 32#include <sched.h>
d155b47d 33#include <signal.h>
72a50694
CB
34#include <stdio.h>
35#include <stdlib.h>
d155b47d 36#include <string.h>
72a50694 37#include <sys/mount.h>
d155b47d 38#include <sys/stat.h>
72a50694 39#include <sys/syscall.h>
d155b47d
SH
40#include <sys/types.h>
41#include <sys/wait.h>
72a50694 42#include <unistd.h>
f2363e38 43
0e6e3a41 44#include "conf.h"
02af8066
CB
45#include "list.h"
46#include "log.h"
c881c810 47#include "macro.h"
d155b47d 48#include "namespace.h"
7ee37fac 49#include "utils.h"
d155b47d 50
2d22b22d
TA
51extern int lxc_log_fd;
52
d155b47d
SH
53static void usage(const char *name)
54{
adade80c 55 printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
d155b47d 56 printf("\n");
6f94152d 57 printf(" -h this message\n");
d155b47d
SH
58 printf("\n");
59 printf(" -m <uid-maps> uid maps to use\n");
60 printf("\n");
61 printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
62 printf(" [u|g|b]: map user id, group id, or both\n");
63 printf(" ns_id: the base id in the new namespace\n");
64 printf(" host_id: the base id in the parent namespace\n");
65 printf(" range: how many ids to map\n");
66 printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n");
67 printf(" As such, /etc/subuid and /etc/subgid must grant the\n");
68 printf(" calling user permission to use the mapped ranges\n");
d155b47d
SH
69}
70
a0ee564f
CB
71static void opentty(const char *tty, int which)
72{
02af8066 73 int fd, flags, ret;
b5f4bc78
SH
74
75 if (tty[0] == '\0')
76 return;
d155b47d
SH
77
78 fd = open(tty, O_RDWR | O_NONBLOCK);
a0ee564f 79 if (fd < 0) {
02af8066 80 CMD_SYSERROR("Failed to open tty");
8d4b877a 81 return;
d155b47d
SH
82 }
83
84 flags = fcntl(fd, F_GETFL);
85 flags &= ~O_NONBLOCK;
02af8066
CB
86 ret = fcntl(fd, F_SETFL, flags);
87 if (ret < 0) {
88 CMD_SYSINFO("Failed to remove O_NONBLOCK from file descriptor %d", fd);
22417436 89 close(fd);
35e3a0cd
SG
90 return;
91 }
d155b47d 92
b5f4bc78
SH
93 close(which);
94 if (fd != which) {
a0ee564f 95 (void)dup2(fd, which);
d155b47d 96 close(fd);
b5f4bc78 97 }
d155b47d 98}
1a0e70ac 99/* Code copy end */
d155b47d
SH
100
101static int do_child(void *vargv)
102{
02af8066 103 int ret;
d155b47d
SH
104 char **argv = (char **)vargv;
105
1a0e70ac 106 /* Assume we want to become root */
197c9293
CB
107 ret = lxc_switch_uid_gid(0, 0);
108 if (ret < 0)
d155b47d 109 return -1;
02af8066 110
8af07f82 111 if (!lxc_setgroups(0, NULL))
d155b47d 112 return -1;
02af8066
CB
113
114 ret = unshare(CLONE_NEWNS);
115 if (ret < 0) {
116 CMD_SYSERROR("Failed to unshare mount namespace");
d155b47d
SH
117 return -1;
118 }
02af8066 119
2c6f3fc9 120 if (detect_shared_rootfs()) {
02af8066
CB
121 ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
122 if (ret < 0) {
123 CMD_SYSINFO("Failed to make \"/\" rslave");
2c6f3fc9
SH
124 return -1;
125 }
126 }
02af8066 127
d155b47d 128 execvp(argv[0], argv);
02af8066 129 CMD_SYSERROR("Failed to execute \"%s\"", argv[0]);
d155b47d
SH
130 return -1;
131}
132
0e6e3a41 133static struct lxc_list active_map;
d155b47d
SH
134
135/*
02af8066
CB
136 * Given a string like "b:0:100000:10", map both uids and gids 0-10 to 100000
137 * to 100010
d155b47d
SH
138 */
139static int parse_map(char *map)
140{
02af8066
CB
141 int i, ret;
142 long host_id, ns_id, range;
143 char which;
d155b47d 144 struct id_map *newmap;
0e6e3a41 145 char types[2] = {'u', 'g'};
02af8066 146 struct lxc_list *tmp = NULL;
d155b47d
SH
147
148 if (!map)
149 return -1;
0e6e3a41
SG
150
151 ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
d155b47d 152 if (ret != 4)
0e6e3a41 153 return -1;
d155b47d 154
0e6e3a41
SG
155 if (which != 'b' && which != 'u' && which != 'g')
156 return -1;
157
158 for (i = 0; i < 2; i++) {
159 if (which != types[i] && which != 'b')
160 continue;
161
162 newmap = malloc(sizeof(*newmap));
163 if (!newmap)
164 return -1;
165
166 newmap->hostid = host_id;
167 newmap->nsid = ns_id;
168 newmap->range = range;
169
170 if (types[i] == 'u')
171 newmap->idtype = ID_TYPE_UID;
172 else
173 newmap->idtype = ID_TYPE_GID;
174
175 tmp = malloc(sizeof(*tmp));
176 if (!tmp) {
177 free(newmap);
178 return -1;
179 }
180
181 tmp->elem = newmap;
182 lxc_list_add_tail(&active_map, tmp);
183 }
184
185 return 0;
d155b47d
SH
186}
187
188/*
02af8066
CB
189 * This is called if the user did not pass any uid ranges in through -m flags.
190 * It's called once to get the default uid map, and once for the default gid
191 * map.
192 * Go through /etc/subuids and /etc/subgids to find this user's allowed map. We
193 * only use the first one for each of uid and gid, because otherwise we're not
194 * sure which entries the user wanted.
d155b47d 195 */
5ff02844 196static int read_default_map(char *fnam, int which, char *user)
d155b47d 197{
5ff02844 198 size_t len;
02af8066 199 char *p1, *p2;
d155b47d 200 FILE *fin;
62a38dff 201 int ret = -1;
02af8066
CB
202 size_t sz = 0;
203 char *line = NULL;
0e6e3a41 204 struct lxc_list *tmp = NULL;
62a38dff 205 struct id_map *newmap = NULL;
d155b47d
SH
206
207 fin = fopen(fnam, "r");
208 if (!fin)
209 return -1;
02af8066 210
5ff02844 211 len = strlen(user);
d155b47d 212 while (getline(&line, &sz, fin) != -1) {
5ff02844 213 if (sz <= len || strncmp(line, user, len) != 0 || line[len] != ':')
d155b47d 214 continue;
02af8066 215
46cd2845 216 p1 = strchr(line, ':');
d155b47d
SH
217 if (!p1)
218 continue;
02af8066
CB
219
220 p2 = strchr(p1 + 1, ':');
d155b47d
SH
221 if (!p2)
222 continue;
02af8066 223
d155b47d 224 newmap = malloc(sizeof(*newmap));
62a38dff
CB
225 if (!newmap)
226 goto on_error;
02af8066 227
d3b413e7 228 ret = lxc_safe_ulong(p1 + 1, &newmap->hostid);
62a38dff
CB
229 if (ret < 0)
230 goto on_error;
d3b413e7
CB
231
232 ret = lxc_safe_ulong(p2 + 1, &newmap->range);
62a38dff
CB
233 if (ret < 0)
234 goto on_error;
d3b413e7 235
0e6e3a41
SG
236 newmap->nsid = 0;
237 newmap->idtype = which;
238
62a38dff 239 ret = -1;
0e6e3a41 240 tmp = malloc(sizeof(*tmp));
62a38dff
CB
241 if (!tmp)
242 goto on_error;
0e6e3a41
SG
243
244 tmp->elem = newmap;
245 lxc_list_add_tail(&active_map, tmp);
d155b47d
SH
246 break;
247 }
248
62a38dff
CB
249 ret = 0;
250
251on_error:
d155b47d 252 fclose(fin);
62a38dff
CB
253 free(line);
254 free(newmap);
02af8066 255
62a38dff 256 return ret;
d155b47d
SH
257}
258
d155b47d
SH
259static int find_default_map(void)
260{
02af8066
CB
261 size_t bufsize;
262 char *buf;
cb7aa5e8 263 struct passwd pwent;
02af8066 264 int ret = -1;
cb7aa5e8 265 struct passwd *pwentp = NULL;
cb7aa5e8
DJ
266
267 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
268 if (bufsize == -1)
269 bufsize = 1024;
270
271 buf = malloc(bufsize);
272 if (!buf)
d155b47d 273 return -1;
cb7aa5e8
DJ
274
275 ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
276 if (!pwentp) {
277 if (ret == 0)
02af8066 278 CMD_SYSERROR("Failed to find matched password record");
cb7aa5e8 279
02af8066
CB
280 CMD_SYSERROR("Failed to get password record for uid %d", getuid());
281 ret = -1;
282 goto out;
cb7aa5e8
DJ
283 }
284
02af8066
CB
285 ret = read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name);
286 if (ret < 0)
287 goto out;
cb7aa5e8 288
02af8066
CB
289 ret = read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name);
290 if (ret < 0)
291 goto out;
292
293 ret = 0;
cb7aa5e8 294
02af8066 295out:
cb7aa5e8 296 free(buf);
02af8066
CB
297
298 return ret;
d155b47d
SH
299}
300
d155b47d
SH
301int main(int argc, char *argv[])
302{
02af8066
CB
303 int c, pid, ret, status;
304 char buf[1];
305 int pipe_fds1[2], /* child tells parent it has unshared */
306 pipe_fds2[2]; /* parent tells child it is mapped and may proceed */
d155b47d 307 unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
02af8066 308 char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0};
d155b47d 309 char *default_args[] = {"/bin/sh", NULL};
d155b47d 310
2d22b22d
TA
311 lxc_log_fd = STDERR_FILENO;
312
02af8066 313 if (isatty(STDIN_FILENO)) {
5d1df05b
SH
314 ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
315 if (ret < 0) {
02af8066
CB
316 CMD_SYSERROR("Failed to open stdin");
317 _exit(EXIT_FAILURE);
5d1df05b 318 }
02af8066 319
5d1df05b
SH
320 ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
321 if (ret < 0) {
02af8066
CB
322 CMD_SYSINFO("Failed to open stdout. Continuing");
323 ttyname1[0] = '\0';
5d1df05b 324 }
02af8066 325
5d1df05b
SH
326 ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
327 if (ret < 0) {
02af8066
CB
328 CMD_SYSINFO("Failed to open stderr. Continuing");
329 ttyname2[0] = '\0';
5d1df05b 330 }
b5f4bc78 331 }
d155b47d 332
0e6e3a41
SG
333 lxc_list_init(&active_map);
334
d155b47d
SH
335 while ((c = getopt(argc, argv, "m:h")) != EOF) {
336 switch (c) {
27fdb6be 337 case 'm':
02af8066
CB
338 ret = parse_map(optarg);
339 if (ret < 0) {
27fdb6be 340 usage(argv[0]);
02af8066 341 _exit(EXIT_FAILURE);
27fdb6be
TH
342 }
343 break;
344 case 'h':
345 usage(argv[0]);
02af8066 346 _exit(EXIT_SUCCESS);
27fdb6be
TH
347 default:
348 usage(argv[0]);
02af8066 349 _exit(EXIT_FAILURE);
d155b47d
SH
350 }
351 };
352
0e6e3a41 353 if (lxc_list_empty(&active_map)) {
02af8066
CB
354 ret = find_default_map();
355 if (ret < 0) {
356 fprintf(stderr, "Failed to find subuid or subgid allocation\n");
357 _exit(EXIT_FAILURE);
d155b47d
SH
358 }
359 }
360
361 argv = &argv[optind];
362 argc = argc - optind;
1285f7d5 363 if (argc < 1)
d155b47d 364 argv = default_args;
d155b47d 365
02af8066
CB
366 ret = pipe2(pipe_fds1, O_CLOEXEC);
367 if (ret < 0) {
368 CMD_SYSERROR("Failed to open new pipe");
369 _exit(EXIT_FAILURE);
370 }
371
372 ret = pipe2(pipe_fds2, O_CLOEXEC);
373 if (ret < 0) {
374 CMD_SYSERROR("Failed to open new pipe");
375 close(pipe_fds1[0]);
376 close(pipe_fds1[1]);
377 _exit(EXIT_FAILURE);
d155b47d 378 }
02af8066 379
f0a86c6d 380 pid = fork();
02af8066 381 if (pid < 0) {
04dc1c00 382 close(pipe_fds1[0]);
02af8066
CB
383 close(pipe_fds1[1]);
384 close(pipe_fds2[0]);
04dc1c00 385 close(pipe_fds2[1]);
02af8066
CB
386 _exit(EXIT_FAILURE);
387 }
388
389 if (pid == 0) {
390 close(pipe_fds1[0]);
391 close(pipe_fds2[1]);
392
393 opentty(ttyname0, STDIN_FILENO);
394 opentty(ttyname1, STDOUT_FILENO);
395 opentty(ttyname2, STDERR_FILENO);
d155b47d
SH
396
397 ret = unshare(flags);
398 if (ret < 0) {
02af8066
CB
399 CMD_SYSERROR("Failed to unshare mount and user namespace");
400 close(pipe_fds1[1]);
401 close(pipe_fds2[0]);
402 _exit(EXIT_FAILURE);
d155b47d 403 }
02af8066 404
cbaed76d 405 buf[0] = '1';
02af8066
CB
406 ret = lxc_write_nointr(pipe_fds1[1], buf, 1);
407 if (ret != 1) {
408 CMD_SYSERROR("Failed to write to pipe file descriptor %d",
409 pipe_fds1[1]);
410 close(pipe_fds1[1]);
411 close(pipe_fds2[0]);
412 _exit(EXIT_FAILURE);
d155b47d 413 }
02af8066
CB
414
415 ret = lxc_read_nointr(pipe_fds2[0], buf, 1);
416 if (ret != 1) {
417 CMD_SYSERROR("Failed to read from pipe file descriptor %d",
418 pipe_fds2[0]);
419 close(pipe_fds1[1]);
420 close(pipe_fds2[0]);
421 _exit(EXIT_FAILURE);
d155b47d
SH
422 }
423
04dc1c00
CB
424 close(pipe_fds1[1]);
425 close(pipe_fds2[0]);
02af8066
CB
426
427 if (buf[0] != '1') {
428 fprintf(stderr, "Received unexpected value from parent process\n");
429 _exit(EXIT_FAILURE);
430 }
431
432 ret = do_child((void *)argv);
433 if (ret < 0)
434 _exit(EXIT_FAILURE);
435
436 _exit(EXIT_SUCCESS);
d155b47d
SH
437 }
438
04dc1c00
CB
439 close(pipe_fds1[1]);
440 close(pipe_fds2[0]);
02af8066 441
ede912b4 442 ret = lxc_read_nointr(pipe_fds1[0], buf, 1);
02af8066
CB
443 if (ret <= 0)
444 CMD_SYSERROR("Failed to read from pipe file descriptor %d", pipe_fds1[0]);
d155b47d 445
b543ce96 446 buf[0] = '1';
0e6e3a41 447
02af8066
CB
448 ret = lxc_map_ids(&active_map, pid);
449 if (ret < 0)
450 fprintf(stderr, "Failed to write id mapping for child process\n");
1285f7d5 451
02af8066
CB
452 ret = lxc_write_nointr(pipe_fds2[1], buf, 1);
453 if (ret < 0) {
454 CMD_SYSERROR("Failed to write to pipe file descriptor %d", pipe_fds2[1]);
455 _exit(EXIT_FAILURE);
d155b47d 456 }
02af8066 457
f0a86c6d
TH
458 ret = waitpid(pid, &status, __WALL);
459 if (ret < 0) {
02af8066
CB
460 CMD_SYSERROR("Failed to wait on child process");
461 _exit(EXIT_FAILURE);
d155b47d
SH
462 }
463
02af8066 464 _exit(WEXITSTATUS(status));
d155b47d 465}