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