From 087b5222fa0c2c880d8462f14e93bf0f1c9c7e9d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 22 Jun 2017 17:43:12 +0100 Subject: [PATCH 18/49] bus/containers: Create a DBusServer and add it to the main loop This means we can accept connections on the new socket. For now, we don't process them and they get closed. Signed-off-by: Simon McVittie --- bus/Makefile.am | 1 + bus/bus.c | 32 ++++--- bus/bus.h | 3 + bus/containers.c | 207 ++++++++++++++++++++++++++++++++++++++++++-- bus/tmpfiles.d/dbus.conf.in | 4 + dbus/dbus-internals.h | 1 + 6 files changed, 232 insertions(+), 16 deletions(-) diff --git a/bus/Makefile.am b/bus/Makefile.am index d00837af..3dfa0d72 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -29,6 +29,7 @@ AM_CPPFLAGS = \ $(EXPAT_CFLAGS) \ $(APPARMOR_CFLAGS) \ -DDBUS_SYSTEM_CONFIG_FILE=\""$(dbusdatadir)/system.conf"\" \ + -DDBUS_RUNSTATEDIR=\""$(runstatedir)"\" \ -DDBUS_COMPILATION \ $(NULL) diff --git a/bus/bus.c b/bus/bus.c index 450677da..82ba25fe 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -98,7 +98,8 @@ server_get_context (DBusServer *server) bd = BUS_SERVER_DATA (server); - /* every DBusServer in the dbus-daemon has gone through setup_server() */ + /* every DBusServer in the dbus-daemon's main loop has gone through + * bus_context_setup_server() */ _dbus_assert (bd != NULL); context = bd->context; @@ -217,6 +218,25 @@ setup_server (BusContext *context, char **auth_mechanisms, DBusError *error) { + if (!bus_context_setup_server (context, server, error)) + return FALSE; + + if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms)) + { + BUS_SET_OOM (error); + return FALSE; + } + + dbus_server_set_new_connection_function (server, new_connection_callback, + context, NULL); + return TRUE; +} + +dbus_bool_t +bus_context_setup_server (BusContext *context, + DBusServer *server, + DBusError *error) +{ BusServerData *bd; bd = dbus_new0 (BusServerData, 1); @@ -231,16 +251,6 @@ setup_server (BusContext *context, bd->context = context; - if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms)) - { - BUS_SET_OOM (error); - return FALSE; - } - - dbus_server_set_new_connection_function (server, - new_connection_callback, - context, NULL); - if (!dbus_server_set_watch_functions (server, add_server_watch, remove_server_watch, diff --git a/bus/bus.h b/bus/bus.h index 5a531ed0..c382575c 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -147,5 +147,8 @@ dbus_bool_t bus_context_check_security_policy (BusContext BusActivationEntry *activation_entry, DBusError *error); void bus_context_check_all_watches (BusContext *context); +dbus_bool_t bus_context_setup_server (BusContext *context, + DBusServer *server, + DBusError *error); #endif /* BUS_BUS_H */ diff --git a/bus/containers.c b/bus/containers.c index e3912ffc..1c254db1 100644 --- a/bus/containers.c +++ b/bus/containers.c @@ -51,6 +51,7 @@ typedef struct DBusVariant *metadata; BusContext *context; BusContainers *containers; + DBusServer *server; } BusContainerInstance; /* @@ -63,6 +64,7 @@ struct BusContainers /* path borrowed from BusContainerInstance => unowned BusContainerInstance * The BusContainerInstance removes itself from here on destruction. */ DBusHashTable *instances_by_path; + DBusString address_template; dbus_uint64_t next_container_id; }; @@ -72,14 +74,55 @@ bus_containers_new (void) /* We allocate the hash table lazily, expecting that the common case will * be a connection where this feature is never used */ BusContainers *self = dbus_new0 (BusContainers, 1); + DBusString dir; + const char *tmpdir; if (self == NULL) - return NULL; + goto oom; self->refcount = 1; self->instances_by_path = NULL; self->next_container_id = DBUS_UINT64_CONSTANT (0); + /* So we can safely call _dbus_free on it, even before _dbus_string_init() */ + _dbus_string_init_const (&self->address_template, ""); + + if (_dbus_getuid () == 0) + { + _dbus_string_init_const (&dir, DBUS_RUNSTATEDIR); + } + else + { + const char *runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR"); + + if (runtime_dir != NULL) + { + _dbus_string_init_const (&dir, runtime_dir); + } + else + { + tmpdir = _dbus_get_tmpdir (); + + if (tmpdir == NULL) + goto oom; + + _dbus_string_init_const (&dir, tmpdir); + } + } + + /* We specifically use paths, because an abstract socket that you can't + * bind-mount is not particularly useful. */ + if (!_dbus_string_init (&self->address_template) || + !_dbus_string_append (&self->address_template, "unix:dir=") || + !_dbus_address_append_escaped (&self->address_template, &dir)) + goto oom; + return self; + +oom: + if (self != NULL) + bus_containers_unref (self); + + return NULL; } BusContainers * @@ -101,10 +144,20 @@ bus_containers_unref (BusContainers *self) if (self->instances_by_path != NULL) _dbus_hash_table_unref (self->instances_by_path); + _dbus_string_free (&self->address_template); dbus_free (self); } } +static BusContainerInstance * +bus_container_instance_ref (BusContainerInstance *self) +{ + _dbus_assert (self->refcount > 0); + + self->refcount++; + return self; +} + static void bus_container_instance_unref (BusContainerInstance *self) { @@ -112,6 +165,11 @@ bus_container_instance_unref (BusContainerInstance *self) if (--self->refcount == 0) { + /* As long as the server is listening, the BusContainerInstance can't + * be freed, because the DBusServer holds a reference to the + * BusContainerInstance */ + _dbus_assert (self->server == NULL); + /* It's OK to do this even if we were never added to instances_by_path, * because the paths are globally unique. */ if (self->path != NULL && self->containers->instances_by_path != NULL) @@ -130,6 +188,23 @@ bus_container_instance_unref (BusContainerInstance *self) } } +static void +bus_container_instance_stop_listening (BusContainerInstance *self) +{ + /* In case the DBusServer holds the last reference to self */ + bus_container_instance_ref (self); + + if (self->server != NULL) + { + dbus_server_set_new_connection_function (self->server, NULL, NULL, NULL); + dbus_server_disconnect (self->server); + dbus_server_unref (self->server); + self->server = NULL; + } + + bus_container_instance_unref (self); +} + static BusContainerInstance * bus_container_instance_new (BusContext *context, BusContainers *containers, @@ -162,6 +237,7 @@ bus_container_instance_new (BusContext *context, self->metadata = NULL; self->context = bus_context_ref (context); self->containers = bus_containers_ref (containers); + self->server = NULL; if (containers->next_container_id >= DBUS_UINT64_CONSTANT (0xFFFFFFFFFFFFFFFF)) @@ -197,6 +273,51 @@ fail: return NULL; } +/* We only accept EXTERNAL authentication, because Unix platforms that are + * sufficiently capable to have app-containers ought to have it. */ +static const char * const auth_mechanisms[] = +{ + "EXTERNAL", + NULL +}; + +static void +new_connection_cb (DBusServer *server, + DBusConnection *new_connection, + void *data) +{ + /* TODO: handle new connection */ +} + +static dbus_bool_t +bus_container_instance_listen (BusContainerInstance *self, + DBusError *error) +{ + BusContainers *containers = bus_context_get_containers (self->context); + + self->server = dbus_server_listen ( + _dbus_string_get_const_data (&containers->address_template), error); + + if (self->server == NULL) + return FALSE; + + if (!bus_context_setup_server (self->context, self->server, error)) + return FALSE; + + if (!dbus_server_set_auth_mechanisms (self->server, + (const char **) auth_mechanisms)) + { + BUS_SET_OOM (error); + return FALSE; + } + + /* Cannot fail because the memory it uses was already allocated */ + dbus_server_set_new_connection_function (self->server, new_connection_cb, + bus_container_instance_ref (self), + (DBusFreeFunction) bus_container_instance_unref); + return TRUE; +} + dbus_bool_t bus_containers_handle_add_container_server (DBusConnection *connection, BusTransaction *transaction, @@ -205,11 +326,18 @@ bus_containers_handle_add_container_server (DBusConnection *connection, { DBusMessageIter iter; DBusMessageIter dict_iter; + DBusMessageIter writer; + DBusMessageIter array_writer; const char *type; const char *name; + const char *path; BusContainerInstance *instance = NULL; BusContext *context; BusContainers *containers; + char *address = NULL; + DBusAddressEntry **entries = NULL; + int n_entries; + DBusMessage *reply = NULL; context = bus_transaction_get_context (transaction); containers = bus_context_get_containers (context); @@ -285,6 +413,13 @@ bus_containers_handle_add_container_server (DBusConnection *connection, /* End of arguments */ _dbus_assert (!dbus_message_iter_has_next (&iter)); + if (_dbus_string_get_length (&containers->address_template) == 0) + { + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to choose directory for container sockets"); + goto fail; + } + if (containers->instances_by_path == NULL) { containers->instances_by_path = _dbus_hash_table_new (DBUS_HASH_STRING, @@ -298,18 +433,80 @@ bus_containers_handle_add_container_server (DBusConnection *connection, instance->path, instance)) goto oom; - /* TODO: Actually implement the method */ - dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, "Not yet implemented"); - goto fail; + /* This part is separated out because we eventually want to be able to + * accept a fd-passed server socket in the named parameters, instead of + * creating our own server, and defer listening on it until later */ + if (!bus_container_instance_listen (instance, error)) + goto fail; + + address = dbus_server_get_address (instance->server); + + if (!dbus_parse_address (address, &entries, &n_entries, error)) + _dbus_assert_not_reached ("listening on unix:dir= should yield a valid address"); + + _dbus_assert (n_entries == 1); + _dbus_assert (strcmp (dbus_address_entry_get_method (entries[0]), "unix") == 0); + + path = dbus_address_entry_get_value (entries[0], "path"); + _dbus_assert (path != NULL); + + reply = dbus_message_new_method_return (message); + + if (!dbus_message_append_args (reply, + DBUS_TYPE_OBJECT_PATH, &instance->path, + DBUS_TYPE_INVALID)) + goto oom; + + dbus_message_iter_init_append (reply, &writer); + + if (!dbus_message_iter_open_container (&writer, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array_writer)) + goto oom; + + if (!dbus_message_iter_append_fixed_array (&array_writer, DBUS_TYPE_BYTE, + &path, strlen (path) + 1)) + { + dbus_message_iter_abandon_container (&writer, &array_writer); + goto oom; + } + + if (!dbus_message_iter_close_container (&writer, &array_writer)) + goto oom; + + if (!dbus_message_append_args (reply, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID)) + goto oom; + + _dbus_assert (dbus_message_has_signature (reply, "oays")); + + if (! bus_transaction_send_from_driver (transaction, connection, reply)) + goto oom; + + dbus_message_unref (reply); + bus_container_instance_unref (instance); + dbus_address_entries_free (entries); + dbus_free (address); + return TRUE; oom: BUS_SET_OOM (error); /* fall through */ fail: + if (reply != NULL) + dbus_message_unref (reply); + + if (entries != NULL) + dbus_address_entries_free (entries); if (instance != NULL) - bus_container_instance_unref (instance); + { + bus_container_instance_stop_listening (instance); + bus_container_instance_unref (instance); + } + dbus_free (address); return FALSE; } diff --git a/bus/tmpfiles.d/dbus.conf.in b/bus/tmpfiles.d/dbus.conf.in index 754f0220..6ebecb6d 100644 --- a/bus/tmpfiles.d/dbus.conf.in +++ b/bus/tmpfiles.d/dbus.conf.in @@ -3,3 +3,7 @@ # Make ${localstatedir}/lib/dbus/machine-id a symlink to /etc/machine-id # if it does not already exist L @EXPANDED_LOCALSTATEDIR@/lib/dbus/machine-id - - - - /etc/machine-id + +# Create ${runstatedir}/dbus/containers owned by the system bus user. +# org.freedesktop.DBus.Containers1 uses this to create sockets. +d @EXPANDED_RUNSTATEDIR@/dbus/containers 0755 @DBUS_USER@ - - - diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 4af106ef..b0f43a4e 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -372,6 +372,7 @@ void _dbus_unlock (DBusGlobalLock lock); DBUS_PRIVATE_EXPORT dbus_bool_t _dbus_threads_init_debug (void); +DBUS_PRIVATE_EXPORT dbus_bool_t _dbus_address_append_escaped (DBusString *escaped, const DBusString *unescaped); -- 2.11.0