Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <string.h>
5 : : #include <sys/mount.h>
6 : : #include <sys/wait.h>
7 : :
8 : : /* When we include libgen.h because we need dirname() we immediately
9 : : * undefine basename() since libgen.h defines it as a macro to the POSIX
10 : : * version which is really broken. We prefer GNU basename(). */
11 : : #include <libgen.h>
12 : : #undef basename
13 : :
14 : : #include "alloc-util.h"
15 : : #include "bus-common-errors.h"
16 : : #include "bus-internal.h"
17 : : #include "bus-label.h"
18 : : #include "bus-util.h"
19 : : #include "copy.h"
20 : : #include "env-file.h"
21 : : #include "env-util.h"
22 : : #include "fd-util.h"
23 : : #include "format-util.h"
24 : : #include "fs-util.h"
25 : : #include "in-addr-util.h"
26 : : #include "io-util.h"
27 : : #include "local-addresses.h"
28 : : #include "machine-dbus.h"
29 : : #include "machine.h"
30 : : #include "missing_capability.h"
31 : : #include "mkdir.h"
32 : : #include "namespace-util.h"
33 : : #include "os-util.h"
34 : : #include "path-util.h"
35 : : #include "process-util.h"
36 : : #include "signal-util.h"
37 : : #include "strv.h"
38 : : #include "terminal-util.h"
39 : : #include "tmpfile-util.h"
40 : : #include "user-util.h"
41 : :
42 [ # # # # : 0 : static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
# # ]
43 [ # # # # : 0 : static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", Machine, machine_get_state, machine_state_to_string);
# # ]
44 : :
45 : 0 : static int property_get_netif(
46 : : sd_bus *bus,
47 : : const char *path,
48 : : const char *interface,
49 : : const char *property,
50 : : sd_bus_message *reply,
51 : : void *userdata,
52 : : sd_bus_error *error) {
53 : :
54 : 0 : Machine *m = userdata;
55 : :
56 [ # # ]: 0 : assert(bus);
57 [ # # ]: 0 : assert(reply);
58 [ # # ]: 0 : assert(m);
59 : :
60 : : assert_cc(sizeof(int) == sizeof(int32_t));
61 : :
62 : 0 : return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
63 : : }
64 : :
65 : 0 : int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
66 : 0 : Machine *m = userdata;
67 : : int r;
68 : :
69 [ # # ]: 0 : assert(message);
70 [ # # ]: 0 : assert(m);
71 : :
72 : 0 : r = bus_verify_polkit_async(
73 : : message,
74 : : CAP_KILL,
75 : : "org.freedesktop.machine1.manage-machines",
76 : : NULL,
77 : : false,
78 : : UID_INVALID,
79 : 0 : &m->manager->polkit_registry,
80 : : error);
81 [ # # ]: 0 : if (r < 0)
82 : 0 : return r;
83 [ # # ]: 0 : if (r == 0)
84 : 0 : return 1; /* Will call us back */
85 : :
86 : 0 : r = machine_stop(m);
87 [ # # ]: 0 : if (r < 0)
88 : 0 : return r;
89 : :
90 : 0 : return sd_bus_reply_method_return(message, NULL);
91 : : }
92 : :
93 : 0 : int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
94 : 0 : Machine *m = userdata;
95 : : const char *swho;
96 : : int32_t signo;
97 : : KillWho who;
98 : : int r;
99 : :
100 [ # # ]: 0 : assert(message);
101 [ # # ]: 0 : assert(m);
102 : :
103 : 0 : r = sd_bus_message_read(message, "si", &swho, &signo);
104 [ # # ]: 0 : if (r < 0)
105 : 0 : return r;
106 : :
107 [ # # ]: 0 : if (isempty(swho))
108 : 0 : who = KILL_ALL;
109 : : else {
110 : 0 : who = kill_who_from_string(swho);
111 [ # # ]: 0 : if (who < 0)
112 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
113 : : }
114 : :
115 [ # # ]: 0 : if (!SIGNAL_VALID(signo))
116 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
117 : :
118 : 0 : r = bus_verify_polkit_async(
119 : : message,
120 : : CAP_KILL,
121 : : "org.freedesktop.machine1.manage-machines",
122 : : NULL,
123 : : false,
124 : : UID_INVALID,
125 : 0 : &m->manager->polkit_registry,
126 : : error);
127 [ # # ]: 0 : if (r < 0)
128 : 0 : return r;
129 [ # # ]: 0 : if (r == 0)
130 : 0 : return 1; /* Will call us back */
131 : :
132 : 0 : r = machine_kill(m, who, signo);
133 [ # # ]: 0 : if (r < 0)
134 : 0 : return r;
135 : :
136 : 0 : return sd_bus_reply_method_return(message, NULL);
137 : : }
138 : :
139 : 0 : int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) {
140 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
141 : 0 : Machine *m = userdata;
142 : : int r;
143 : :
144 [ # # ]: 0 : assert(message);
145 [ # # ]: 0 : assert(m);
146 : :
147 : 0 : r = sd_bus_message_new_method_return(message, &reply);
148 [ # # ]: 0 : if (r < 0)
149 : 0 : return r;
150 : :
151 : 0 : r = sd_bus_message_open_container(reply, 'a', "(iay)");
152 [ # # ]: 0 : if (r < 0)
153 : 0 : return r;
154 : :
155 [ # # # ]: 0 : switch (m->class) {
156 : :
157 : 0 : case MACHINE_HOST: {
158 [ # # ]: 0 : _cleanup_free_ struct local_address *addresses = NULL;
159 : : struct local_address *a;
160 : : int n, i;
161 : :
162 : 0 : n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
163 [ # # ]: 0 : if (n < 0)
164 : 0 : return n;
165 : :
166 [ # # ]: 0 : for (a = addresses, i = 0; i < n; a++, i++) {
167 : :
168 : 0 : r = sd_bus_message_open_container(reply, 'r', "iay");
169 [ # # ]: 0 : if (r < 0)
170 : 0 : return r;
171 : :
172 : 0 : r = sd_bus_message_append(reply, "i", addresses[i].family);
173 [ # # ]: 0 : if (r < 0)
174 : 0 : return r;
175 : :
176 : 0 : r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family));
177 [ # # ]: 0 : if (r < 0)
178 : 0 : return r;
179 : :
180 : 0 : r = sd_bus_message_close_container(reply);
181 [ # # ]: 0 : if (r < 0)
182 : 0 : return r;
183 : : }
184 : :
185 : 0 : break;
186 : : }
187 : :
188 : 0 : case MACHINE_CONTAINER: {
189 [ # # ]: 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
190 [ # # # # ]: 0 : _cleanup_free_ char *us = NULL, *them = NULL;
191 [ # # ]: 0 : _cleanup_close_ int netns_fd = -1;
192 : : const char *p;
193 : : pid_t child;
194 : :
195 : 0 : r = readlink_malloc("/proc/self/ns/net", &us);
196 [ # # ]: 0 : if (r < 0)
197 : 0 : return r;
198 : :
199 [ # # # # : 0 : p = procfs_file_alloca(m->leader, "ns/net");
# # ]
200 : 0 : r = readlink_malloc(p, &them);
201 [ # # ]: 0 : if (r < 0)
202 : 0 : return r;
203 : :
204 [ # # ]: 0 : if (streq(us, them))
205 : 0 : return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
206 : :
207 : 0 : r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL);
208 [ # # ]: 0 : if (r < 0)
209 : 0 : return r;
210 : :
211 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
212 : 0 : return -errno;
213 : :
214 : 0 : r = namespace_fork("(sd-addrns)", "(sd-addr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
215 : : -1, -1, netns_fd, -1, -1, &child);
216 [ # # ]: 0 : if (r < 0)
217 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
218 [ # # ]: 0 : if (r == 0) {
219 : 0 : _cleanup_free_ struct local_address *addresses = NULL;
220 : : struct local_address *a;
221 : : int i, n;
222 : :
223 : 0 : pair[0] = safe_close(pair[0]);
224 : :
225 : 0 : n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
226 [ # # ]: 0 : if (n < 0)
227 : 0 : _exit(EXIT_FAILURE);
228 : :
229 [ # # ]: 0 : for (a = addresses, i = 0; i < n; a++, i++) {
230 : 0 : struct iovec iov[2] = {
231 : 0 : { .iov_base = &a->family, .iov_len = sizeof(a->family) },
232 : 0 : { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
233 : : };
234 : :
235 : 0 : r = writev(pair[1], iov, 2);
236 [ # # ]: 0 : if (r < 0)
237 : 0 : _exit(EXIT_FAILURE);
238 : : }
239 : :
240 : 0 : pair[1] = safe_close(pair[1]);
241 : :
242 : 0 : _exit(EXIT_SUCCESS);
243 : : }
244 : :
245 : 0 : pair[1] = safe_close(pair[1]);
246 : :
247 : 0 : for (;;) {
248 : : int family;
249 : : ssize_t n;
250 : : union in_addr_union in_addr;
251 : : struct iovec iov[2];
252 : 0 : struct msghdr mh = {
253 : : .msg_iov = iov,
254 : : .msg_iovlen = 2,
255 : : };
256 : :
257 : 0 : iov[0] = IOVEC_MAKE(&family, sizeof(family));
258 : 0 : iov[1] = IOVEC_MAKE(&in_addr, sizeof(in_addr));
259 : :
260 : 0 : n = recvmsg(pair[0], &mh, 0);
261 [ # # ]: 0 : if (n < 0)
262 : 0 : return -errno;
263 [ # # ]: 0 : if ((size_t) n < sizeof(family))
264 : 0 : break;
265 : :
266 : 0 : r = sd_bus_message_open_container(reply, 'r', "iay");
267 [ # # ]: 0 : if (r < 0)
268 : 0 : return r;
269 : :
270 : 0 : r = sd_bus_message_append(reply, "i", family);
271 [ # # ]: 0 : if (r < 0)
272 : 0 : return r;
273 : :
274 [ # # # ]: 0 : switch (family) {
275 : :
276 : 0 : case AF_INET:
277 [ # # ]: 0 : if (n != sizeof(struct in_addr) + sizeof(family))
278 : 0 : return -EIO;
279 : :
280 : 0 : r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
281 : 0 : break;
282 : :
283 : 0 : case AF_INET6:
284 [ # # ]: 0 : if (n != sizeof(struct in6_addr) + sizeof(family))
285 : 0 : return -EIO;
286 : :
287 : 0 : r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
288 : 0 : break;
289 : : }
290 [ # # ]: 0 : if (r < 0)
291 : 0 : return r;
292 : :
293 : 0 : r = sd_bus_message_close_container(reply);
294 [ # # ]: 0 : if (r < 0)
295 : 0 : return r;
296 : : }
297 : :
298 : 0 : r = wait_for_terminate_and_check("(sd-addrns)", child, 0);
299 [ # # ]: 0 : if (r < 0)
300 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
301 [ # # ]: 0 : if (r != EXIT_SUCCESS)
302 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
303 : 0 : break;
304 : : }
305 : :
306 : 0 : default:
307 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
308 : : }
309 : :
310 : 0 : r = sd_bus_message_close_container(reply);
311 [ # # ]: 0 : if (r < 0)
312 : 0 : return r;
313 : :
314 : 0 : return sd_bus_send(NULL, reply, NULL);
315 : : }
316 : :
317 : : #define EXIT_NOT_FOUND 2
318 : :
319 : 0 : int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) {
320 : 0 : _cleanup_strv_free_ char **l = NULL;
321 : 0 : Machine *m = userdata;
322 : : int r;
323 : :
324 [ # # ]: 0 : assert(message);
325 [ # # ]: 0 : assert(m);
326 : :
327 [ # # # ]: 0 : switch (m->class) {
328 : :
329 : 0 : case MACHINE_HOST:
330 : 0 : r = load_os_release_pairs(NULL, &l);
331 [ # # ]: 0 : if (r < 0)
332 : 0 : return r;
333 : :
334 : 0 : break;
335 : :
336 : 0 : case MACHINE_CONTAINER: {
337 [ # # # # : 0 : _cleanup_close_ int mntns_fd = -1, root_fd = -1, pidns_fd = -1;
# # ]
338 [ # # ]: 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
339 [ # # ]: 0 : _cleanup_fclose_ FILE *f = NULL;
340 : : pid_t child;
341 : :
342 : 0 : r = namespace_open(m->leader, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
343 [ # # ]: 0 : if (r < 0)
344 : 0 : return r;
345 : :
346 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
347 : 0 : return -errno;
348 : :
349 : 0 : r = namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
350 : : pidns_fd, mntns_fd, -1, -1, root_fd,
351 : : &child);
352 [ # # ]: 0 : if (r < 0)
353 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
354 [ # # ]: 0 : if (r == 0) {
355 : 0 : int fd = -1;
356 : :
357 : 0 : pair[0] = safe_close(pair[0]);
358 : :
359 : 0 : r = open_os_release(NULL, NULL, &fd);
360 [ # # ]: 0 : if (r == -ENOENT)
361 : 0 : _exit(EXIT_NOT_FOUND);
362 [ # # ]: 0 : if (r < 0)
363 : 0 : _exit(EXIT_FAILURE);
364 : :
365 : 0 : r = copy_bytes(fd, pair[1], (uint64_t) -1, 0);
366 [ # # ]: 0 : if (r < 0)
367 : 0 : _exit(EXIT_FAILURE);
368 : :
369 : 0 : _exit(EXIT_SUCCESS);
370 : : }
371 : :
372 : 0 : pair[1] = safe_close(pair[1]);
373 : :
374 : 0 : f = fdopen(pair[0], "r");
375 [ # # ]: 0 : if (!f)
376 : 0 : return -errno;
377 : :
378 : 0 : pair[0] = -1;
379 : :
380 : 0 : r = load_env_file_pairs(f, "/etc/os-release", &l);
381 [ # # ]: 0 : if (r < 0)
382 : 0 : return r;
383 : :
384 : 0 : r = wait_for_terminate_and_check("(sd-osrelns)", child, 0);
385 [ # # ]: 0 : if (r < 0)
386 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
387 [ # # ]: 0 : if (r == EXIT_NOT_FOUND)
388 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
389 [ # # ]: 0 : if (r != EXIT_SUCCESS)
390 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
391 : :
392 : 0 : break;
393 : : }
394 : :
395 : 0 : default:
396 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
397 : : }
398 : :
399 : 0 : return bus_reply_pair_array(message, l);
400 : : }
401 : :
402 : 0 : int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) {
403 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
404 : 0 : _cleanup_free_ char *pty_name = NULL;
405 : 0 : _cleanup_close_ int master = -1;
406 : 0 : Machine *m = userdata;
407 : : int r;
408 : :
409 [ # # ]: 0 : assert(message);
410 [ # # ]: 0 : assert(m);
411 : :
412 : 0 : r = bus_verify_polkit_async(
413 : : message,
414 : : CAP_SYS_ADMIN,
415 : 0 : m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
416 : : NULL,
417 : : false,
418 : : UID_INVALID,
419 [ # # ]: 0 : &m->manager->polkit_registry,
420 : : error);
421 [ # # ]: 0 : if (r < 0)
422 : 0 : return r;
423 [ # # ]: 0 : if (r == 0)
424 : 0 : return 1; /* Will call us back */
425 : :
426 : 0 : master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
427 [ # # ]: 0 : if (master < 0)
428 : 0 : return master;
429 : :
430 : 0 : r = sd_bus_message_new_method_return(message, &reply);
431 [ # # ]: 0 : if (r < 0)
432 : 0 : return r;
433 : :
434 : 0 : r = sd_bus_message_append(reply, "hs", master, pty_name);
435 [ # # ]: 0 : if (r < 0)
436 : 0 : return r;
437 : :
438 : 0 : return sd_bus_send(NULL, reply, NULL);
439 : : }
440 : :
441 : 0 : static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
442 : : int r;
443 : :
444 [ # # ]: 0 : assert(m);
445 [ # # ]: 0 : assert(ret);
446 : :
447 [ # # # ]: 0 : switch (m->class) {
448 : :
449 : 0 : case MACHINE_HOST:
450 : 0 : *ret = NULL;
451 : 0 : break;
452 : :
453 : 0 : case MACHINE_CONTAINER: {
454 [ # # ]: 0 : _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
455 : : char *address;
456 : :
457 : 0 : r = sd_bus_new(&bus);
458 [ # # ]: 0 : if (r < 0)
459 : 0 : return r;
460 : :
461 [ # # ]: 0 : if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0)
462 : 0 : return -ENOMEM;
463 : :
464 : 0 : bus->address = address;
465 : 0 : bus->bus_client = true;
466 : 0 : bus->trusted = false;
467 : 0 : bus->is_system = true;
468 : :
469 : 0 : r = sd_bus_start(bus);
470 [ # # ]: 0 : if (r == -ENOENT)
471 : 0 : return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
472 [ # # ]: 0 : if (r < 0)
473 : 0 : return r;
474 : :
475 : 0 : *ret = TAKE_PTR(bus);
476 : 0 : break;
477 : : }
478 : :
479 : 0 : default:
480 : 0 : return -EOPNOTSUPP;
481 : : }
482 : :
483 : 0 : return 0;
484 : : }
485 : :
486 : 0 : int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
487 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
488 : 0 : _cleanup_free_ char *pty_name = NULL;
489 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
490 : 0 : _cleanup_close_ int master = -1;
491 : 0 : sd_bus *container_bus = NULL;
492 : 0 : Machine *m = userdata;
493 : : const char *p, *getty;
494 : : int r;
495 : :
496 [ # # ]: 0 : assert(message);
497 [ # # ]: 0 : assert(m);
498 : :
499 : 0 : r = bus_verify_polkit_async(
500 : : message,
501 : : CAP_SYS_ADMIN,
502 : 0 : m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
503 : : NULL,
504 : : false,
505 : : UID_INVALID,
506 [ # # ]: 0 : &m->manager->polkit_registry,
507 : : error);
508 [ # # ]: 0 : if (r < 0)
509 : 0 : return r;
510 [ # # ]: 0 : if (r == 0)
511 : 0 : return 1; /* Will call us back */
512 : :
513 : 0 : master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
514 [ # # ]: 0 : if (master < 0)
515 : 0 : return master;
516 : :
517 : 0 : p = path_startswith(pty_name, "/dev/pts/");
518 [ # # ]: 0 : assert(p);
519 : :
520 : 0 : r = container_bus_new(m, error, &allocated_bus);
521 [ # # ]: 0 : if (r < 0)
522 : 0 : return r;
523 : :
524 [ # # ]: 0 : container_bus = allocated_bus ?: m->manager->bus;
525 : :
526 [ # # # # : 0 : getty = strjoina("container-getty@", p, ".service");
# # # # #
# # # ]
527 : :
528 : 0 : r = sd_bus_call_method(
529 : : container_bus,
530 : : "org.freedesktop.systemd1",
531 : : "/org/freedesktop/systemd1",
532 : : "org.freedesktop.systemd1.Manager",
533 : : "StartUnit",
534 : : error, NULL,
535 : : "ss", getty, "replace");
536 [ # # ]: 0 : if (r < 0)
537 : 0 : return r;
538 : :
539 : 0 : r = sd_bus_message_new_method_return(message, &reply);
540 [ # # ]: 0 : if (r < 0)
541 : 0 : return r;
542 : :
543 : 0 : r = sd_bus_message_append(reply, "hs", master, pty_name);
544 [ # # ]: 0 : if (r < 0)
545 : 0 : return r;
546 : :
547 : 0 : return sd_bus_send(NULL, reply, NULL);
548 : : }
549 : :
550 : 0 : int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
551 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL;
552 : 0 : _cleanup_free_ char *pty_name = NULL;
553 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
554 : 0 : sd_bus *container_bus = NULL;
555 : 0 : _cleanup_close_ int master = -1, slave = -1;
556 : 0 : _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
557 : 0 : Machine *m = userdata;
558 : : const char *p, *unit, *user, *path, *description, *utmp_id;
559 : : int r;
560 : :
561 [ # # ]: 0 : assert(message);
562 [ # # ]: 0 : assert(m);
563 : :
564 : 0 : r = sd_bus_message_read(message, "ss", &user, &path);
565 [ # # ]: 0 : if (r < 0)
566 : 0 : return r;
567 : 0 : user = empty_to_null(user);
568 : 0 : r = sd_bus_message_read_strv(message, &args_wire);
569 [ # # ]: 0 : if (r < 0)
570 : 0 : return r;
571 [ # # ]: 0 : if (isempty(path)) {
572 : 0 : path = "/bin/sh";
573 : :
574 : 0 : args = new0(char*, 3 + 1);
575 [ # # ]: 0 : if (!args)
576 : 0 : return -ENOMEM;
577 : 0 : args[0] = strdup("sh");
578 [ # # ]: 0 : if (!args[0])
579 : 0 : return -ENOMEM;
580 : 0 : args[1] = strdup("-c");
581 [ # # ]: 0 : if (!args[1])
582 : 0 : return -ENOMEM;
583 [ # # ]: 0 : r = asprintf(&args[2],
584 : : "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
585 : : "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
586 : 0 : isempty(user) ? "root" : user);
587 [ # # ]: 0 : if (r < 0) {
588 : 0 : args[2] = NULL;
589 : 0 : return -ENOMEM;
590 : : }
591 : : } else {
592 [ # # ]: 0 : if (!path_is_absolute(path))
593 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
594 : 0 : args = TAKE_PTR(args_wire);
595 [ # # ]: 0 : if (strv_isempty(args)) {
596 : 0 : args = strv_free(args);
597 : :
598 : 0 : args = strv_new(path);
599 [ # # ]: 0 : if (!args)
600 : 0 : return -ENOMEM;
601 : : }
602 : : }
603 : :
604 : 0 : r = sd_bus_message_read_strv(message, &env);
605 [ # # ]: 0 : if (r < 0)
606 : 0 : return r;
607 [ # # ]: 0 : if (!strv_env_is_valid(env))
608 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
609 : :
610 : 0 : r = bus_verify_polkit_async(
611 : : message,
612 : : CAP_SYS_ADMIN,
613 : 0 : m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
614 : : NULL,
615 : : false,
616 : : UID_INVALID,
617 [ # # ]: 0 : &m->manager->polkit_registry,
618 : : error);
619 [ # # ]: 0 : if (r < 0)
620 : 0 : return r;
621 [ # # ]: 0 : if (r == 0)
622 : 0 : return 1; /* Will call us back */
623 : :
624 : 0 : master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
625 [ # # ]: 0 : if (master < 0)
626 : 0 : return master;
627 : :
628 : 0 : p = path_startswith(pty_name, "/dev/pts/");
629 [ # # ]: 0 : assert(p);
630 : :
631 : 0 : slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
632 [ # # ]: 0 : if (slave < 0)
633 : 0 : return slave;
634 : :
635 : 0 : utmp_id = path_startswith(pty_name, "/dev/");
636 [ # # ]: 0 : assert(utmp_id);
637 : :
638 : 0 : r = container_bus_new(m, error, &allocated_bus);
639 [ # # ]: 0 : if (r < 0)
640 : 0 : return r;
641 : :
642 [ # # ]: 0 : container_bus = allocated_bus ?: m->manager->bus;
643 : :
644 : 0 : r = sd_bus_message_new_method_call(
645 : : container_bus,
646 : : &tm,
647 : : "org.freedesktop.systemd1",
648 : : "/org/freedesktop/systemd1",
649 : : "org.freedesktop.systemd1.Manager",
650 : : "StartTransientUnit");
651 [ # # ]: 0 : if (r < 0)
652 : 0 : return r;
653 : :
654 : : /* Name and mode */
655 [ # # # # : 0 : unit = strjoina("container-shell@", p, ".service");
# # # # #
# # # ]
656 : 0 : r = sd_bus_message_append(tm, "ss", unit, "fail");
657 [ # # ]: 0 : if (r < 0)
658 : 0 : return r;
659 : :
660 : : /* Properties */
661 : 0 : r = sd_bus_message_open_container(tm, 'a', "(sv)");
662 [ # # ]: 0 : if (r < 0)
663 : 0 : return r;
664 : :
665 [ # # # # : 0 : description = strjoina("Shell for User ", isempty(user) ? "root" : user);
# # # # #
# # # #
# ]
666 : 0 : r = sd_bus_message_append(tm,
667 : : "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
668 : : "Description", "s", description,
669 : : "StandardInputFileDescriptor", "h", slave,
670 : : "StandardOutputFileDescriptor", "h", slave,
671 : : "StandardErrorFileDescriptor", "h", slave,
672 : : "SendSIGHUP", "b", true,
673 : : "IgnoreSIGPIPE", "b", false,
674 : : "KillMode", "s", "mixed",
675 : : "TTYReset", "b", true,
676 : : "UtmpIdentifier", "s", utmp_id,
677 : : "UtmpMode", "s", "user",
678 : : "PAMName", "s", "login",
679 : : "WorkingDirectory", "s", "-~");
680 [ # # ]: 0 : if (r < 0)
681 : 0 : return r;
682 : :
683 [ # # ]: 0 : r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user);
684 [ # # ]: 0 : if (r < 0)
685 : 0 : return r;
686 : :
687 [ # # ]: 0 : if (!strv_isempty(env)) {
688 : 0 : r = sd_bus_message_open_container(tm, 'r', "sv");
689 [ # # ]: 0 : if (r < 0)
690 : 0 : return r;
691 : :
692 : 0 : r = sd_bus_message_append(tm, "s", "Environment");
693 [ # # ]: 0 : if (r < 0)
694 : 0 : return r;
695 : :
696 : 0 : r = sd_bus_message_open_container(tm, 'v', "as");
697 [ # # ]: 0 : if (r < 0)
698 : 0 : return r;
699 : :
700 : 0 : r = sd_bus_message_append_strv(tm, env);
701 [ # # ]: 0 : if (r < 0)
702 : 0 : return r;
703 : :
704 : 0 : r = sd_bus_message_close_container(tm);
705 [ # # ]: 0 : if (r < 0)
706 : 0 : return r;
707 : :
708 : 0 : r = sd_bus_message_close_container(tm);
709 [ # # ]: 0 : if (r < 0)
710 : 0 : return r;
711 : : }
712 : :
713 : : /* Exec container */
714 : 0 : r = sd_bus_message_open_container(tm, 'r', "sv");
715 [ # # ]: 0 : if (r < 0)
716 : 0 : return r;
717 : :
718 : 0 : r = sd_bus_message_append(tm, "s", "ExecStart");
719 [ # # ]: 0 : if (r < 0)
720 : 0 : return r;
721 : :
722 : 0 : r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
723 [ # # ]: 0 : if (r < 0)
724 : 0 : return r;
725 : :
726 : 0 : r = sd_bus_message_open_container(tm, 'a', "(sasb)");
727 [ # # ]: 0 : if (r < 0)
728 : 0 : return r;
729 : :
730 : 0 : r = sd_bus_message_open_container(tm, 'r', "sasb");
731 [ # # ]: 0 : if (r < 0)
732 : 0 : return r;
733 : :
734 : 0 : r = sd_bus_message_append(tm, "s", path);
735 [ # # ]: 0 : if (r < 0)
736 : 0 : return r;
737 : :
738 : 0 : r = sd_bus_message_append_strv(tm, args);
739 [ # # ]: 0 : if (r < 0)
740 : 0 : return r;
741 : :
742 : 0 : r = sd_bus_message_append(tm, "b", true);
743 [ # # ]: 0 : if (r < 0)
744 : 0 : return r;
745 : :
746 : 0 : r = sd_bus_message_close_container(tm);
747 [ # # ]: 0 : if (r < 0)
748 : 0 : return r;
749 : :
750 : 0 : r = sd_bus_message_close_container(tm);
751 [ # # ]: 0 : if (r < 0)
752 : 0 : return r;
753 : :
754 : 0 : r = sd_bus_message_close_container(tm);
755 [ # # ]: 0 : if (r < 0)
756 : 0 : return r;
757 : :
758 : 0 : r = sd_bus_message_close_container(tm);
759 [ # # ]: 0 : if (r < 0)
760 : 0 : return r;
761 : :
762 : 0 : r = sd_bus_message_close_container(tm);
763 [ # # ]: 0 : if (r < 0)
764 : 0 : return r;
765 : :
766 : : /* Auxiliary units */
767 : 0 : r = sd_bus_message_append(tm, "a(sa(sv))", 0);
768 [ # # ]: 0 : if (r < 0)
769 : 0 : return r;
770 : :
771 : 0 : r = sd_bus_call(container_bus, tm, 0, error, NULL);
772 [ # # ]: 0 : if (r < 0)
773 : 0 : return r;
774 : :
775 : 0 : slave = safe_close(slave);
776 : :
777 : 0 : r = sd_bus_message_new_method_return(message, &reply);
778 [ # # ]: 0 : if (r < 0)
779 : 0 : return r;
780 : :
781 : 0 : r = sd_bus_message_append(reply, "hs", master, pty_name);
782 [ # # ]: 0 : if (r < 0)
783 : 0 : return r;
784 : :
785 : 0 : return sd_bus_send(NULL, reply, NULL);
786 : : }
787 : :
788 : 0 : int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) {
789 : 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
790 : 0 : char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
791 : 0 : bool mount_slave_created = false, mount_slave_mounted = false,
792 : 0 : mount_tmp_created = false, mount_tmp_mounted = false,
793 : 0 : mount_outside_created = false, mount_outside_mounted = false;
794 : 0 : _cleanup_free_ char *chased_src = NULL;
795 : : int read_only, make_file_or_directory;
796 : : const char *dest, *src;
797 : 0 : Machine *m = userdata;
798 : : struct stat st;
799 : : pid_t child;
800 : : uid_t uid;
801 : : int r;
802 : :
803 [ # # ]: 0 : assert(message);
804 [ # # ]: 0 : assert(m);
805 : :
806 [ # # ]: 0 : if (m->class != MACHINE_CONTAINER)
807 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
808 : :
809 : 0 : r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory);
810 [ # # ]: 0 : if (r < 0)
811 : 0 : return r;
812 : :
813 [ # # # # ]: 0 : if (!path_is_absolute(src) || !path_is_normalized(src))
814 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
815 : :
816 [ # # ]: 0 : if (isempty(dest))
817 : 0 : dest = src;
818 [ # # # # ]: 0 : else if (!path_is_absolute(dest) || !path_is_normalized(dest))
819 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
820 : :
821 : 0 : r = bus_verify_polkit_async(
822 : : message,
823 : : CAP_SYS_ADMIN,
824 : : "org.freedesktop.machine1.manage-machines",
825 : : NULL,
826 : : false,
827 : : UID_INVALID,
828 : 0 : &m->manager->polkit_registry,
829 : : error);
830 [ # # ]: 0 : if (r < 0)
831 : 0 : return r;
832 [ # # ]: 0 : if (r == 0)
833 : 0 : return 1; /* Will call us back */
834 : :
835 : 0 : r = machine_get_uid_shift(m, &uid);
836 [ # # ]: 0 : if (r < 0)
837 : 0 : return r;
838 [ # # ]: 0 : if (uid != 0)
839 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
840 : :
841 : : /* One day, when bind mounting /proc/self/fd/n works across
842 : : * namespace boundaries we should rework this logic to make
843 : : * use of it... */
844 : :
845 [ # # # # : 0 : p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/");
# # # # #
# # # ]
846 [ # # ]: 0 : if (laccess(p, F_OK) < 0)
847 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points.");
848 : :
849 : 0 : r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, &chased_src);
850 [ # # ]: 0 : if (r < 0)
851 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to resolve source path: %m");
852 : :
853 [ # # ]: 0 : if (lstat(chased_src, &st) < 0)
854 : 0 : return sd_bus_error_set_errnof(error, errno, "Failed to stat() source path: %m");
855 [ # # ]: 0 : if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */
856 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Source directory can't be a symbolic link");
857 : :
858 : : /* Our goal is to install a new bind mount into the container,
859 : : possibly read-only. This is irritatingly complex
860 : : unfortunately, currently.
861 : :
862 : : First, we start by creating a private playground in /tmp,
863 : : that we can mount MS_SLAVE. (Which is necessary, since
864 : : MS_MOVE cannot be applied to mounts with MS_SHARED parent
865 : : mounts.) */
866 : :
867 [ # # ]: 0 : if (!mkdtemp(mount_slave))
868 : 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave);
869 : :
870 : 0 : mount_slave_created = true;
871 : :
872 [ # # ]: 0 : if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
873 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave);
874 : 0 : goto finish;
875 : : }
876 : :
877 : 0 : mount_slave_mounted = true;
878 : :
879 [ # # ]: 0 : if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
880 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave);
881 : 0 : goto finish;
882 : : }
883 : :
884 : : /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
885 [ # # # # : 0 : mount_tmp = strjoina(mount_slave, "/mount");
# # # # #
# # # ]
886 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
887 : 0 : r = mkdir_errno_wrapper(mount_tmp, 0700);
888 : : else
889 : 0 : r = touch(mount_tmp);
890 [ # # ]: 0 : if (r < 0) {
891 : 0 : sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp);
892 : 0 : goto finish;
893 : : }
894 : :
895 : 0 : mount_tmp_created = true;
896 : :
897 [ # # ]: 0 : if (mount(chased_src, mount_tmp, NULL, MS_BIND, NULL) < 0) {
898 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to mount %s: %m", chased_src);
899 : 0 : goto finish;
900 : : }
901 : :
902 : 0 : mount_tmp_mounted = true;
903 : :
904 : : /* Third, we remount the new bind mount read-only if requested. */
905 [ # # ]: 0 : if (read_only)
906 [ # # ]: 0 : if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
907 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp);
908 : 0 : goto finish;
909 : : }
910 : :
911 : : /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
912 : : * right-away. */
913 : :
914 [ # # # # : 0 : mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX");
# # # # #
# # # ]
915 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
916 [ # # ]: 0 : r = mkdtemp(mount_outside) ? 0 : -errno;
917 : : else {
918 : 0 : r = mkostemp_safe(mount_outside);
919 : 0 : safe_close(r);
920 : : }
921 [ # # ]: 0 : if (r < 0) {
922 : 0 : sd_bus_error_set_errnof(error, errno, "Cannot create propagation file or directory %s: %m", mount_outside);
923 : 0 : goto finish;
924 : : }
925 : :
926 : 0 : mount_outside_created = true;
927 : :
928 [ # # ]: 0 : if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
929 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside);
930 : 0 : goto finish;
931 : : }
932 : :
933 : 0 : mount_outside_mounted = true;
934 : 0 : mount_tmp_mounted = false;
935 : :
936 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
937 : 0 : (void) rmdir(mount_tmp);
938 : : else
939 : 0 : (void) unlink(mount_tmp);
940 : 0 : mount_tmp_created = false;
941 : :
942 : 0 : (void) umount(mount_slave);
943 : 0 : mount_slave_mounted = false;
944 : :
945 : 0 : (void) rmdir(mount_slave);
946 : 0 : mount_slave_created = false;
947 : :
948 [ # # ]: 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
949 : 0 : r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
950 : 0 : goto finish;
951 : : }
952 : :
953 : 0 : r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child);
954 [ # # ]: 0 : if (r < 0) {
955 : 0 : sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
956 : 0 : goto finish;
957 : : }
958 [ # # ]: 0 : if (r == 0) {
959 : : const char *mount_inside;
960 : : int mntfd;
961 : : const char *q;
962 : :
963 : 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
964 : :
965 [ # # # # : 0 : q = procfs_file_alloca(m->leader, "ns/mnt");
# # ]
966 : 0 : mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
967 [ # # ]: 0 : if (mntfd < 0) {
968 [ # # ]: 0 : r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
969 : 0 : goto child_fail;
970 : : }
971 : :
972 [ # # ]: 0 : if (setns(mntfd, CLONE_NEWNS) < 0) {
973 [ # # ]: 0 : r = log_error_errno(errno, "Failed to join namespace of leader: %m");
974 : 0 : goto child_fail;
975 : : }
976 : :
977 [ # # ]: 0 : if (make_file_or_directory) {
978 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
979 : 0 : (void) mkdir_p(dest, 0755);
980 : : else {
981 : 0 : (void) mkdir_parents(dest, 0755);
982 : 0 : safe_close(open(dest, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600));
983 : : }
984 : : }
985 : :
986 : : /* Fifth, move the mount to the right place inside */
987 [ # # # # : 0 : mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
# # # # #
# # # ]
988 [ # # ]: 0 : if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
989 [ # # ]: 0 : r = log_error_errno(errno, "Failed to mount: %m");
990 : 0 : goto child_fail;
991 : : }
992 : :
993 : 0 : _exit(EXIT_SUCCESS);
994 : :
995 : 0 : child_fail:
996 : 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
997 : 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
998 : :
999 : 0 : _exit(EXIT_FAILURE);
1000 : : }
1001 : :
1002 : 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1003 : :
1004 : 0 : r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0);
1005 [ # # ]: 0 : if (r < 0) {
1006 : 0 : r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
1007 : 0 : goto finish;
1008 : : }
1009 [ # # ]: 0 : if (r != EXIT_SUCCESS) {
1010 [ # # ]: 0 : if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
1011 : 0 : r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
1012 : : else
1013 : 0 : r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed.");
1014 : 0 : goto finish;
1015 : : }
1016 : :
1017 : 0 : r = sd_bus_reply_method_return(message, NULL);
1018 : :
1019 : 0 : finish:
1020 [ # # ]: 0 : if (mount_outside_mounted)
1021 : 0 : (void) umount(mount_outside);
1022 [ # # ]: 0 : if (mount_outside_created) {
1023 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
1024 : 0 : (void) rmdir(mount_outside);
1025 : : else
1026 : 0 : (void) unlink(mount_outside);
1027 : : }
1028 : :
1029 [ # # ]: 0 : if (mount_tmp_mounted)
1030 : 0 : (void) umount(mount_tmp);
1031 [ # # ]: 0 : if (mount_tmp_created) {
1032 [ # # ]: 0 : if (S_ISDIR(st.st_mode))
1033 : 0 : (void) rmdir(mount_tmp);
1034 : : else
1035 : 0 : (void) unlink(mount_tmp);
1036 : : }
1037 : :
1038 [ # # ]: 0 : if (mount_slave_mounted)
1039 : 0 : (void) umount(mount_slave);
1040 [ # # ]: 0 : if (mount_slave_created)
1041 : 0 : (void) rmdir(mount_slave);
1042 : :
1043 : 0 : return r;
1044 : : }
1045 : :
1046 : 0 : int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
1047 : : const char *src, *dest, *host_path, *container_path, *host_basename, *container_basename, *container_dirname;
1048 : 0 : _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
1049 : 0 : CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE;
1050 : 0 : _cleanup_close_ int hostfd = -1;
1051 : 0 : Machine *m = userdata;
1052 : : bool copy_from;
1053 : : pid_t child;
1054 : : uid_t uid_shift;
1055 : : char *t;
1056 : : int r;
1057 : :
1058 [ # # ]: 0 : assert(message);
1059 [ # # ]: 0 : assert(m);
1060 : :
1061 [ # # ]: 0 : if (m->manager->n_operations >= OPERATIONS_MAX)
1062 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
1063 : :
1064 [ # # ]: 0 : if (m->class != MACHINE_CONTAINER)
1065 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
1066 : :
1067 : 0 : r = sd_bus_message_read(message, "ss", &src, &dest);
1068 [ # # ]: 0 : if (r < 0)
1069 : 0 : return r;
1070 : :
1071 [ # # ]: 0 : if (!path_is_absolute(src))
1072 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
1073 : :
1074 [ # # ]: 0 : if (isempty(dest))
1075 : 0 : dest = src;
1076 [ # # ]: 0 : else if (!path_is_absolute(dest))
1077 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
1078 : :
1079 : 0 : r = bus_verify_polkit_async(
1080 : : message,
1081 : : CAP_SYS_ADMIN,
1082 : : "org.freedesktop.machine1.manage-machines",
1083 : : NULL,
1084 : : false,
1085 : : UID_INVALID,
1086 : 0 : &m->manager->polkit_registry,
1087 : : error);
1088 [ # # ]: 0 : if (r < 0)
1089 : 0 : return r;
1090 [ # # ]: 0 : if (r == 0)
1091 : 0 : return 1; /* Will call us back */
1092 : :
1093 : 0 : r = machine_get_uid_shift(m, &uid_shift);
1094 [ # # ]: 0 : if (r < 0)
1095 : 0 : return r;
1096 : :
1097 : 0 : copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
1098 : :
1099 [ # # ]: 0 : if (copy_from) {
1100 : 0 : container_path = src;
1101 : 0 : host_path = dest;
1102 : : } else {
1103 : 0 : host_path = src;
1104 : 0 : container_path = dest;
1105 : : }
1106 : :
1107 : 0 : host_basename = basename(host_path);
1108 : :
1109 : 0 : container_basename = basename(container_path);
1110 : 0 : t = strdupa(container_path);
1111 : 0 : container_dirname = dirname(t);
1112 : :
1113 : 0 : hostfd = open_parent(host_path, O_CLOEXEC, 0);
1114 [ # # ]: 0 : if (hostfd < 0)
1115 : 0 : return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path);
1116 : :
1117 [ # # ]: 0 : if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
1118 : 0 : return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
1119 : :
1120 : 0 : r = safe_fork("(sd-copy)", FORK_RESET_SIGNALS, &child);
1121 [ # # ]: 0 : if (r < 0)
1122 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
1123 [ # # ]: 0 : if (r == 0) {
1124 : : int containerfd;
1125 : : const char *q;
1126 : : int mntfd;
1127 : :
1128 : 0 : errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
1129 : :
1130 [ # # # # : 0 : q = procfs_file_alloca(m->leader, "ns/mnt");
# # ]
1131 : 0 : mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
1132 [ # # ]: 0 : if (mntfd < 0) {
1133 [ # # ]: 0 : r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
1134 : 0 : goto child_fail;
1135 : : }
1136 : :
1137 [ # # ]: 0 : if (setns(mntfd, CLONE_NEWNS) < 0) {
1138 [ # # ]: 0 : r = log_error_errno(errno, "Failed to join namespace of leader: %m");
1139 : 0 : goto child_fail;
1140 : : }
1141 : :
1142 : 0 : containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
1143 [ # # ]: 0 : if (containerfd < 0) {
1144 [ # # ]: 0 : r = log_error_errno(errno, "Failed to open destination directory: %m");
1145 : 0 : goto child_fail;
1146 : : }
1147 : :
1148 : : /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
1149 : : * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1150 : : * the UID/GIDs as they are. */
1151 [ # # ]: 0 : if (copy_from)
1152 [ # # # # ]: 0 : r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags);
1153 : : else
1154 [ # # # # ]: 0 : r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags);
1155 : :
1156 : 0 : hostfd = safe_close(hostfd);
1157 : 0 : containerfd = safe_close(containerfd);
1158 : :
1159 [ # # ]: 0 : if (r < 0) {
1160 [ # # ]: 0 : r = log_error_errno(r, "Failed to copy tree: %m");
1161 : 0 : goto child_fail;
1162 : : }
1163 : :
1164 : 0 : _exit(EXIT_SUCCESS);
1165 : :
1166 : 0 : child_fail:
1167 : 0 : (void) write(errno_pipe_fd[1], &r, sizeof(r));
1168 : 0 : _exit(EXIT_FAILURE);
1169 : : }
1170 : :
1171 : 0 : errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
1172 : :
1173 : : /* Copying might take a while, hence install a watch on the child, and return */
1174 : :
1175 : 0 : r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL);
1176 [ # # ]: 0 : if (r < 0) {
1177 : 0 : (void) sigkill_wait(child);
1178 : 0 : return r;
1179 : : }
1180 : 0 : errno_pipe_fd[0] = -1;
1181 : :
1182 : 0 : return 1;
1183 : : }
1184 : :
1185 : 0 : int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) {
1186 : 0 : _cleanup_close_ int fd = -1;
1187 : 0 : Machine *m = userdata;
1188 : : int r;
1189 : :
1190 [ # # ]: 0 : assert(message);
1191 [ # # ]: 0 : assert(m);
1192 : :
1193 : 0 : r = bus_verify_polkit_async(
1194 : : message,
1195 : : CAP_SYS_ADMIN,
1196 : : "org.freedesktop.machine1.manage-machines",
1197 : : NULL,
1198 : : false,
1199 : : UID_INVALID,
1200 : 0 : &m->manager->polkit_registry,
1201 : : error);
1202 [ # # ]: 0 : if (r < 0)
1203 : 0 : return r;
1204 [ # # ]: 0 : if (r == 0)
1205 : 0 : return 1; /* Will call us back */
1206 : :
1207 [ # # # ]: 0 : switch (m->class) {
1208 : :
1209 : 0 : case MACHINE_HOST:
1210 : 0 : fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1211 [ # # ]: 0 : if (fd < 0)
1212 : 0 : return -errno;
1213 : :
1214 : 0 : break;
1215 : :
1216 : 0 : case MACHINE_CONTAINER: {
1217 [ # # # # ]: 0 : _cleanup_close_ int mntns_fd = -1, root_fd = -1;
1218 [ # # ]: 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
1219 : : pid_t child;
1220 : :
1221 : 0 : r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd);
1222 [ # # ]: 0 : if (r < 0)
1223 : 0 : return r;
1224 : :
1225 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1226 : 0 : return -errno;
1227 : :
1228 : 0 : r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
1229 : : -1, mntns_fd, -1, -1, root_fd, &child);
1230 [ # # ]: 0 : if (r < 0)
1231 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
1232 [ # # ]: 0 : if (r == 0) {
1233 : 0 : _cleanup_close_ int dfd = -1;
1234 : :
1235 : 0 : pair[0] = safe_close(pair[0]);
1236 : :
1237 : 0 : dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
1238 [ # # ]: 0 : if (dfd < 0)
1239 : 0 : _exit(EXIT_FAILURE);
1240 : :
1241 : 0 : r = send_one_fd(pair[1], dfd, 0);
1242 : 0 : dfd = safe_close(dfd);
1243 [ # # ]: 0 : if (r < 0)
1244 : 0 : _exit(EXIT_FAILURE);
1245 : :
1246 : 0 : _exit(EXIT_SUCCESS);
1247 : : }
1248 : :
1249 : 0 : pair[1] = safe_close(pair[1]);
1250 : :
1251 : 0 : r = wait_for_terminate_and_check("(sd-openrootns)", child, 0);
1252 [ # # ]: 0 : if (r < 0)
1253 : 0 : return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
1254 [ # # ]: 0 : if (r != EXIT_SUCCESS)
1255 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
1256 : :
1257 : 0 : fd = receive_one_fd(pair[0], MSG_DONTWAIT);
1258 [ # # ]: 0 : if (fd < 0)
1259 : 0 : return fd;
1260 : :
1261 : 0 : break;
1262 : : }
1263 : :
1264 : 0 : default:
1265 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
1266 : : }
1267 : :
1268 : 0 : return sd_bus_reply_method_return(message, "h", fd);
1269 : : }
1270 : :
1271 : 0 : int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) {
1272 : 0 : Machine *m = userdata;
1273 : 0 : uid_t shift = 0;
1274 : : int r;
1275 : :
1276 [ # # ]: 0 : assert(message);
1277 [ # # ]: 0 : assert(m);
1278 : :
1279 : : /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1280 : : * we kinda have to for this. */
1281 : :
1282 [ # # ]: 0 : if (m->class == MACHINE_HOST)
1283 : 0 : return sd_bus_reply_method_return(message, "u", UINT32_C(0));
1284 : :
1285 [ # # ]: 0 : if (m->class != MACHINE_CONTAINER)
1286 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
1287 : :
1288 : 0 : r = machine_get_uid_shift(m, &shift);
1289 [ # # ]: 0 : if (r == -ENXIO)
1290 : 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name);
1291 [ # # ]: 0 : if (r < 0)
1292 : 0 : return r;
1293 : :
1294 : 0 : return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
1295 : : }
1296 : :
1297 : : const sd_bus_vtable machine_vtable[] = {
1298 : : SD_BUS_VTABLE_START(0),
1299 : : SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
1300 : : SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST),
1301 : : BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
1302 : : SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
1303 : : SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
1304 : : SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
1305 : : SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
1306 : : SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
1307 : : SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
1308 : : SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
1309 : : SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
1310 : : SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
1311 : : SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
1312 : : SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
1313 : : SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
1314 : : SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
1315 : : SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
1316 : : SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
1317 : : SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
1318 : : SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
1319 : : SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
1320 : : SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
1321 : : SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
1322 : : SD_BUS_VTABLE_END
1323 : : };
1324 : :
1325 : 0 : int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1326 : 0 : Manager *m = userdata;
1327 : : Machine *machine;
1328 : : int r;
1329 : :
1330 [ # # ]: 0 : assert(bus);
1331 [ # # ]: 0 : assert(path);
1332 [ # # ]: 0 : assert(interface);
1333 [ # # ]: 0 : assert(found);
1334 [ # # ]: 0 : assert(m);
1335 : :
1336 [ # # ]: 0 : if (streq(path, "/org/freedesktop/machine1/machine/self")) {
1337 [ # # ]: 0 : _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
1338 : : sd_bus_message *message;
1339 : : pid_t pid;
1340 : :
1341 : 0 : message = sd_bus_get_current_message(bus);
1342 [ # # ]: 0 : if (!message)
1343 : 0 : return 0;
1344 : :
1345 : 0 : r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
1346 [ # # ]: 0 : if (r < 0)
1347 : 0 : return r;
1348 : :
1349 : 0 : r = sd_bus_creds_get_pid(creds, &pid);
1350 [ # # ]: 0 : if (r < 0)
1351 : 0 : return r;
1352 : :
1353 : 0 : r = manager_get_machine_by_pid(m, pid, &machine);
1354 [ # # ]: 0 : if (r <= 0)
1355 : 0 : return 0;
1356 : : } else {
1357 [ # # ]: 0 : _cleanup_free_ char *e = NULL;
1358 : : const char *p;
1359 : :
1360 : 0 : p = startswith(path, "/org/freedesktop/machine1/machine/");
1361 [ # # ]: 0 : if (!p)
1362 : 0 : return 0;
1363 : :
1364 : 0 : e = bus_label_unescape(p);
1365 [ # # ]: 0 : if (!e)
1366 : 0 : return -ENOMEM;
1367 : :
1368 : 0 : machine = hashmap_get(m->machines, e);
1369 [ # # ]: 0 : if (!machine)
1370 : 0 : return 0;
1371 : : }
1372 : :
1373 : 0 : *found = machine;
1374 : 0 : return 1;
1375 : : }
1376 : :
1377 : 0 : char *machine_bus_path(Machine *m) {
1378 : 0 : _cleanup_free_ char *e = NULL;
1379 : :
1380 [ # # ]: 0 : assert(m);
1381 : :
1382 : 0 : e = bus_label_escape(m->name);
1383 [ # # ]: 0 : if (!e)
1384 : 0 : return NULL;
1385 : :
1386 : 0 : return strjoin("/org/freedesktop/machine1/machine/", e);
1387 : : }
1388 : :
1389 : 0 : int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1390 : 0 : _cleanup_strv_free_ char **l = NULL;
1391 : 0 : Machine *machine = NULL;
1392 : 0 : Manager *m = userdata;
1393 : : Iterator i;
1394 : : int r;
1395 : :
1396 [ # # ]: 0 : assert(bus);
1397 [ # # ]: 0 : assert(path);
1398 [ # # ]: 0 : assert(nodes);
1399 : :
1400 [ # # ]: 0 : HASHMAP_FOREACH(machine, m->machines, i) {
1401 : : char *p;
1402 : :
1403 : 0 : p = machine_bus_path(machine);
1404 [ # # ]: 0 : if (!p)
1405 : 0 : return -ENOMEM;
1406 : :
1407 : 0 : r = strv_consume(&l, p);
1408 [ # # ]: 0 : if (r < 0)
1409 : 0 : return r;
1410 : : }
1411 : :
1412 : 0 : *nodes = TAKE_PTR(l);
1413 : :
1414 : 0 : return 1;
1415 : : }
1416 : :
1417 : 0 : int machine_send_signal(Machine *m, bool new_machine) {
1418 : 0 : _cleanup_free_ char *p = NULL;
1419 : :
1420 [ # # ]: 0 : assert(m);
1421 : :
1422 : 0 : p = machine_bus_path(m);
1423 [ # # ]: 0 : if (!p)
1424 : 0 : return -ENOMEM;
1425 : :
1426 [ # # ]: 0 : return sd_bus_emit_signal(
1427 : 0 : m->manager->bus,
1428 : : "/org/freedesktop/machine1",
1429 : : "org.freedesktop.machine1.Manager",
1430 : : new_machine ? "MachineNew" : "MachineRemoved",
1431 : : "so", m->name, p);
1432 : : }
1433 : :
1434 : 0 : int machine_send_create_reply(Machine *m, sd_bus_error *error) {
1435 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
1436 : 0 : _cleanup_free_ char *p = NULL;
1437 : :
1438 [ # # ]: 0 : assert(m);
1439 : :
1440 [ # # ]: 0 : if (!m->create_message)
1441 : 0 : return 0;
1442 : :
1443 : 0 : c = TAKE_PTR(m->create_message);
1444 : :
1445 [ # # ]: 0 : if (error)
1446 : 0 : return sd_bus_reply_method_error(c, error);
1447 : :
1448 : : /* Update the machine state file before we notify the client
1449 : : * about the result. */
1450 : 0 : machine_save(m);
1451 : :
1452 : 0 : p = machine_bus_path(m);
1453 [ # # ]: 0 : if (!p)
1454 : 0 : return -ENOMEM;
1455 : :
1456 : 0 : return sd_bus_reply_method_return(c, "o", p);
1457 : : }
|