static void do_io_error (DBusTransport *transport) { _dbus_transport_ref (transport); _dbus_transport_disconnect (transport); _dbus_transport_unref (transport); }
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(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; }
/** * 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 { /* We didn't call the notify function when we added the counter, so * catch up now. Since we have the connection's lock, it's desirable * that we bypass the notify function and call this virtual method * directly. */ if (transport->vtable->live_messages_changed) (* transport->vtable->live_messages_changed) (transport); /* 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; }
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; }
/** * 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); _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_free (transport->address); dbus_free (transport->expected_guid); }
/** * 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_size_counter (message, transport->live_messages_size)) { _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; }
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 socket_handle_watch (DBusTransport *transport, DBusWatch *watch, unsigned int flags) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; _dbus_assert (watch == socket_transport->read_watch || watch == socket_transport->write_watch); _dbus_assert (watch != NULL); /* If we hit an error here on a write watch, don't disconnect the transport yet because data can * still be in the buffer and do_reading may need several iteration to read * it all (because of its max_bytes_read_per_iteration limit). */ if (!(flags & DBUS_WATCH_READABLE) && unix_error_with_read_to_come (transport, watch, flags)) { _dbus_verbose ("Hang up or error on watch\n"); _dbus_transport_disconnect (transport); return TRUE; } if (watch == socket_transport->read_watch && (flags & DBUS_WATCH_READABLE)) { dbus_bool_t auth_finished; #if 1 _dbus_verbose ("handling read watch %p flags = %x\n", watch, flags); #endif if (!do_authentication (transport, TRUE, FALSE, &auth_finished)) return FALSE; /* We don't want to do a read immediately following * a successful authentication. This is so we * have a chance to propagate the authentication * state further up. Specifically, we need to * process any pending data from the auth object. */ if (!auth_finished) { if (!do_reading (transport)) { _dbus_verbose ("no memory to read\n"); return FALSE; } } else { _dbus_verbose ("Not reading anything since we just completed the authentication\n"); } } else if (watch == socket_transport->write_watch && (flags & DBUS_WATCH_WRITABLE)) { #if 1 _dbus_verbose ("handling write watch, have_outgoing_messages = %d\n", _dbus_connection_has_messages_to_send_unlocked (transport->connection)); #endif if (!do_authentication (transport, FALSE, TRUE, NULL)) return FALSE; if (!do_writing (transport)) { _dbus_verbose ("no memory to write\n"); return FALSE; } /* See if we still need the write watch */ check_write_watch (transport); } #ifdef DBUS_ENABLE_VERBOSE_MODE else { if (watch == socket_transport->read_watch) _dbus_verbose ("asked to handle read watch with non-read condition 0x%x\n", flags); else if (watch == socket_transport->write_watch) _dbus_verbose ("asked to handle write watch with non-write condition 0x%x\n", flags); else _dbus_verbose ("asked to handle watch %p on fd %" DBUS_SOCKET_FORMAT " that we don't recognize\n", watch, dbus_watch_get_socket (watch)); } #endif /* DBUS_ENABLE_VERBOSE_MODE */ return TRUE; }
/** * 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 (transport->expected_guid == NULL) { transport->expected_guid = _dbus_strdup (server_guid); if (transport->expected_guid == NULL) { _dbus_verbose ("No memory to complete auth in %s\n", _DBUS_FUNCTION_NAME); 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; } }
/** * 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, * 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 (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 (transport->expected_guid == NULL) { transport->expected_guid = _dbus_strdup (server_guid); if (transport->expected_guid == NULL) { _dbus_verbose ("No memory to complete auth in %s\n", _DBUS_FUNCTION_NAME); return 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 (maybe_authenticated && transport->is_server) { DBusCredentials auth_identity; _dbus_auth_get_identity (transport->auth, &auth_identity); if (transport->unix_user_function != NULL) { dbus_bool_t allow; DBusConnection *connection; DBusAllowUnixUserFunction unix_user_function; void *unix_user_data; /* Dropping the lock here probably isn't that safe. */ connection = transport->connection; unix_user_function = transport->unix_user_function; unix_user_data = transport->unix_user_data; _dbus_verbose ("unlock %s\n", _DBUS_FUNCTION_NAME); _dbus_connection_unlock (connection); allow = (* unix_user_function) (connection, auth_identity.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", auth_identity.uid); } else { _dbus_verbose ("Client UID "DBUS_UID_FORMAT " was rejected, disconnecting\n", auth_identity.uid); _dbus_transport_disconnect (transport); _dbus_connection_unref_unlocked (connection); return FALSE; } } 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); _dbus_connection_unref_unlocked (transport->connection); 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); } } } transport->authenticated = maybe_authenticated; _dbus_connection_unref_unlocked (transport->connection); return maybe_authenticated; } }