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