X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-transport.c;h=bec4a5acf84dbcdc73f47a9e2026e56c8166080f;hb=dbecdeabb20e0ce11121819c63373f0afba57c58;hp=da487cc7f2f5c1542db023671fa10ccd041ff233;hpb=43605a6f4e78a8c28afb4b1e924dff0301e0e95c;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index da487cc..bec4a5a 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -1,5 +1,5 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* dbus-transport.c DBusTransport object (internal to D-BUS implementation) +/* -*- 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. * @@ -17,16 +17,21 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include #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-message-private.h" +#include "dbus-marshal-header.h" #ifdef DBUS_BUILD_TESTS #include "dbus-server-debug-pipe.h" #endif @@ -53,7 +58,7 @@ */ static void -live_messages_size_notify (DBusCounter *counter, +live_messages_notify (DBusCounter *counter, void *user_data) { DBusTransport *transport = user_data; @@ -61,46 +66,52 @@ live_messages_size_notify (DBusCounter *counter, _dbus_transport_ref (transport); #if 0 - _dbus_verbose ("Counter value is now %d\n", - (int) _dbus_counter_get_value (counter)); + _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) + 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. + * 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 #TRUE if this transport is on the server side of a connection + * @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, - dbus_bool_t server, + 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) - auth = _dbus_auth_server_new (); + if (server_guid) + auth = _dbus_auth_server_new (server_guid); else auth = _dbus_auth_client_new (); if (auth == NULL) @@ -116,8 +127,17 @@ _dbus_transport_init_base (DBusTransport *transport, _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) + if (server_guid) { _dbus_assert (address == NULL); address_copy = NULL; @@ -128,6 +148,7 @@ _dbus_transport_init_base (DBusTransport *transport, 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); @@ -139,36 +160,45 @@ _dbus_transport_init_base (DBusTransport *transport, transport->vtable = vtable; transport->loader = loader; transport->auth = auth; - transport->live_messages_size = counter; + transport->live_messages = counter; transport->authenticated = FALSE; - transport->messages_need_sending = FALSE; transport->disconnected = FALSE; - transport->send_credentials_pending = !server; - transport->receive_credentials_pending = server; - transport->is_server = server; + 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; - - transport->credentials.pid = -1; - transport->credentials.uid = -1; - transport->credentials.gid = -1; - _dbus_counter_set_notify (transport->live_messages_size, + /* 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, - live_messages_size_notify, + 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; } @@ -186,179 +216,231 @@ _dbus_transport_finalize_base (DBusTransport *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_size, - 0, NULL, NULL); - _dbus_counter_unref (transport->live_messages_size); + _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); } + /** - * Opens a new transport for the given address. (This opens a - * client-side-of-the-connection transport.) + * 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. * - * @todo error messages on bad address could really be better. - * DBusResultCode is a bit limiting here. - * - * @param address the address. - * @param error location to store reason for failure. - * @returns new transport of #NULL on failure. + * @param error address where an error can be returned. + * @returns a new transport, or #NULL on failure. */ -DBusTransport* -_dbus_transport_open (const char *address, - DBusError *error) +static DBusTransport* +check_address (const char *address, DBusError *error) { - DBusTransport *transport; DBusAddressEntry **entries; - DBusError tmp_error; - DBusError first_error; + DBusTransport *transport = NULL; int len, i; - const char *address_problem_type; - const char *address_problem_field; - const char *address_problem_other; - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + _dbus_assert (address != NULL); + _dbus_assert (*address != '\0'); + if (!dbus_parse_address (address, &entries, &len, error)) - return NULL; + 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 (DBusError *error) +{ + DBusString address; + DBusTransport *result = NULL; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - transport = NULL; - address_problem_type = NULL; - address_problem_field = NULL; - address_problem_other = NULL; - dbus_error_init (&tmp_error); - dbus_error_init (&first_error); - for (i = 0; i < len; i++) + if (!_dbus_string_init (&address)) { - const char *method; + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return NULL; + } - method = dbus_address_entry_get_method (entries[i]); - - if (strcmp (method, "unix") == 0) - { - const char *path = dbus_address_entry_get_value (entries[i], "path"); - const char *tmpdir = dbus_address_entry_get_value (entries[i], "tmpdir"); - const char *abstract = dbus_address_entry_get_value (entries[i], "abstract"); - - if (tmpdir != NULL) - { - address_problem_other = "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on"; - goto bad_address; - } - - if (path == NULL && abstract == NULL) - { - address_problem_type = "unix"; - address_problem_field = "path or abstract"; - goto bad_address; - } + if (!_dbus_get_autolaunch_address (&address, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto out; + } - if (path != NULL && abstract != NULL) - { - address_problem_other = "can't specify both \"path\" and \"abstract\" options in an address"; - goto bad_address; - } + 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); - if (path) - transport = _dbus_transport_new_for_domain_socket (path, FALSE, - &tmp_error); - else - transport = _dbus_transport_new_for_domain_socket (abstract, TRUE, - &tmp_error); - } - else if (strcmp (method, "tcp") == 0) - { - const char *host = dbus_address_entry_get_value (entries[i], "host"); - const char *port = dbus_address_entry_get_value (entries[i], "port"); - DBusString str; - long lport; - dbus_bool_t sresult; - - if (port == NULL) - { - address_problem_type = "tcp"; - address_problem_field = "port"; - goto bad_address; - } + out: + _dbus_string_free (&address); + return result; +} - _dbus_string_init_const (&str, port); - sresult = _dbus_string_parse_int (&str, 0, &lport, NULL); - _dbus_string_free (&str); - - if (sresult == FALSE || lport <= 0 || lport > 65535) - { - address_problem_other = "Port is not an integer between 0 and 65535"; - goto bad_address; - } - - transport = _dbus_transport_new_for_tcp_socket (host, lport, &tmp_error); - } -#ifdef DBUS_BUILD_TESTS - else if (strcmp (method, "debug-pipe") == 0) - { - const char *name = dbus_address_entry_get_value (entries[i], "name"); +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 (name == NULL) - { - address_problem_type = "debug-pipe"; - address_problem_field = "name"; - goto bad_address; - } - - transport = _dbus_transport_debug_pipe_new (name, &tmp_error); - } -#endif - else + if (strcmp (method, "autolaunch") == 0) + { + *transport_p = _dbus_transport_new_for_autolaunch (error); + + if (*transport_p == NULL) { - address_problem_other = "Unknown address type (examples of valid types are \"unix\" and \"tcp\")"; - goto bad_address; + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; } - - if (transport) - break; - - _DBUS_ASSERT_ERROR_IS_SET (&tmp_error); - - if (i == 0) - dbus_move_error (&tmp_error, &first_error); else - dbus_error_free (&tmp_error); + { + _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); - _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); - if (transport == NULL) + 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_ASSERT_ERROR_IS_SET (&first_error); - dbus_move_error (&first_error, error); + _DBUS_SET_OOM (error); + return NULL; } - else + + for (i = 0; i < (int) _DBUS_N_ELEMENTS (open_funcs); ++i) { - dbus_error_free (&first_error); - } - - dbus_address_entries_free (entries); - return transport; + DBusTransportOpenResult result; - bad_address: - dbus_address_entries_free (entries); + _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error); + result = (* open_funcs[i].func) (entry, &transport, &tmp_error); - if (address_problem_type != NULL) - dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, - "Address of type %s was missing argument %s", - address_problem_type, address_problem_field); + 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_set_error (error, DBUS_ERROR_BAD_ADDRESS, - "Could not parse address: %s", - address_problem_other); + { + _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 NULL; + return transport; } /** @@ -389,10 +471,12 @@ _dbus_transport_unref (DBusTransport *transport) { _dbus_assert (transport != NULL); _dbus_assert (transport->refcount > 0); - + transport->refcount -= 1; if (transport->refcount == 0) { + _dbus_verbose ("%s: finalizing\n", _DBUS_FUNCTION_NAME); + _dbus_assert (transport->vtable->finalize != NULL); (* transport->vtable->finalize) (transport); @@ -410,14 +494,18 @@ _dbus_transport_unref (DBusTransport *transport) void _dbus_transport_disconnect (DBusTransport *transport) { + _dbus_verbose ("%s start\n", _DBUS_FUNCTION_NAME); + _dbus_assert (transport->vtable->disconnect != NULL); - + if (transport->disconnected) return; (* transport->vtable->disconnect) (transport); transport->disconnected = TRUE; + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); } /** @@ -434,11 +522,170 @@ _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 %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (connection); + + allow = (* unix_user_function) (connection, + uid, + unix_user_data); + + _dbus_verbose ("lock %s post unix user function\n", _DBUS_FUNCTION_NAME); + _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 %s\n", _DBUS_FUNCTION_NAME); + _dbus_connection_unlock (connection); + + allow = (* windows_user_function) (connection, + windows_sid, + windows_user_data); + + _dbus_verbose ("lock %s post windows user function\n", _DBUS_FUNCTION_NAME); + _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) : ""), + (_dbus_credentials_get_windows_sid(our_identity) ? + _dbus_credentials_get_windows_sid(our_identity) : "")); + 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 needs to drop connection->mutex when calling the unix_user_function + * @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 @@ -454,6 +701,9 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) 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 || @@ -470,70 +720,113 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) maybe_authenticated = FALSE; } } - - /* If we've authenticated as some identity, check that the auth - * identity is the same as our own identity. In the future, we - * may have API allowing applications to specify how this is - * done, for example they may allow connection as any identity, - * but then impose restrictions on certain identities. - * Or they may give certain identities extra privileges. + + /* If we're the client, verify the GUID */ - - if (maybe_authenticated && transport->is_server) + if (maybe_authenticated && !transport->is_server) { - DBusCredentials auth_identity; + const char *server_guid; - _dbus_auth_get_identity (transport->auth, &auth_identity); + server_guid = _dbus_auth_get_guid_from_server (transport->auth); + _dbus_assert (server_guid != NULL); - if (transport->unix_user_function != NULL) + if (transport->expected_guid && + strcmp (transport->expected_guid, server_guid) != 0) { - /* FIXME we hold the connection lock here and should drop it */ - if (!(* transport->unix_user_function) (transport->connection, - auth_identity.uid, - transport->unix_user_data)) + _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 (transport->expected_guid == NULL) + { + transport->expected_guid = _dbus_strdup (server_guid); + + if (transport->expected_guid == NULL) { - _dbus_verbose ("Client UID "DBUS_UID_FORMAT - " was rejected, disconnecting\n", - auth_identity.uid); - _dbus_transport_disconnect (transport); + _dbus_verbose ("No memory to complete auth in %s\n", _DBUS_FUNCTION_NAME); return FALSE; } - else - { - _dbus_verbose ("Client UID "DBUS_UID_FORMAT" authorized\n", auth_identity.uid); - } } + } + + /* 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 { - DBusCredentials our_identity; - - _dbus_credentials_from_current_process (&our_identity); - - if (!_dbus_credentials_match (&our_identity, - &auth_identity)) - { - _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT - " but our UID is "DBUS_UID_FORMAT", disconnecting\n", - auth_identity.uid, our_identity.uid); - _dbus_transport_disconnect (transport); - return FALSE; - } - else - { - _dbus_verbose ("Client authorized as UID "DBUS_UID_FORMAT - " matching our UID "DBUS_UID_FORMAT"\n", - auth_identity.uid, our_identity.uid); - } + allow = auth_via_default_rules (transport); } + + if (!allow) + maybe_authenticated = FALSE; } transport->authenticated = maybe_authenticated; - - return transport->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. * @@ -547,6 +840,22 @@ _dbus_transport_get_address (DBusTransport *transport) } /** + * 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 + return transport->expected_guid; +} + +/** * Handles a watch by reading data, writing data, or disconnecting * the transport, as appropriate for the given condition. * @@ -567,9 +876,9 @@ _dbus_transport_handle_watch (DBusTransport *transport, if (transport->disconnected) return TRUE; - if (dbus_watch_get_fd (watch) < 0) + if (dbus_watch_get_socket (watch) < 0) { - _dbus_warn ("Tried to handle an invalidated watch; this watch should have been removed\n"); + _dbus_warn_check_failed ("Tried to handle an invalidated watch; this watch should have been removed\n"); return TRUE; } @@ -611,45 +920,19 @@ _dbus_transport_set_connection (DBusTransport *transport, } /** - * Notifies the transport when the outgoing message queue goes from - * empty to non-empty or vice versa. Typically causes the transport to - * add or remove its DBUS_WATCH_WRITABLE watch. - * - * @param transport the transport. - * @param queue_length the length of the outgoing message queue. - * - */ -void -_dbus_transport_messages_pending (DBusTransport *transport, - int queue_length) -{ - _dbus_assert (transport->vtable->messages_pending != NULL); - - if (transport->disconnected) - return; - - transport->messages_need_sending = queue_length > 0; - - _dbus_transport_ref (transport); - (* transport->vtable->messages_pending) (transport, - queue_length); - _dbus_transport_unref (transport); -} - -/** - * Get the UNIX file descriptor, if any. + * 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_unix_fd (DBusTransport *transport, - int *fd_p) +_dbus_transport_get_socket_fd (DBusTransport *transport, + int *fd_p) { dbus_bool_t retval; - if (transport->vtable->get_unix_fd == NULL) + if (transport->vtable->get_socket_fd == NULL) return FALSE; if (transport->disconnected) @@ -657,8 +940,8 @@ _dbus_transport_get_unix_fd (DBusTransport *transport, _dbus_transport_ref (transport); - retval = (* transport->vtable->get_unix_fd) (transport, - fd_p); + retval = (* transport->vtable->get_socket_fd) (transport, + fd_p); _dbus_transport_unref (transport); @@ -697,6 +980,8 @@ _dbus_transport_do_iteration (DBusTransport *transport, (* transport->vtable->do_iteration) (transport, flags, timeout_milliseconds); _dbus_transport_unref (transport); + + _dbus_verbose ("%s end\n", _DBUS_FUNCTION_NAME); } static dbus_bool_t @@ -798,7 +1083,8 @@ recover_unused_bytes (DBusTransport *transport) DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport) { - if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size) + 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)) @@ -855,7 +1141,7 @@ _dbus_transport_queue_messages (DBusTransport *transport) _dbus_verbose ("queueing received message %p\n", message); - if (!_dbus_message_add_size_counter (message, transport->live_messages_size)) + if (!_dbus_message_add_counter (message, transport->live_messages)) { _dbus_message_loader_putback_message_link (transport->loader, link); @@ -893,6 +1179,19 @@ _dbus_transport_set_max_message_size (DBusTransport *transport, } /** + * 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 @@ -905,6 +1204,18 @@ _dbus_transport_get_max_message_size (DBusTransport *transport) } /** + * 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 @@ -915,12 +1226,30 @@ _dbus_transport_set_max_received_size (DBusTransport *transport, long size) { transport->max_live_messages_size = size; - _dbus_counter_set_notify (transport->live_messages_size, + _dbus_counter_set_notify (transport->live_messages, transport->max_live_messages_size, - live_messages_size_notify, + 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(). @@ -935,6 +1264,18 @@ _dbus_transport_get_max_received_size (DBusTransport *transport) } /** + * 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 @@ -945,21 +1286,22 @@ dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport, unsigned long *uid) { - DBusCredentials auth_identity; + DBusCredentials *auth_identity; - *uid = _DBUS_INT_MAX; /* better than some root or system user in - * case of bugs in the caller. Caller should - * never use this value on purpose, however. - */ + *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; - _dbus_auth_get_identity (transport->auth, &auth_identity); + auth_identity = _dbus_auth_get_identity (transport->auth); - if (auth_identity.uid != DBUS_UID_UNSET) + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_UNIX_USER_ID)) { - *uid = auth_identity.uid; + *uid = _dbus_credentials_get_unix_uid (auth_identity); return TRUE; } else @@ -977,7 +1319,7 @@ dbus_bool_t _dbus_transport_get_unix_process_id (DBusTransport *transport, unsigned long *pid) { - DBusCredentials auth_identity; + 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, @@ -987,11 +1329,46 @@ _dbus_transport_get_unix_process_id (DBusTransport *transport, if (!transport->authenticated) return FALSE; - _dbus_auth_get_identity (transport->auth, &auth_identity); + auth_identity = _dbus_auth_get_identity (transport->auth); - if (auth_identity.pid != DBUS_PID_UNSET) + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_UNIX_PROCESS_ID)) { - *pid = auth_identity.pid; + *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 @@ -1025,6 +1402,65 @@ _dbus_transport_set_unix_user_function (DBusTransport *transport, } /** + * 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 @@ -1039,5 +1475,17 @@ _dbus_transport_set_auth_mechanisms (DBusTransport *transport, 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; +} /** @} */