]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/lxc_usernsexec.c
define lxc-usernsexec
[mirror_lxc.git] / src / lxc / 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
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <sched.h>
29#include <sys/syscall.h>
30#include <signal.h>
31#include <string.h>
32#include <errno.h>
33#include <libgen.h>
34#include <fcntl.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/wait.h>
38#include <sched.h>
39#include <pwd.h>
40#include <grp.h>
41#include "namespace.h"
42
43int unshare(int flags);
44
45static void usage(const char *name)
46{
47 printf("usage: %s [-h] [-c] [-mnuUip] [-P <pid-file>]"
48 "[command [arg ..]]\n", name);
49 printf("\n");
50 printf(" -h this message\n");
51 printf("\n");
52 printf(" -m <uid-maps> uid maps to use\n");
53 printf("\n");
54 printf(" uid-maps: [u|g|b]:ns_id:host_id:range\n");
55 printf(" [u|g|b]: map user id, group id, or both\n");
56 printf(" ns_id: the base id in the new namespace\n");
57 printf(" host_id: the base id in the parent namespace\n");
58 printf(" range: how many ids to map\n");
59 printf(" Note: This program uses newuidmap(2) and newgidmap(2).\n");
60 printf(" As such, /etc/subuid and /etc/subgid must grant the\n");
61 printf(" calling user permission to use the mapped ranges\n");
62 exit(1);
63}
64
65static void opentty(const char * tty) {
66 int i, fd, flags;
67
68 fd = open(tty, O_RDWR | O_NONBLOCK);
69 if (fd == -1) {
70 printf("FATAL: can't reopen tty: %s", strerror(errno));
71 sleep(1);
72 exit(1);
73 }
74
75 flags = fcntl(fd, F_GETFL);
76 flags &= ~O_NONBLOCK;
77 fcntl(fd, F_SETFL, flags);
78
79 for (i = 0; i < fd; i++)
80 close(i);
81 for (i = 0; i < 3; i++)
82 if (fd != i)
83 dup2(fd, i);
84 if (fd >= 3)
85 close(fd);
86}
87// Code copy end
88
89static int do_child(void *vargv)
90{
91 char **argv = (char **)vargv;
92
93 // Assume we want to become root
94 if (setgid(0) < 0) {
95 perror("setgid");
96 return -1;
97 }
98 if (setuid(0) < 0) {
99 perror("setuid");
100 return -1;
101 }
102 if (setgroups(0, NULL) < 0) {
103 perror("setgroups");
104 return -1;
105 }
106 if (unshare(CLONE_NEWNS) < 0) {
107 perror("unshare CLONE_NEWNS");
108 return -1;
109 }
110 execvp(argv[0], argv);
111 perror("execvpe");
112 return -1;
113}
114
115struct id_map {
116 char which; // b or u or g
117 long host_id, ns_id, range;
118 struct id_map *next;
119};
120
121struct id_map default_map = {
122 .which = 'b',
123 .host_id = 100000,
124 .ns_id = 0,
125 .range = 10000,
126};
127static struct id_map *active_map = &default_map;
128
129/*
130 * given a string like "b:0:100000:10", map both uids and gids
131 * 0-10 to 100000 to 100010
132 */
133static int parse_map(char *map)
134{
135 struct id_map *newmap;
136 int ret;
137
138 if (!map)
139 return -1;
140 newmap = malloc(sizeof(*newmap));
141 if (!newmap)
142 return -1;
143 ret = sscanf(map, "%c:%ld:%ld:%ld", &newmap->which, &newmap->ns_id, &newmap->host_id, &newmap->range);
144 if (ret != 4)
145 goto out_free_map;
146 if (newmap->which != 'b' && newmap->which != 'u' && newmap->which != 'g')
147 goto out_free_map;
148 if (active_map != &default_map)
149 newmap->next = active_map;
150 else
151 newmap->next = NULL;
152 active_map = newmap;
153 return 0;
154
155out_free_map:
156 free(newmap);
157 return -1;
158}
159
160/*
161 * go through /etc/subuids and /etc/subgids to find this user's
162 * allowed map. We only use the first one (bc otherwise we're
163 * not sure which ns ids he wants to use).
164 */
165static int read_default_map(char *fnam, char which, char *username)
166{
167 FILE *fin;
168 char *line = NULL;
169 size_t sz = 0;
170 struct id_map *newmap;
171 char *p1, *p2;
172
173 fin = fopen(fnam, "r");
174 if (!fin)
175 return -1;
176 while (getline(&line, &sz, fin) != -1) {
177 if (sz <= strlen(username) ||
178 strncmp(line, username, strlen(username)) != 0 ||
179 line[strlen(username)] != ':')
180 continue;
181 p1 = index(line, ':');
182 if (!p1)
183 continue;
184 p2 = index(p1+1, ':');
185 if (!p2)
186 continue;
187 newmap = malloc(sizeof(*newmap));
188 if (!newmap)
189 return -1;
190 newmap->host_id = atol(p1+1);
191 newmap->range = atol(p2+1);
192 newmap->ns_id = 0;
193 newmap->which = which;
194 if (active_map != &default_map)
195 newmap->next = active_map;
196 else
197 newmap->next = NULL;
198 break;
199 }
200
201 if (line)
202 free(line);
203 fclose(fin);
204 return 0;
205}
206
207#define subuidfile "/etc/subuid"
208#define subgidfile "/etc/subgid"
209static int find_default_map(void)
210{
211 struct passwd *p = getpwuid(getuid());
212 if (!p)
213 return -1;
214 if (read_default_map(subuidfile, 'u', p->pw_name) < 0)
215 return -1;
216 if (read_default_map(subgidfile, 'g', p->pw_name) < 0)
217 return -1;
218 return 0;
219}
220
221static int run_cmd(char **argv)
222{
223 int status;
224 pid_t pid = fork();
225
226 if (pid < 0)
227 return pid;
228 if (pid == 0) {
229 execvp(argv[0], argv);
230 perror("exec failed");
231 exit(1);
232 }
233 if (waitpid(pid, &status, __WALL) < 0) {
234 perror("waitpid");
235 return -1;
236 }
237
238 return WEXITSTATUS(status);
239}
240
241static int map_child_uids(int pid, struct id_map *map)
242{
243 char **uidargs = NULL, **gidargs = NULL;
244 int i, nuargs = 2, ngargs = 2;
245 struct id_map *m;
246
247 uidargs = malloc(3 * sizeof(*uidargs));
248 gidargs = malloc(3 * sizeof(*gidargs));
249 if (uidargs == NULL || gidargs == NULL)
250 return -1;
251 uidargs[0] = malloc(10);
252 gidargs[0] = malloc(10);
253 uidargs[1] = malloc(21);
254 gidargs[1] = malloc(21);
255 uidargs[2] = NULL;
256 gidargs[2] = NULL;
257 if (!uidargs[0] || !uidargs[1] || !gidargs[0] || !gidargs[1])
258 return -1;
259 sprintf(uidargs[0], "newuidmap");
260 sprintf(gidargs[0], "newgidmap");
261 sprintf(uidargs[1], "%d", pid);
262 sprintf(gidargs[1], "%d", pid);
263 for (m=map; m; m = m->next) {
264 if (m->which == 'b' || m->which == 'u') {
265 nuargs += 3;
266 uidargs = realloc(uidargs, (nuargs+1) * sizeof(*uidargs));
267 if (!uidargs)
268 return -1;
269 uidargs[nuargs - 3] = malloc(21);
270 uidargs[nuargs - 2] = malloc(21);
271 uidargs[nuargs - 1] = malloc(21);
272 if (!uidargs[nuargs-3] || !uidargs[nuargs-2] || !uidargs[nuargs-1])
273 return -1;
274 sprintf(uidargs[nuargs - 3], "%ld", m->ns_id);
275 sprintf(uidargs[nuargs - 2], "%ld", m->host_id);
276 sprintf(uidargs[nuargs - 1], "%ld", m->range);
277 uidargs[nuargs] = NULL;
278 }
279 if (m->which == 'b' || m->which == 'g') {
280 ngargs += 3;
281 gidargs = realloc(gidargs, (ngargs+1) * sizeof(*gidargs));
282 if (!gidargs)
283 return -1;
284 gidargs[ngargs - 3] = malloc(21);
285 gidargs[ngargs - 2] = malloc(21);
286 gidargs[ngargs - 1] = malloc(21);
287 if (!gidargs[ngargs-3] || !gidargs[ngargs-2] || !gidargs[ngargs-1])
288 return -1;
289 sprintf(gidargs[ngargs - 3], "%ld", m->ns_id);
290 sprintf(gidargs[ngargs - 2], "%ld", m->host_id);
291 sprintf(gidargs[ngargs - 1], "%ld", m->range);
292 gidargs[ngargs] = NULL;
293 }
294 }
295
296 // exec newuidmap
297 if (nuargs > 2 && run_cmd(uidargs) != 0) {
298 fprintf(stderr, "Error mapping uids\n");
299 return -2;
300 }
301 // exec newgidmap
302 if (ngargs > 2 && run_cmd(gidargs) != 0) {
303 fprintf(stderr, "Error mapping gids\n");
304 return -2;
305 }
306
307 for (i=0; i<nuargs; i++)
308 free(uidargs[i]);
309 for (i=0; i<ngargs; i++)
310 free(gidargs[i]);
311 free(uidargs);
312 free(gidargs);
313
314 return 0;
315}
316
317int main(int argc, char *argv[])
318{
319 int c;
320 unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
321 char ttyname[256];
322 int status;
323 int ret;
324 int pid;
325 char *default_args[] = {"/bin/sh", NULL};
326 int pipe1[2], // child tells parent it has unshared
327 pipe2[2]; // parent tells child it is mapped and may proceed
328
329 memset(ttyname, '\0', sizeof(ttyname));
330 ret = readlink("/proc/self/fd/0", ttyname, sizeof(ttyname));
331 if (ret < 0) {
332 perror("readlink on fd 0");
333 exit(1);
334 }
335
336 while ((c = getopt(argc, argv, "m:h")) != EOF) {
337 switch (c) {
338 case 'm': if (parse_map(optarg)) usage(argv[0]); break;
339 case 'h':
340 default:
341 usage(argv[0]);
342 }
343 };
344
345 if (active_map == &default_map) {
346 if (find_default_map()) {
347 fprintf(stderr, "You have no allocated subuids or subgids\n");
348 exit(1);
349 }
350 }
351
352 argv = &argv[optind];
353 argc = argc - optind;
354 if (argc < 1) {
355 argv = default_args;
356 argc = 1;
357 }
358
359 if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
360 perror("pipe");
361 exit(1);
362 }
363 if ((pid = fork()) == 0) {
364 // Child.
365
366 close(pipe1[0]);
367 close(pipe2[1]);
368 opentty(ttyname);
369
370 ret = unshare(flags);
371 if (ret < 0) {
372 perror("unshare");
373 return 1;
374 }
375 ret = 1;
376 if (write(pipe1[1], &ret, 1) < 1) {
377 perror("write pipe");
378 exit(1);
379 }
380 if (read(pipe2[0], &ret, 1) < 1) {
381 perror("read pipe");
382 exit(1);
383 }
384 if (ret != 1) {
385 fprintf(stderr, "parent had an error, child exiting\n");
386 exit(1);
387 }
388
389 close(pipe1[1]);
390 close(pipe2[0]);
391 return do_child((void*)argv);
392 }
393
394 close(pipe1[1]);
395 close(pipe2[0]);
396 if (read(pipe1[0], &ret, 1) < 1) {
397 perror("read pipe");
398 exit(1);
399 }
400
401 ret = 1;
402 if (map_child_uids(pid, active_map)) {
403 fprintf(stderr, "error mapping child\n");
404 ret = 0;
405 }
406 if (write(pipe2[1], &ret, 1) < 0) {
407 perror("write to pipe");
408 exit(1);
409 }
410
411 if ((ret = waitpid(pid, &status, __WALL)) < 0) {
412 printf("waitpid() returns %d, errno %d\n", ret, errno);
413 exit(ret);
414 }
415
416 exit(WEXITSTATUS(status));
417}