logind: make "self" and "auto" magic strings when operating on seats + sessions

Most of the operations one can do on sessions so far accepted an empty
session name as a shortcut for the caller's session. This is quite
useful traditionally, but much less useful than it used to be, since
most user code now (rightfully) runs in --user context, not in a
session.

With this change we tweak the logic a bit: we introduce the two special
session and seat names "self" and "auto". The former refers to the
session/seat the client is in, and is hence mostly equivalent to te
empty string "" as before. However, the latter refers to the
session/seat the client is in if that exists, with a fallback of the
user's display session if not. Clients can hence reference "auto"
instead of the empty string if they really don't want to think much
about sessions.

Why "self" btw? Previously, we'd already expose a special dbus object
with the path /org/freedesktop/login1/session/self (and similar for the
seat), matching what the empty string did for bus calls that took a
session name. With this scheme we reuse this identifier and introduce
"auto" in a similar way.

Of course this means real-life seats and sessions can never be named
"self" or "auto", but they aren't anyway: valid seat names have to start
with "seat" anyway, and sessions are generated server-side as either a
numeric value or "c" suffixed with a counter ID.

Fixes: #12399
This commit is contained in:
Lennart Poettering 2019-04-28 17:55:36 +02:00
parent 469df514c7
commit 3b92c086a8
5 changed files with 179 additions and 87 deletions

View File

@ -46,47 +46,78 @@
#include "utmp-wtmp.h"
#include "virt.h"
static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) {
static int get_sender_session(
Manager *m,
sd_bus_message *message,
bool consult_display,
sd_bus_error *error,
Session **ret) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
Session *session = NULL;
const char *name;
Session *session;
int r;
/* Get client login session. This is not what you are looking for these days,
* as apps may instead belong to a user service unit. This includes terminal
* emulators and hence command-line apps. */
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
/* Acquire the sender's session. This first checks if the sending process is inside a session itself,
* and returns that. If not and 'consult_display' is true, this returns the display session of the
* owning user of the caller. */
r = sd_bus_query_sender_creds(message,
SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT|
(consult_display ? SD_BUS_CREDS_OWNER_UID : 0), &creds);
if (r < 0)
return r;
r = sd_bus_creds_get_session(creds, &name);
if (r == -ENXIO)
goto err_no_session;
if (r < 0)
return r;
if (r < 0) {
if (r != -ENXIO)
return r;
if (consult_display) {
uid_t uid;
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r < 0) {
if (r != -ENXIO)
return r;
} else {
User *user;
user = hashmap_get(m->users, UID_TO_PTR(uid));
if (user)
session = user->display;
}
}
} else
session = hashmap_get(m->sessions, name);
session = hashmap_get(m->sessions, name);
if (!session)
goto err_no_session;
return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
consult_display ?
"Caller does not belong to any known session and doesn't own any suitable session." :
"Caller does not belong to any known session.");
*ret = session;
return 0;
err_no_session:
return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
"Caller does not belong to any known session");
}
int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
int manager_get_session_from_creds(
Manager *m,
sd_bus_message *message,
const char *name,
sd_bus_error *error,
Session **ret) {
Session *session;
assert(m);
assert(message);
assert(ret);
if (isempty(name))
return get_sender_session(m, message, error, ret);
if (SEAT_IS_SELF(name)) /* the caller's own session */
return get_sender_session(m, message, false, error, ret);
if (SEAT_IS_AUTO(name)) /* The caller's own session if they have one, otherwise their user's display session */
return get_sender_session(m, message, true, error, ret);
session = hashmap_get(m->sessions, name);
if (!session)
@ -97,7 +128,6 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch
}
static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *error, User **ret) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t uid;
User *user;
@ -109,21 +139,20 @@ static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *er
return r;
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r == -ENXIO)
goto err_no_user;
if (r < 0)
return r;
if (r < 0) {
if (r != -ENXIO)
return r;
user = NULL;
} else
user = hashmap_get(m->users, UID_TO_PTR(uid));
user = hashmap_get(m->users, UID_TO_PTR(uid));
if (!user)
goto err_no_user;
return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
"Caller does not belong to any logged in or lingering user");
*ret = user;
return 0;
err_no_user:
return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
"Caller does not belong to any logged in user or lingering user");
}
int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) {
@ -145,7 +174,13 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid,
return 0;
}
int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) {
int manager_get_seat_from_creds(
Manager *m,
sd_bus_message *message,
const char *name,
sd_bus_error *error,
Seat **ret) {
Seat *seat;
int r;
@ -153,16 +188,17 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
assert(message);
assert(ret);
if (isempty(name)) {
if (SEAT_IS_SELF(name) || SEAT_IS_AUTO(name)) {
Session *session;
r = manager_get_session_from_creds(m, message, NULL, error, &session);
/* Use these special seat names as session names */
r = manager_get_session_from_creds(m, message, name, error, &session);
if (r < 0)
return r;
seat = session->seat;
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat.");
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session '%s' has no seat.", session->id);
} else {
seat = hashmap_get(m->seats, name);
if (!seat)
@ -830,6 +866,10 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
} while (hashmap_get(m->sessions, id));
}
/* The generated names should not clash with 'auto' or 'self' */
assert(!SESSION_IS_SELF(id));
assert(!SESSION_IS_AUTO(id));
/* If we are not watching utmp already, try again */
manager_reconnect_utmp(m);
@ -990,8 +1030,7 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda
assert(message);
assert(m);
/* Same as ActivateSession() but refuses to work if
* the seat doesn't match */
/* Same as ActivateSession() but refuses to work if the seat doesn't match */
r = sd_bus_message_read(message, "ss", &session_name, &seat_name);
if (r < 0)

View File

@ -255,7 +255,10 @@ const sd_bus_vtable seat_vtable[] = {
};
int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
_cleanup_free_ char *e = NULL;
sd_bus_message *message;
Manager *m = userdata;
const char *p;
Seat *seat;
int r;
@ -265,32 +268,25 @@ int seat_object_find(sd_bus *bus, const char *path, const char *interface, void
assert(found);
assert(m);
if (streq(path, "/org/freedesktop/login1/seat/self")) {
sd_bus_message *message;
p = startswith(path, "/org/freedesktop/login1/seat/");
if (!p)
return 0;
message = sd_bus_get_current_message(bus);
if (!message)
return 0;
e = bus_label_unescape(p);
if (!e)
return -ENOMEM;
r = manager_get_seat_from_creds(m, message, NULL, error, &seat);
if (r < 0)
return r;
} else {
_cleanup_free_ char *e = NULL;
const char *p;
message = sd_bus_get_current_message(bus);
if (!message)
return 0;
p = startswith(path, "/org/freedesktop/login1/seat/");
if (!p)
return 0;
e = bus_label_unescape(p);
if (!e)
return -ENOMEM;
seat = hashmap_get(m->seats, e);
if (!seat)
return 0;
r = manager_get_seat_from_creds(m, message, e, error, &seat);
if (r == -ENXIO) {
sd_bus_error_free(error);
return 0;
}
if (r < 0)
return r;
*found = seat;
return 1;
@ -335,25 +331,47 @@ int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
message = sd_bus_get_current_message(bus);
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *name;
Session *session;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
if (r >= 0) {
bool may_auto = false;
const char *name;
r = sd_bus_creds_get_session(creds, &name);
if (r >= 0) {
Session *session;
session = hashmap_get(m->sessions, name);
if (session && session->seat) {
r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
if (r < 0)
return r;
may_auto = true;
}
}
if (!may_auto) {
uid_t uid;
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r >= 0) {
User *user;
user = hashmap_get(m->users, UID_TO_PTR(uid));
may_auto = user && user->display && user->display->seat;
}
}
if (may_auto) {
r = strv_extend(&l, "/org/freedesktop/login1/seat/auto");
if (r < 0)
return r;
}
}
}
*nodes = TAKE_PTR(l);
return 1;
}

View File

@ -77,3 +77,11 @@ int seat_send_signal(Seat *s, bool new_seat);
int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_;
int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
static inline bool SEAT_IS_SELF(const char *name) {
return isempty(name) || streq(name, "self");
}
static inline bool SEAT_IS_AUTO(const char *name) {
return streq_ptr(name, "auto");
}

View File

@ -17,6 +17,7 @@
#include "signal-util.h"
#include "stat-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
static int property_get_user(
@ -583,8 +584,11 @@ const sd_bus_vtable session_vtable[] = {
};
int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
_cleanup_free_ char *e = NULL;
sd_bus_message *message;
Manager *m = userdata;
Session *session;
const char *p;
int r;
assert(bus);
@ -593,32 +597,25 @@ int session_object_find(sd_bus *bus, const char *path, const char *interface, vo
assert(found);
assert(m);
if (streq(path, "/org/freedesktop/login1/session/self")) {
sd_bus_message *message;
p = startswith(path, "/org/freedesktop/login1/session/");
if (!p)
return 0;
message = sd_bus_get_current_message(bus);
if (!message)
return 0;
e = bus_label_unescape(p);
if (!e)
return -ENOMEM;
r = manager_get_session_from_creds(m, message, NULL, error, &session);
if (r < 0)
return r;
} else {
_cleanup_free_ char *e = NULL;
const char *p;
message = sd_bus_get_current_message(bus);
if (!message)
return 0;
p = startswith(path, "/org/freedesktop/login1/session/");
if (!p)
return 0;
e = bus_label_unescape(p);
if (!e)
return -ENOMEM;
session = hashmap_get(m->sessions, e);
if (!session)
return 0;
r = manager_get_session_from_creds(m, message, e, error, &session);
if (r == -ENXIO) {
sd_bus_error_free(error);
return 0;
}
if (r < 0)
return r;
*found = session;
return 1;
@ -663,10 +660,12 @@ int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
message = sd_bus_get_current_message(bus);
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *name;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
if (r >= 0) {
bool may_auto = false;
const char *name;
r = sd_bus_creds_get_session(creds, &name);
if (r >= 0) {
session = hashmap_get(m->sessions, name);
@ -674,13 +673,32 @@ int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
r = strv_extend(&l, "/org/freedesktop/login1/session/self");
if (r < 0)
return r;
may_auto = true;
}
}
if (!may_auto) {
uid_t uid;
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r >= 0) {
User *user;
user = hashmap_get(m->users, UID_TO_PTR(uid));
may_auto = user && user->display;
}
}
if (may_auto) {
r = strv_extend(&l, "/org/freedesktop/login1/session/auto");
if (r < 0)
return r;
}
}
}
*nodes = TAKE_PTR(l);
return 1;
}

View File

@ -7,6 +7,7 @@ typedef enum KillWho KillWho;
#include "list.h"
#include "login-util.h"
#include "logind-user.h"
#include "string-util.h"
typedef enum SessionState {
SESSION_OPENING, /* Session scope is being created */
@ -183,3 +184,11 @@ int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_
int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
static inline bool SESSION_IS_SELF(const char *name) {
return isempty(name) || streq(name, "self");
}
static inline bool SESSION_IS_AUTO(const char *name) {
return streq_ptr(name, "auto");
}