From: Lennart Poettering Date: Thu, 5 Jun 2014 10:24:03 +0000 (+0200) Subject: kdbus: when uploading bus name policy, resolve users/groups out-of-process X-Git-Tag: v214~72 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=a4152e3fe28b53b8919cc404dd7eca7ead1bf9bd kdbus: when uploading bus name policy, resolve users/groups out-of-process It's not safe invoking NSS from PID 1, hence fork off worker processes that upload the policy into the kernel for busnames. --- diff --git a/src/core/busname.c b/src/core/busname.c index e6063669e..6be1b8ba5 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -29,13 +29,17 @@ static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = { [BUSNAME_DEAD] = UNIT_INACTIVE, + [BUSNAME_MAKING] = UNIT_ACTIVATING, [BUSNAME_REGISTERED] = UNIT_ACTIVE, [BUSNAME_LISTENING] = UNIT_ACTIVE, [BUSNAME_RUNNING] = UNIT_ACTIVE, + [BUSNAME_SIGTERM] = UNIT_DEACTIVATING, + [BUSNAME_SIGKILL] = UNIT_DEACTIVATING, [BUSNAME_FAILED] = UNIT_FAILED }; static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); static void busname_init(Unit *u) { BusName *n = BUSNAME(u); @@ -46,20 +50,81 @@ static void busname_init(Unit *u) { n->starter_fd = -1; n->accept_fd = true; n->activating = true; + + n->timeout_usec = u->manager->default_timeout_start_usec; +} + +static void busname_unwatch_control_pid(BusName *n) { + assert(n); + + if (n->control_pid <= 0) + return; + + unit_unwatch_pid(UNIT(n), n->control_pid); + n->control_pid = 0; +} + +static void busname_free_policy(BusName *n) { + BusNamePolicy *p; + + assert(n); + + while ((p = n->policy)) { + LIST_REMOVE(policy, n->policy, p); + + free(p->name); + free(p); + } +} + +static void busname_close_fd(BusName *n) { + assert(n); + + n->starter_event_source = sd_event_source_unref(n->starter_event_source); + n->starter_fd = safe_close(n->starter_fd); } static void busname_done(Unit *u) { BusName *n = BUSNAME(u); - assert(u); + assert(n); free(n->name); n->name = NULL; + busname_free_policy(n); + busname_unwatch_control_pid(n); + busname_close_fd(n); + unit_ref_unset(&n->service); - n->event_source = sd_event_source_unref(n->event_source); - n->starter_fd = safe_close(n->starter_fd); + n->timer_event_source = sd_event_source_unref(n->timer_event_source); +} + +static int busname_arm_timer(BusName *n) { + int r; + + assert(n); + + if (n->timeout_usec <= 0) { + n->timer_event_source = sd_event_source_unref(n->timer_event_source); + return 0; + } + + if (n->timer_event_source) { + r = sd_event_source_set_time(n->timer_event_source, now(CLOCK_MONOTONIC) + n->timeout_usec); + if (r < 0) + return r; + + return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT); + } + + return sd_event_add_time( + UNIT(n)->manager->event, + &n->timer_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + n->timeout_usec, 0, + busname_dispatch_timer, n); } static int busname_add_default_default_dependencies(BusName *n) { @@ -183,6 +248,11 @@ static void busname_dump(Unit *u, FILE *f, const char *prefix) { prefix, n->name, prefix, yes_no(n->activating), prefix, yes_no(n->accept_fd)); + + if (n->control_pid > 0) + fprintf(f, + "%sControl PID: "PID_FMT"\n", + prefix, n->control_pid); } static void busname_unwatch_fd(BusName *n) { @@ -190,22 +260,12 @@ static void busname_unwatch_fd(BusName *n) { assert(n); - if (n->event_source) { - r = sd_event_source_set_enabled(n->event_source, SD_EVENT_OFF); - if (r < 0) - log_debug_unit(UNIT(n)->id, "Failed to disable event source."); - } -} - -static void busname_close_fd(BusName *n) { - assert(n); - - busname_unwatch_fd(n); - - if (n->starter_fd <= 0) + if (!n->starter_event_source) return; - n->starter_fd = safe_close(n->starter_fd); + r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_OFF); + if (r < 0) + log_debug_unit(UNIT(n)->id, "Failed to disable event source."); } static int busname_watch_fd(BusName *n) { @@ -216,10 +276,10 @@ static int busname_watch_fd(BusName *n) { if (n->starter_fd < 0) return 0; - if (n->event_source) - r = sd_event_source_set_enabled(n->event_source, SD_EVENT_ON); + if (n->starter_event_source) + r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON); else - r = sd_event_add_io(UNIT(n)->manager->event, &n->event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); + r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); if (r < 0) { log_warning_unit(UNIT(n)->id, "Failed to watch starter fd: %s", strerror(-r)); busname_unwatch_fd(n); @@ -235,10 +295,7 @@ static int busname_open_fd(BusName *n) { if (n->starter_fd >= 0) return 0; - n->starter_fd = bus_kernel_create_starter( - UNIT(n)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user", - n->name, n->activating, n->accept_fd, n->policy); - + n->starter_fd = bus_kernel_open_bus_fd(UNIT(n)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user"); if (n->starter_fd < 0) { log_warning_unit(UNIT(n)->id, "Failed to create starter fd: %s", strerror(-n->starter_fd)); return n->starter_fd; @@ -254,10 +311,15 @@ static void busname_set_state(BusName *n, BusNameState state) { old_state = n->state; n->state = state; + if (!IN_SET(state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { + n->timer_event_source = sd_event_source_unref(n->timer_event_source); + busname_unwatch_control_pid(n); + } + if (state != BUSNAME_LISTENING) busname_unwatch_fd(n); - if (!IN_SET(state, BUSNAME_LISTENING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) + if (!IN_SET(state, BUSNAME_LISTENING, BUSNAME_MAKING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) busname_close_fd(n); if (state != old_state) @@ -277,7 +339,21 @@ static int busname_coldplug(Unit *u) { if (n->deserialized_state == n->state) return 0; - if (IN_SET(n->deserialized_state, BUSNAME_LISTENING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) { + if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) { + + if (n->control_pid <= 0) + return -EBADMSG; + + r = unit_watch_pid(UNIT(n), n->control_pid); + if (r < 0) + return r; + + r = busname_arm_timer(n); + if (r < 0) + return r; + } + + if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_LISTENING, BUSNAME_REGISTERED, BUSNAME_RUNNING)) { r = busname_open_fd(n); if (r < 0) return r; @@ -293,6 +369,56 @@ static int busname_coldplug(Unit *u) { return 0; } +static int busname_make_starter(BusName *n, pid_t *_pid) { + pid_t pid; + int r; + + r = busname_arm_timer(n); + if (r < 0) + goto fail; + + /* We have to resolve the user/group names out-of-process, + * hence let's fork here. It's messy, but well, what can we + * do? */ + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + int ret; + + default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + ignore_signals(SIGPIPE, -1); + log_forget_fds(); + + r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, n->policy, n->policy_world); + if (r < 0) { + ret = EXIT_MAKE_STARTER; + goto fail_child; + } + + _exit(0); + + fail_child: + log_open(); + log_error("Failed to create starter connection at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r)); + + _exit(ret); + } + + r = unit_watch_pid(UNIT(n), pid); + if (r < 0) + goto fail; + + *_pid = pid; + return 0; + +fail: + n->timer_event_source = sd_event_source_unref(n->timer_event_source); + return r; +} + static void busname_enter_dead(BusName *n, BusNameResult f) { assert(n); @@ -302,19 +428,52 @@ static void busname_enter_dead(BusName *n, BusNameResult f) { busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); } -static void busname_enter_listening(BusName *n) { +static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f) { + KillContext kill_context = {}; int r; assert(n); - r = busname_open_fd(n); + if (f != BUSNAME_SUCCESS) + n->result = f; + + kill_context_init(&kill_context); + + r = unit_kill_context(UNIT(n), + &kill_context, + state != BUSNAME_SIGTERM, + -1, + n->control_pid, + false); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to %s: %s", UNIT(n)->id, - n->activating ? "listen on bus name" : "register policy for name", - strerror(-r)); + log_warning_unit(UNIT(n)->id, "%s failed to kill control proces: %s", UNIT(n)->id, strerror(-r)); goto fail; } + if (r > 0) { + r = busname_arm_timer(n); + if (r < 0) { + log_warning_unit(UNIT(n)->id, "%s failed to arm timer: %s", UNIT(n)->id, strerror(-r)); + goto fail; + } + + busname_set_state(n, state); + } else if (state == BUSNAME_SIGTERM) + busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_SUCCESS); + else + busname_enter_dead(n, BUSNAME_SUCCESS); + + return; + +fail: + busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); +} + +static void busname_enter_listening(BusName *n) { + int r; + + assert(n); + if (n->activating) { r = busname_watch_fd(n); if (r < 0) { @@ -328,6 +487,47 @@ static void busname_enter_listening(BusName *n) { return; +fail: + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_RESOURCES); +} + +static void busname_enter_making(BusName *n) { + int r; + + assert(n); + + r = busname_open_fd(n); + if (r < 0) + goto fail; + + if (n->policy) { + /* If there's a policy we need to resolve user/group + * names, which we can't do from PID1, hence let's + * fork. */ + busname_unwatch_control_pid(n); + + r = busname_make_starter(n, &n->control_pid); + if (r < 0) { + log_warning_unit(UNIT(n)->id, "%s failed to fork 'making' task: %s", UNIT(n)->id, strerror(-r)); + goto fail; + } + + busname_set_state(n, BUSNAME_MAKING); + } else { + /* If there's no policy then we can do everything + * directly from PID 1, hence do so. */ + + r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world); + if (r < 0) { + log_warning_unit(UNIT(n)->id, "%s failed to make starter: %s", UNIT(n)->id, strerror(-r)); + goto fail; + } + + busname_enter_listening(n); + } + + return; + fail: busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); } @@ -384,6 +584,15 @@ static int busname_start(Unit *u) { assert(n); + /* We cannot fulfill this request right now, try again later + * please! */ + if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) + return -EAGAIN; + + /* Already on it! */ + if (n->state == BUSNAME_MAKING) + return 0; + if (n->activating && UNIT_ISSET(n->service)) { Service *service; @@ -398,7 +607,7 @@ static int busname_start(Unit *u) { assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); n->result = BUSNAME_SUCCESS; - busname_enter_listening(n); + busname_enter_making(n); return 0; } @@ -407,6 +616,19 @@ static int busname_stop(Unit *u) { BusName *n = BUSNAME(u); assert(n); + + /* Already on it */ + if (IN_SET(n->state, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) + return 0; + + /* If there's already something running, we go directly into + * kill mode. */ + + if (n->state == BUSNAME_MAKING) { + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_SUCCESS); + return -EAGAIN; + } + assert(IN_SET(n->state, BUSNAME_REGISTERED, BUSNAME_LISTENING, BUSNAME_RUNNING)); busname_enter_dead(n, BUSNAME_SUCCESS); @@ -423,6 +645,9 @@ static int busname_serialize(Unit *u, FILE *f, FDSet *fds) { unit_serialize_item(u, f, "state", busname_state_to_string(n->state)); unit_serialize_item(u, f, "result", busname_result_to_string(n->result)); + if (n->control_pid > 0) + unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid); + if (n->starter_fd >= 0) { int copy; @@ -461,6 +686,13 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value, else if (f != BUSNAME_SUCCESS) n->result = f; + } else if (streq(key, "control-pid")) { + pid_t pid; + + if (parse_pid(value, &pid) < 0) + log_debug_unit(u->id, "Failed to parse control-pid value %s", value); + else + n->control_pid = pid; } else if (streq(key, "starter-fd")) { int fd; @@ -513,6 +745,88 @@ fail: return 0; } +static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { + BusName *n = BUSNAME(u); + BusNameResult f; + + assert(n); + assert(pid >= 0); + + if (pid != n->control_pid) + return; + + n->control_pid = 0; + + if (is_clean_exit(code, status, NULL)) + f = BUSNAME_SUCCESS; + else if (code == CLD_EXITED) + f = BUSNAME_FAILURE_EXIT_CODE; + else if (code == CLD_KILLED) + f = BUSNAME_FAILURE_SIGNAL; + else if (code == CLD_KILLED) + f = BUSNAME_FAILURE_CORE_DUMP; + else + assert_not_reached("Unknown sigchld code"); + + log_full_unit(f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + u->id, "%s control process exited, code=%s status=%i", + u->id, sigchld_code_to_string(code), status); + + if (f != BUSNAME_SUCCESS) + n->result = f; + + switch (n->state) { + + case BUSNAME_MAKING: + if (f == BUSNAME_SUCCESS) + busname_enter_listening(n); + else + busname_enter_signal(n, BUSNAME_SIGTERM, f); + break; + + case BUSNAME_SIGTERM: + case BUSNAME_SIGKILL: + busname_enter_dead(n, f); + break; + + default: + assert_not_reached("Uh, control process died at wrong time."); + } + + /* Notify clients about changed exit status */ + unit_add_to_dbus_queue(u); +} + +static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { + BusName *n = BUSNAME(userdata); + + assert(n); + assert(n->timer_event_source == source); + + switch (n->state) { + + case BUSNAME_MAKING: + log_warning_unit(UNIT(n)->id, "%s making timed out. Terminating.", UNIT(n)->id); + busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_TIMEOUT); + break; + + case BUSNAME_SIGTERM: + log_warning_unit(UNIT(n)->id, "%s stopping timed out. Killing.", UNIT(n)->id); + busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_FAILURE_TIMEOUT); + break; + + case BUSNAME_SIGKILL: + log_warning_unit(UNIT(n)->id, "%s still around after SIGKILL. Ignoring.", UNIT(n)->id); + busname_enter_dead(n, BUSNAME_FAILURE_TIMEOUT); + break; + + default: + assert_not_reached("Timeout at wrong time."); + } + + return 0; +} + static void busname_reset_failed(Unit *u) { BusName *n = BUSNAME(u); @@ -549,12 +863,33 @@ static void busname_trigger_notify(Unit *u, Unit *other) { busname_enter_listening(n); } +static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { + return unit_kill_common(u, who, signo, -1, BUSNAME(u)->control_pid, error); +} + +static int busname_get_timeout(Unit *u, uint64_t *timeout) { + BusName *n = BUSNAME(u); + int r; + + if (!n->timer_event_source) + return 0; + + r = sd_event_source_get_time(n->timer_event_source, timeout); + if (r < 0) + return r; + + return 1; +} + static const char* const busname_state_table[_BUSNAME_STATE_MAX] = { [BUSNAME_DEAD] = "dead", + [BUSNAME_MAKING] = "making", [BUSNAME_REGISTERED] = "registered", [BUSNAME_LISTENING] = "listening", [BUSNAME_RUNNING] = "running", - [BUSNAME_FAILED] = "failed" + [BUSNAME_SIGTERM] = "sigterm", + [BUSNAME_SIGKILL] = "sigkill", + [BUSNAME_FAILED] = "failed", }; DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState); @@ -562,6 +897,10 @@ DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState); static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_SUCCESS] = "success", [BUSNAME_FAILURE_RESOURCES] = "resources", + [BUSNAME_FAILURE_TIMEOUT] = "timeout", + [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", + [BUSNAME_FAILURE_SIGNAL] = "signal", + [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", [BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent", }; @@ -595,12 +934,18 @@ const UnitVTable busname_vtable = { .start = busname_start, .stop = busname_stop, + .kill = busname_kill, + + .get_timeout = busname_get_timeout, + .serialize = busname_serialize, .deserialize_item = busname_deserialize_item, .active_state = busname_active_state, .sub_state_to_string = busname_sub_state_to_string, + .sigchld_event = busname_sigchld_event, + .trigger_notify = busname_trigger_notify, .reset_failed = busname_reset_failed, diff --git a/src/core/busname.h b/src/core/busname.h index ab3082bb0..65d57f710 100644 --- a/src/core/busname.h +++ b/src/core/busname.h @@ -28,9 +28,12 @@ typedef struct BusNamePolicy BusNamePolicy; typedef enum BusNameState { BUSNAME_DEAD, + BUSNAME_MAKING, BUSNAME_REGISTERED, BUSNAME_LISTENING, BUSNAME_RUNNING, + BUSNAME_SIGTERM, + BUSNAME_SIGKILL, BUSNAME_FAILED, _BUSNAME_STATE_MAX, _BUSNAME_STATE_INVALID = -1 @@ -39,34 +42,18 @@ typedef enum BusNameState { typedef enum BusNameResult { BUSNAME_SUCCESS, BUSNAME_FAILURE_RESOURCES, + BUSNAME_FAILURE_TIMEOUT, + BUSNAME_FAILURE_EXIT_CODE, + BUSNAME_FAILURE_SIGNAL, + BUSNAME_FAILURE_CORE_DUMP, BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT, _BUSNAME_RESULT_MAX, _BUSNAME_RESULT_INVALID = -1 } BusNameResult; -struct BusName { - Unit meta; - - char *name; - int starter_fd; - - bool activating; - bool accept_fd; - - UnitRef service; - - BusNameState state, deserialized_state; - BusNameResult result; - - sd_event_source *event_source; - - LIST_HEAD(BusNamePolicy, policy); -}; - typedef enum BusNamePolicyType { BUSNAME_POLICY_TYPE_USER, BUSNAME_POLICY_TYPE_GROUP, - BUSNAME_POLICY_TYPE_WORLD, _BUSNAME_POLICY_TYPE_MAX, _BUSNAME_POLICY_TYPE_INVALID = -1 } BusNamePolicyType; @@ -83,14 +70,36 @@ struct BusNamePolicy { BusNamePolicyType type; BusNamePolicyAccess access; - union { - uid_t uid; - gid_t gid; - }; + char *name; LIST_FIELDS(BusNamePolicy, policy); }; +struct BusName { + Unit meta; + + char *name; + int starter_fd; + + bool activating; + bool accept_fd; + + UnitRef service; + + BusNameState state, deserialized_state; + BusNameResult result; + + usec_t timeout_usec; + + sd_event_source *starter_event_source; + sd_event_source *timer_event_source; + + pid_t control_pid; + + LIST_HEAD(BusNamePolicy, policy); + BusNamePolicyAccess policy_world; +}; + extern const UnitVTable busname_vtable; const char* busname_state_to_string(BusNameState i) _const_; diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c index 5dd3a5e1e..28f192403 100644 --- a/src/core/dbus-busname.c +++ b/src/core/dbus-busname.c @@ -30,6 +30,8 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusName const sd_bus_vtable bus_busname_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Name", "s", NULL, offsetof(BusName, name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(BusName, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(BusName, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(BusName, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Activating", "b", bus_property_get_bool, offsetof(BusName, activating), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AcceptFileDescriptors", "b", bus_property_get_bool, offsetof(BusName, accept_fd), SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 55fbe9b52..2d98bba41 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -263,7 +263,7 @@ BusName.Activating, config_parse_bool, 0, BusName.Service, config_parse_busname_service, 0, 0 BusName.AllowUser, config_parse_bus_policy, 0, 0 BusName.AllowGroup, config_parse_bus_policy, 0, 0 -BusName.AllowWorld, config_parse_bus_policy, 0, 0 +BusName.AllowWorld, config_parse_bus_policy_world, 0, offsetof(BusName, policy_world) BusName.SELinuxContext, config_parse_exec_selinux_context, 0, 0 BusName.AcceptFileDescriptors, config_parse_bool, 0, offsetof(BusName, accept_fd) m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 54d3af1a9..0f5e71b8d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1656,6 +1656,8 @@ int config_parse_busname_service( return 0; } +DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, busname_policy_access, BusNamePolicyAccess, "Failed to parse bus name policy access"); + int config_parse_bus_policy( const char *unit, const char *filename, @@ -1672,7 +1674,6 @@ int config_parse_bus_policy( _cleanup_free_ char *id_str = NULL; BusName *busname = data; char *access_str; - int r; assert(filename); assert(lvalue); @@ -1687,8 +1688,6 @@ int config_parse_bus_policy( p->type = BUSNAME_POLICY_TYPE_USER; else if (streq(lvalue, "AllowGroup")) p->type = BUSNAME_POLICY_TYPE_GROUP; - else if (streq(lvalue, "AllowWorld")) - p->type = BUSNAME_POLICY_TYPE_WORLD; else assert_not_reached("Unknown lvalue"); @@ -1696,43 +1695,25 @@ int config_parse_bus_policy( if (!id_str) return log_oom(); - if (p->type != BUSNAME_POLICY_TYPE_WORLD) { - access_str = strchr(id_str, ' '); - if (!access_str) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy value '%s'", rvalue); - return 0; - } - - *access_str = '\0'; - access_str++; - - if (p->type == BUSNAME_POLICY_TYPE_USER) { - const char *user = id_str; - - r = get_user_creds(&user, &p->uid, NULL, NULL, NULL); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Unable to parse uid from '%s'", id_str); - return 0; - } - } else { - const char *group = id_str; - - r = get_group_creds(&group, &p->gid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -errno, "Unable to parse gid from '%s'", id_str); - return 0; - } - } - } else { - access_str = id_str; + access_str = strpbrk(id_str, WHITESPACE); + if (!access_str) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy value '%s'", rvalue); + return 0; } + *access_str = '\0'; + access_str++; + access_str += strspn(access_str, WHITESPACE); + p->access = busname_policy_access_from_string(access_str); if (p->access < 0) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy access type '%s'", access_str); return 0; } + p->name = id_str; + id_str = NULL; + LIST_PREPEND(policy, busname->policy, p); p = NULL; diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 1c21c466d..9a1d7c5aa 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -66,6 +66,7 @@ int config_parse_socket_service(const char *unit, const char *filename, unsigned int config_parse_service_sockets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_busname_service(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_bus_policy_world(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_condition_path(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/socket.c b/src/core/socket.c index e834b045a..9c4943e0b 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1173,11 +1173,12 @@ static void socket_unwatch_fds(Socket *s) { if (p->fd < 0) continue; - if (p->event_source) { - r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); - if (r < 0) - log_debug_unit(UNIT(s)->id, "Failed to disable event source."); - } + if (!p->event_source) + continue; + + r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); + if (r < 0) + log_debug_unit(UNIT(s)->id, "Failed to disable event source."); } } @@ -1843,6 +1844,7 @@ static int socket_start(Unit *u) { SOCKET_FINAL_SIGKILL)) return -EAGAIN; + /* Already on it! */ if (IN_SET(s->state, SOCKET_START_PRE, SOCKET_START_CHOWN, @@ -1871,8 +1873,7 @@ static int socket_start(Unit *u) { #ifdef HAVE_SYSV_COMPAT if (service->is_sysv) { - log_error_unit(u->id, - "Using SysV services for socket activation is not supported. Refusing."); + log_error_unit(u->id, "Using SysV services for socket activation is not supported. Refusing."); return -ENOENT; } #endif @@ -2282,7 +2283,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { else if (code == CLD_DUMPED) f = SOCKET_FAILURE_CORE_DUMP; else - assert_not_reached("Unknown code"); + assert_not_reached("Unknown sigchld code"); if (s->control_command) { exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index 8bab6ad1b..f49fb5b39 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -1305,7 +1305,7 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { make->size += ALIGN8(n->size); n = KDBUS_ITEM_NEXT(n); - sprintf(n->str, UID_FMT"-%s", getuid(), name); + sprintf(n->str, UID_FMT "-%s", getuid(), name); n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; n->type = KDBUS_ITEM_MAKE_NAME; make->size += ALIGN8(n->size); @@ -1339,72 +1339,107 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { return fd; } -static void bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item) { +static int bus_kernel_translate_access(BusNamePolicyAccess access) { + assert(access >= 0); + assert(access < _BUSNAME_POLICY_ACCESS_MAX); + + switch (access) { + + case BUSNAME_POLICY_ACCESS_SEE: + return KDBUS_POLICY_SEE; + + case BUSNAME_POLICY_ACCESS_TALK: + return KDBUS_POLICY_TALK; + + case BUSNAME_POLICY_ACCESS_OWN: + return KDBUS_POLICY_OWN; + + default: + assert_not_reached("Unknown policy access"); + } +} + +static int bus_kernel_translate_policy(const BusNamePolicy *policy, struct kdbus_item *item) { + int r; assert(policy); assert(item); switch (policy->type) { - case BUSNAME_POLICY_TYPE_USER: + case BUSNAME_POLICY_TYPE_USER: { + const char *user = policy->name; + uid_t uid; + + r = get_user_creds(&user, &uid, NULL, NULL, NULL); + if (r < 0) + return r; + item->policy_access.type = KDBUS_POLICY_ACCESS_USER; - item->policy_access.id = policy->uid; + item->policy_access.id = uid; break; + } - case BUSNAME_POLICY_TYPE_GROUP: - item->policy_access.type = KDBUS_POLICY_ACCESS_GROUP; - item->policy_access.id = policy->gid; - break; + case BUSNAME_POLICY_TYPE_GROUP: { + const char *group = policy->name; + gid_t gid; - case BUSNAME_POLICY_TYPE_WORLD: - item->policy_access.type = KDBUS_POLICY_ACCESS_WORLD; + r = get_group_creds(&group, &gid); + if (r < 0) + return r; + + item->policy_access.type = KDBUS_POLICY_ACCESS_GROUP; + item->policy_access.id = gid; break; + } default: assert_not_reached("Unknown policy type"); } - switch (policy->access) { + item->policy_access.access = bus_kernel_translate_access(policy->access); - case BUSNAME_POLICY_ACCESS_SEE: - item->policy_access.access = KDBUS_POLICY_SEE; - break; + return 0; +} - case BUSNAME_POLICY_ACCESS_TALK: - item->policy_access.access = KDBUS_POLICY_TALK; - break; +int bus_kernel_open_bus_fd(const char *bus) { + char *p; + int fd; - case BUSNAME_POLICY_ACCESS_OWN: - item->policy_access.access = KDBUS_POLICY_OWN; - break; + p = alloca(strlen("/dev/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1); + sprintf(p, "/dev/kdbus/" UID_FMT "-%s/bus", getuid(), bus); - default: - assert_not_reached("Unknown policy access"); - } + fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + return fd; } -int bus_kernel_create_starter(const char *bus, const char *name, bool activating, bool accept_fd, BusNamePolicy *policy) { +int bus_kernel_make_starter( + int fd, + const char *name, + bool activating, + bool accept_fd, + BusNamePolicy *policy, + BusNamePolicyAccess world_policy) { + struct kdbus_cmd_hello *hello; struct kdbus_item *n; size_t policy_cnt = 0; BusNamePolicy *po; size_t size; - char *p; - int fd; + int r; - assert(bus); + assert(fd >= 0); assert(name); - p = alloca(strlen("/dev/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1); - sprintf(p, "/dev/kdbus/"UID_FMT"-%s/bus", getuid(), bus); - - fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - LIST_FOREACH(policy, po, policy) policy_cnt++; + if (world_policy >= 0) + policy_cnt++; + size = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + ALIGN8(offsetof(struct kdbus_item, str) + strlen(name) + 1) + policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); @@ -1420,10 +1455,21 @@ int bus_kernel_create_starter(const char *bus, const char *name, bool activating LIST_FOREACH(policy, po, policy) { n->type = KDBUS_ITEM_POLICY_ACCESS; n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); - bus_kernel_translate_policy(po, n); + + r = bus_kernel_translate_policy(po, n); + if (r < 0) + return r; + n = KDBUS_ITEM_NEXT(n); } + if (world_policy >= 0) { + n->type = KDBUS_ITEM_POLICY_ACCESS; + n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); + n->policy_access.type = KDBUS_POLICY_ACCESS_WORLD; + n->policy_access.access = bus_kernel_translate_access(world_policy); + } + hello->size = size; hello->conn_flags = (activating ? KDBUS_HELLO_ACTIVATOR : KDBUS_HELLO_POLICY_HOLDER) | @@ -1431,23 +1477,17 @@ int bus_kernel_create_starter(const char *bus, const char *name, bool activating hello->pool_size = KDBUS_POOL_SIZE; hello->attach_flags = _KDBUS_ATTACH_ALL; - if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) { - safe_close(fd); + if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) return -errno; - } /* The higher 32bit of both flags fields are considered * 'incompatible flags'. Refuse them all for now. */ if (hello->bus_flags > 0xFFFFFFFFULL || - hello->conn_flags > 0xFFFFFFFFULL) { - safe_close(fd); + hello->conn_flags > 0xFFFFFFFFULL) return -ENOTSUP; - } - if (!bloom_validate_parameters((size_t) hello->bloom.size, (unsigned) hello->bloom.n_hash)) { - safe_close(fd); + if (!bloom_validate_parameters((size_t) hello->bloom.size, (unsigned) hello->bloom.n_hash)) return -ENOTSUP; - } return fd; } @@ -1505,17 +1545,13 @@ int bus_kernel_create_domain(const char *name, char **s) { int bus_kernel_create_monitor(const char *bus) { struct kdbus_cmd_hello *hello; - char *p; int fd; assert(bus); - p = alloca(strlen("/dev/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1); - sprintf(p, "/dev/kdbus/"UID_FMT"-%s/bus", getuid(), bus); - - fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + fd = bus_kernel_open_bus_fd(bus); if (fd < 0) - return -errno; + return fd; hello = alloca0(sizeof(struct kdbus_cmd_hello)); hello->size = sizeof(struct kdbus_cmd_hello); diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h index 4ef26fce6..2fe2495a6 100644 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ b/src/libsystemd/sd-bus/bus-kernel.h @@ -65,9 +65,11 @@ int bus_kernel_take_fd(sd_bus *b); int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call); int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority); +int bus_kernel_open_bus_fd(const char *bus); +int bus_kernel_make_starter(int fd, const char *name, bool activating, bool accept_fd, BusNamePolicy *policy, BusNamePolicyAccess world_policy); + int bus_kernel_create_bus(const char *name, bool world, char **s); int bus_kernel_create_domain(const char *name, char **s); -int bus_kernel_create_starter(const char *bus, const char *name, bool activating, bool accept_fd, BusNamePolicy *policy); int bus_kernel_create_monitor(const char *bus); int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *allocated); diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c index 0a1034d8e..ce1f1bde6 100644 --- a/src/shared/exit-status.c +++ b/src/shared/exit-status.c @@ -145,6 +145,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { case EXIT_CHOWN: return "CHOWN"; + + case EXIT_MAKE_STARTER: + return "MAKE_STARTER"; } } diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h index 0104c8b12..57d066f15 100644 --- a/src/shared/exit-status.h +++ b/src/shared/exit-status.h @@ -75,6 +75,7 @@ typedef enum ExitStatus { EXIT_APPARMOR_PROFILE, EXIT_ADDRESS_FAMILIES, EXIT_RUNTIME_DIRECTORY, + EXIT_MAKE_STARTER, EXIT_CHOWN, } ExitStatus;