]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_unshare.c
Merge pull request #2395 from brauner/2018-06-11/restore_old_create_behavior
[mirror_lxc.git] / src / lxc / tools / lxc_unshare.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <getopt.h>
28 #include <libgen.h>
29 #include <pwd.h>
30 #include <sched.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <netinet/in.h>
36 #include <unistd.h>
37 #include <sys/eventfd.h>
38 #include <sys/socket.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41
42 #include "arguments.h"
43 #include "tool_utils.h"
44
45 /* Define sethostname() if missing from the C library also workaround some
46 * quirky with having this defined in multiple places.
47 */
48 static inline int sethostname_including_android(const char *name, size_t len)
49 {
50 #ifndef HAVE_SETHOSTNAME
51 #ifdef __NR_sethostname
52 return syscall(__NR_sethostname, name, len);
53 #else
54 errno = ENOSYS;
55 return -1;
56 #endif
57 #else
58 return sethostname(name, len);
59 #endif
60 }
61
62 struct my_iflist
63 {
64 char *mi_ifname;
65 struct my_iflist *mi_next;
66 };
67
68 static void usage(char *cmd)
69 {
70 fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
71 fprintf(stderr, "Options are:\n");
72 fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \
73 "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n");
74 fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
75 fprintf(stderr, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
76 fprintf(stderr, "\t -H <hostname>: Set the hostname in the container\n");
77 fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n");
78 fprintf(stderr, "\t -M : Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n");
79 _exit(EXIT_SUCCESS);
80 }
81
82 static bool lookup_user(const char *optarg, uid_t *uid)
83 {
84 char name[TOOL_MAXPATHLEN];
85 struct passwd *pwent = NULL;
86
87 if (!optarg || (optarg[0] == '\0'))
88 return false;
89
90 if (sscanf(optarg, "%u", uid) < 1) {
91 /* not a uid -- perhaps a username */
92 if (sscanf(optarg, "%s", name) < 1)
93 return false;
94
95 pwent = getpwnam(name);
96 if (!pwent) {
97 fprintf(stderr, "invalid username %s\n", name);
98 return false;
99 }
100 *uid = pwent->pw_uid;
101 } else {
102 pwent = getpwuid(*uid);
103 if (!pwent) {
104 fprintf(stderr, "invalid uid %u\n", *uid);
105 return false;
106 }
107 }
108 return true;
109 }
110
111 struct start_arg {
112 char ***args;
113 int *flags;
114 uid_t *uid;
115 bool setuid;
116 int want_default_mounts;
117 int wait_fd;
118 const char *want_hostname;
119 };
120
121 static int do_start(void *arg)
122 {
123 int ret;
124 uint64_t wait_val;
125 struct start_arg *start_arg = arg;
126 char **args = *start_arg->args;
127 int flags = *start_arg->flags;
128 uid_t uid = *start_arg->uid;
129 int want_default_mounts = start_arg->want_default_mounts;
130 const char *want_hostname = start_arg->want_hostname;
131 int wait_fd = start_arg->wait_fd;
132
133 if (start_arg->setuid) {
134 /* waiting until uid maps is set */
135 ret = read(wait_fd, &wait_val, sizeof(wait_val));
136 if (ret == -1) {
137 close(wait_fd);
138 fprintf(stderr, "read eventfd failed\n");
139 exit(EXIT_FAILURE);
140 }
141 }
142
143 if ((flags & CLONE_NEWNS) && want_default_mounts)
144 lxc_setup_fs();
145
146 if ((flags & CLONE_NEWUTS) && want_hostname)
147 if (sethostname_including_android(want_hostname, strlen(want_hostname)) < 0) {
148 fprintf(stderr, "failed to set hostname %s: %s\n", want_hostname, strerror(errno));
149 exit(EXIT_FAILURE);
150 }
151
152 /* Setuid is useful even without a new user id space. */
153 if (start_arg->setuid && setuid(uid)) {
154 fprintf(stderr, "failed to set uid %d: %s\n", uid, strerror(errno));
155 exit(EXIT_FAILURE);
156 }
157
158 execvp(args[0], args);
159
160 fprintf(stderr, "failed to exec: '%s': %s\n", args[0], strerror(errno));
161 return 1;
162 }
163
164 int write_id_mapping(pid_t pid, const char *buf, size_t buf_size)
165 {
166 char path[TOOL_MAXPATHLEN];
167 int fd, ret;
168
169
170 ret = snprintf(path, TOOL_MAXPATHLEN, "/proc/%d/uid_map", pid);
171 if (ret < 0 || ret >= TOOL_MAXPATHLEN)
172 return -E2BIG;
173
174 fd = open(path, O_WRONLY);
175 if (fd < 0)
176 return -1;
177
178 errno = 0;
179 ret = lxc_write_nointr(fd, buf, buf_size);
180 close(fd);
181 if (ret < 0 || (size_t)ret != buf_size)
182 return -1;
183
184 return 0;
185 }
186
187 int main(int argc, char *argv[])
188 {
189 char *del;
190 char **it, **args;
191 int opt;
192 int ret;
193 char *namespaces = NULL;
194 int flags = 0, daemonize = 0;
195 uid_t uid = 0; /* valid only if (flags & CLONE_NEWUSER) */
196 pid_t pid;
197 uint64_t wait_val = 1;
198 struct my_iflist *tmpif, *my_iflist = NULL;
199 struct start_arg start_arg = {
200 .args = &args,
201 .uid = &uid,
202 .setuid = false,
203 .flags = &flags,
204 .want_hostname = NULL,
205 .want_default_mounts = 0,
206 };
207
208 while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) {
209 switch (opt) {
210 case 's':
211 namespaces = optarg;
212 break;
213 case 'i':
214 if (!(tmpif = malloc(sizeof(*tmpif)))) {
215 perror("malloc");
216 exit(EXIT_FAILURE);
217 }
218 tmpif->mi_ifname = optarg;
219 tmpif->mi_next = my_iflist;
220 my_iflist = tmpif;
221 break;
222 case 'd':
223 daemonize = 1;
224 break;
225 case 'M':
226 start_arg.want_default_mounts = 1;
227 break;
228 case 'H':
229 start_arg.want_hostname = optarg;
230 break;
231 case 'h':
232 usage(argv[0]);
233 break;
234 case 'u':
235 if (!lookup_user(optarg, &uid))
236 exit(EXIT_FAILURE);
237 start_arg.setuid = true;
238 }
239 }
240
241 if (argv[optind] == NULL) {
242 fprintf(stderr, "a command to execute in the new namespace is required\n");
243 exit(EXIT_FAILURE);
244 }
245
246 args = &argv[optind];
247
248 ret = lxc_caps_init();
249 if (ret)
250 exit(EXIT_FAILURE);
251
252 /* The identifiers for namespaces used with lxc-unshare as given on the
253 * manpage do not align with the standard identifiers. This affects
254 * network, mount, and uts namespaces. The standard identifiers are:
255 * "mnt", "uts", and "net" whereas lxc-unshare uses "MOUNT", "UTSNAME",
256 * and "NETWORK". So let's use some cheap memmove()s to replace them by
257 * their standard identifiers. Let's illustrate this with an example:
258 * Assume the string:
259 *
260 * "IPC|MOUNT|PID"
261 *
262 * then we memmove()
263 *
264 * dest: del + 1 == OUNT|PID
265 * src: del + 3 == NT|PID
266 */
267 if (!namespaces)
268 usage(argv[0]);
269
270 while ((del = strstr(namespaces, "MOUNT")))
271 memmove(del + 1, del + 3, strlen(del) - 2);
272
273 for (it = (char *[]){"NETWORK", "UTSNAME", NULL}; it && *it; it++)
274 while ((del = strstr(namespaces, *it)))
275 memmove(del + 3, del + 7, strlen(del) - 6);
276
277 ret = lxc_fill_namespace_flags(namespaces, &flags);
278 if (ret)
279 usage(argv[0]);
280
281 if (!(flags & CLONE_NEWNET) && my_iflist) {
282 fprintf(stderr, "-i <interfacename> needs -s NETWORK option\n");
283 exit(EXIT_FAILURE);
284 }
285
286 if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) {
287 fprintf(stderr, "-H <hostname> needs -s UTSNAME option\n");
288 exit(EXIT_FAILURE);
289 }
290
291 if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) {
292 fprintf(stderr, "-M needs -s MOUNT option\n");
293 exit(EXIT_FAILURE);
294 }
295
296 if (start_arg.setuid) {
297 start_arg.wait_fd = eventfd(0, EFD_CLOEXEC);
298 if (start_arg.wait_fd < 0) {
299 fprintf(stderr, "failed to create eventfd\n");
300 exit(EXIT_FAILURE);
301 }
302 }
303
304 pid = lxc_clone(do_start, &start_arg, flags);
305 if (pid < 0) {
306 fprintf(stderr, "failed to clone\n");
307 exit(EXIT_FAILURE);
308 }
309
310 if (start_arg.setuid) {
311 /* enough space to accommodate uids */
312 char *umap = (char *)alloca(100);
313
314 /* create new uid mapping using current UID and the one
315 * specified as parameter
316 */
317 ret = snprintf(umap, 100, "%d %d 1\n" , *(start_arg.uid), getuid());
318 if (ret < 0 || ret >= 100) {
319 close(start_arg.wait_fd);
320 fprintf(stderr, "snprintf failed");
321 exit(EXIT_FAILURE);
322 }
323
324 ret = write_id_mapping(pid, umap, strlen(umap));
325 if (ret < 0) {
326 close(start_arg.wait_fd);
327 fprintf(stderr, "uid mapping failed\n");
328 exit(EXIT_FAILURE);
329 }
330
331 ret = write(start_arg.wait_fd, &wait_val, sizeof(wait_val));
332 if (ret < 0) {
333 close(start_arg.wait_fd);
334 fprintf(stderr, "write to eventfd failed\n");
335 exit(EXIT_FAILURE);
336 }
337 }
338
339 if (my_iflist) {
340 for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) {
341 pid_t pid;
342
343 pid = fork();
344 if (pid < 0)
345 fprintf(stderr, "Failed to move network device "
346 "\"%s\" to network namespace\n",
347 tmpif->mi_ifname);
348
349 if (pid == 0) {
350 char buf[256];
351
352 ret = snprintf(buf, 256, "%d", pid);
353 if (ret < 0 || ret >= 256)
354 exit(EXIT_FAILURE);
355
356 execlp("ip", "ip", "link", "set", "dev", tmpif->mi_ifname, "netns", buf, (char *)NULL);
357 exit(EXIT_FAILURE);
358 }
359
360 if (wait_for_pid(pid) != 0)
361 fprintf(stderr, "Could not move interface %s "
362 "into container %d: %s\n",
363 tmpif->mi_ifname, pid, strerror(errno));
364 }
365 }
366
367 if (daemonize)
368 exit(EXIT_SUCCESS);
369
370 if (wait_for_pid(pid) != 0) {
371 fprintf(stderr, "failed to wait for '%d'\n", pid);
372 exit(EXIT_FAILURE);
373 }
374
375 /* Call exit() directly on this function because it retuns an exit code. */
376 exit(EXIT_SUCCESS);
377 }