dbus_bool_t bus_registry_acquire_service (BusRegistry *registry, DBusConnection *connection, const DBusString *service_name, dbus_uint32_t flags, dbus_uint32_t *result, BusTransaction *transaction, DBusError *error) { dbus_bool_t retval; DBusConnection *old_owner_conn; BusClientPolicy *policy; BusService *service; BusActivation *activation; BusSELinuxID *sid; BusOwner *primary_owner; retval = FALSE; if (!_dbus_validate_bus_name (service_name, 0, _dbus_string_get_length (service_name))) { dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Requested bus name \"%s\" is not valid", _dbus_string_get_const_data (service_name)); _dbus_verbose ("Attempt to acquire invalid service name\n"); goto out; } if (_dbus_string_get_byte (service_name, 0) == ':') { /* 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\"", _dbus_string_get_const_data (service_name)); _dbus_verbose ("Attempt to acquire 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)) { 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", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)", DBUS_SERVICE_DBUS); goto out; } policy = bus_connection_get_policy (connection); _dbus_assert (policy != NULL); /* Note that if sid is #NULL then the bus's own context gets used * in bus_connection_selinux_allows_acquire_service() */ sid = bus_selinux_id_table_lookup (registry->service_sid_table, service_name); if (!bus_selinux_allows_acquire_service (connection, sid, _dbus_string_get_const_data (service_name), 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, 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; } 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; } else old_owner_conn = NULL; if (service == NULL) { 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; if (old_owner_conn == NULL) { _dbus_assert (primary_owner->conn == connection); *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; } else if (((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && !(bus_service_get_allow_replacement (service))) || ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && !(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); if (!retval) BUS_SET_OOM (error); out: return retval; }
static dbus_bool_t bus_driver_handle_add_match (DBusConnection *connection, BusTransaction *transaction, DBusMessage *message, DBusError *error) { BusMatchRule *rule; const char *text; DBusString str; BusMatchmaker *matchmaker; _DBUS_ASSERT_ERROR_IS_CLEAR (error); text = NULL; rule = NULL; if (bus_connection_get_n_match_rules (connection) >= bus_context_get_max_match_rules_per_connection (bus_transaction_get_context (transaction))) { dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, "Connection \"%s\" is not allowed to add more match rules " "(increase limits in configuration file if required)", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)"); goto failed; } if (!dbus_message_get_args (message, error, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID)) { _dbus_verbose ("No memory to get arguments to AddMatch\n"); goto failed; } _dbus_string_init_const (&str, text); rule = bus_match_rule_parse (connection, &str, error); if (rule == NULL) goto failed; matchmaker = bus_connection_get_matchmaker (connection); if (!bus_matchmaker_add_rule (matchmaker, rule)) { BUS_SET_OOM (error); goto failed; } if (!send_ack_reply (connection, transaction, message, error)) { bus_matchmaker_remove_rule (matchmaker, rule); goto failed; } bus_match_rule_unref (rule); return TRUE; failed: _DBUS_ASSERT_ERROR_IS_SET (error); if (rule) bus_match_rule_unref (rule); return FALSE; }
/** * Returns true if the given connection can acquire a service, * using the tasks security context * * @param connection connection that wants to own the service * @param bustype name of the bus * @param service_name the name of the service to acquire * @param error the reason for failure when FALSE is returned * @returns TRUE if acquire is permitted */ dbus_bool_t bus_apparmor_allows_acquire_service (DBusConnection *connection, const char *bustype, const char *service_name, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *con = NULL; DBusString qstr, auxdata; dbus_bool_t free_auxdata = FALSE; /* the AppArmor API uses pointers to int for pointers to boolean, and * int is not strictly guaranteed to be the same as dbus_bool_t */ int allow = FALSE, audit = TRUE; unsigned long pid; int res, serrno = 0; if (!apparmor_enabled) return TRUE; _dbus_assert (connection != NULL); con = bus_connection_dup_apparmor_confinement (connection); if (is_unconfined (con->label, con->mode)) { allow = TRUE; audit = FALSE; goto out; } if (!_dbus_string_init (&qstr)) goto oom; if (!build_service_query (&qstr, con->label, bustype, service_name)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (AA_DBUS_BIND, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &allow, &audit); _dbus_string_free (&qstr); if (res == -1) { serrno = errno; set_error_from_query_errno (error, serrno); goto audit; } /* Don't fail operations on profiles in complain mode */ if (modestr_is_complain (con->mode)) allow = TRUE; if (!allow) dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Connection \"%s\" is not allowed to own the service " "\"%s\" due to AppArmor policy", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)", service_name); if (!audit) goto out; audit: if (!_dbus_string_init (&auxdata)) goto oom; free_auxdata = TRUE; if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) goto oom; if (!_dbus_append_pair_str (&auxdata, "name", service_name)) goto oom; if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) goto oom; if (!_dbus_append_mask (&auxdata, AA_DBUS_BIND)) goto oom; if (connection && dbus_connection_get_unix_process_id (connection, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) goto oom; log_message (allow, "bind", &auxdata); out: if (con != NULL) bus_apparmor_confinement_unref (con); if (free_auxdata) _dbus_string_free (&auxdata); return allow; oom: if (error != NULL && !dbus_error_is_set (error)) BUS_SET_OOM (error); allow = FALSE; goto out; #else return TRUE; #endif /* HAVE_APPARMOR */ }
static dbus_bool_t bus_driver_handle_hello (DBusConnection *connection, BusTransaction *transaction, DBusMessage *message, DBusError *error) { DBusString unique_name; BusService *service; dbus_bool_t retval; BusRegistry *registry; BusConnections *connections; _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (bus_connection_is_active (connection)) { /* We already handled an Hello message for this connection. */ dbus_set_error (error, DBUS_ERROR_FAILED, "Already handled an Hello message"); return FALSE; } /* Note that when these limits are exceeded we don't disconnect the * connection; we just sort of leave it hanging there until it times * out or disconnects itself or is dropped due to the max number of * incomplete connections. It's even OK if the connection wants to * retry the hello message, we support that. */ connections = bus_connection_get_connections (connection); if (!bus_connections_check_limits (connections, connection, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); return FALSE; } if (!_dbus_string_init (&unique_name)) { BUS_SET_OOM (error); return FALSE; } retval = FALSE; registry = bus_connection_get_registry (connection); if (!create_unique_client_name (registry, &unique_name)) { BUS_SET_OOM (error); goto out_0; } if (!bus_connection_complete (connection, &unique_name, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); goto out_0; } if (!dbus_message_set_sender (message, bus_connection_get_name (connection))) { BUS_SET_OOM (error); goto out_0; } if (!bus_driver_send_welcome_message (connection, message, transaction, error)) goto out_0; /* Create the service */ service = bus_registry_ensure (registry, &unique_name, connection, 0, transaction, error); if (service == NULL) goto out_0; _dbus_assert (bus_connection_is_active (connection)); retval = TRUE; out_0: _dbus_string_free (&unique_name); return retval; }
/** * Check if Apparmor security controls allow the connection to eavesdrop on * other connections. * * @param connection the connection attempting to eavesdrop. * @param bustype name of the bus * @param error the reason for failure when FALSE is returned * @returns TRUE if eavesdropping is permitted */ dbus_bool_t bus_apparmor_allows_eavesdropping (DBusConnection *connection, const char *bustype, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *con = NULL; DBusString qstr, auxdata; int allow = FALSE, audit = TRUE; dbus_bool_t free_auxdata = FALSE; unsigned long pid; int res, serrno = 0; if (!apparmor_enabled) return TRUE; con = bus_connection_dup_apparmor_confinement (connection); if (is_unconfined (con->label, con->mode)) { allow = TRUE; audit = FALSE; goto out; } if (!_dbus_string_init (&qstr)) goto oom; if (!build_eavesdrop_query (&qstr, con->label, bustype)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (AA_DBUS_EAVESDROP, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &allow, &audit); _dbus_string_free (&qstr); if (res == -1) { serrno = errno; set_error_from_query_errno (error, serrno); goto audit; } /* Don't fail operations on profiles in complain mode */ if (modestr_is_complain (con->mode)) allow = TRUE; if (!allow) dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Connection \"%s\" is not allowed to eavesdrop due to " "AppArmor policy", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)"); if (!audit) goto out; audit: if (!_dbus_string_init (&auxdata)) goto oom; free_auxdata = TRUE; if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) goto oom; if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) goto oom; if (!_dbus_append_pair_str (&auxdata, "mask", "eavesdrop")) goto oom; if (connection && dbus_connection_get_unix_process_id (connection, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) goto oom; log_message (allow, "eavesdrop", &auxdata); out: if (con != NULL) bus_apparmor_confinement_unref (con); if (free_auxdata) _dbus_string_free (&auxdata); return allow; oom: if (error != NULL && !dbus_error_is_set (error)) BUS_SET_OOM (error); allow = FALSE; goto out; #else return TRUE; #endif /* HAVE_APPARMOR */ }