*
* Copyright (C) 2003 Red Hat, Inc.
* Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2013 Samsung Electronics
*
* Licensed under the Academic Free License version 2.1
*
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
+#include <config.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-list.h>
#include <dbus/dbus-mempool.h>
#include <dbus/dbus-marshal-validate.h>
-
#include "driver.h"
#include "services.h"
#include "connection.h"
#include "bus.h"
#include "selinux.h"
+#ifdef ENABLE_KDBUS_TRANSPORT
+#include <linux/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "kdbus-d.h"
+#include "dbus/kdbus.h"
+#include "dbus/kdbus-common.h"
+#endif
+
struct BusService
{
int refcount;
unsigned int allow_replacement : 1;
unsigned int do_not_queue : 1;
+#ifdef ENABLE_KDBUS_TRANSPORT
+ unsigned int is_kdbus_starter : 1;
+#endif
};
struct BusRegistry
return link;
}
+#ifdef ENABLE_KDBUS_TRANSPORT
+static DBusConnection *
+_bus_service_find_owner_connection (BusService *service,
+ const char* unique_name)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&service->owners);
+
+ while (link != NULL)
+ {
+ BusOwner *bus_owner;
+
+ bus_owner = (BusOwner *) link->data;
+ if(!strcmp(bus_connection_get_name(bus_owner->conn), unique_name))
+ return bus_owner->conn;
+
+ link = _dbus_list_get_next_link (&service->owners, link);
+ }
+
+ return NULL;
+}
+#endif
+
static void
bus_owner_set_flags (BusOwner *owner,
dbus_uint32_t flags)
owner->do_not_queue =
(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) != FALSE;
+
+#ifdef ENABLE_KDBUS_TRANSPORT
+ owner->is_kdbus_starter =
+ (flags & KDBUS_NAME_STARTER_NAME) != FALSE;
+#endif
}
static BusOwner *
BUS_SET_OOM (error);
return NULL;
}
-
+
return service;
}
error:
for (j = 0; j < i; j++)
- dbus_free (retval[i]);
+ dbus_free (retval[j]);
dbus_free (retval);
return FALSE;
{
dbus_bool_t retval;
DBusConnection *old_owner_conn;
- DBusConnection *current_owner_conn;
BusClientPolicy *policy;
BusService *service;
BusActivation *activation;
* in bus_connection_selinux_allows_acquire_service()
*/
sid = bus_selinux_id_table_lookup (registry->service_sid_table,
- service_name);
+ service_name);
if (!bus_selinux_allows_acquire_service (connection, sid,
_dbus_string_get_const_data (service_name), error))
- {
+ {
- if (dbus_error_is_set (error) &&
+ if (dbus_error_is_set (error) &&
dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
{
goto out;
}
- dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
- "Connection \"%s\" is not allowed to own the service \"%s\" due "
- "to SELinux policy",
- bus_connection_is_active (connection) ?
- bus_connection_get_name (connection) :
- "(inactive)",
- _dbus_string_get_const_data (service_name));
- goto out;
- }
-
- if (!bus_client_policy_check_can_own (policy, connection,
- service_name))
- {
- dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
- "Connection \"%s\" is not allowed to own the service \"%s\" due "
- "to security policies in the configuration file",
- bus_connection_is_active (connection) ?
- bus_connection_get_name (connection) :
- "(inactive)",
- _dbus_string_get_const_data (service_name));
- goto out;
- }
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service \"%s\" due "
+ "to SELinux policy",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)",
+ _dbus_string_get_const_data (service_name));
+ goto out;
+ }
+
+ if (!bus_client_policy_check_can_own (policy, service_name))
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service \"%s\" due "
+ "to security policies in the configuration file",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)",
+ _dbus_string_get_const_data (service_name));
+ goto out;
+ }
if (bus_connection_get_n_services_owned (connection) >=
- bus_context_get_max_services_per_connection (registry->context))
- {
- dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
- "Connection \"%s\" is not allowed to own more services "
- "(increase limits in configuration file if required)",
- bus_connection_is_active (connection) ?
- bus_connection_get_name (connection) :
- "(inactive)");
- goto out;
- }
-
+ bus_context_get_max_services_per_connection (registry->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Connection \"%s\" is not allowed to own more services "
+ "(increase limits in configuration file if required)",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)");
+ goto out;
+ }
+
service = bus_registry_lookup (registry, service_name);
if (service != NULL)
- {
- primary_owner = bus_service_get_primary_owner (service);
- if (primary_owner != NULL)
- old_owner_conn = primary_owner->conn;
- else
- old_owner_conn = NULL;
- }
+ {
+ primary_owner = bus_service_get_primary_owner (service);
+ if (primary_owner != NULL)
+ old_owner_conn = primary_owner->conn;
+ else
+ old_owner_conn = NULL;
+ }
else
- old_owner_conn = NULL;
-
+ old_owner_conn = NULL;
+
if (service == NULL)
- {
- service = bus_registry_ensure (registry,
- service_name, connection, flags,
- transaction, error);
- if (service == NULL)
- goto out;
- }
+ {
+ service = bus_registry_ensure (registry,
+ service_name, connection, flags,
+ transaction, error);
+ if (service == NULL)
+ goto out;
+ }
primary_owner = bus_service_get_primary_owner (service);
if (primary_owner == NULL)
- goto out;
-
- current_owner_conn = primary_owner->conn;
-
+ goto out;
+
if (old_owner_conn == NULL)
- {
- _dbus_assert (current_owner_conn == connection);
+ {
+ _dbus_assert (primary_owner->conn == connection);
- *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
- }
+ *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+ }
else if (old_owner_conn == connection)
- {
- bus_owner_set_flags (primary_owner, flags);
- *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
- }
+ {
+ bus_owner_set_flags (primary_owner, flags);
+ *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+ }
else if (((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
- !(bus_service_get_allow_replacement (service))) ||
+ !(bus_service_get_allow_replacement (service))) ||
((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
- !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING)))
+ !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING)))
+ {
+ DBusList *link;
+ BusOwner *temp_owner;
+ /* Since we can't be queued if we are already in the queue
+ remove us */
+
+ link = _bus_service_find_owner_link (service, connection);
+ if (link != NULL)
+ {
+ _dbus_list_unlink (&service->owners, link);
+ temp_owner = (BusOwner *)link->data;
+ bus_owner_unref (temp_owner);
+ _dbus_list_free_link (link);
+ }
+
+ *result = DBUS_REQUEST_NAME_REPLY_EXISTS;
+ }
+ else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+ (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
+ !(bus_service_get_allow_replacement (service))))
+ {
+ /* Queue the connection */
+ if (!bus_service_add_owner (service, connection,
+ flags,
+ transaction, error))
+ goto out;
+
+ *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+ }
+ else
+ {
+ /* Replace the current owner */
+
+ /* We enqueue the new owner and remove the first one because
+ * that will cause NameAcquired and NameLost messages to
+ * be sent.
+ */
+
+ if (!bus_service_add_owner (service, connection,
+ flags,
+ transaction, error))
+ goto out;
+
+ if (primary_owner->do_not_queue)
+ {
+ if (!bus_service_remove_owner (service, old_owner_conn,
+ transaction, error))
+ goto out;
+ }
+ else
+ {
+ if (!bus_service_swap_owner (service, old_owner_conn,
+ transaction, error))
+ goto out;
+ }
+
+
+ _dbus_assert (connection == bus_service_get_primary_owner (service)->conn);
+ *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+ }
+
+ activation = bus_context_get_activation (registry->context);
+ retval = bus_activation_send_pending_auto_activation_messages (activation,
+ service,
+ transaction,
+ error);
+
+ out:
+ return retval;
+}
+
+#ifdef ENABLE_KDBUS_TRANSPORT
+dbus_bool_t
+bus_registry_acquire_kdbus_service (BusRegistry *registry,
+ DBusConnection *connection,
+ DBusMessage *message,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ dbus_bool_t retval;
+ BusService *service;
+ BusActivation *activation;
+ DBusString service_name_real;
+ const DBusString *service_name = &service_name_real;
+ char* name;
+ dbus_uint32_t flags;
+ __u64 sender_id;
+ const char* conn_unique_name;
+ DBusConnection* phantom;
+ unsigned long int uid;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ retval = FALSE;
+
+ _dbus_string_init_const (&service_name_real, name);
+
+ if (!_dbus_validate_bus_name (service_name, 0,
+ _dbus_string_get_length (service_name)))
{
- DBusList *link;
- BusOwner *temp_owner;
- /* Since we can't be queued if we are already in the queue
- remove us */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Requested bus name \"%s\" is not valid", name);
- link = _bus_service_find_owner_link (service, connection);
- if (link != NULL)
- {
- _dbus_list_unlink (&service->owners, link);
- temp_owner = (BusOwner *)link->data;
- bus_owner_unref (temp_owner);
- _dbus_list_free_link (link);
- }
-
- *result = DBUS_REQUEST_NAME_REPLY_EXISTS;
+ _dbus_verbose ("Attempt to acquire invalid service name\n");
+
+ return FALSE;
}
- else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
- (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
- !(bus_service_get_allow_replacement (service))))
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
{
- /* Queue the connection */
- if (!bus_service_add_owner (service, connection,
- flags,
- transaction, error))
- goto out;
-
- *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+ /* Not allowed; only base services can start with ':' */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot acquire a service starting with ':' such as \"%s\"", name);
+
+ _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", name);
+
+ return FALSE;
}
- else
+
+ conn_unique_name = dbus_message_get_sender(message);
+
+ if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
{
- /* Replace the current owner */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Connection \"%s\" is not allowed to own the service \"%s\"because "
+ "it is reserved for D-Bus' use only",
+ conn_unique_name, DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
- /* We enqueue the new owner and remove the first one because
- * that will cause NameAcquired and NameLost messages to
- * be sent.
- */
-
- if (!bus_service_add_owner (service, connection,
- flags,
- transaction, error))
- goto out;
+ sender_id = sender_name_to_id(conn_unique_name, error);
+ if(dbus_error_is_set(error))
+ return FALSE;
- if (primary_owner->do_not_queue)
+ phantom = bus_connections_find_conn_by_name(bus_connection_get_connections(connection), conn_unique_name);
+ if(phantom == NULL)
+ {
+ phantom = create_phantom_connection(connection, conn_unique_name, error);
+ if(phantom == NULL)
+ return FALSE;
+ }
+
+ if (!bus_client_policy_check_can_own (bus_connection_get_policy (phantom), service_name))
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service \"%s\" due "
+ "to security policies in the configuration file", conn_unique_name, name);
+ goto failed;
+ }
+
+ if (!kdbus_get_unix_user(phantom, conn_unique_name, &uid, NULL))
+ goto failed;
+
+#ifdef POLICY_TO_KDBUS
+ if (!register_kdbus_policy(name, dbus_connection_get_transport(phantom), uid))
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Kdbus error when setting policy for connection \"%s\" and service name \"%s\"",
+ conn_unique_name, name);
+ goto failed;
+ }
+#endif
+
+ *result = kdbus_request_name(connection, service_name, flags, sender_id);
+ if(*result == -EPERM)
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Kdbus not allowed %s to own the service \"%s\"",
+ conn_unique_name, _dbus_string_get_const_data (service_name));
+ goto failed;
+ }
+ else if(*result < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED , "Name \"%s\" could not be acquired", name);
+ goto failed;
+ }
+
+ if((*result == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) || (*result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER))
+ {
+ service = bus_registry_lookup (registry, service_name);
+ if (service == NULL)
{
- if (!bus_service_remove_owner (service, old_owner_conn,
- transaction, error))
- goto out;
+ service = bus_registry_ensure (registry, service_name, phantom, flags,
+ transaction, error);
+ if (service == NULL)
+ goto failed2;
}
else
{
- if (!bus_service_swap_owner (service, old_owner_conn,
- transaction, error))
- goto out;
+ if (!bus_service_add_owner (service, phantom, flags, transaction, error))
+ goto failed2;
}
-
-
- _dbus_assert (connection == bus_service_get_primary_owner (service)->conn);
- *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+
+ activation = bus_context_get_activation (registry->context);
+ retval = bus_activation_send_pending_auto_activation_messages (activation,
+ service,
+ transaction,
+ error);
}
+ else
+ retval = TRUE;
- activation = bus_context_get_activation (registry->context);
- retval = bus_activation_send_pending_auto_activation_messages (activation,
- service,
- transaction,
- error);
-
- out:
return retval;
+
+failed2:
+ kdbus_release_name(phantom, service_name, sender_id);
+failed:
+ bus_connection_disconnected(phantom);
+
+ return FALSE;
}
+#endif
dbus_bool_t
bus_registry_release_service (BusRegistry *registry,
return retval;
}
+#ifdef ENABLE_KDBUS_TRANSPORT
+dbus_bool_t
+bus_registry_release_service_kdbus (const char* sender_name,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ dbus_bool_t retval = FALSE;
+ __u64 sender_id;
+
+ if (!_dbus_validate_bus_name (service_name, 0,
+ _dbus_string_get_length (service_name)))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Given bus name \"%s\" is not valid",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to release invalid service name\n");
+
+ goto out;
+ }
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release a service starting with ':' such as \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to release invalid base service name \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ goto out;
+ }
+
+ if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release the %s service because it is owned by the bus",
+ DBUS_SERVICE_DBUS);
+
+ _dbus_verbose ("Attempt to release service name \"%s\"",
+ DBUS_SERVICE_DBUS);
+
+ goto out;
+ }
+
+ sender_id = sender_name_to_id(sender_name, error);
+ if(dbus_error_is_set(error))
+ return FALSE;
+
+ *result = kdbus_release_name(connection, service_name, sender_id);
+
+ if(*result == DBUS_RELEASE_NAME_REPLY_RELEASED)
+ {
+ BusRegistry* registry;
+ BusService *service;
+
+ registry = bus_connection_get_registry (connection);
+ service = bus_registry_lookup (registry, service_name);
+ if(service)
+ {
+ DBusConnection* phantom;
+
+ phantom = _bus_service_find_owner_connection(service, sender_name);
+ if(phantom)
+ {
+ bus_service_remove_owner (service, phantom, transaction, NULL);
+ /* todo we could remove phantom if he doesn't own any name
+ * to do this we should write function in connection.c to check if
+ * _dbus_list_get_last (&d->services_owned) returns not NULL
+ * or we can leave phantom - he will be removed when he disconnects from the bus
+ */
+ }
+ else
+ _dbus_verbose ("Didn't find phantom connection for released name!\n");
+ }
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+#endif
+
dbus_bool_t
bus_registry_set_service_context_table (BusRegistry *registry,
DBusHashTable *table)
bus_service_ref (d->service);
bus_owner_ref (owner);
dbus_connection_ref (d->owner->conn);
-
+
return TRUE;
}
bus_service_get_primary_owners_connection (BusService *service)
{
BusOwner *owner;
+#ifdef ENABLE_KDBUS_TRANSPORT
+ char unique_name[(unsigned int)(snprintf((char*)NULL, 0, "%llu", ULLONG_MAX) + sizeof(":1."))];
+#endif
owner = bus_service_get_primary_owner (service);
+#ifdef ENABLE_KDBUS_TRANSPORT
+ if(!owner)
+ return NULL;
+ if(bus_context_is_kdbus(service->registry->context))
+ {
+ if(kdbus_get_name_owner(owner->conn, bus_service_get_name(service), unique_name) < 0)
+ return NULL;
+ return _bus_service_find_owner_connection(service, unique_name); //bus_connections_find_conn_by_name would be safer? but slower
+ }
+ else
+ return owner->conn;
+#else
if (owner != NULL)
return owner->conn;
else
return NULL;
+#endif
}
BusOwner*
return owner->allow_replacement;
}
+#ifdef ENABLE_KDBUS_TRANSPORT
+dbus_bool_t
+bus_service_get_is_kdbus_starter (BusService *service)
+{
+ BusOwner *owner;
+ DBusList *link;
+
+ _dbus_assert (service->owners != NULL);
+
+ link = _dbus_list_get_first_link (&service->owners);
+ owner = (BusOwner *) link->data;
+
+ return owner->is_kdbus_starter;
+}
+#endif
+
dbus_bool_t
bus_service_has_owner (BusService *service,
DBusConnection *connection)
owner = (BusOwner *) link->data;
uname = bus_connection_get_name (owner->conn);
- if (!_dbus_list_append (return_list, (char *)uname))
- goto oom;
+#ifdef ENABLE_KDBUS_TRANSPORT
+ if(!owner->is_kdbus_starter)
+#endif
+ if (!_dbus_list_append (return_list, (char *)uname))
+ goto oom;
link = _dbus_list_get_next_link (&service->owners, link);
}