]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/cmd/lxc_usernsexec.c
coverity: #1436916
[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{
256 struct passwd *p = getpwuid(getuid());
257 if (!p)
258 return -1;
0e6e3a41 259 if (read_default_map(subuidfile, ID_TYPE_UID, p->pw_name) < 0)
d155b47d 260 return -1;
0e6e3a41 261 if (read_default_map(subgidfile, ID_TYPE_GID, p->pw_name) < 0)
d155b47d
SH
262 return -1;
263 return 0;
264}
265
d155b47d
SH
266int main(int argc, char *argv[])
267{
268 int c;
269 unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
b5f4bc78 270 char ttyname0[256], ttyname1[256], ttyname2[256];
d155b47d
SH
271 int status;
272 int ret;
273 int pid;
274 char *default_args[] = {"/bin/sh", NULL};
cbaed76d 275 char buf[1];
1a0e70ac
CB
276 int pipe1[2], /* child tells parent it has unshared */
277 pipe2[2]; /* parent tells child it is mapped and may proceed */
d155b47d 278
2d22b22d
TA
279 lxc_log_fd = STDERR_FILENO;
280
b5f4bc78
SH
281 memset(ttyname0, '\0', sizeof(ttyname0));
282 memset(ttyname1, '\0', sizeof(ttyname1));
283 memset(ttyname2, '\0', sizeof(ttyname2));
5d1df05b
SH
284 if (isatty(0)) {
285 ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
286 if (ret < 0) {
287 perror("unable to open stdin.");
b52b0595 288 exit(EXIT_FAILURE);
5d1df05b
SH
289 }
290 ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
291 if (ret < 0) {
3eaff0ba 292 printf("Warning: unable to open stdout, continuing.\n");
5d1df05b
SH
293 memset(ttyname1, '\0', sizeof(ttyname1));
294 }
295 ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
296 if (ret < 0) {
3eaff0ba 297 printf("Warning: unable to open stderr, continuing.\n");
5d1df05b
SH
298 memset(ttyname2, '\0', sizeof(ttyname2));
299 }
b5f4bc78 300 }
d155b47d 301
0e6e3a41
SG
302 lxc_list_init(&active_map);
303
d155b47d
SH
304 while ((c = getopt(argc, argv, "m:h")) != EOF) {
305 switch (c) {
d8208db4
TA
306 case 'm':
307 if (parse_map(optarg)) {
308 usage(argv[0]);
309 exit(EXIT_FAILURE);
310 }
311 break;
d155b47d 312 case 'h':
d8208db4
TA
313 usage(argv[0]);
314 exit(EXIT_SUCCESS);
d155b47d
SH
315 default:
316 usage(argv[0]);
d8208db4 317 exit(EXIT_FAILURE);
d155b47d
SH
318 }
319 };
320
0e6e3a41 321 if (lxc_list_empty(&active_map)) {
d155b47d
SH
322 if (find_default_map()) {
323 fprintf(stderr, "You have no allocated subuids or subgids\n");
b52b0595 324 exit(EXIT_FAILURE);
d155b47d
SH
325 }
326 }
327
328 argv = &argv[optind];
329 argc = argc - optind;
1285f7d5 330 if (argc < 1)
d155b47d 331 argv = default_args;
d155b47d
SH
332
333 if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
334 perror("pipe");
b52b0595 335 exit(EXIT_FAILURE);
d155b47d
SH
336 }
337 if ((pid = fork()) == 0) {
1a0e70ac 338 /* Child. */
d155b47d
SH
339
340 close(pipe1[0]);
341 close(pipe2[1]);
b5f4bc78
SH
342 opentty(ttyname0, 0);
343 opentty(ttyname1, 1);
344 opentty(ttyname2, 2);
d155b47d
SH
345
346 ret = unshare(flags);
347 if (ret < 0) {
348 perror("unshare");
349 return 1;
350 }
cbaed76d
SH
351 buf[0] = '1';
352 if (write(pipe1[1], buf, 1) < 1) {
d155b47d 353 perror("write pipe");
b52b0595 354 exit(EXIT_FAILURE);
d155b47d 355 }
cbaed76d 356 if (read(pipe2[0], buf, 1) < 1) {
d155b47d 357 perror("read pipe");
b52b0595 358 exit(EXIT_FAILURE);
d155b47d 359 }
cbaed76d 360 if (buf[0] != '1') {
d155b47d 361 fprintf(stderr, "parent had an error, child exiting\n");
b52b0595 362 exit(EXIT_FAILURE);
d155b47d
SH
363 }
364
365 close(pipe1[1]);
366 close(pipe2[0]);
367 return do_child((void*)argv);
368 }
369
370 close(pipe1[1]);
371 close(pipe2[0]);
cbaed76d 372 if (read(pipe1[0], buf, 1) < 1) {
d155b47d 373 perror("read pipe");
b52b0595 374 exit(EXIT_FAILURE);
d155b47d
SH
375 }
376
b543ce96 377 buf[0] = '1';
0e6e3a41 378
1285f7d5 379 if (lxc_map_ids(&active_map, pid))
d155b47d 380 fprintf(stderr, "error mapping child\n");
1285f7d5 381
cbaed76d 382 if (write(pipe2[1], buf, 1) < 0) {
d155b47d 383 perror("write to pipe");
b52b0595 384 exit(EXIT_FAILURE);
d155b47d
SH
385 }
386
387 if ((ret = waitpid(pid, &status, __WALL)) < 0) {
388 printf("waitpid() returns %d, errno %d\n", ret, errno);
b52b0595 389 exit(EXIT_FAILURE);
d155b47d
SH
390 }
391
392 exit(WEXITSTATUS(status));
393}