]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_unshare.c
Merge pull request #3403 from brauner/2020-05-07/fixes
[mirror_lxc.git] / src / lxc / tools / lxc_unshare.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
d06245b8 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
7a401679 6#include <errno.h>
d567a9a7 7#include <fcntl.h>
633cb02a 8#include <getopt.h>
7a401679 9#include <libgen.h>
d38dd64a 10#include <netinet/in.h>
7a401679 11#include <pwd.h>
d567a9a7 12#include <sched.h>
633cb02a 13#include <signal.h>
d567a9a7 14#include <stdio.h>
7a401679
SG
15#include <stdlib.h>
16#include <string.h>
344c9d81 17#include <sys/eventfd.h>
7a401679 18#include <sys/socket.h>
633cb02a 19#include <sys/types.h>
20#include <sys/wait.h>
d38dd64a 21#include <unistd.h>
633cb02a 22
d567a9a7 23#include "arguments.h"
4f66541c 24#include "caps.h"
d38dd64a 25#include "config.h"
59e1663a 26#include "list.h"
727b9b16 27#include "log.h"
4f66541c 28#include "namespace.h"
9b57809d
CB
29#include "syscall_numbers.h"
30#include "syscall_wrappers.h"
4f66541c 31#include "utils.h"
7a401679 32
727b9b16 33lxc_log_define(lxc_unshare, lxc);
34
35struct start_arg {
36 char *const *args;
37 int flags;
38 uid_t uid;
39 bool setuid;
40 int want_default_mounts;
41 int wait_fd;
42 const char *want_hostname;
43};
44
727b9b16 45static int my_parser(struct lxc_arguments *args, int c, char *arg);
727b9b16 46static int get_namespace_flags(char *namespaces);
a7547c5c 47static bool lookup_user(const char *oparg, uid_t *uid);
727b9b16 48static int mount_fs(const char *source, const char *target, const char *type);
49static void lxc_setup_fs(void);
50static int do_start(void *arg);
59e1663a 51static void free_ifname_list(void);
727b9b16 52
59e1663a 53static struct lxc_list ifnames;
727b9b16 54
55static const struct option my_longopts[] = {
56 {"namespaces", required_argument, 0, 's'},
57 {"user", required_argument, 0, 'u'},
58 {"hostname", required_argument, 0, 'H'},
59 {"ifname", required_argument, 0, 'i'},
60 {"daemon", no_argument, 0, 'd'},
61 {"remount", no_argument, 0, 'M'},
62 LXC_COMMON_OPTIONS
63};
64
65static struct lxc_arguments my_args = {
66 .progname = "lxc-unshare",
67 .help = "\
68-s NAMESPACES COMMAND\n\
69\n\
70lxc-unshare run a COMMAND in a new set of NAMESPACES\n\
71\n\
72Options :\n\
73 -s, --namespaces=FLAGS\n\
74 ORed list of flags to unshare:\n\
75 MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n\
76 -u, --user=USERID\n\
77 new id to be set if -s USER is specified\n\
78 -H, --hostname=HOSTNAME\n\
79 Set the hostname in the container\n\
80 -i, --ifname=IFNAME\n\
81 Interface name to be moved into container (presumably with NETWORK unsharing set)\n\
82 -d, --daemon Daemonize (do not wait for container to exit)\n\
83 -M, --remount Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n\
84",
85 .options = my_longopts,
86 .parser = my_parser,
87 .checker = NULL,
d67beb9e 88 .log_priority = "ERROR",
89 .log_file = "none",
727b9b16 90 .daemonize = 0,
727b9b16 91};
92
93static int my_parser(struct lxc_arguments *args, int c, char *arg)
94{
59e1663a 95 struct lxc_list *tmplist;
96
727b9b16 97 switch (c) {
98 case 's':
99 args->flags = get_namespace_flags(arg);
100 if (args->flags < 0)
101 return -1;
102 break;
103 case 'u':
104 if (!lookup_user(arg, &args->uid))
105 return -1;
106
107 args->setuid = true;
108 break;
109 case 'H':
110 args->want_hostname = arg;
111 break;
112 case 'i':
59e1663a 113 tmplist = malloc(sizeof(*tmplist));
114 if (!tmplist) {
115 SYSERROR("Failed to alloc lxc list");
116 free_ifname_list();
727b9b16 117 return -1;
118 }
119
59e1663a 120 lxc_list_add_elem(tmplist, arg);
121 lxc_list_add_tail(&ifnames, tmplist);
727b9b16 122 break;
123 case 'd':
124 args->daemonize = 1;
125 break;
126 case 'M':
127 args->want_default_mounts = 1;
128 break;
129 }
130 return 0;
131}
132
727b9b16 133static int get_namespace_flags(char *namespaces)
c1bb25a8 134{
727b9b16 135 int flags = 0;
c1bb25a8 136
727b9b16 137 if (lxc_namespace_2_std_identifiers(namespaces) < 0)
138 return -1;
139
140 if (lxc_fill_namespace_flags(namespaces, &flags) < 0)
141 return -1;
142
143 return flags;
633cb02a 144}
145
a7547c5c 146static bool lookup_user(const char *oparg, uid_t *uid)
b7f85ccb 147{
3a5996ff 148 char name[PATH_MAX];
2dce415b
DJ
149 struct passwd pwent;
150 struct passwd *pwentp = NULL;
151 char *buf;
152 size_t bufsize;
153 int ret;
b7f85ccb 154
a7547c5c 155 if (!oparg || (oparg[0] == '\0'))
31a1209d 156 return false;
b7f85ccb 157
2dce415b
DJ
158 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
159 if (bufsize == -1)
160 bufsize = 1024;
161
162 buf = malloc(bufsize);
163 if (!buf)
164 return false;
165
a7547c5c 166 if (sscanf(oparg, "%u", uid) < 1) {
1615dc39 167 /* not a uid -- perhaps a username */
a7547c5c 168 if (sscanf(oparg, "%s", name) < 1) {
db8b325a 169 free(buf);
31a1209d 170 return false;
db8b325a 171 }
1615dc39 172
2dce415b
DJ
173 ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp);
174 if (!pwentp) {
175 if (ret == 0)
727b9b16 176 SYSERROR("Could not find matched password record");
2dce415b 177
727b9b16 178 SYSERROR("Invalid username \"%s\"", name);
2dce415b 179 free(buf);
31a1209d 180 return false;
1615dc39 181 }
4f66541c 182
2dce415b 183 *uid = pwent.pw_uid;
b7f85ccb 184 } else {
2dce415b
DJ
185 ret = getpwuid_r(*uid, &pwent, buf, bufsize, &pwentp);
186 if (!pwentp) {
187 if (ret == 0)
727b9b16 188 SYSERROR("Could not find matched password record");
2dce415b 189
727b9b16 190 SYSERROR("Invalid uid : %u", *uid);
2dce415b 191 free(buf);
31a1209d 192 return false;
b7f85ccb
MH
193 }
194 }
2dce415b
DJ
195
196 free(buf);
31a1209d 197 return true;
b7f85ccb
MH
198}
199
4f66541c 200static int mount_fs(const char *source, const char *target, const char *type)
201{
202 /* the umount may fail */
203 if (umount(target) < 0)
204
205 if (mount(source, target, type, 0, NULL) < 0)
206 return -1;
207
208 return 0;
209}
210
211static void lxc_setup_fs(void)
212{
213 (void)mount_fs("proc", "/proc", "proc");
214
215 /* if /dev has been populated by us, /dev/shm does not exist */
216 if (access("/dev/shm", F_OK))
e581b9b5 217 (void)mkdir("/dev/shm", 0770);
4f66541c 218
219 /* if we can't mount /dev/shm, continue anyway */
220 (void)mount_fs("shmfs", "/dev/shm", "tmpfs");
221
222 /* If we were able to mount /dev/shm, then /dev exists */
223 /* Sure, but it's read-only per config :) */
224 if (access("/dev/mqueue", F_OK))
e581b9b5 225 (void)mkdir("/dev/mqueue", 0660);
4f66541c 226
227 /* continue even without posix message queue support */
228 (void)mount_fs("mqueue", "/dev/mqueue", "mqueue");
229}
230
50e98013
DL
231static int do_start(void *arg)
232{
344c9d81
MPS
233 int ret;
234 uint64_t wait_val;
50e98013 235 struct start_arg *start_arg = arg;
727b9b16 236 char *const *args = start_arg->args;
c1bb25a8 237 const char *want_hostname = start_arg->want_hostname;
344c9d81
MPS
238
239 if (start_arg->setuid) {
240 /* waiting until uid maps is set */
7b6f89cd 241 ret = lxc_read_nointr(start_arg->wait_fd, &wait_val, sizeof(wait_val));
344c9d81 242 if (ret == -1) {
727b9b16 243 SYSERROR("Failed to read eventfd");
244 close(start_arg->wait_fd);
4f66541c 245 _exit(EXIT_FAILURE);
344c9d81
MPS
246 }
247 }
c1bb25a8 248
727b9b16 249 if ((start_arg->flags & CLONE_NEWNS) && start_arg->want_default_mounts)
c1bb25a8
SR
250 lxc_setup_fs();
251
727b9b16 252 if ((start_arg->flags & CLONE_NEWUTS) && want_hostname)
bed09c9c 253 if (sethostname(want_hostname, strlen(want_hostname)) < 0) {
727b9b16 254 SYSERROR("Failed to set hostname %s", want_hostname);
4f66541c 255 _exit(EXIT_FAILURE);
c1bb25a8 256 }
50e98013 257
1a0e70ac 258 /* Setuid is useful even without a new user id space. */
727b9b16 259 if (start_arg->setuid && setuid(start_arg->uid)) {
260 SYSERROR("Failed to set uid %d", start_arg->uid);
4f66541c 261 _exit(EXIT_FAILURE);
50e98013
DL
262 }
263
264 execvp(args[0], args);
265
727b9b16 266 SYSERROR("Failed to exec: '%s'", args[0]);
50e98013
DL
267 return 1;
268}
269
59e1663a 270static void free_ifname_list(void)
271{
272 struct lxc_list *iterator, *next;
273
274 lxc_list_for_each_safe (iterator, &ifnames, next) {
275 lxc_list_del(iterator);
276 free(iterator);
277 }
278}
279
633cb02a 280int main(int argc, char *argv[])
281{
a4255c08 282 int ret;
633cb02a 283 pid_t pid;
727b9b16 284 struct lxc_log log;
285 struct start_arg start_arg;
4f66541c 286
59e1663a 287 lxc_list_init(&ifnames);
288
727b9b16 289 if (lxc_caps_init())
290 exit(EXIT_FAILURE);
633cb02a 291
727b9b16 292 if (lxc_arguments_parse(&my_args, argc, argv))
b52b0595 293 exit(EXIT_FAILURE);
a11a544f 294
d67beb9e 295 log.name = my_args.name;
296 log.file = my_args.log_file;
297 log.level = my_args.log_priority;
298 log.prefix = my_args.progname;
299 log.quiet = my_args.quiet;
300 log.lxcpath = my_args.lxcpath[0];
633cb02a 301
d67beb9e 302 if (lxc_log_init(&log)) {
303 free_ifname_list();
304 exit(EXIT_FAILURE);
727b9b16 305 }
c7029344 306
35bfea7a 307 if (!*my_args.argv) {
727b9b16 308 ERROR("A command to execute in the new namespace is required");
59e1663a 309 free_ifname_list();
727b9b16 310 exit(EXIT_FAILURE);
311 }
40625b63 312
727b9b16 313 if (my_args.flags == 0) {
314 ERROR("A namespace to execute command is required");
59e1663a 315 free_ifname_list();
727b9b16 316 exit(EXIT_FAILURE);
317 }
633cb02a 318
59e1663a 319 if (!(my_args.flags & CLONE_NEWNET) && lxc_list_len(&ifnames) > 0) {
727b9b16 320 ERROR("-i <interfacename> needs -s NETWORK option");
59e1663a 321 free_ifname_list();
b52b0595 322 exit(EXIT_FAILURE);
c1bb25a8
SR
323 }
324
727b9b16 325 if (!(my_args.flags & CLONE_NEWUTS) && my_args.want_hostname) {
326 ERROR("-H <hostname> needs -s UTSNAME option");
59e1663a 327 free_ifname_list();
b52b0595 328 exit(EXIT_FAILURE);
c1bb25a8
SR
329 }
330
727b9b16 331 if (!(my_args.flags & CLONE_NEWNS) && my_args.want_default_mounts) {
332 ERROR("-M needs -s MOUNT option");
59e1663a 333 free_ifname_list();
b52b0595 334 exit(EXIT_FAILURE);
633cb02a 335 }
336
727b9b16 337 if (my_args.setuid) {
344c9d81
MPS
338 start_arg.wait_fd = eventfd(0, EFD_CLOEXEC);
339 if (start_arg.wait_fd < 0) {
727b9b16 340 SYSERROR("Failed to create eventfd");
59e1663a 341 free_ifname_list();
344c9d81
MPS
342 exit(EXIT_FAILURE);
343 }
344 }
345
727b9b16 346 /* set start arguments for lxc_clone from lxc_arguments */
347 start_arg.args = my_args.argv;
348 start_arg.uid = my_args.uid; /* valid only if (flags & CLONE_NEWUSER) */
349 start_arg.setuid = my_args.setuid;
350 start_arg.flags = my_args.flags;
351 start_arg.want_hostname = my_args.want_hostname;
352 start_arg.want_default_mounts = my_args.want_default_mounts;
727b9b16 353
33258b95 354 pid = lxc_clone(do_start, &start_arg, my_args.flags, NULL);
50e98013 355 if (pid < 0) {
727b9b16 356 ERROR("Failed to clone");
59e1663a 357 free_ifname_list();
b52b0595 358 exit(EXIT_FAILURE);
633cb02a 359 }
360
727b9b16 361 if (my_args.setuid) {
362 uint64_t wait_val = 1;
344c9d81 363 /* enough space to accommodate uids */
57e2af15 364 char umap[100];
344c9d81
MPS
365
366 /* create new uid mapping using current UID and the one
367 * specified as parameter
368 */
727b9b16 369 ret = snprintf(umap, 100, "%d %d 1\n" , my_args.uid, getuid());
344c9d81 370 if (ret < 0 || ret >= 100) {
727b9b16 371 ERROR("snprintf is failed");
59e1663a 372 free_ifname_list();
344c9d81 373 close(start_arg.wait_fd);
344c9d81
MPS
374 exit(EXIT_FAILURE);
375 }
376
23ccbded 377 ret = write_id_mapping(ID_TYPE_UID, pid, umap, strlen(umap));
344c9d81 378 if (ret < 0) {
727b9b16 379 ERROR("Failed to map uid");
59e1663a 380 free_ifname_list();
344c9d81 381 close(start_arg.wait_fd);
344c9d81
MPS
382 exit(EXIT_FAILURE);
383 }
384
8367b31e 385 ret = lxc_write_nointr(start_arg.wait_fd, &wait_val, sizeof(wait_val));
344c9d81 386 if (ret < 0) {
727b9b16 387 SYSERROR("Failed to write eventfd");
59e1663a 388 free_ifname_list();
344c9d81 389 close(start_arg.wait_fd);
344c9d81
MPS
390 exit(EXIT_FAILURE);
391 }
392 }
393
59e1663a 394 if (lxc_list_len(&ifnames) > 0) {
395 struct lxc_list *iterator;
396 char* ifname;
a7547c5c 397 pid_t lpid;
59e1663a 398
399 lxc_list_for_each(iterator, &ifnames) {
400 ifname = iterator->elem;
401 if (!ifname)
402 continue;
d567a9a7 403
a7547c5c
CB
404 lpid = fork();
405 if (lpid < 0) {
727b9b16 406 SYSERROR("Failed to move network device \"%s\" to network namespace",
59e1663a 407 ifname);
727b9b16 408 continue;
409 }
d567a9a7 410
a7547c5c 411 if (lpid == 0) {
d567a9a7
CB
412 char buf[256];
413
a7547c5c 414 ret = snprintf(buf, 256, "%d", lpid);
d567a9a7 415 if (ret < 0 || ret >= 256)
4f66541c 416 _exit(EXIT_FAILURE);
d567a9a7 417
59e1663a 418 execlp("ip", "ip", "link", "set", "dev", ifname, "netns", buf, (char *)NULL);
4f66541c 419 _exit(EXIT_FAILURE);
d567a9a7
CB
420 }
421
a7547c5c 422 if (wait_for_pid(lpid) != 0)
727b9b16 423 SYSERROR("Could not move interface \"%s\" into container %d",
a7547c5c 424 ifname, lpid);
c1bb25a8 425 }
59e1663a 426
427 free_ifname_list();
c1bb25a8
SR
428 }
429
727b9b16 430 if (my_args.daemonize)
f0c6ee28 431 exit(EXIT_SUCCESS);
c1bb25a8 432
d567a9a7 433 if (wait_for_pid(pid) != 0) {
727b9b16 434 SYSERROR("Failed to wait for '%d'", pid);
b52b0595 435 exit(EXIT_FAILURE);
633cb02a 436 }
437
f3e65e7b 438 /* Call exit() directly on this function because it returns an exit code. */
d567a9a7 439 exit(EXIT_SUCCESS);
35bfea7a 440}