From 49b3ea6197f88e98bacf22d7c46294a21a2bdc8d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 13 Feb 2017 19:57:03 +0000 Subject: sysdeps: Add accessor for a list of transient service directories These directories can be used by service managers like `systemd --user` and its generators, or by session infrastructure like gnome-session, to synthesize D-Bus service files at runtime from some more canonical source of information. The intention is that this is in the XDG_RUNTIME_DIR as defined by the freedesktop.org Base Directory Specification, which is private to the user, and has a lifetime equal to the union of all the user's concurrent login sessions. This directory is provided on Linux systems that have systemd-logind and pam_systemd, on other systems with PAM that have pam-xdg-support (which has been abandoned by Ubuntu in favour of logind, but could be forked by non-systemd environments that are interested in this functionality), or any compatible reimplementation. In practice this is most likely to be useful on systems that run `dbus-daemon --session` from `systemd --user`. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=99825 Reviewed-by: Philip Withnall Signed-off-by: Simon McVittie --- dbus/dbus-sysdeps-util-unix.c | 154 ++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps-util-win.c | 15 ++++ dbus/dbus-sysdeps.h | 2 + 3 files changed, 171 insertions(+) diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c index d9e8c4fc..5d08c0ba 100644 --- a/dbus/dbus-sysdeps-util-unix.c +++ b/dbus/dbus-sysdeps-util-unix.c @@ -26,6 +26,7 @@ #include "dbus-sysdeps.h" #include "dbus-sysdeps-unix.h" #include "dbus-internals.h" +#include "dbus-list.h" #include "dbus-pipe.h" #include "dbus-protocol.h" #include "dbus-string.h" @@ -1188,9 +1189,162 @@ _dbus_replace_install_prefix (DBusString *path) return TRUE; } +static dbus_bool_t +ensure_owned_directory (const char *label, + const DBusString *string, + dbus_bool_t create, + DBusError *error) +{ + const char *dir = _dbus_string_get_const_data (string); + struct stat buf; + + if (create && !_dbus_ensure_directory (string, error)) + return FALSE; + + /* + * The stat()-based checks in this function are to protect against + * mistakes, not malice. We are working in a directory that is meant + * to be trusted; but if a user has used `su` or similar to escalate + * their privileges without correctly clearing the environment, the + * XDG_RUNTIME_DIR in the environment might still be the user's + * and not root's. We don't want to write root-owned files into that + * directory, so just warn and don't provide support for transient + * services in that case. + * + * In particular, we use stat() and not lstat() so that if we later + * decide to use a different directory name for transient services, + * we can drop in a compatibility symlink without breaking older + * libdbus. + */ + + if (stat (dir, &buf) != 0) + { + int saved_errno = errno; + + dbus_set_error (error, _dbus_error_from_errno (saved_errno), + "%s \"%s\" not available: %s", label, dir, + _dbus_strerror (saved_errno)); + return FALSE; + } + + if (!S_ISDIR (buf.st_mode)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, "%s \"%s\" is not a directory", + label, dir); + return FALSE; + } + + if (buf.st_uid != geteuid ()) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "%s \"%s\" is owned by uid %ld, not our uid %ld", + label, dir, (long) buf.st_uid, (long) geteuid ()); + return FALSE; + } + + /* This is just because we have the stat() results already, so we might + * as well check opportunistically. */ + if ((S_IWOTH | S_IWGRP) & buf.st_mode) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "%s \"%s\" can be written by others (mode 0%o)", + label, dir, buf.st_mode); + return FALSE; + } + + return TRUE; +} + #define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" #define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" +/** + * Returns the standard directories for a session bus to look for + * transient service activation files. + * + * @param dirs the directory list we are returning + * @returns #FALSE on error + */ +dbus_bool_t +_dbus_set_up_transient_session_servicedirs (DBusList **dirs, + DBusError *error) +{ + const char *xdg_runtime_dir; + DBusString services; + DBusString dbus1; + DBusString xrd; + dbus_bool_t ret = FALSE; + char *data = NULL; + + if (!_dbus_string_init (&dbus1)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&services)) + { + _dbus_string_free (&dbus1); + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_init (&xrd)) + { + _dbus_string_free (&dbus1); + _dbus_string_free (&services); + _DBUS_SET_OOM (error); + return FALSE; + } + + xdg_runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR"); + + /* Not an error, we just can't have transient session services */ + if (xdg_runtime_dir == NULL) + { + _dbus_verbose ("XDG_RUNTIME_DIR is unset: transient session services " + "not available here\n"); + ret = TRUE; + goto out; + } + + if (!_dbus_string_append (&xrd, xdg_runtime_dir) || + !_dbus_string_append_printf (&dbus1, "%s/dbus-1", + xdg_runtime_dir) || + !_dbus_string_append_printf (&services, "%s/dbus-1/services", + xdg_runtime_dir)) + { + _DBUS_SET_OOM (error); + goto out; + } + + if (!ensure_owned_directory ("XDG_RUNTIME_DIR", &xrd, FALSE, error) || + !ensure_owned_directory ("XDG_RUNTIME_DIR subdirectory", &dbus1, TRUE, + error) || + !ensure_owned_directory ("XDG_RUNTIME_DIR subdirectory", &services, + TRUE, error)) + goto out; + + if (!_dbus_string_steal_data (&services, &data) || + !_dbus_list_append (dirs, data)) + { + _DBUS_SET_OOM (error); + goto out; + } + + _dbus_verbose ("Transient service directory is %s\n", data); + /* Ownership was transferred to @dirs */ + data = NULL; + ret = TRUE; + +out: + _dbus_string_free (&dbus1); + _dbus_string_free (&services); + _dbus_string_free (&xrd); + dbus_free (data); + return ret; +} + /** * Returns the standard directories for a session bus to look for service * activation files diff --git a/dbus/dbus-sysdeps-util-win.c b/dbus/dbus-sysdeps-util-win.c index eb860f4d..923fe3c5 100644 --- a/dbus/dbus-sysdeps-util-win.c +++ b/dbus/dbus-sysdeps-util-win.c @@ -1479,6 +1479,21 @@ _dbus_replace_install_prefix (DBusString *path) #define DBUS_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" #define DBUS_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" +/** + * Returns the standard directories for a session bus to look for + * transient service activation files. On Windows, there are none. + * + * @param dirs the directory list we are returning + * @returns #TRUE + */ +dbus_bool_t +_dbus_set_up_transient_session_servicedirs (DBusList **dirs, + DBusError *error) +{ + /* Not an error, we just don't have transient session services on Windows */ + return TRUE; +} + /** * Returns the standard directories for a session bus to look for service * activation files diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 543ce57f..5ed4c45e 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -428,6 +428,8 @@ dbus_bool_t _dbus_path_is_absolute (const DBusString *filename); dbus_bool_t _dbus_get_standard_session_servicedirs (DBusList **dirs); dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs); +dbus_bool_t _dbus_set_up_transient_session_servicedirs (DBusList **dirs, + DBusError *error); dbus_bool_t _dbus_get_system_config_file (DBusString *str); dbus_bool_t _dbus_get_session_config_file (DBusString *str); -- cgit v1.2.3