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) { _dbus_connection_lock (transport->connection); (* transport->vtable->live_messages_changed) (transport); _dbus_connection_unlock (transport->connection); } _dbus_transport_unref (transport); }
static dbus_bool_t match_acquired_or_lost_signal (DBusConnection *conn, const char *member, const char *name) { int tries; DBusMessage *msg; const char *interface = "org.freedesktop.DBus"; for (tries = 0; tries < NUM_TRIES_TIL_FAIL; tries++) { _dbus_connection_lock (conn); _dbus_connection_do_iteration_unlocked (conn, DBUS_ITERATION_DO_READING | DBUS_ITERATION_DO_WRITING | DBUS_ITERATION_BLOCK, 0); _dbus_connection_unlock (conn); msg = dbus_connection_pop_message (conn); if (msg != NULL) { if (dbus_message_is_signal (msg, interface, member)) { const char *n; DBusError error; dbus_error_init (&error); dbus_message_get_args (msg, &error, DBUS_TYPE_STRING, &n, DBUS_TYPE_INVALID); if (dbus_error_is_set (&error)) { fprintf (stderr, "Error getting args: %s\n", error.message); dbus_error_free (&error); dbus_message_unref (msg); return FALSE; } if (strcmp (n, name) == 0) { dbus_message_unref (msg); break; } } dbus_message_unref (msg); } } if (tries == NUM_TRIES_TIL_FAIL) { fprintf (stderr, "Did not recive the expected %s.%s signal!!!\n", interface, member); return FALSE; } return TRUE; }
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_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; }
/** * @todo We need to have a way to wake up the select sleep if * a new iteration request comes in with a flag (read/write) that * we're not currently serving. Otherwise a call that just reads * could block a write call forever (if there are no incoming * messages). */ static void socket_do_iteration (DBusTransport *transport, unsigned int flags, int timeout_milliseconds) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusPollFD poll_fd; int poll_res; int poll_timeout; _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %" DBUS_SOCKET_FORMAT "\n", flags & DBUS_ITERATION_DO_READING ? "read" : "", flags & DBUS_ITERATION_DO_WRITING ? "write" : "", timeout_milliseconds, socket_transport->read_watch, socket_transport->write_watch, _dbus_socket_printable (socket_transport->fd)); /* the passed in DO_READING/DO_WRITING flags indicate whether to * read/write messages, but regardless of those we may need to block * for reading/writing to do auth. But if we do reading for auth, * we don't want to read any messages yet if not given DO_READING. */ poll_fd.fd = _dbus_socket_get_pollable (socket_transport->fd); poll_fd.events = 0; if (_dbus_transport_try_to_authenticate (transport)) { /* This is kind of a hack; if we have stuff to write, then try * to avoid the poll. This is probably about a 5% speedup on an * echo client/server. * * If both reading and writing were requested, we want to avoid this * since it could have funky effects: * - both ends spinning waiting for the other one to read * data so they can finish writing * - prioritizing all writing ahead of reading */ if ((flags & DBUS_ITERATION_DO_WRITING) && !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) && !transport->disconnected && _dbus_connection_has_messages_to_send_unlocked (transport->connection)) { do_writing (transport); if (transport->disconnected || !_dbus_connection_has_messages_to_send_unlocked (transport->connection)) goto out; } /* If we get here, we decided to do the poll() after all */ _dbus_assert (socket_transport->read_watch); if (flags & DBUS_ITERATION_DO_READING) poll_fd.events |= _DBUS_POLLIN; _dbus_assert (socket_transport->write_watch); if (flags & DBUS_ITERATION_DO_WRITING) poll_fd.events |= _DBUS_POLLOUT; } else { DBusAuthState auth_state; auth_state = _dbus_auth_do_work (transport->auth); if (transport->receive_credentials_pending || auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) poll_fd.events |= _DBUS_POLLIN; if (transport->send_credentials_pending || auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) poll_fd.events |= _DBUS_POLLOUT; } if (poll_fd.events) { int saved_errno; if (flags & DBUS_ITERATION_BLOCK) poll_timeout = timeout_milliseconds; else poll_timeout = 0; /* For blocking selects we drop the connection lock here * to avoid blocking out connection access during a potentially * indefinite blocking call. The io path is still protected * by the io_path_cond condvar, so we won't reenter this. */ if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("unlock pre poll\n"); _dbus_connection_unlock (transport->connection); } again: poll_res = _dbus_poll (&poll_fd, 1, poll_timeout); saved_errno = _dbus_save_socket_errno (); if (poll_res < 0 && _dbus_get_is_errno_eintr (saved_errno)) goto again; if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("lock post poll\n"); _dbus_connection_lock (transport->connection); } if (poll_res >= 0) { if (poll_res == 0) poll_fd.revents = 0; /* some concern that posix does not guarantee this; * valgrind flags it as an error. though it probably * is guaranteed on linux at least. */ if (poll_fd.revents & _DBUS_POLLERR) do_io_error (transport); else { dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0; dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0; dbus_bool_t authentication_completed; _dbus_verbose ("in iteration, need_read=%d need_write=%d\n", need_read, need_write); do_authentication (transport, need_read, need_write, &authentication_completed); /* See comment in socket_handle_watch. */ if (authentication_completed) goto out; if (need_read && (flags & DBUS_ITERATION_DO_READING)) do_reading (transport); if (need_write && (flags & DBUS_ITERATION_DO_WRITING)) do_writing (transport); } } else { _dbus_verbose ("Error from _dbus_poll(): %s\n", _dbus_strerror (saved_errno)); } } out: /* We need to install the write watch only if we did not * successfully write everything. Note we need to be careful that we * don't call check_write_watch *before* do_writing, since it's * inefficient to add the write watch, and we can avoid it most of * the time since we can write immediately. * * However, we MUST always call check_write_watch(); DBusConnection code * relies on the fact that running an iteration will notice that * messages are pending. */ check_write_watch (transport); _dbus_verbose (" ... leaving do_iteration()\n"); }
static dbus_bool_t match_name_owner_changed_signal (DBusConnection *conn, const char *bus_name, const char *lost_name, const char *acquired_name) { int tries; DBusMessage *msg; for (tries = 0; tries < NUM_TRIES_TIL_FAIL; tries++) { _dbus_connection_lock (conn); _dbus_connection_do_iteration_unlocked (conn, DBUS_ITERATION_DO_READING | DBUS_ITERATION_DO_WRITING | DBUS_ITERATION_BLOCK, 0); _dbus_connection_unlock (conn); msg = dbus_connection_pop_message (conn); if (msg != NULL) { if (dbus_message_is_signal (msg, "org.freedesktop.DBus", "NameOwnerChanged")) { const char *n; const char *ln; const char *an; DBusError error; dbus_error_init (&error); dbus_message_get_args (msg, &error, DBUS_TYPE_STRING, &n, DBUS_TYPE_STRING, &ln, DBUS_TYPE_STRING, &an, DBUS_TYPE_INVALID); if (dbus_error_is_set (&error)) { fprintf (stderr, "Error getting args: %s\n", error.message); dbus_error_free (&error); dbus_message_unref (msg); return FALSE; } if (strcmp (n, bus_name) == 0) { if ((lost_name == NULL && strcmp (ln, "") == 0) || strcmp (lost_name, ln) == 0) { if ((acquired_name == NULL && strcmp (an, "") == 0) || strcmp (acquired_name, an) == 0) { dbus_message_unref (msg); break; } else { fprintf (stderr, "Error: name %s was expected to be acquired but we got %s instead\n", acquired_name, an); dbus_message_unref (msg); return FALSE; } } else { fprintf (stderr, "Error: name %s was expected to be lost but we got %s instead\n", lost_name, ln); dbus_message_unref (msg); return FALSE; } } } dbus_message_unref (msg); } } if (tries == NUM_TRIES_TIL_FAIL) { fprintf (stderr, "Did not recive the expected NameOwnerChanged signal!!!\n"); return FALSE; } 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, * 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; } }
static void test_pending_call (Fixture *f, gconstpointer data) { Thread public_api = { f, NULL, (RefFunc) dbus_pending_call_ref, NULL, (VoidFunc) dbus_pending_call_unref, NULL, NULL, NULL }; Thread internal_api = { f, NULL, (RefFunc) _dbus_pending_call_ref_unlocked, NULL, (VoidFunc) dbus_pending_call_unref, f->connection, (VoidFunc) _dbus_connection_lock, (VoidFunc) _dbus_connection_unlock }; /* This one can't be used to ref, only to cycle or unref. */ Thread unref_and_unlock_api = { f, NULL, (RefFunc) _dbus_pending_call_ref_unlocked, NULL, (VoidFunc) _dbus_pending_call_unref_and_unlock, f->connection, (VoidFunc) _dbus_connection_lock, NULL }; unsigned i; DBusPendingCall *pending_call; _dbus_connection_lock (f->connection); pending_call = _dbus_pending_call_new_unlocked (f->connection, DBUS_TIMEOUT_INFINITE, NULL); g_assert (pending_call != NULL); _dbus_connection_unlock (f->connection); public_api.thing = pending_call; internal_api.thing = pending_call; unref_and_unlock_api.thing = pending_call; if (!dbus_pending_call_set_data (pending_call, pending_call_slot, f, last_unref)) g_error ("OOM"); for (i = 0; i < f->n_threads; i++) { if ((i % 2) == 0) f->threads[i] = g_thread_new (NULL, ref_thread, &public_api); else f->threads[i] = g_thread_new (NULL, ref_thread, &internal_api); g_assert (f->threads[i] != NULL); } wait_for_all_threads (f); for (i = 0; i < f->n_threads; i++) { switch (i % 3) { case 0: f->threads[i] = g_thread_new (NULL, cycle_thread, &public_api); break; case 1: f->threads[i] = g_thread_new (NULL, cycle_thread, &internal_api); break; default: f->threads[i] = g_thread_new (NULL, cycle_thread, &unref_and_unlock_api); } g_assert (f->threads[i] != NULL); } wait_for_all_threads (f); for (i = 0; i < f->n_threads; i++) { switch (i % 3) { case 0: f->threads[i] = g_thread_new (NULL, unref_thread, &public_api); break; case 1: f->threads[i] = g_thread_new (NULL, unref_thread, &internal_api); break; default: f->threads[i] = g_thread_new (NULL, unref_thread, &unref_and_unlock_api); } g_assert (f->threads[i] != NULL); } wait_for_all_threads (f); /* Destroy the pending call. This should be the last-unref. */ g_assert (!f->last_unref); dbus_pending_call_unref (pending_call); g_assert (f->last_unref); }