[systemd-devel] [PATCH v2 03/10] logind: add session controllers
Lennart Poettering
lennart at poettering.net
Tue Sep 17 09:29:00 PDT 2013
On Tue, 17.09.13 17:39, David Herrmann (dh.herrmann at gmail.com) wrote:
Please rename the bus calls to TakeControl()/ReleaseControl() (as
discussed at the hackfest). Otherwise patch #2 and #3 look good to merge!
> A session usually has only a single compositor or other application that
> controls graphics and input devices on it. To avoid multiple applications
> from hijacking each other's devices or even using the devices in parallel,
> we add session controllers.
>
> A session controller is an application that manages a session. Specific
> API calls may be limited to controllers to avoid others from getting
> unprivileged access to restricted resources. A session becomes a
> controller by calling the RequestControl() dbus API call. It can drop it
> via ReleaseControl().
>
> logind tracks bus-names to release the controller once an application
> closes the bus. We use the new bus-name tracking to do that. Note that
> during ReleaseControl() we need to check whether some other session also
> tracks the name before we remove it from the bus-name tracking list.
>
> Currently, we only allow one controller at a time. However, the public API
> does not enforce this restriction. So if it makes sense, we can allow
> multiple controllers in parallel later. Or we can add a "scope" parameter,
> which allows a different controller for graphics-devices, sound-devices
> and whatever you want.
> Note that currently you get -EBUSY if there is already a controller. You
> can force the RequestControl() call (root-only) to drop the current
> controller and recover the session during an emergency. To recover a seat,
> this is not needed, though. You can simply create a new session or
> force-activate it.
>
> To become a session controller, a dbus caller must either be root or the
> same user as the user of the session. This allows us to run a session
> compositor as user and we no longer need any CAP_SYS_ADMIN.
> ---
> src/login/logind-dbus.c | 8 +++++++
> src/login/logind-session-dbus.c | 42 ++++++++++++++++++++++++++++++++++++
> src/login/logind-session.c | 48 +++++++++++++++++++++++++++++++++++++++++
> src/login/logind-session.h | 6 ++++++
> src/login/logind.c | 10 +++++++++
> 5 files changed, 114 insertions(+)
>
> diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
> index 5decf81..113a2b7 100644
> --- a/src/login/logind-dbus.c
> +++ b/src/login/logind-dbus.c
> @@ -2473,8 +2473,16 @@ DBusHandlerResult bus_message_filter(
> goto finish;
> }
>
> + /* drop all controllers owned by this name */
> if (*old && !*new && (key = hashmap_remove(m->busnames, old))) {
> + Session *session;
> + Iterator i;
> +
> free(key);
> +
> + HASHMAP_FOREACH(session, m->sessions, i)
> + if (session_is_controller(session, old))
> + session_drop_controller(session);
> }
> }
>
> diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
> index 2cc4d85..b8b32cd 100644
> --- a/src/login/logind-session-dbus.c
> +++ b/src/login/logind-session-dbus.c
> @@ -40,6 +40,10 @@
> " <arg name=\"who\" type=\"s\"/>\n" \
> " <arg name=\"signal\" type=\"s\"/>\n" \
> " </method>\n" \
> + " <method name=\"RequestControl\"/>\n" \
> + " <arg name=\"force\" type=\"b\"/>\n" \
> + " </method>\n" \
> + " <method name=\"DropControl\"/>\n" \
> " <signal name=\"Lock\"/>\n" \
> " <signal name=\"Unlock\"/>\n" \
> " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
> @@ -366,6 +370,44 @@ static DBusHandlerResult session_message_dispatch(
> if (!reply)
> goto oom;
>
> + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "RequestControl")) {
> + dbus_bool_t force;
> + unsigned long ul;
> +
> + if (!dbus_message_get_args(
> + message,
> + &error,
> + DBUS_TYPE_BOOLEAN, &force,
> + DBUS_TYPE_INVALID))
> + return bus_send_error_reply(connection, message, &error, -EINVAL);
> +
> + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
> + if (ul == (unsigned long) -1)
> + return bus_send_error_reply(connection, message, &error, -EIO);
> +
> + if (ul != 0 && (force || ul != s->user->uid))
> + return bus_send_error_reply(connection, message, NULL, -EPERM);
> +
> + r = session_set_controller(s, bus_message_get_sender_with_fallback(message), force);
> + if (r < 0)
> + return bus_send_error_reply(connection, message, NULL, r);
> +
> + reply = dbus_message_new_method_return(message);
> + if (!reply)
> + goto oom;
> +
> + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "DropControl")) {
> + const char *sender = bus_message_get_sender_with_fallback(message);
> +
> + if (!session_is_controller(s, sender))
> + return bus_send_error_reply(connection, message, NULL, -EPERM);
> +
> + session_drop_controller(s);
> +
> + reply = dbus_message_new_method_return(message);
> + if (!reply)
> + goto oom;
> +
> } else {
> const BusBoundProperties bps[] = {
> { "org.freedesktop.login1.Session", bus_login_session_properties, s },
> diff --git a/src/login/logind-session.c b/src/login/logind-session.c
> index 2d22a68..fe5fa27 100644
> --- a/src/login/logind-session.c
> +++ b/src/login/logind-session.c
> @@ -73,6 +73,8 @@ void session_free(Session *s) {
> if (s->in_gc_queue)
> LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
>
> + session_drop_controller(s);
> +
> if (s->user) {
> LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
>
> @@ -918,6 +920,52 @@ int session_kill(Session *s, KillWho who, int signo) {
> return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
> }
>
> +bool session_is_controller(Session *s, const char *sender)
> +{
> + assert(s);
> +
> + return streq_ptr(s->controller, sender);
> +}
> +
> +int session_set_controller(Session *s, const char *sender, bool force) {
> + char *t;
> + int r;
> +
> + assert(s);
> + assert(sender);
> +
> + if (session_is_controller(s, sender))
> + return 0;
> + if (s->controller && !force)
> + return -EBUSY;
> +
> + t = strdup(sender);
> + if (!t)
> + return -ENOMEM;
> +
> + r = manager_watch_busname(s->manager, sender);
> + if (r) {
> + free(t);
> + return r;
> + }
> +
> + session_drop_controller(s);
> +
> + s->controller = t;
> + return 0;
> +}
> +
> +void session_drop_controller(Session *s) {
> + assert(s);
> +
> + if (!s->controller)
> + return;
> +
> + manager_drop_busname(s->manager, s->controller);
> + free(s->controller);
> + s->controller = NULL;
> +}
> +
> static const char* const session_state_table[_SESSION_STATE_MAX] = {
> [SESSION_OPENING] = "opening",
> [SESSION_ONLINE] = "online",
> diff --git a/src/login/logind-session.h b/src/login/logind-session.h
> index 9cf6485..839fb23 100644
> --- a/src/login/logind-session.h
> +++ b/src/login/logind-session.h
> @@ -106,6 +106,8 @@ struct Session {
>
> DBusMessage *create_message;
>
> + char *controller;
> +
> LIST_FIELDS(Session, sessions_by_user);
> LIST_FIELDS(Session, sessions_by_seat);
>
> @@ -154,3 +156,7 @@ SessionClass session_class_from_string(const char *s) _pure_;
>
> const char *kill_who_to_string(KillWho k) _const_;
> KillWho kill_who_from_string(const char *s) _pure_;
> +
> +bool session_is_controller(Session *s, const char *sender);
> +int session_set_controller(Session *s, const char *sender, bool force);
> +void session_drop_controller(Session *s);
> diff --git a/src/login/logind.c b/src/login/logind.c
> index 89e4eee..c99c284 100644
> --- a/src/login/logind.c
> +++ b/src/login/logind.c
> @@ -391,11 +391,21 @@ int manager_watch_busname(Manager *m, const char *name) {
> }
>
> void manager_drop_busname(Manager *m, const char *name) {
> + Session *session;
> + Iterator i;
> char *key;
>
> assert(m);
> assert(name);
>
> + if (!hashmap_get(m->busnames, name))
> + return;
> +
> + /* keep it if the name still owns a controller */
> + HASHMAP_FOREACH(session, m->sessions, i)
> + if (session_is_controller(session, name))
> + return;
> +
> key = hashmap_remove(m->busnames, name);
> if (key)
> free(key);
Lennart
--
Lennart Poettering - Red Hat, Inc.
More information about the systemd-devel
mailing list