]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cmd/lxc_usernsexec.c
Merge pull request #2417 from 2xsec/bugfix
[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 */
7a435f9a 24#include "config.h"
d155b47d
SH
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>
2c6f3fc9 38#include <sys/mount.h>
d155b47d
SH
39#include <sys/wait.h>
40#include <sched.h>
41#include <pwd.h>
42#include <grp.h>
f2363e38 43
0e6e3a41 44#include "conf.h"
d155b47d 45#include "namespace.h"
7ee37fac 46#include "utils.h"
d155b47d 47
d6a3c917
SG
48#ifndef MS_REC
49#define MS_REC 16384
50#endif
51
52#ifndef MS_SLAVE
eab15c1e 53#define MS_SLAVE (1 << 19)
d6a3c917
SG
54#endif
55
2d22b22d
TA
56extern int lxc_log_fd;
57
d155b47d
SH
58int unshare(int flags);
59
60static void usage(const char *name)
61{
adade80c 62 printf("usage: %s [-h] [-m <uid-maps>] -- [command [arg ..]]\n", name);
d155b47d 63 printf("\n");
6f94152d 64 printf(" -h this message\n");
d155b47d
SH
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");
d155b47d
SH
76}
77
b5f4bc78
SH
78static void opentty(const char * tty, int which) {
79 int fd, flags;
80
81 if (tty[0] == '\0')
82 return;
d155b47d
SH
83
84 fd = open(tty, O_RDWR | O_NONBLOCK);
85 if (fd == -1) {
35e3a0cd 86 printf("WARN: could not reopen tty: %s\n", strerror(errno));
8d4b877a 87 return;
d155b47d
SH
88 }
89
90 flags = fcntl(fd, F_GETFL);
91 flags &= ~O_NONBLOCK;
35e3a0cd
SG
92 if (fcntl(fd, F_SETFL, flags) < 0) {
93 printf("WARN: could not set fd flags: %s\n", strerror(errno));
94 return;
95 }
d155b47d 96
b5f4bc78
SH
97 close(which);
98 if (fd != which) {
99 dup2(fd, which);
d155b47d 100 close(fd);
b5f4bc78 101 }
d155b47d 102}
1a0e70ac 103/* Code copy end */
d155b47d
SH
104
105static int do_child(void *vargv)
106{
107 char **argv = (char **)vargv;
108
1a0e70ac 109 /* Assume we want to become root */
d155b47d
SH
110 if (setgid(0) < 0) {
111 perror("setgid");
112 return -1;
113 }
114 if (setuid(0) < 0) {
115 perror("setuid");
116 return -1;
117 }
118 if (setgroups(0, NULL) < 0) {
119 perror("setgroups");
120 return -1;
121 }
122 if (unshare(CLONE_NEWNS) < 0) {
123 perror("unshare CLONE_NEWNS");
124 return -1;
125 }
2c6f3fc9
SH
126 if (detect_shared_rootfs()) {
127 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
3eaff0ba 128 printf("Failed to make / rslave\n");
2c6f3fc9
SH
129 return -1;
130 }
131 }
d155b47d
SH
132 execvp(argv[0], argv);
133 perror("execvpe");
134 return -1;
135}
136
0e6e3a41 137static struct lxc_list active_map;
d155b47d
SH
138
139/*
140 * given a string like "b:0:100000:10", map both uids and gids
141 * 0-10 to 100000 to 100010
142 */
143static int parse_map(char *map)
144{
145 struct id_map *newmap;
0e6e3a41
SG
146 struct lxc_list *tmp = NULL;
147 int ret;
148 int i;
149 char types[2] = {'u', 'g'};
150 char which;
151 long host_id, ns_id, range;
d155b47d
SH
152
153 if (!map)
154 return -1;
0e6e3a41
SG
155
156 ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
d155b47d 157 if (ret != 4)
0e6e3a41 158 return -1;
d155b47d 159
0e6e3a41
SG
160 if (which != 'b' && which != 'u' && which != 'g')
161 return -1;
162
163 for (i = 0; i < 2; i++) {
164 if (which != types[i] && which != 'b')
165 continue;
166
167 newmap = malloc(sizeof(*newmap));
168 if (!newmap)
169 return -1;
170
171 newmap->hostid = host_id;
172 newmap->nsid = ns_id;
173 newmap->range = range;
174
175 if (types[i] == 'u')
176 newmap->idtype = ID_TYPE_UID;
177 else
178 newmap->idtype = ID_TYPE_GID;
179
180 tmp = malloc(sizeof(*tmp));
181 if (!tmp) {
182 free(newmap);
183 return -1;
184 }
185
186 tmp->elem = newmap;
187 lxc_list_add_tail(&active_map, tmp);
188 }
189
190 return 0;
d155b47d
SH
191}
192
193/*
c7e426be
SH
194 * This is called if the user did not pass any uid ranges in
195 * through -m flags. It's called once to get the default uid
196 * map, and once for the default gid map.
197 * Go through /etc/subuids and /etc/subgids to find this user's
198 * allowed map. We only use the first one for each of uid and
199 * gid, because otherwise we're not sure which entries the user
200 * wanted.
d155b47d 201 */
0e6e3a41 202static int read_default_map(char *fnam, int which, char *username)
d155b47d
SH
203{
204 FILE *fin;
205 char *line = NULL;
206 size_t sz = 0;
207 struct id_map *newmap;
0e6e3a41 208 struct lxc_list *tmp = NULL;
c7e426be 209 char *p1, *p2;
d155b47d
SH
210
211 fin = fopen(fnam, "r");
212 if (!fin)
213 return -1;
214 while (getline(&line, &sz, fin) != -1) {
215 if (sz <= strlen(username) ||
216 strncmp(line, username, strlen(username)) != 0 ||
217 line[strlen(username)] != ':')
218 continue;
46cd2845 219 p1 = strchr(line, ':');
d155b47d
SH
220 if (!p1)
221 continue;
46cd2845 222 p2 = strchr(p1+1, ':');
d155b47d
SH
223 if (!p2)
224 continue;
225 newmap = malloc(sizeof(*newmap));
c7e426be
SH
226 if (!newmap) {
227 fclose(fin);
228 free(line);
d155b47d 229 return -1;
c7e426be 230 }
0e6e3a41 231 newmap->hostid = atol(p1+1);
d155b47d 232 newmap->range = atol(p2+1);
0e6e3a41
SG
233 newmap->nsid = 0;
234 newmap->idtype = which;
235
236 tmp = malloc(sizeof(*tmp));
237 if (!tmp) {
238 fclose(fin);
239 free(line);
240 free(newmap);
241 return -1;
242 }
243
244 tmp->elem = newmap;
245 lxc_list_add_tail(&active_map, tmp);
d155b47d
SH
246 break;
247 }
248
f10fad2f 249 free(line);
d155b47d
SH
250 fclose(fin);
251 return 0;
252}
253
d155b47d
SH
254static int find_default_map(void)
255{
cb7aa5e8
DJ
256 struct passwd pwent;
257 struct passwd *pwentp = NULL;
258 char *buf;
259 size_t bufsize;
260 int ret;
261
262 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
263 if (bufsize == -1)
264 bufsize = 1024;
265
266 buf = malloc(bufsize);
267 if (!buf)
d155b47d 268 return -1;
cb7aa5e8
DJ
269
270 ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
271 if (!pwentp) {
272 if (ret == 0)
273 printf("WARN: could not find matched password record\n");
274
275 printf("Failed to get password record - %u\n", getuid());
276 free(buf);
d155b47d 277 return -1;
cb7aa5e8
DJ
278 }
279
280 if (read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name) < 0) {
281 free(buf);
d155b47d 282 return -1;
cb7aa5e8
DJ
283 }
284
285 if (read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name) < 0) {
286 free(buf);
287 return -1;
288 }
289
290 free(buf);
291 return 0;
d155b47d
SH
292}
293
d155b47d
SH
294int main(int argc, char *argv[])
295{
296 int c;
297 unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
b5f4bc78 298 char ttyname0[256], ttyname1[256], ttyname2[256];
d155b47d
SH
299 int status;
300 int ret;
301 int pid;
302 char *default_args[] = {"/bin/sh", NULL};
cbaed76d 303 char buf[1];
1a0e70ac
CB
304 int pipe1[2], /* child tells parent it has unshared */
305 pipe2[2]; /* parent tells child it is mapped and may proceed */
d155b47d 306
2d22b22d
TA
307 lxc_log_fd = STDERR_FILENO;
308
b5f4bc78
SH
309 memset(ttyname0, '\0', sizeof(ttyname0));
310 memset(ttyname1, '\0', sizeof(ttyname1));
311 memset(ttyname2, '\0', sizeof(ttyname2));
5d1df05b
SH
312 if (isatty(0)) {
313 ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
314 if (ret < 0) {
315 perror("unable to open stdin.");
b52b0595 316 exit(EXIT_FAILURE);
5d1df05b
SH
317 }
318 ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
319 if (ret < 0) {
3eaff0ba 320 printf("Warning: unable to open stdout, continuing.\n");
5d1df05b
SH
321 memset(ttyname1, '\0', sizeof(ttyname1));
322 }
323 ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
324 if (ret < 0) {
3eaff0ba 325 printf("Warning: unable to open stderr, continuing.\n");
5d1df05b
SH
326 memset(ttyname2, '\0', sizeof(ttyname2));
327 }
b5f4bc78 328 }
d155b47d 329
0e6e3a41
SG
330 lxc_list_init(&active_map);
331
d155b47d
SH
332 while ((c = getopt(argc, argv, "m:h")) != EOF) {
333 switch (c) {
d8208db4
TA
334 case 'm':
335 if (parse_map(optarg)) {
336 usage(argv[0]);
337 exit(EXIT_FAILURE);
338 }
339 break;
d155b47d 340 case 'h':
d8208db4
TA
341 usage(argv[0]);
342 exit(EXIT_SUCCESS);
d155b47d
SH
343 default:
344 usage(argv[0]);
d8208db4 345 exit(EXIT_FAILURE);
d155b47d
SH
346 }
347 };
348
0e6e3a41 349 if (lxc_list_empty(&active_map)) {
d155b47d
SH
350 if (find_default_map()) {
351 fprintf(stderr, "You have no allocated subuids or subgids\n");
b52b0595 352 exit(EXIT_FAILURE);
d155b47d
SH
353 }
354 }
355
356 argv = &argv[optind];
357 argc = argc - optind;
1285f7d5 358 if (argc < 1)
d155b47d 359 argv = default_args;
d155b47d
SH
360
361 if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
362 perror("pipe");
b52b0595 363 exit(EXIT_FAILURE);
d155b47d
SH
364 }
365 if ((pid = fork()) == 0) {
1a0e70ac 366 /* Child. */
d155b47d
SH
367
368 close(pipe1[0]);
369 close(pipe2[1]);
b5f4bc78
SH
370 opentty(ttyname0, 0);
371 opentty(ttyname1, 1);
372 opentty(ttyname2, 2);
d155b47d
SH
373
374 ret = unshare(flags);
375 if (ret < 0) {
376 perror("unshare");
377 return 1;
378 }
cbaed76d
SH
379 buf[0] = '1';
380 if (write(pipe1[1], buf, 1) < 1) {
d155b47d 381 perror("write pipe");
b52b0595 382 exit(EXIT_FAILURE);
d155b47d 383 }
cbaed76d 384 if (read(pipe2[0], buf, 1) < 1) {
d155b47d 385 perror("read pipe");
b52b0595 386 exit(EXIT_FAILURE);
d155b47d 387 }
cbaed76d 388 if (buf[0] != '1') {
d155b47d 389 fprintf(stderr, "parent had an error, child exiting\n");
b52b0595 390 exit(EXIT_FAILURE);
d155b47d
SH
391 }
392
393 close(pipe1[1]);
394 close(pipe2[0]);
395 return do_child((void*)argv);
396 }
397
398 close(pipe1[1]);
399 close(pipe2[0]);
cbaed76d 400 if (read(pipe1[0], buf, 1) < 1) {
d155b47d 401 perror("read pipe");
b52b0595 402 exit(EXIT_FAILURE);
d155b47d
SH
403 }
404
b543ce96 405 buf[0] = '1';
0e6e3a41 406
1285f7d5 407 if (lxc_map_ids(&active_map, pid))
d155b47d 408 fprintf(stderr, "error mapping child\n");
1285f7d5 409
cbaed76d 410 if (write(pipe2[1], buf, 1) < 0) {
d155b47d 411 perror("write to pipe");
b52b0595 412 exit(EXIT_FAILURE);
d155b47d
SH
413 }
414
415 if ((ret = waitpid(pid, &status, __WALL)) < 0) {
416 printf("waitpid() returns %d, errno %d\n", ret, errno);
b52b0595 417 exit(EXIT_FAILURE);
d155b47d
SH
418 }
419
420 exit(WEXITSTATUS(status));
421}