]>
| Commit | Line | Data |
|---|---|---|
| 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
| 2 | ||
| 3 | #include "sd-bus.h" | |
| 4 | ||
| 5 | #include "alloc-util.h" | |
| 6 | #include "bus-common-errors.h" | |
| 7 | #include "bus-get-properties.h" | |
| 8 | #include "bus-label.h" | |
| 9 | #include "bus-object.h" | |
| 10 | #include "bus-polkit.h" | |
| 11 | #include "bus-util.h" | |
| 12 | #include "copy.h" | |
| 13 | #include "env-util.h" | |
| 14 | #include "errno-util.h" | |
| 15 | #include "fd-util.h" | |
| 16 | #include "hashmap.h" | |
| 17 | #include "in-addr-util.h" | |
| 18 | #include "local-addresses.h" | |
| 19 | #include "machine.h" | |
| 20 | #include "machine-dbus.h" | |
| 21 | #include "machined.h" | |
| 22 | #include "mount-util.h" | |
| 23 | #include "namespace-util.h" | |
| 24 | #include "operation.h" | |
| 25 | #include "path-util.h" | |
| 26 | #include "signal-util.h" | |
| 27 | #include "string-util.h" | |
| 28 | #include "strv.h" | |
| 29 | ||
| 30 | static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass); | |
| 31 | static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", Machine, machine_get_state, machine_state_to_string); | |
| 32 | ||
| 33 | static int property_get_netif( | |
| 34 | sd_bus *bus, | |
| 35 | const char *path, | |
| 36 | const char *interface, | |
| 37 | const char *property, | |
| 38 | sd_bus_message *reply, | |
| 39 | void *userdata, | |
| 40 | sd_bus_error *error) { | |
| 41 | ||
| 42 | Machine *m = ASSERT_PTR(userdata); | |
| 43 | ||
| 44 | assert(bus); | |
| 45 | assert(reply); | |
| 46 | ||
| 47 | assert_cc(sizeof(int) == sizeof(int32_t)); | |
| 48 | ||
| 49 | return sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int)); | |
| 50 | } | |
| 51 | ||
| 52 | int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 53 | Machine *m = ASSERT_PTR(userdata); | |
| 54 | int r; | |
| 55 | ||
| 56 | assert(message); | |
| 57 | ||
| 58 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 59 | const char *details[] = { | |
| 60 | "machine", m->name, | |
| 61 | "verb", "unregister", | |
| 62 | NULL | |
| 63 | }; | |
| 64 | ||
| 65 | r = bus_verify_polkit_async_full( | |
| 66 | message, | |
| 67 | "org.freedesktop.machine1.manage-machines", | |
| 68 | details, | |
| 69 | m->uid, | |
| 70 | /* flags= */ 0, | |
| 71 | &m->manager->polkit_registry, | |
| 72 | error); | |
| 73 | if (r < 0) | |
| 74 | return r; | |
| 75 | if (r == 0) | |
| 76 | return 1; /* Will call us back */ | |
| 77 | } | |
| 78 | ||
| 79 | r = machine_finalize(m); | |
| 80 | if (r < 0) | |
| 81 | return r; | |
| 82 | ||
| 83 | return sd_bus_reply_method_return(message, NULL); | |
| 84 | } | |
| 85 | ||
| 86 | int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 87 | Machine *m = ASSERT_PTR(userdata); | |
| 88 | int r; | |
| 89 | ||
| 90 | assert(message); | |
| 91 | ||
| 92 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 93 | const char *details[] = { | |
| 94 | "machine", m->name, | |
| 95 | "verb", "terminate", | |
| 96 | NULL | |
| 97 | }; | |
| 98 | ||
| 99 | r = bus_verify_polkit_async_full( | |
| 100 | message, | |
| 101 | "org.freedesktop.machine1.manage-machines", | |
| 102 | details, | |
| 103 | m->uid, | |
| 104 | /* flags= */ 0, | |
| 105 | &m->manager->polkit_registry, | |
| 106 | error); | |
| 107 | if (r < 0) | |
| 108 | return r; | |
| 109 | if (r == 0) | |
| 110 | return 1; /* Will call us back */ | |
| 111 | } | |
| 112 | ||
| 113 | r = machine_stop(m); | |
| 114 | if (r < 0) | |
| 115 | return r; | |
| 116 | ||
| 117 | return sd_bus_reply_method_return(message, NULL); | |
| 118 | } | |
| 119 | ||
| 120 | int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 121 | Machine *m = ASSERT_PTR(userdata); | |
| 122 | const char *swho; | |
| 123 | int32_t signo; | |
| 124 | KillWhom whom; | |
| 125 | int r; | |
| 126 | ||
| 127 | assert(message); | |
| 128 | ||
| 129 | r = sd_bus_message_read(message, "si", &swho, &signo); | |
| 130 | if (r < 0) | |
| 131 | return r; | |
| 132 | ||
| 133 | if (isempty(swho)) | |
| 134 | whom = KILL_ALL; | |
| 135 | else { | |
| 136 | whom = kill_whom_from_string(swho); | |
| 137 | if (whom < 0) | |
| 138 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); | |
| 139 | } | |
| 140 | ||
| 141 | if (!SIGNAL_VALID(signo)) | |
| 142 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); | |
| 143 | ||
| 144 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 145 | const char *details[] = { | |
| 146 | "machine", m->name, | |
| 147 | "verb", "kill", | |
| 148 | NULL | |
| 149 | }; | |
| 150 | ||
| 151 | r = bus_verify_polkit_async_full( | |
| 152 | message, | |
| 153 | "org.freedesktop.machine1.manage-machines", | |
| 154 | details, | |
| 155 | m->uid, | |
| 156 | /* flags= */ 0, | |
| 157 | &m->manager->polkit_registry, | |
| 158 | error); | |
| 159 | if (r < 0) | |
| 160 | return r; | |
| 161 | if (r == 0) | |
| 162 | return 1; /* Will call us back */ | |
| 163 | } | |
| 164 | ||
| 165 | r = machine_kill(m, whom, signo); | |
| 166 | if (r < 0) | |
| 167 | return r; | |
| 168 | ||
| 169 | return sd_bus_reply_method_return(message, NULL); | |
| 170 | } | |
| 171 | ||
| 172 | int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 173 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
| 174 | _cleanup_free_ struct local_address *addresses = NULL; | |
| 175 | Machine *m = ASSERT_PTR(userdata); | |
| 176 | int r; | |
| 177 | ||
| 178 | assert(message); | |
| 179 | ||
| 180 | r = sd_bus_message_new_method_return(message, &reply); | |
| 181 | if (r < 0) | |
| 182 | return r; | |
| 183 | ||
| 184 | r = sd_bus_message_open_container(reply, 'a', "(iay)"); | |
| 185 | if (r < 0) | |
| 186 | return r; | |
| 187 | ||
| 188 | int n = machine_get_addresses(m, &addresses); | |
| 189 | if (n == -ENONET) | |
| 190 | return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); | |
| 191 | if (ERRNO_IS_NEG_NOT_SUPPORTED(n)) | |
| 192 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); | |
| 193 | if (n < 0) | |
| 194 | return sd_bus_error_set_errnof(error, n, "Failed to get addresses: %m"); | |
| 195 | ||
| 196 | for (int i = 0; i < n; i++) { | |
| 197 | r = sd_bus_message_open_container(reply, 'r', "iay"); | |
| 198 | if (r < 0) | |
| 199 | return r; | |
| 200 | ||
| 201 | r = sd_bus_message_append(reply, "i", addresses[i].family); | |
| 202 | if (r < 0) | |
| 203 | return r; | |
| 204 | ||
| 205 | r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); | |
| 206 | if (r < 0) | |
| 207 | return r; | |
| 208 | ||
| 209 | r = sd_bus_message_close_container(reply); | |
| 210 | if (r < 0) | |
| 211 | return r; | |
| 212 | } | |
| 213 | ||
| 214 | r = sd_bus_message_close_container(reply); | |
| 215 | if (r < 0) | |
| 216 | return r; | |
| 217 | ||
| 218 | return sd_bus_message_send(reply); | |
| 219 | } | |
| 220 | ||
| 221 | int bus_machine_method_get_ssh_info(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 222 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
| 223 | Machine *m = ASSERT_PTR(userdata); | |
| 224 | int r; | |
| 225 | ||
| 226 | assert(message); | |
| 227 | ||
| 228 | r = sd_bus_message_new_method_return(message, &reply); | |
| 229 | if (r < 0) | |
| 230 | return r; | |
| 231 | ||
| 232 | if (!m->ssh_address || !m->ssh_private_key_path) | |
| 233 | return -ENOENT; | |
| 234 | ||
| 235 | r = sd_bus_message_append(reply, "ss", m->ssh_address, m->ssh_private_key_path); | |
| 236 | if (r < 0) | |
| 237 | return r; | |
| 238 | ||
| 239 | return sd_bus_message_send(reply); | |
| 240 | } | |
| 241 | ||
| 242 | int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 243 | _cleanup_strv_free_ char **l = NULL; | |
| 244 | Machine *m = ASSERT_PTR(userdata); | |
| 245 | int r; | |
| 246 | ||
| 247 | assert(message); | |
| 248 | ||
| 249 | r = machine_get_os_release(m, &l); | |
| 250 | if (r == -ENONET) | |
| 251 | return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information."); | |
| 252 | if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) | |
| 253 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); | |
| 254 | if (r < 0) | |
| 255 | return sd_bus_error_set_errnof(error, r, "Failed to get OS release: %m"); | |
| 256 | ||
| 257 | return bus_reply_pair_array(message, l); | |
| 258 | } | |
| 259 | ||
| 260 | int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 261 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
| 262 | _cleanup_free_ char *pty_name = NULL; | |
| 263 | _cleanup_close_ int master = -EBADF; | |
| 264 | Machine *m = ASSERT_PTR(userdata); | |
| 265 | int r; | |
| 266 | ||
| 267 | assert(message); | |
| 268 | ||
| 269 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 270 | const char *details[] = { | |
| 271 | "machine", m->name, | |
| 272 | NULL | |
| 273 | }; | |
| 274 | ||
| 275 | r = bus_verify_polkit_async_full( | |
| 276 | message, | |
| 277 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", | |
| 278 | details, | |
| 279 | m->uid, | |
| 280 | /* flags= */ 0, | |
| 281 | &m->manager->polkit_registry, | |
| 282 | error); | |
| 283 | if (r < 0) | |
| 284 | return r; | |
| 285 | if (r == 0) | |
| 286 | return 1; /* Will call us back */ | |
| 287 | } | |
| 288 | ||
| 289 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
| 290 | if (master < 0) | |
| 291 | return master; | |
| 292 | ||
| 293 | r = sd_bus_message_new_method_return(message, &reply); | |
| 294 | if (r < 0) | |
| 295 | return r; | |
| 296 | ||
| 297 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
| 298 | if (r < 0) | |
| 299 | return r; | |
| 300 | ||
| 301 | return sd_bus_message_send(reply); | |
| 302 | } | |
| 303 | ||
| 304 | int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 305 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
| 306 | _cleanup_free_ char *pty_name = NULL; | |
| 307 | _cleanup_close_ int master = -EBADF; | |
| 308 | Machine *m = ASSERT_PTR(userdata); | |
| 309 | int r; | |
| 310 | ||
| 311 | assert(message); | |
| 312 | ||
| 313 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 314 | const char *details[] = { | |
| 315 | "machine", m->name, | |
| 316 | "verb", "login", | |
| 317 | NULL | |
| 318 | }; | |
| 319 | ||
| 320 | r = bus_verify_polkit_async_full( | |
| 321 | message, | |
| 322 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", | |
| 323 | details, | |
| 324 | m->uid, | |
| 325 | /* flags= */ 0, | |
| 326 | &m->manager->polkit_registry, | |
| 327 | error); | |
| 328 | if (r < 0) | |
| 329 | return r; | |
| 330 | if (r == 0) | |
| 331 | return 1; /* Will call us back */ | |
| 332 | } | |
| 333 | ||
| 334 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
| 335 | if (master < 0) | |
| 336 | return master; | |
| 337 | ||
| 338 | r = machine_start_getty(m, pty_name, error); | |
| 339 | if (r < 0) | |
| 340 | return r; | |
| 341 | ||
| 342 | r = sd_bus_message_new_method_return(message, &reply); | |
| 343 | if (r < 0) | |
| 344 | return r; | |
| 345 | ||
| 346 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
| 347 | if (r < 0) | |
| 348 | return r; | |
| 349 | ||
| 350 | return sd_bus_message_send(reply); | |
| 351 | } | |
| 352 | ||
| 353 | int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 354 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
| 355 | _cleanup_free_ char *pty_name = NULL; | |
| 356 | _cleanup_close_ int master = -EBADF; | |
| 357 | _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL; | |
| 358 | Machine *m = ASSERT_PTR(userdata); | |
| 359 | const char *user, *path; | |
| 360 | int r; | |
| 361 | ||
| 362 | assert(message); | |
| 363 | ||
| 364 | r = sd_bus_message_read(message, "ss", &user, &path); | |
| 365 | if (r < 0) | |
| 366 | return r; | |
| 367 | user = isempty(user) ? "root" : user; | |
| 368 | ||
| 369 | /* Ensure only root can shell into the root namespace, unless it's specifically the host machine, | |
| 370 | * which is owned by uid 0 anyway and cannot be self-registered. This is to avoid unprivileged | |
| 371 | * users registering a process they own in the root user namespace, and then shelling in as root | |
| 372 | * or another user. Note that the shell operation is privileged and requires 'auth_admin', so we | |
| 373 | * do not need to check the caller's uid, as that will be checked by polkit, and if they machine's | |
| 374 | * and the caller's do not match, authorization will be required. It's only the case where the | |
| 375 | * caller owns the machine that will be shortcut and needs to be checked here. */ | |
| 376 | if (m->uid != 0 && m->class != MACHINE_HOST) { | |
| 377 | r = pidref_in_same_namespace(&PIDREF_MAKE_FROM_PID(1), &m->leader, NAMESPACE_USER); | |
| 378 | if (r < 0) | |
| 379 | return r; | |
| 380 | if (r != 0) | |
| 381 | return sd_bus_error_set( | |
| 382 | error, | |
| 383 | SD_BUS_ERROR_ACCESS_DENIED, | |
| 384 | "Only root may shell into the root user namespace"); | |
| 385 | } | |
| 386 | ||
| 387 | r = sd_bus_message_read_strv(message, &args_wire); | |
| 388 | if (r < 0) | |
| 389 | return r; | |
| 390 | if (isempty(path)) { | |
| 391 | path = machine_default_shell_path(); | |
| 392 | args = machine_default_shell_args(user); | |
| 393 | if (!args) | |
| 394 | return -ENOMEM; | |
| 395 | } else { | |
| 396 | if (!path_is_absolute(path)) | |
| 397 | return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); | |
| 398 | args = TAKE_PTR(args_wire); | |
| 399 | if (strv_isempty(args)) { | |
| 400 | args = strv_free(args); | |
| 401 | ||
| 402 | args = strv_new(path); | |
| 403 | if (!args) | |
| 404 | return -ENOMEM; | |
| 405 | } | |
| 406 | } | |
| 407 | ||
| 408 | r = sd_bus_message_read_strv(message, &env); | |
| 409 | if (r < 0) | |
| 410 | return r; | |
| 411 | if (!strv_env_is_valid(env)) | |
| 412 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); | |
| 413 | ||
| 414 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 415 | _cleanup_free_ char *command_line = strv_join(args, " "); | |
| 416 | if (!command_line) | |
| 417 | return -ENOMEM; | |
| 418 | ||
| 419 | const char *details[] = { | |
| 420 | "machine", m->name, | |
| 421 | "user", user, | |
| 422 | "program", path, | |
| 423 | "command_line", command_line, | |
| 424 | NULL | |
| 425 | }; | |
| 426 | ||
| 427 | r = bus_verify_polkit_async_full( | |
| 428 | message, | |
| 429 | m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", | |
| 430 | details, | |
| 431 | m->uid, | |
| 432 | /* flags= */ 0, | |
| 433 | &m->manager->polkit_registry, | |
| 434 | error); | |
| 435 | if (r < 0) | |
| 436 | return r; | |
| 437 | if (r == 0) | |
| 438 | return 1; /* Will call us back */ | |
| 439 | } | |
| 440 | ||
| 441 | master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name); | |
| 442 | if (master < 0) | |
| 443 | return master; | |
| 444 | ||
| 445 | r = machine_start_shell(m, master, pty_name, user, path, args, env, error); | |
| 446 | if (r < 0) | |
| 447 | return r; | |
| 448 | ||
| 449 | r = sd_bus_message_new_method_return(message, &reply); | |
| 450 | if (r < 0) | |
| 451 | return r; | |
| 452 | ||
| 453 | r = sd_bus_message_append(reply, "hs", master, pty_name); | |
| 454 | if (r < 0) | |
| 455 | return r; | |
| 456 | ||
| 457 | return sd_bus_message_send(reply); | |
| 458 | } | |
| 459 | ||
| 460 | int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 461 | int read_only, make_file_or_directory; | |
| 462 | const char *dest, *src, *propagate_directory; | |
| 463 | Machine *m = ASSERT_PTR(userdata); | |
| 464 | MountInNamespaceFlags flags = 0; | |
| 465 | uid_t uid; | |
| 466 | int r; | |
| 467 | ||
| 468 | assert(message); | |
| 469 | ||
| 470 | if (m->class != MACHINE_CONTAINER) | |
| 471 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines."); | |
| 472 | ||
| 473 | r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory); | |
| 474 | if (r < 0) | |
| 475 | return r; | |
| 476 | ||
| 477 | if (!path_is_absolute(src) || !path_is_normalized(src)) | |
| 478 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized."); | |
| 479 | ||
| 480 | if (isempty(dest)) | |
| 481 | dest = src; | |
| 482 | else if (!path_is_absolute(dest) || !path_is_normalized(dest)) | |
| 483 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized."); | |
| 484 | ||
| 485 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 486 | const char *details[] = { | |
| 487 | "machine", m->name, | |
| 488 | "verb", "bind", | |
| 489 | "src", src, | |
| 490 | "dest", dest, | |
| 491 | NULL | |
| 492 | }; | |
| 493 | ||
| 494 | /* NB: For now not opened up to owner of machine without auth */ | |
| 495 | r = bus_verify_polkit_async( | |
| 496 | message, | |
| 497 | "org.freedesktop.machine1.manage-machines", | |
| 498 | details, | |
| 499 | &m->manager->polkit_registry, | |
| 500 | error); | |
| 501 | if (r < 0) | |
| 502 | return r; | |
| 503 | if (r == 0) | |
| 504 | return 1; /* Will call us back */ | |
| 505 | } | |
| 506 | ||
| 507 | r = machine_get_uid_shift(m, &uid); | |
| 508 | if (r < 0) | |
| 509 | return r; | |
| 510 | if (uid != 0) | |
| 511 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied."); | |
| 512 | ||
| 513 | if (read_only) | |
| 514 | flags |= MOUNT_IN_NAMESPACE_READ_ONLY; | |
| 515 | if (make_file_or_directory) | |
| 516 | flags |= MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY; | |
| 517 | ||
| 518 | propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name); | |
| 519 | r = bind_mount_in_namespace( | |
| 520 | &m->leader, | |
| 521 | propagate_directory, | |
| 522 | "/run/host/incoming/", | |
| 523 | src, dest, | |
| 524 | flags); | |
| 525 | if (r < 0) | |
| 526 | return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest); | |
| 527 | ||
| 528 | return sd_bus_reply_method_return(message, NULL); | |
| 529 | } | |
| 530 | ||
| 531 | int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 532 | const char *src, *dest, *host_path, *container_path; | |
| 533 | CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS; | |
| 534 | Machine *m = ASSERT_PTR(userdata); | |
| 535 | Manager *manager = m->manager; | |
| 536 | bool copy_from; | |
| 537 | int r; | |
| 538 | ||
| 539 | assert(message); | |
| 540 | ||
| 541 | if (m->manager->n_operations >= OPERATIONS_MAX) | |
| 542 | return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); | |
| 543 | ||
| 544 | if (m->class != MACHINE_CONTAINER) | |
| 545 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines."); | |
| 546 | ||
| 547 | r = sd_bus_message_read(message, "ss", &src, &dest); | |
| 548 | if (r < 0) | |
| 549 | return r; | |
| 550 | ||
| 551 | if (endswith(sd_bus_message_get_member(message), "WithFlags")) { | |
| 552 | uint64_t raw_flags; | |
| 553 | ||
| 554 | r = sd_bus_message_read(message, "t", &raw_flags); | |
| 555 | if (r < 0) | |
| 556 | return r; | |
| 557 | ||
| 558 | if ((raw_flags & ~_MACHINE_COPY_FLAGS_MASK_PUBLIC) != 0) | |
| 559 | return -EINVAL; | |
| 560 | ||
| 561 | if (raw_flags & MACHINE_COPY_REPLACE) | |
| 562 | copy_flags |= COPY_REPLACE; | |
| 563 | } | |
| 564 | ||
| 565 | if (!path_is_absolute(src)) | |
| 566 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute."); | |
| 567 | ||
| 568 | if (isempty(dest)) | |
| 569 | dest = src; | |
| 570 | else if (!path_is_absolute(dest)) | |
| 571 | return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); | |
| 572 | ||
| 573 | if (manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 574 | const char *details[] = { | |
| 575 | "machine", m->name, | |
| 576 | "verb", "copy", | |
| 577 | "src", src, | |
| 578 | "dest", dest, | |
| 579 | NULL | |
| 580 | }; | |
| 581 | ||
| 582 | /* NB: For now not opened up to owner of machine without auth */ | |
| 583 | r = bus_verify_polkit_async( | |
| 584 | message, | |
| 585 | "org.freedesktop.machine1.manage-machines", | |
| 586 | details, | |
| 587 | &manager->polkit_registry, | |
| 588 | error); | |
| 589 | if (r < 0) | |
| 590 | return r; | |
| 591 | if (r == 0) | |
| 592 | return 1; /* Will call us back */ | |
| 593 | } | |
| 594 | ||
| 595 | copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); | |
| 596 | ||
| 597 | if (copy_from) { | |
| 598 | container_path = src; | |
| 599 | host_path = dest; | |
| 600 | } else { | |
| 601 | host_path = src; | |
| 602 | container_path = dest; | |
| 603 | } | |
| 604 | ||
| 605 | Operation *op; | |
| 606 | r = machine_copy_from_to_operation(manager, m, host_path, container_path, copy_from, copy_flags, &op); | |
| 607 | if (r < 0) | |
| 608 | return sd_bus_error_set_errnof(error, r, "Failed to copy from/to machine '%s': %m", m->name); | |
| 609 | ||
| 610 | operation_attach_bus_reply(op, message); | |
| 611 | return 1; | |
| 612 | } | |
| 613 | ||
| 614 | int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 615 | _cleanup_close_ int fd = -EBADF; | |
| 616 | Machine *m = ASSERT_PTR(userdata); | |
| 617 | int r; | |
| 618 | ||
| 619 | assert(message); | |
| 620 | ||
| 621 | if (m->manager->runtime_scope != RUNTIME_SCOPE_USER) { | |
| 622 | const char *details[] = { | |
| 623 | "machine", m->name, | |
| 624 | "verb", "open_root_directory", | |
| 625 | NULL | |
| 626 | }; | |
| 627 | ||
| 628 | /* NB: For now not opened up to owner of machine without auth */ | |
| 629 | r = bus_verify_polkit_async( | |
| 630 | message, | |
| 631 | "org.freedesktop.machine1.manage-machines", | |
| 632 | details, | |
| 633 | &m->manager->polkit_registry, | |
| 634 | error); | |
| 635 | if (r < 0) | |
| 636 | return r; | |
| 637 | if (r == 0) | |
| 638 | return 1; /* Will call us back */ | |
| 639 | } | |
| 640 | ||
| 641 | fd = machine_open_root_directory(m); | |
| 642 | if (ERRNO_IS_NEG_NOT_SUPPORTED(fd)) | |
| 643 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); | |
| 644 | if (fd < 0) | |
| 645 | return sd_bus_error_set_errnof(error, fd, "Failed to open root directory of machine '%s': %m", m->name); | |
| 646 | ||
| 647 | return sd_bus_reply_method_return(message, "h", fd); | |
| 648 | } | |
| 649 | ||
| 650 | int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) { | |
| 651 | Machine *m = ASSERT_PTR(userdata); | |
| 652 | uid_t shift = 0; | |
| 653 | int r; | |
| 654 | ||
| 655 | assert(message); | |
| 656 | ||
| 657 | /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but | |
| 658 | * we kinda have to for this. */ | |
| 659 | ||
| 660 | if (m->class == MACHINE_HOST) | |
| 661 | return sd_bus_reply_method_return(message, "u", UINT32_C(0)); | |
| 662 | ||
| 663 | if (m->class != MACHINE_CONTAINER) | |
| 664 | return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines."); | |
| 665 | ||
| 666 | r = machine_get_uid_shift(m, &shift); | |
| 667 | if (r == -ENXIO) | |
| 668 | return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name); | |
| 669 | if (r < 0) | |
| 670 | return r; | |
| 671 | ||
| 672 | return sd_bus_reply_method_return(message, "u", (uint32_t) shift); | |
| 673 | } | |
| 674 | ||
| 675 | static int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { | |
| 676 | Manager *m = ASSERT_PTR(userdata); | |
| 677 | Machine *machine; | |
| 678 | int r; | |
| 679 | ||
| 680 | assert(bus); | |
| 681 | assert(path); | |
| 682 | assert(interface); | |
| 683 | assert(found); | |
| 684 | ||
| 685 | if (streq(path, "/org/freedesktop/machine1/machine/self")) { | |
| 686 | _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; | |
| 687 | _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; | |
| 688 | sd_bus_message *message; | |
| 689 | ||
| 690 | message = sd_bus_get_current_message(bus); | |
| 691 | if (!message) | |
| 692 | return 0; | |
| 693 | ||
| 694 | r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID|SD_BUS_CREDS_PIDFD, &creds); | |
| 695 | if (r < 0) | |
| 696 | return r; | |
| 697 | ||
| 698 | r = bus_creds_get_pidref(creds, &pidref); | |
| 699 | if (r < 0) | |
| 700 | return r; | |
| 701 | ||
| 702 | r = manager_get_machine_by_pidref(m, &pidref, &machine); | |
| 703 | if (r <= 0) | |
| 704 | return 0; | |
| 705 | } else { | |
| 706 | _cleanup_free_ char *e = NULL; | |
| 707 | const char *p; | |
| 708 | ||
| 709 | p = startswith(path, "/org/freedesktop/machine1/machine/"); | |
| 710 | if (!p) | |
| 711 | return 0; | |
| 712 | ||
| 713 | e = bus_label_unescape(p); | |
| 714 | if (!e) | |
| 715 | return -ENOMEM; | |
| 716 | ||
| 717 | machine = hashmap_get(m->machines, e); | |
| 718 | if (!machine) | |
| 719 | return 0; | |
| 720 | } | |
| 721 | ||
| 722 | *found = machine; | |
| 723 | return 1; | |
| 724 | } | |
| 725 | ||
| 726 | char* machine_bus_path(Machine *m) { | |
| 727 | _cleanup_free_ char *e = NULL; | |
| 728 | ||
| 729 | assert(m); | |
| 730 | ||
| 731 | e = bus_label_escape(m->name); | |
| 732 | if (!e) | |
| 733 | return NULL; | |
| 734 | ||
| 735 | return strjoin("/org/freedesktop/machine1/machine/", e); | |
| 736 | } | |
| 737 | ||
| 738 | static int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { | |
| 739 | _cleanup_strv_free_ char **l = NULL; | |
| 740 | Machine *machine = NULL; | |
| 741 | Manager *m = userdata; | |
| 742 | int r; | |
| 743 | ||
| 744 | assert(bus); | |
| 745 | assert(path); | |
| 746 | assert(nodes); | |
| 747 | ||
| 748 | HASHMAP_FOREACH(machine, m->machines) { | |
| 749 | char *p; | |
| 750 | ||
| 751 | p = machine_bus_path(machine); | |
| 752 | if (!p) | |
| 753 | return -ENOMEM; | |
| 754 | ||
| 755 | r = strv_consume(&l, p); | |
| 756 | if (r < 0) | |
| 757 | return r; | |
| 758 | } | |
| 759 | ||
| 760 | *nodes = TAKE_PTR(l); | |
| 761 | ||
| 762 | return 1; | |
| 763 | } | |
| 764 | ||
| 765 | static const sd_bus_vtable machine_vtable[] = { | |
| 766 | SD_BUS_VTABLE_START(0), | |
| 767 | SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 768 | SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 769 | BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 770 | SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 771 | SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 772 | SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), | |
| 773 | SD_BUS_PROPERTY("Subgroup", "s", NULL, offsetof(Machine, subgroup), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 774 | SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Machine, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 775 | SD_BUS_PROPERTY("LeaderPIDFDId", "t", bus_property_get_pidfdid, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 776 | SD_BUS_PROPERTY("Supervisor", "u", bus_property_get_pid, offsetof(Machine, supervisor.pid), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 777 | SD_BUS_PROPERTY("SupervisorPIDFDId", "t", bus_property_get_pidfdid, offsetof(Machine, supervisor), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 778 | SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 779 | SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 780 | SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST), | |
| 781 | SD_BUS_PROPERTY("VSockCID", "u", NULL, offsetof(Machine, vsock_cid), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 782 | SD_BUS_PROPERTY("SSHAddress", "s", NULL, offsetof(Machine, ssh_address), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 783 | SD_BUS_PROPERTY("SSHPrivateKeyPath", "s", NULL, offsetof(Machine, ssh_private_key_path), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 784 | SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), | |
| 785 | SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Machine, uid), SD_BUS_VTABLE_PROPERTY_CONST), | |
| 786 | ||
| 787 | SD_BUS_METHOD("Terminate", | |
| 788 | NULL, | |
| 789 | NULL, | |
| 790 | bus_machine_method_terminate, | |
| 791 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 792 | SD_BUS_METHOD_WITH_ARGS("Kill", | |
| 793 | SD_BUS_ARGS("s", whom, "i", signal), | |
| 794 | SD_BUS_NO_RESULT, | |
| 795 | bus_machine_method_kill, | |
| 796 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 797 | SD_BUS_METHOD_WITH_ARGS("GetAddresses", | |
| 798 | SD_BUS_NO_ARGS, | |
| 799 | SD_BUS_RESULT("a(iay)", addresses), | |
| 800 | bus_machine_method_get_addresses, | |
| 801 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 802 | SD_BUS_METHOD_WITH_ARGS("GetSSHInfo", | |
| 803 | SD_BUS_NO_ARGS, | |
| 804 | SD_BUS_RESULT("s", ssh_address, "s", ssh_private_key_path), | |
| 805 | bus_machine_method_get_ssh_info, | |
| 806 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 807 | SD_BUS_METHOD_WITH_ARGS("GetOSRelease", | |
| 808 | SD_BUS_NO_ARGS, | |
| 809 | SD_BUS_RESULT("a{ss}", fields), | |
| 810 | bus_machine_method_get_os_release, | |
| 811 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 812 | SD_BUS_METHOD_WITH_ARGS("GetUIDShift", | |
| 813 | SD_BUS_NO_ARGS, | |
| 814 | SD_BUS_RESULT("u", shift), | |
| 815 | bus_machine_method_get_uid_shift, | |
| 816 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 817 | SD_BUS_METHOD_WITH_ARGS("OpenPTY", | |
| 818 | SD_BUS_NO_ARGS, | |
| 819 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
| 820 | bus_machine_method_open_pty, | |
| 821 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 822 | SD_BUS_METHOD_WITH_ARGS("OpenLogin", | |
| 823 | SD_BUS_NO_ARGS, | |
| 824 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
| 825 | bus_machine_method_open_login, | |
| 826 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 827 | SD_BUS_METHOD_WITH_ARGS("OpenShell", | |
| 828 | SD_BUS_ARGS("s", user, "s", path, "as", args, "as", environment), | |
| 829 | SD_BUS_RESULT("h", pty, "s", pty_path), | |
| 830 | bus_machine_method_open_shell, | |
| 831 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 832 | SD_BUS_METHOD_WITH_ARGS("BindMount", | |
| 833 | SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir), | |
| 834 | SD_BUS_NO_RESULT, | |
| 835 | bus_machine_method_bind_mount, | |
| 836 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 837 | SD_BUS_METHOD_WITH_ARGS("CopyFrom", | |
| 838 | SD_BUS_ARGS("s", source, "s", destination), | |
| 839 | SD_BUS_NO_RESULT, | |
| 840 | bus_machine_method_copy, | |
| 841 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 842 | SD_BUS_METHOD_WITH_ARGS("CopyTo", | |
| 843 | SD_BUS_ARGS("s", source, "s", destination), | |
| 844 | SD_BUS_NO_RESULT, | |
| 845 | bus_machine_method_copy, | |
| 846 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 847 | SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags", | |
| 848 | SD_BUS_ARGS("s", source, "s", destination, "t", flags), | |
| 849 | SD_BUS_NO_RESULT, | |
| 850 | bus_machine_method_copy, | |
| 851 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 852 | SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags", | |
| 853 | SD_BUS_ARGS("s", source, "s", destination, "t", flags), | |
| 854 | SD_BUS_NO_RESULT, | |
| 855 | bus_machine_method_copy, | |
| 856 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 857 | SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory", | |
| 858 | SD_BUS_NO_ARGS, | |
| 859 | SD_BUS_RESULT("h", fd), | |
| 860 | bus_machine_method_open_root_directory, | |
| 861 | SD_BUS_VTABLE_UNPRIVILEGED), | |
| 862 | ||
| 863 | SD_BUS_VTABLE_END | |
| 864 | }; | |
| 865 | ||
| 866 | const BusObjectImplementation machine_object = { | |
| 867 | "/org/freedesktop/machine1/machine", | |
| 868 | "org.freedesktop.machine1.Machine", | |
| 869 | .fallback_vtables = BUS_FALLBACK_VTABLES({machine_vtable, machine_object_find}), | |
| 870 | .node_enumerator = machine_node_enumerator, | |
| 871 | }; | |
| 872 | ||
| 873 | int machine_send_signal(Machine *m, bool new_machine) { | |
| 874 | _cleanup_free_ char *p = NULL; | |
| 875 | ||
| 876 | assert(m); | |
| 877 | ||
| 878 | p = machine_bus_path(m); | |
| 879 | if (!p) | |
| 880 | return -ENOMEM; | |
| 881 | ||
| 882 | return sd_bus_emit_signal( | |
| 883 | m->manager->api_bus, | |
| 884 | "/org/freedesktop/machine1", | |
| 885 | "org.freedesktop.machine1.Manager", | |
| 886 | new_machine ? "MachineNew" : "MachineRemoved", | |
| 887 | "so", m->name, p); | |
| 888 | } | |
| 889 | ||
| 890 | int machine_send_create_reply(Machine *m, sd_bus_error *error) { | |
| 891 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; | |
| 892 | _cleanup_free_ char *p = NULL; | |
| 893 | ||
| 894 | assert(m); | |
| 895 | ||
| 896 | if (!m->create_message) | |
| 897 | return 0; | |
| 898 | ||
| 899 | c = TAKE_PTR(m->create_message); | |
| 900 | ||
| 901 | if (error) | |
| 902 | return sd_bus_reply_method_error(c, error); | |
| 903 | ||
| 904 | /* Update the machine state file before we notify the client | |
| 905 | * about the result. */ | |
| 906 | machine_save(m); | |
| 907 | ||
| 908 | p = machine_bus_path(m); | |
| 909 | if (!p) | |
| 910 | return -ENOMEM; | |
| 911 | ||
| 912 | return sd_bus_reply_method_return(c, "o", p); | |
| 913 | } |