| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* dbus-transport.c DBusTransport object (internal to D-Bus implementation) |
| * |
| * Copyright (C) 2002, 2003 Red Hat Inc. |
| * |
| * Licensed under the Academic Free License version 2.1 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <config.h> |
| #include "dbus-transport-protected.h" |
| #include "dbus-transport-unix.h" |
| #include "dbus-transport-socket.h" |
| #include "dbus-connection-internal.h" |
| #include "dbus-watch.h" |
| #include "dbus-auth.h" |
| #include "dbus-address.h" |
| #include "dbus-credentials.h" |
| #include "dbus-mainloop.h" |
| #include "dbus-message-private.h" |
| #include "dbus-marshal-header.h" |
| #ifdef DBUS_BUILD_TESTS |
| #include "dbus-server-debug-pipe.h" |
| #endif |
| |
| /** |
| * @defgroup DBusTransport DBusTransport object |
| * @ingroup DBusInternals |
| * @brief "Backend" for a DBusConnection. |
| * |
| * Types and functions related to DBusTransport. A transport is an |
| * abstraction that can send and receive data via various kinds of |
| * network connections or other IPC mechanisms. |
| * |
| * @{ |
| */ |
| |
| /** |
| * @typedef DBusTransport |
| * |
| * Opaque object representing a way message stream. |
| * DBusTransport abstracts various kinds of actual |
| * transport mechanism, such as different network protocols, |
| * or encryption schemes. |
| */ |
| |
| static void |
| live_messages_notify (DBusCounter *counter, |
| void *user_data) |
| { |
| DBusTransport *transport = user_data; |
| |
| _dbus_transport_ref (transport); |
| |
| #if 0 |
| _dbus_verbose ("Size counter value is now %d\n", |
| (int) _dbus_counter_get_size_value (counter)); |
| _dbus_verbose ("Unix FD counter value is now %d\n", |
| (int) _dbus_counter_get_unix_fd_value (counter)); |
| #endif |
| |
| /* disable or re-enable the read watch for the transport if |
| * required. |
| */ |
| if (transport->vtable->live_messages_changed) |
| (* transport->vtable->live_messages_changed) (transport); |
| |
| _dbus_transport_unref (transport); |
| } |
| |
| /** |
| * Initializes the base class members of DBusTransport. Chained up to |
| * by subclasses in their constructor. The server GUID is the |
| * globally unique ID for the server creating this connection |
| * and will be #NULL for the client side of a connection. The GUID |
| * is in hex format. |
| * |
| * @param transport the transport being created. |
| * @param vtable the subclass vtable. |
| * @param server_guid non-#NULL if this transport is on the server side of a connection |
| * @param address the address of the transport |
| * @returns #TRUE on success. |
| */ |
| dbus_bool_t |
| _dbus_transport_init_base (DBusTransport *transport, |
| const DBusTransportVTable *vtable, |
| const DBusString *server_guid, |
| const DBusString *address) |
| { |
| DBusMessageLoader *loader; |
| DBusAuth *auth; |
| DBusCounter *counter; |
| char *address_copy; |
| DBusCredentials *creds; |
| |
| loader = _dbus_message_loader_new (); |
| if (loader == NULL) |
| return FALSE; |
| |
| if (server_guid) |
| auth = _dbus_auth_server_new (server_guid); |
| else |
| auth = _dbus_auth_client_new (); |
| if (auth == NULL) |
| { |
| _dbus_message_loader_unref (loader); |
| return FALSE; |
| } |
| |
| counter = _dbus_counter_new (); |
| if (counter == NULL) |
| { |
| _dbus_auth_unref (auth); |
| _dbus_message_loader_unref (loader); |
| return FALSE; |
| } |
| |
| creds = _dbus_credentials_new (); |
| if (creds == NULL) |
| { |
| _dbus_counter_unref (counter); |
| _dbus_auth_unref (auth); |
| _dbus_message_loader_unref (loader); |
| return FALSE; |
| } |
| |
| if (server_guid) |
| { |
| _dbus_assert (address == NULL); |
| address_copy = NULL; |
| } |
| else |
| { |
| _dbus_assert (address != NULL); |
| |
| if (!_dbus_string_copy_data (address, &address_copy)) |
| { |
| _dbus_credentials_unref (creds); |
| _dbus_counter_unref (counter); |
| _dbus_auth_unref (auth); |
| _dbus_message_loader_unref (loader); |
| return FALSE; |
| } |
| } |
| |
| transport->refcount = 1; |
| transport->vtable = vtable; |
| transport->loader = loader; |
| transport->auth = auth; |
| transport->live_messages = counter; |
| transport->authenticated = FALSE; |
| transport->disconnected = FALSE; |
| transport->is_server = (server_guid != NULL); |
| transport->send_credentials_pending = !transport->is_server; |
| transport->receive_credentials_pending = transport->is_server; |
| transport->address = address_copy; |
| |
| transport->unix_user_function = NULL; |
| transport->unix_user_data = NULL; |
| transport->free_unix_user_data = NULL; |
| |
| transport->windows_user_function = NULL; |
| transport->windows_user_data = NULL; |
| transport->free_windows_user_data = NULL; |
| |
| transport->expected_guid = NULL; |
| |
| /* Try to default to something that won't totally hose the system, |
| * but doesn't impose too much of a limitation. |
| */ |
| transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63; |
| |
| /* On Linux RLIMIT_NOFILE defaults to 1024, so allowing 4096 fds live |
| should be more than enough */ |
| transport->max_live_messages_unix_fds = 4096; |
| |
| /* credentials read from socket if any */ |
| transport->credentials = creds; |
| |
| _dbus_counter_set_notify (transport->live_messages, |
| transport->max_live_messages_size, |
| transport->max_live_messages_unix_fds, |
| live_messages_notify, |
| transport); |
| |
| if (transport->address) |
| _dbus_verbose ("Initialized transport on address %s\n", transport->address); |
| |
| return TRUE; |
| } |
| |
| /** |
| * Finalizes base class members of DBusTransport. |
| * Chained up to from subclass finalizers. |
| * |
| * @param transport the transport. |
| */ |
| void |
| _dbus_transport_finalize_base (DBusTransport *transport) |
| { |
| if (!transport->disconnected) |
| _dbus_transport_disconnect (transport); |
| |
| if (transport->free_unix_user_data != NULL) |
| (* transport->free_unix_user_data) (transport->unix_user_data); |
| |
| if (transport->free_windows_user_data != NULL) |
| (* transport->free_windows_user_data) (transport->windows_user_data); |
| |
| _dbus_message_loader_unref (transport->loader); |
| _dbus_auth_unref (transport->auth); |
| _dbus_counter_set_notify (transport->live_messages, |
| 0, 0, NULL, NULL); |
| _dbus_counter_unref (transport->live_messages); |
| dbus_free (transport->address); |
| dbus_free (transport->expected_guid); |
| if (transport->credentials) |
| _dbus_credentials_unref (transport->credentials); |
| } |
| |
| |
| /** |
| * Verifies if a given D-Bus address is a valid address |
| * by attempting to connect to it. If it is, returns the |
| * opened DBusTransport object. If it isn't, returns #NULL |
| * and sets @p error. |
| * |
| * @param error address where an error can be returned. |
| * @returns a new transport, or #NULL on failure. |
| */ |
| static DBusTransport* |
| check_address (const char *address, DBusError *error) |
| { |
| DBusAddressEntry **entries; |
| DBusTransport *transport = NULL; |
| int len, i; |
| |
| _dbus_assert (address != NULL); |
| _dbus_assert (*address != '\0'); |
| |
| if (!dbus_parse_address (address, &entries, &len, error)) |
| return NULL; /* not a valid address */ |
| |
| for (i = 0; i < len; i++) |
| { |
| transport = _dbus_transport_open (entries[i], error); |
| if (transport != NULL) |
| break; |
| } |
| |
| dbus_address_entries_free (entries); |
| return transport; |
| } |
| |
| /** |
| * Creates a new transport for the "autostart" method. |
| * This creates a client-side of a transport. |
| * |
| * @param error address where an error can be returned. |
| * @returns a new transport, or #NULL on failure. |
| */ |
| static DBusTransport* |
| _dbus_transport_new_for_autolaunch (const char *scope, DBusError *error) |
| { |
| DBusString address; |
| DBusTransport *result = NULL; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| if (!_dbus_string_init (&address)) |
| { |
| dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); |
| return NULL; |
| } |
| |
| if (!_dbus_get_autolaunch_address (scope, &address, error)) |
| { |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| goto out; |
| } |
| |
| result = check_address (_dbus_string_get_const_data (&address), error); |
| if (result == NULL) |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| else |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| out: |
| _dbus_string_free (&address); |
| return result; |
| } |
| |
| static DBusTransportOpenResult |
| _dbus_transport_open_autolaunch (DBusAddressEntry *entry, |
| DBusTransport **transport_p, |
| DBusError *error) |
| { |
| const char *method; |
| |
| method = dbus_address_entry_get_method (entry); |
| _dbus_assert (method != NULL); |
| |
| if (strcmp (method, "autolaunch") == 0) |
| { |
| const char *scope = dbus_address_entry_get_value (entry, "scope"); |
| |
| *transport_p = _dbus_transport_new_for_autolaunch (scope, error); |
| |
| if (*transport_p == NULL) |
| { |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; |
| } |
| else |
| { |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| return DBUS_TRANSPORT_OPEN_OK; |
| } |
| } |
| else |
| { |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| return DBUS_TRANSPORT_OPEN_NOT_HANDLED; |
| } |
| } |
| |
| static const struct { |
| DBusTransportOpenResult (* func) (DBusAddressEntry *entry, |
| DBusTransport **transport_p, |
| DBusError *error); |
| } open_funcs[] = { |
| { _dbus_transport_open_socket }, |
| { _dbus_transport_open_platform_specific }, |
| { _dbus_transport_open_autolaunch } |
| #ifdef DBUS_BUILD_TESTS |
| , { _dbus_transport_open_debug_pipe } |
| #endif |
| }; |
| |
| /** |
| * Try to open a new transport for the given address entry. (This |
| * opens a client-side-of-the-connection transport.) |
| * |
| * @param entry the address entry |
| * @param error location to store reason for failure. |
| * @returns new transport of #NULL on failure. |
| */ |
| DBusTransport* |
| _dbus_transport_open (DBusAddressEntry *entry, |
| DBusError *error) |
| { |
| DBusTransport *transport; |
| const char *expected_guid_orig; |
| char *expected_guid; |
| int i; |
| DBusError tmp_error = DBUS_ERROR_INIT; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| transport = NULL; |
| expected_guid_orig = dbus_address_entry_get_value (entry, "guid"); |
| expected_guid = _dbus_strdup (expected_guid_orig); |
| |
| if (expected_guid_orig != NULL && expected_guid == NULL) |
| { |
| _DBUS_SET_OOM (error); |
| return NULL; |
| } |
| |
| for (i = 0; i < (int) _DBUS_N_ELEMENTS (open_funcs); ++i) |
| { |
| DBusTransportOpenResult result; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); |
| result = (* open_funcs[i].func) (entry, &transport, &tmp_error); |
| |
| switch (result) |
| { |
| case DBUS_TRANSPORT_OPEN_OK: |
| _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); |
| goto out; |
| break; |
| case DBUS_TRANSPORT_OPEN_NOT_HANDLED: |
| _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); |
| /* keep going through the loop of open funcs */ |
| break; |
| case DBUS_TRANSPORT_OPEN_BAD_ADDRESS: |
| _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); |
| goto out; |
| break; |
| case DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT: |
| _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); |
| goto out; |
| break; |
| } |
| } |
| |
| out: |
| |
| if (transport == NULL) |
| { |
| if (!dbus_error_is_set (&tmp_error)) |
| _dbus_set_bad_address (&tmp_error, |
| NULL, NULL, |
| "Unknown address type (examples of valid types are \"tcp\" and on UNIX \"unix\")"); |
| |
| _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); |
| dbus_move_error(&tmp_error, error); |
| dbus_free (expected_guid); |
| } |
| else |
| { |
| _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); |
| |
| /* In the case of autostart the initial guid is NULL |
| * and the autostart transport recursively calls |
| * _dbus_open_transport wich returns a transport |
| * with a guid. That guid is the definitive one. |
| * |
| * FIXME: if more transports are added they may have |
| * an effect on the expected_guid semantics (i.e. |
| * expected_guid and transport->expected_guid may |
| * both have values). This is very unlikely though |
| * we should either throw asserts here for those |
| * corner cases or refactor the code so it is |
| * clearer on what is expected and what is not |
| */ |
| if(expected_guid) |
| transport->expected_guid = expected_guid; |
| } |
| |
| return transport; |
| } |
| |
| /** |
| * Increments the reference count for the transport. |
| * |
| * @param transport the transport. |
| * @returns the transport. |
| */ |
| DBusTransport * |
| _dbus_transport_ref (DBusTransport *transport) |
| { |
| _dbus_assert (transport->refcount > 0); |
| |
| transport->refcount += 1; |
| |
| return transport; |
| } |
| |
| /** |
| * Decrements the reference count for the transport. |
| * Disconnects and finalizes the transport if |
| * the reference count reaches zero. |
| * |
| * @param transport the transport. |
| */ |
| void |
| _dbus_transport_unref (DBusTransport *transport) |
| { |
| _dbus_assert (transport != NULL); |
| _dbus_assert (transport->refcount > 0); |
| |
| transport->refcount -= 1; |
| if (transport->refcount == 0) |
| { |
| _dbus_verbose ("finalizing\n"); |
| |
| _dbus_assert (transport->vtable->finalize != NULL); |
| |
| (* transport->vtable->finalize) (transport); |
| } |
| } |
| |
| /** |
| * Closes our end of the connection to a remote application. Further |
| * attempts to use this transport will fail. Only the first call to |
| * _dbus_transport_disconnect() will have an effect. |
| * |
| * @param transport the transport. |
| * |
| */ |
| void |
| _dbus_transport_disconnect (DBusTransport *transport) |
| { |
| _dbus_verbose ("start\n"); |
| |
| _dbus_assert (transport->vtable->disconnect != NULL); |
| |
| if (transport->disconnected) |
| return; |
| |
| (* transport->vtable->disconnect) (transport); |
| |
| transport->disconnected = TRUE; |
| |
| _dbus_verbose ("end\n"); |
| } |
| |
| /** |
| * Returns #TRUE if the transport has not been disconnected. |
| * Disconnection can result from _dbus_transport_disconnect() |
| * or because the server drops its end of the connection. |
| * |
| * @param transport the transport. |
| * @returns whether we're connected |
| */ |
| dbus_bool_t |
| _dbus_transport_get_is_connected (DBusTransport *transport) |
| { |
| return !transport->disconnected; |
| } |
| |
| static dbus_bool_t |
| auth_via_unix_user_function (DBusTransport *transport) |
| { |
| DBusCredentials *auth_identity; |
| dbus_bool_t allow; |
| DBusConnection *connection; |
| DBusAllowUnixUserFunction unix_user_function; |
| void *unix_user_data; |
| dbus_uid_t uid; |
| |
| /* Dropping the lock here probably isn't that safe. */ |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| _dbus_assert (auth_identity != NULL); |
| |
| connection = transport->connection; |
| unix_user_function = transport->unix_user_function; |
| unix_user_data = transport->unix_user_data; |
| uid = _dbus_credentials_get_unix_uid (auth_identity); |
| |
| _dbus_verbose ("unlock\n"); |
| _dbus_connection_unlock (connection); |
| |
| allow = (* unix_user_function) (connection, |
| uid, |
| unix_user_data); |
| |
| _dbus_verbose ("lock post unix user function\n"); |
| _dbus_connection_lock (connection); |
| |
| if (allow) |
| { |
| _dbus_verbose ("Client UID "DBUS_UID_FORMAT" authorized\n", uid); |
| } |
| else |
| { |
| _dbus_verbose ("Client UID "DBUS_UID_FORMAT |
| " was rejected, disconnecting\n", |
| _dbus_credentials_get_unix_uid (auth_identity)); |
| _dbus_transport_disconnect (transport); |
| } |
| |
| return allow; |
| } |
| |
| static dbus_bool_t |
| auth_via_windows_user_function (DBusTransport *transport) |
| { |
| DBusCredentials *auth_identity; |
| dbus_bool_t allow; |
| DBusConnection *connection; |
| DBusAllowWindowsUserFunction windows_user_function; |
| void *windows_user_data; |
| char *windows_sid; |
| |
| /* Dropping the lock here probably isn't that safe. */ |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| _dbus_assert (auth_identity != NULL); |
| |
| connection = transport->connection; |
| windows_user_function = transport->windows_user_function; |
| windows_user_data = transport->unix_user_data; |
| windows_sid = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity)); |
| |
| if (windows_sid == NULL) |
| { |
| /* OOM */ |
| return FALSE; |
| } |
| |
| _dbus_verbose ("unlock\n"); |
| _dbus_connection_unlock (connection); |
| |
| allow = (* windows_user_function) (connection, |
| windows_sid, |
| windows_user_data); |
| |
| _dbus_verbose ("lock post windows user function\n"); |
| _dbus_connection_lock (connection); |
| |
| if (allow) |
| { |
| _dbus_verbose ("Client SID '%s' authorized\n", windows_sid); |
| } |
| else |
| { |
| _dbus_verbose ("Client SID '%s' was rejected, disconnecting\n", |
| _dbus_credentials_get_windows_sid (auth_identity)); |
| _dbus_transport_disconnect (transport); |
| } |
| |
| return allow; |
| } |
| |
| static dbus_bool_t |
| auth_via_default_rules (DBusTransport *transport) |
| { |
| DBusCredentials *auth_identity; |
| DBusCredentials *our_identity; |
| dbus_bool_t allow; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| _dbus_assert (auth_identity != NULL); |
| |
| /* By default, connection is allowed if the client is 1) root or 2) |
| * has the same UID as us or 3) anonymous is allowed. |
| */ |
| |
| our_identity = _dbus_credentials_new_from_current_process (); |
| if (our_identity == NULL) |
| { |
| /* OOM */ |
| return FALSE; |
| } |
| |
| if (transport->allow_anonymous || |
| _dbus_credentials_get_unix_uid (auth_identity) == 0 || |
| _dbus_credentials_same_user (our_identity, |
| auth_identity)) |
| { |
| if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID)) |
| _dbus_verbose ("Client authorized as SID '%s'" |
| "matching our SID '%s'\n", |
| _dbus_credentials_get_windows_sid(auth_identity), |
| _dbus_credentials_get_windows_sid(our_identity)); |
| else |
| _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT |
| " matching our UID "DBUS_UID_FORMAT"\n", |
| _dbus_credentials_get_unix_uid(auth_identity), |
| _dbus_credentials_get_unix_uid(our_identity)); |
| /* We have authenticated! */ |
| allow = TRUE; |
| } |
| else |
| { |
| if (_dbus_credentials_include(our_identity,DBUS_CREDENTIAL_WINDOWS_SID)) |
| _dbus_verbose ("Client authorized as SID '%s'" |
| " but our SID is '%s', disconnecting\n", |
| (_dbus_credentials_get_windows_sid(auth_identity) ? |
| _dbus_credentials_get_windows_sid(auth_identity) : "<null>"), |
| (_dbus_credentials_get_windows_sid(our_identity) ? |
| _dbus_credentials_get_windows_sid(our_identity) : "<null>")); |
| else |
| _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT |
| " but our UID is "DBUS_UID_FORMAT", disconnecting\n", |
| _dbus_credentials_get_unix_uid(auth_identity), |
| _dbus_credentials_get_unix_uid(our_identity)); |
| _dbus_transport_disconnect (transport); |
| allow = FALSE; |
| } |
| |
| _dbus_credentials_unref (our_identity); |
| |
| return allow; |
| } |
| |
| |
| /** |
| * Returns #TRUE if we have been authenticated. Will return #TRUE |
| * even if the transport is disconnected. |
| * |
| * @todo we drop connection->mutex when calling the unix_user_function, |
| * and windows_user_function, which may not be safe really. |
| * |
| * @param transport the transport |
| * @returns whether we're authenticated |
| */ |
| dbus_bool_t |
| _dbus_transport_get_is_authenticated (DBusTransport *transport) |
| { |
| if (transport->authenticated) |
| return TRUE; |
| else |
| { |
| dbus_bool_t maybe_authenticated; |
| |
| if (transport->disconnected) |
| return FALSE; |
| |
| /* paranoia ref since we call user callbacks sometimes */ |
| _dbus_connection_ref_unlocked (transport->connection); |
| |
| maybe_authenticated = |
| (!(transport->send_credentials_pending || |
| transport->receive_credentials_pending)); |
| |
| if (maybe_authenticated) |
| { |
| switch (_dbus_auth_do_work (transport->auth)) |
| { |
| case DBUS_AUTH_STATE_AUTHENTICATED: |
| /* leave as maybe_authenticated */ |
| break; |
| default: |
| maybe_authenticated = FALSE; |
| } |
| } |
| |
| /* If we're the client, verify the GUID |
| */ |
| if (maybe_authenticated && !transport->is_server) |
| { |
| const char *server_guid; |
| |
| server_guid = _dbus_auth_get_guid_from_server (transport->auth); |
| _dbus_assert (server_guid != NULL); |
| |
| if (transport->expected_guid && |
| strcmp (transport->expected_guid, server_guid) != 0) |
| { |
| _dbus_verbose ("Client expected GUID '%s' and we got '%s' from the server\n", |
| transport->expected_guid, server_guid); |
| _dbus_transport_disconnect (transport); |
| _dbus_connection_unref_unlocked (transport->connection); |
| return FALSE; |
| } |
| } |
| |
| /* If we're the server, see if we want to allow this identity to proceed. |
| */ |
| if (maybe_authenticated && transport->is_server) |
| { |
| dbus_bool_t allow; |
| DBusCredentials *auth_identity; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| _dbus_assert (auth_identity != NULL); |
| |
| /* If we have an auth'd user and a user function, delegate |
| * deciding whether auth credentials are good enough to the |
| * app; otherwise, use our default decision process. |
| */ |
| if (transport->unix_user_function != NULL && |
| _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_UNIX_USER_ID)) |
| { |
| allow = auth_via_unix_user_function (transport); |
| } |
| else if (transport->windows_user_function != NULL && |
| _dbus_credentials_include (auth_identity, DBUS_CREDENTIAL_WINDOWS_SID)) |
| { |
| allow = auth_via_windows_user_function (transport); |
| } |
| else |
| { |
| allow = auth_via_default_rules (transport); |
| } |
| |
| if (!allow) |
| maybe_authenticated = FALSE; |
| } |
| |
| transport->authenticated = maybe_authenticated; |
| |
| _dbus_connection_unref_unlocked (transport->connection); |
| return maybe_authenticated; |
| } |
| } |
| |
| /** |
| * See dbus_connection_get_is_anonymous(). |
| * |
| * @param transport the transport |
| * @returns #TRUE if not authenticated or authenticated as anonymous |
| */ |
| dbus_bool_t |
| _dbus_transport_get_is_anonymous (DBusTransport *transport) |
| { |
| DBusCredentials *auth_identity; |
| |
| if (!transport->authenticated) |
| return TRUE; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| |
| if (_dbus_credentials_are_anonymous (auth_identity)) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| /** |
| * Returns TRUE if the transport supports sending unix fds. |
| * |
| * @param transport the transport |
| * @returns #TRUE if TRUE it is possible to send unix fds across the transport. |
| */ |
| dbus_bool_t |
| _dbus_transport_can_pass_unix_fd(DBusTransport *transport) |
| { |
| return DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport); |
| } |
| |
| /** |
| * Gets the address of a transport. It will be |
| * #NULL for a server-side transport. |
| * |
| * @param transport the transport |
| * @returns transport's address |
| */ |
| const char* |
| _dbus_transport_get_address (DBusTransport *transport) |
| { |
| return transport->address; |
| } |
| |
| /** |
| * Gets the id of the server we are connected to (see |
| * dbus_server_get_id()). Only works on client side. |
| * |
| * @param transport the transport |
| * @returns transport's server's id or #NULL if we are the server side |
| */ |
| const char* |
| _dbus_transport_get_server_id (DBusTransport *transport) |
| { |
| if (transport->is_server) |
| return NULL; |
| else if (transport->authenticated) |
| return _dbus_auth_get_guid_from_server (transport->auth); |
| else |
| return transport->expected_guid; |
| } |
| |
| /** |
| * Handles a watch by reading data, writing data, or disconnecting |
| * the transport, as appropriate for the given condition. |
| * |
| * @param transport the transport. |
| * @param watch the watch. |
| * @param condition the current state of the watched file descriptor. |
| * @returns #FALSE if not enough memory to fully handle the watch |
| */ |
| dbus_bool_t |
| _dbus_transport_handle_watch (DBusTransport *transport, |
| DBusWatch *watch, |
| unsigned int condition) |
| { |
| dbus_bool_t retval; |
| |
| _dbus_assert (transport->vtable->handle_watch != NULL); |
| |
| if (transport->disconnected) |
| return TRUE; |
| |
| if (dbus_watch_get_socket (watch) < 0) |
| { |
| _dbus_warn_check_failed ("Tried to handle an invalidated watch; this watch should have been removed\n"); |
| return TRUE; |
| } |
| |
| _dbus_watch_sanitize_condition (watch, &condition); |
| |
| _dbus_transport_ref (transport); |
| _dbus_watch_ref (watch); |
| retval = (* transport->vtable->handle_watch) (transport, watch, condition); |
| _dbus_watch_unref (watch); |
| _dbus_transport_unref (transport); |
| |
| return retval; |
| } |
| |
| /** |
| * Sets the connection using this transport. Allows the transport |
| * to add watches to the connection, queue incoming messages, |
| * and pull outgoing messages. |
| * |
| * @param transport the transport. |
| * @param connection the connection. |
| * @returns #FALSE if not enough memory |
| */ |
| dbus_bool_t |
| _dbus_transport_set_connection (DBusTransport *transport, |
| DBusConnection *connection) |
| { |
| _dbus_assert (transport->vtable->connection_set != NULL); |
| _dbus_assert (transport->connection == NULL); |
| |
| transport->connection = connection; |
| |
| _dbus_transport_ref (transport); |
| if (!(* transport->vtable->connection_set) (transport)) |
| transport->connection = NULL; |
| _dbus_transport_unref (transport); |
| |
| return transport->connection != NULL; |
| } |
| |
| /** |
| * Get the socket file descriptor, if any. |
| * |
| * @param transport the transport |
| * @param fd_p pointer to fill in with the descriptor |
| * @returns #TRUE if a descriptor was available |
| */ |
| dbus_bool_t |
| _dbus_transport_get_socket_fd (DBusTransport *transport, |
| int *fd_p) |
| { |
| dbus_bool_t retval; |
| |
| if (transport->vtable->get_socket_fd == NULL) |
| return FALSE; |
| |
| if (transport->disconnected) |
| return FALSE; |
| |
| _dbus_transport_ref (transport); |
| |
| retval = (* transport->vtable->get_socket_fd) (transport, |
| fd_p); |
| |
| _dbus_transport_unref (transport); |
| |
| return retval; |
| } |
| |
| /** |
| * Performs a single poll()/select() on the transport's file |
| * descriptors and then reads/writes data as appropriate, |
| * queueing incoming messages and sending outgoing messages. |
| * This is the backend for _dbus_connection_do_iteration(). |
| * See _dbus_connection_do_iteration() for full details. |
| * |
| * @param transport the transport. |
| * @param flags indicates whether to read or write, and whether to block. |
| * @param timeout_milliseconds if blocking, timeout or -1 for no timeout. |
| */ |
| void |
| _dbus_transport_do_iteration (DBusTransport *transport, |
| unsigned int flags, |
| int timeout_milliseconds) |
| { |
| _dbus_assert (transport->vtable->do_iteration != NULL); |
| |
| _dbus_verbose ("Transport iteration flags 0x%x timeout %d connected = %d\n", |
| flags, timeout_milliseconds, !transport->disconnected); |
| |
| if ((flags & (DBUS_ITERATION_DO_WRITING | |
| DBUS_ITERATION_DO_READING)) == 0) |
| return; /* Nothing to do */ |
| |
| if (transport->disconnected) |
| return; |
| |
| _dbus_transport_ref (transport); |
| (* transport->vtable->do_iteration) (transport, flags, |
| timeout_milliseconds); |
| _dbus_transport_unref (transport); |
| |
| _dbus_verbose ("end\n"); |
| } |
| |
| static dbus_bool_t |
| recover_unused_bytes (DBusTransport *transport) |
| { |
| if (_dbus_auth_needs_decoding (transport->auth)) |
| { |
| DBusString plaintext; |
| const DBusString *encoded; |
| DBusString *buffer; |
| int orig_len; |
| |
| if (!_dbus_string_init (&plaintext)) |
| goto nomem; |
| |
| _dbus_auth_get_unused_bytes (transport->auth, |
| &encoded); |
| |
| if (!_dbus_auth_decode_data (transport->auth, |
| encoded, &plaintext)) |
| { |
| _dbus_string_free (&plaintext); |
| goto nomem; |
| } |
| |
| _dbus_message_loader_get_buffer (transport->loader, |
| &buffer); |
| |
| orig_len = _dbus_string_get_length (buffer); |
| |
| if (!_dbus_string_move (&plaintext, 0, buffer, |
| orig_len)) |
| { |
| _dbus_string_free (&plaintext); |
| goto nomem; |
| } |
| |
| _dbus_verbose (" %d unused bytes sent to message loader\n", |
| _dbus_string_get_length (buffer) - |
| orig_len); |
| |
| _dbus_message_loader_return_buffer (transport->loader, |
| buffer, |
| _dbus_string_get_length (buffer) - |
| orig_len); |
| |
| _dbus_auth_delete_unused_bytes (transport->auth); |
| |
| _dbus_string_free (&plaintext); |
| } |
| else |
| { |
| const DBusString *bytes; |
| DBusString *buffer; |
| int orig_len; |
| dbus_bool_t succeeded; |
| |
| _dbus_message_loader_get_buffer (transport->loader, |
| &buffer); |
| |
| orig_len = _dbus_string_get_length (buffer); |
| |
| _dbus_auth_get_unused_bytes (transport->auth, |
| &bytes); |
| |
| succeeded = TRUE; |
| if (!_dbus_string_copy (bytes, 0, buffer, _dbus_string_get_length (buffer))) |
| succeeded = FALSE; |
| |
| _dbus_verbose (" %d unused bytes sent to message loader\n", |
| _dbus_string_get_length (buffer) - |
| orig_len); |
| |
| _dbus_message_loader_return_buffer (transport->loader, |
| buffer, |
| _dbus_string_get_length (buffer) - |
| orig_len); |
| |
| if (succeeded) |
| _dbus_auth_delete_unused_bytes (transport->auth); |
| else |
| goto nomem; |
| } |
| |
| return TRUE; |
| |
| nomem: |
| _dbus_verbose ("Not enough memory to transfer unused bytes from auth conversation\n"); |
| return FALSE; |
| } |
| |
| /** |
| * Reports our current dispatch status (whether there's buffered |
| * data to be queued as messages, or not, or we need memory). |
| * |
| * @param transport the transport |
| * @returns current status |
| */ |
| DBusDispatchStatus |
| _dbus_transport_get_dispatch_status (DBusTransport *transport) |
| { |
| if (_dbus_counter_get_size_value (transport->live_messages) >= transport->max_live_messages_size || |
| _dbus_counter_get_unix_fd_value (transport->live_messages) >= transport->max_live_messages_unix_fds) |
| return DBUS_DISPATCH_COMPLETE; /* complete for now */ |
| |
| if (!_dbus_transport_get_is_authenticated (transport)) |
| { |
| if (_dbus_auth_do_work (transport->auth) == |
| DBUS_AUTH_STATE_WAITING_FOR_MEMORY) |
| return DBUS_DISPATCH_NEED_MEMORY; |
| else if (!_dbus_transport_get_is_authenticated (transport)) |
| return DBUS_DISPATCH_COMPLETE; |
| } |
| |
| if (!transport->unused_bytes_recovered && |
| !recover_unused_bytes (transport)) |
| return DBUS_DISPATCH_NEED_MEMORY; |
| |
| transport->unused_bytes_recovered = TRUE; |
| |
| if (!_dbus_message_loader_queue_messages (transport->loader)) |
| return DBUS_DISPATCH_NEED_MEMORY; |
| |
| if (_dbus_message_loader_peek_message (transport->loader) != NULL) |
| return DBUS_DISPATCH_DATA_REMAINS; |
| else |
| return DBUS_DISPATCH_COMPLETE; |
| } |
| |
| /** |
| * Processes data we've read while handling a watch, potentially |
| * converting some of it to messages and queueing those messages on |
| * the connection. |
| * |
| * @param transport the transport |
| * @returns #TRUE if we had enough memory to queue all messages |
| */ |
| dbus_bool_t |
| _dbus_transport_queue_messages (DBusTransport *transport) |
| { |
| DBusDispatchStatus status; |
| |
| #if 0 |
| _dbus_verbose ("_dbus_transport_queue_messages()\n"); |
| #endif |
| |
| /* Queue any messages */ |
| while ((status = _dbus_transport_get_dispatch_status (transport)) == DBUS_DISPATCH_DATA_REMAINS) |
| { |
| DBusMessage *message; |
| DBusList *link; |
| |
| link = _dbus_message_loader_pop_message_link (transport->loader); |
| _dbus_assert (link != NULL); |
| |
| message = link->data; |
| |
| _dbus_verbose ("queueing received message %p\n", message); |
| |
| if (!_dbus_message_add_counter (message, transport->live_messages)) |
| { |
| _dbus_message_loader_putback_message_link (transport->loader, |
| link); |
| status = DBUS_DISPATCH_NEED_MEMORY; |
| break; |
| } |
| else |
| { |
| /* pass ownership of link and message ref to connection */ |
| _dbus_connection_queue_received_message_link (transport->connection, |
| link); |
| } |
| } |
| |
| if (_dbus_message_loader_get_is_corrupted (transport->loader)) |
| { |
| _dbus_verbose ("Corrupted message stream, disconnecting\n"); |
| _dbus_transport_disconnect (transport); |
| } |
| |
| return status != DBUS_DISPATCH_NEED_MEMORY; |
| } |
| |
| /** |
| * See dbus_connection_set_max_message_size(). |
| * |
| * @param transport the transport |
| * @param size the max size of a single message |
| */ |
| void |
| _dbus_transport_set_max_message_size (DBusTransport *transport, |
| long size) |
| { |
| _dbus_message_loader_set_max_message_size (transport->loader, size); |
| } |
| |
| /** |
| * See dbus_connection_set_max_message_unix_fds(). |
| * |
| * @param transport the transport |
| * @param n the max number of unix fds of a single message |
| */ |
| void |
| _dbus_transport_set_max_message_unix_fds (DBusTransport *transport, |
| long n) |
| { |
| _dbus_message_loader_set_max_message_unix_fds (transport->loader, n); |
| } |
| |
| /** |
| * See dbus_connection_get_max_message_size(). |
| * |
| * @param transport the transport |
| * @returns max message size |
| */ |
| long |
| _dbus_transport_get_max_message_size (DBusTransport *transport) |
| { |
| return _dbus_message_loader_get_max_message_size (transport->loader); |
| } |
| |
| /** |
| * See dbus_connection_get_max_message_unix_fds(). |
| * |
| * @param transport the transport |
| * @returns max message unix fds |
| */ |
| long |
| _dbus_transport_get_max_message_unix_fds (DBusTransport *transport) |
| { |
| return _dbus_message_loader_get_max_message_unix_fds (transport->loader); |
| } |
| |
| /** |
| * See dbus_connection_set_max_received_size(). |
| * |
| * @param transport the transport |
| * @param size the max size of all incoming messages |
| */ |
| void |
| _dbus_transport_set_max_received_size (DBusTransport *transport, |
| long size) |
| { |
| transport->max_live_messages_size = size; |
| _dbus_counter_set_notify (transport->live_messages, |
| transport->max_live_messages_size, |
| transport->max_live_messages_unix_fds, |
| live_messages_notify, |
| transport); |
| } |
| |
| /** |
| * See dbus_connection_set_max_received_unix_fds(). |
| * |
| * @param transport the transport |
| * @param n the max unix fds of all incoming messages |
| */ |
| void |
| _dbus_transport_set_max_received_unix_fds (DBusTransport *transport, |
| long n) |
| { |
| transport->max_live_messages_unix_fds = n; |
| _dbus_counter_set_notify (transport->live_messages, |
| transport->max_live_messages_size, |
| transport->max_live_messages_unix_fds, |
| live_messages_notify, |
| transport); |
| } |
| |
| /** |
| * See dbus_connection_get_max_received_size(). |
| * |
| * @param transport the transport |
| * @returns max bytes for all live messages |
| */ |
| long |
| _dbus_transport_get_max_received_size (DBusTransport *transport) |
| { |
| return transport->max_live_messages_size; |
| } |
| |
| /** |
| * See dbus_connection_set_max_received_unix_fds(). |
| * |
| * @param transport the transport |
| * @returns max unix fds for all live messages |
| */ |
| long |
| _dbus_transport_get_max_received_unix_fds (DBusTransport *transport) |
| { |
| return transport->max_live_messages_unix_fds; |
| } |
| |
| /** |
| * See dbus_connection_get_unix_user(). |
| * |
| * @param transport the transport |
| * @param uid return location for the user ID |
| * @returns #TRUE if uid is filled in with a valid user ID |
| */ |
| dbus_bool_t |
| _dbus_transport_get_unix_user (DBusTransport *transport, |
| unsigned long *uid) |
| { |
| DBusCredentials *auth_identity; |
| |
| *uid = _DBUS_INT32_MAX; /* better than some root or system user in |
| * case of bugs in the caller. Caller should |
| * never use this value on purpose, however. |
| */ |
| |
| if (!transport->authenticated) |
| return FALSE; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| |
| if (_dbus_credentials_include (auth_identity, |
| DBUS_CREDENTIAL_UNIX_USER_ID)) |
| { |
| *uid = _dbus_credentials_get_unix_uid (auth_identity); |
| return TRUE; |
| } |
| else |
| return FALSE; |
| } |
| |
| /** |
| * See dbus_connection_get_unix_process_id(). |
| * |
| * @param transport the transport |
| * @param pid return location for the process ID |
| * @returns #TRUE if uid is filled in with a valid process ID |
| */ |
| dbus_bool_t |
| _dbus_transport_get_unix_process_id (DBusTransport *transport, |
| unsigned long *pid) |
| { |
| DBusCredentials *auth_identity; |
| |
| *pid = DBUS_PID_UNSET; /* Caller should never use this value on purpose, |
| * but we set it to a safe number, INT_MAX, |
| * just to root out possible bugs in bad callers. |
| */ |
| |
| if (!transport->authenticated) |
| return FALSE; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| |
| if (_dbus_credentials_include (auth_identity, |
| DBUS_CREDENTIAL_UNIX_PROCESS_ID)) |
| { |
| *pid = _dbus_credentials_get_unix_pid (auth_identity); |
| return TRUE; |
| } |
| else |
| return FALSE; |
| } |
| |
| /** |
| * See dbus_connection_get_adt_audit_session_data(). |
| * |
| * @param transport the transport |
| * @param data return location for the ADT audit data |
| * @param data_size return length of audit data |
| * @returns #TRUE if audit data is filled in with a valid ucred |
| */ |
| dbus_bool_t |
| _dbus_transport_get_adt_audit_session_data (DBusTransport *transport, |
| void **data, |
| int *data_size) |
| { |
| DBusCredentials *auth_identity; |
| |
| *data = NULL; |
| *data_size = 0; |
| |
| if (!transport->authenticated) |
| return FALSE; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| |
| if (_dbus_credentials_include (auth_identity, |
| DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID)) |
| { |
| *data = (void *) _dbus_credentials_get_adt_audit_data (auth_identity); |
| *data_size = _dbus_credentials_get_adt_audit_data_size (auth_identity); |
| return TRUE; |
| } |
| else |
| return FALSE; |
| } |
| |
| /** |
| * See dbus_connection_set_unix_user_function(). |
| * |
| * @param transport the transport |
| * @param function the predicate |
| * @param data data to pass to the predicate |
| * @param free_data_function function to free the data |
| * @param old_data the old user data to be freed |
| * @param old_free_data_function old free data function to free it with |
| */ |
| void |
| _dbus_transport_set_unix_user_function (DBusTransport *transport, |
| DBusAllowUnixUserFunction function, |
| void *data, |
| DBusFreeFunction free_data_function, |
| void **old_data, |
| DBusFreeFunction *old_free_data_function) |
| { |
| *old_data = transport->unix_user_data; |
| *old_free_data_function = transport->free_unix_user_data; |
| |
| transport->unix_user_function = function; |
| transport->unix_user_data = data; |
| transport->free_unix_user_data = free_data_function; |
| } |
| |
| /** |
| * See dbus_connection_get_windows_user(). |
| * |
| * @param transport the transport |
| * @param windows_sid_p return location for the user ID |
| * @returns #TRUE if user is available; the returned value may still be #NULL if no memory to copy it |
| */ |
| dbus_bool_t |
| _dbus_transport_get_windows_user (DBusTransport *transport, |
| char **windows_sid_p) |
| { |
| DBusCredentials *auth_identity; |
| |
| *windows_sid_p = NULL; |
| |
| if (!transport->authenticated) |
| return FALSE; |
| |
| auth_identity = _dbus_auth_get_identity (transport->auth); |
| |
| if (_dbus_credentials_include (auth_identity, |
| DBUS_CREDENTIAL_WINDOWS_SID)) |
| { |
| /* If no memory, we are supposed to return TRUE and set NULL */ |
| *windows_sid_p = _dbus_strdup (_dbus_credentials_get_windows_sid (auth_identity)); |
| |
| return TRUE; |
| } |
| else |
| return FALSE; |
| } |
| |
| /** |
| * See dbus_connection_set_windows_user_function(). |
| * |
| * @param transport the transport |
| * @param function the predicate |
| * @param data data to pass to the predicate |
| * @param free_data_function function to free the data |
| * @param old_data the old user data to be freed |
| * @param old_free_data_function old free data function to free it with |
| */ |
| |
| void |
| _dbus_transport_set_windows_user_function (DBusTransport *transport, |
| DBusAllowWindowsUserFunction function, |
| void *data, |
| DBusFreeFunction free_data_function, |
| void **old_data, |
| DBusFreeFunction *old_free_data_function) |
| { |
| *old_data = transport->windows_user_data; |
| *old_free_data_function = transport->free_windows_user_data; |
| |
| transport->windows_user_function = function; |
| transport->windows_user_data = data; |
| transport->free_windows_user_data = free_data_function; |
| } |
| |
| /** |
| * Sets the SASL authentication mechanisms supported by this transport. |
| * |
| * @param transport the transport |
| * @param mechanisms the #NULL-terminated array of mechanisms |
| * |
| * @returns #FALSE if no memory |
| */ |
| dbus_bool_t |
| _dbus_transport_set_auth_mechanisms (DBusTransport *transport, |
| const char **mechanisms) |
| { |
| return _dbus_auth_set_mechanisms (transport->auth, mechanisms); |
| } |
| |
| /** |
| * See dbus_connection_set_allow_anonymous() |
| * |
| * @param transport the transport |
| * @param value #TRUE to allow anonymous connection |
| */ |
| void |
| _dbus_transport_set_allow_anonymous (DBusTransport *transport, |
| dbus_bool_t value) |
| { |
| transport->allow_anonymous = value != FALSE; |
| } |
| |
| /** @} */ |