[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