/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gdm-common.h" #include "gdm-dbus-util.h" #include "gdm-manager.h" #include "gdm-manager-glue.h" #include "gdm-display-store.h" #include "gdm-display-factory.h" #include "gdm-launch-environment.h" #include "gdm-local-display.h" #include "gdm-local-display-factory.h" #include "gdm-session.h" #include "gdm-session-record.h" #include "gdm-settings-direct.h" #include "gdm-settings-keys.h" #include "gdm-xdmcp-display-factory.h" #include "gdm-xdmcp-chooser-display.h" #define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate)) #define GDM_DBUS_PATH "/org/gnome/DisplayManager" #define GDM_MANAGER_PATH GDM_DBUS_PATH "/Manager" #define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays" #define INITIAL_SETUP_USERNAME "gnome-initial-setup" #define ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT GDM_RUN_DIR "/gdm.ran-initial-setup" typedef struct { GdmManager *manager; GdmSession *session; char *service_name; guint idle_id; } StartUserSessionOperation; struct GdmManagerPrivate { GdmDisplayStore *display_store; GdmLocalDisplayFactory *local_factory; #ifdef HAVE_LIBXDMCP GdmXdmcpDisplayFactory *xdmcp_factory; #endif GdmDisplay *automatic_login_display; GList *user_sessions; GHashTable *transient_sessions; GHashTable *open_reauthentication_requests; gboolean xdmcp_enabled; gboolean started; gboolean show_local_greeter; GDBusConnection *connection; GDBusObjectManagerServer *object_manager; #ifdef WITH_PLYMOUTH guint plymouth_is_running : 1; #endif guint did_automatic_login : 1; }; enum { PROP_0, PROP_XDMCP_ENABLED, PROP_SHOW_LOCAL_GREETER }; enum { DISPLAY_ADDED, DISPLAY_REMOVED, LAST_SIGNAL }; typedef enum { SESSION_RECORD_LOGIN, SESSION_RECORD_LOGOUT, SESSION_RECORD_FAILED, } SessionRecord; static guint signals [LAST_SIGNAL] = { 0, }; static void gdm_manager_class_init (GdmManagerClass *klass); static void gdm_manager_init (GdmManager *manager); static void gdm_manager_dispose (GObject *object); static void create_user_session_for_display (GdmManager *manager, GdmDisplay *display, uid_t allowed_user); static void start_user_session (GdmManager *manager, StartUserSessionOperation *operation); static void clean_user_session (GdmSession *session); static gpointer manager_object = NULL; static void manager_interface_init (GdmDBusManagerIface *interface); G_DEFINE_TYPE_WITH_CODE (GdmManager, gdm_manager, GDM_DBUS_TYPE_MANAGER_SKELETON, G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_MANAGER, manager_interface_init) G_ADD_PRIVATE (GdmManager)); #ifdef WITH_PLYMOUTH static gboolean plymouth_is_running (void) { int status; gboolean res; GError *error; error = NULL; res = g_spawn_command_line_sync ("plymouth --ping", NULL, NULL, &status, &error); if (! res) { g_debug ("Could not ping plymouth: %s", error->message); g_error_free (error); return FALSE; } return WIFEXITED (status) && WEXITSTATUS (status) == 0; } static void plymouth_prepare_for_transition (void) { gboolean res; GError *error; error = NULL; res = g_spawn_command_line_sync ("plymouth deactivate", NULL, NULL, NULL, &error); if (! res) { g_warning ("Could not deactivate plymouth: %s", error->message); g_error_free (error); } } static gboolean plymouth_quit_with_transition (void) { gboolean res; GError *error; error = NULL; res = g_spawn_command_line_async ("plymouth quit --retain-splash", &error); if (! res) { g_warning ("Could not quit plymouth: %s", error->message); g_error_free (error); } return G_SOURCE_REMOVE; } static void plymouth_quit_without_transition (void) { gboolean res; GError *error; error = NULL; res = g_spawn_command_line_async ("plymouth quit", &error); if (! res) { g_warning ("Could not quit plymouth: %s", error->message); g_error_free (error); } } #endif static char * get_session_id_for_pid (pid_t pid, GError **error) { char *session, *gsession; int ret; session = NULL; ret = sd_pid_get_session (pid, &session); if (ret < 0) { g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, "Error getting session id from systemd: %s", g_strerror (-ret)); return NULL; } if (session != NULL) { gsession = g_strdup (session); free (session); return gsession; } else { return NULL; } } static gboolean get_uid_for_session_id (const char *session_id, uid_t *uid, GError **error) { int ret; ret = sd_session_get_uid (session_id, uid); if (ret < 0) { g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, "Error getting uid for session id %s from systemd: %s", session_id, g_strerror (-ret)); return FALSE; } return TRUE; } static gboolean lookup_by_session_id (const char *id, GdmDisplay *display, gpointer user_data) { const char *looking_for = user_data; const char *current; current = gdm_display_get_session_id (display); return g_strcmp0 (current, looking_for) == 0; } static gboolean is_login_session (GdmManager *self, const char *session_id, GError **error) { char *session_class = NULL; int ret; ret = sd_session_get_class (session_id, &session_class); if (ret < 0) { g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, "Error getting class for session id %s from systemd: %s", session_id, g_strerror (-ret)); return FALSE; } if (g_strcmp0 (session_class, "greeter") != 0) { g_free (session_class); return FALSE; } g_free (session_class); return TRUE; } static gboolean session_unlock (GdmManager *manager, const char *ssid) { GError *error = NULL; GVariant *reply; g_debug ("Unlocking session %s", ssid); reply = g_dbus_connection_call_sync (manager->priv->connection, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "UnlockSession", g_variant_new ("(s)", ssid), NULL, /* expected reply */ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (reply == NULL) { g_debug ("GdmManager: logind 'UnlockSession' %s raised:\n %s\n\n", g_dbus_error_get_remote_error (error), error->message); g_error_free (error); return FALSE; } g_variant_unref (reply); return TRUE; } static GdmSession * find_session_for_user_on_seat (GdmManager *manager, const char *username, const char *seat_id, GdmSession *dont_count_session) { GList *node; for (node = manager->priv->user_sessions; node != NULL; node = node->next) { GdmSession *candidate_session = node->data; const char *candidate_username, *candidate_seat_id, *candidate_session_id; candidate_session_id = gdm_session_get_session_id (candidate_session); if (candidate_session == dont_count_session) { g_debug ("GdmSession: Ignoring session %s as requested", candidate_session_id); continue; } if (!gdm_session_is_running (candidate_session)) { g_debug ("GdmSession: Ignoring session %s as it isn't running", candidate_session_id); continue; } candidate_username = gdm_session_get_username (candidate_session); candidate_seat_id = gdm_session_get_display_seat_id (candidate_session); g_debug ("GdmManager: Considering session %s on seat %s belonging to user %s", candidate_session_id, candidate_seat_id, candidate_username); if (g_strcmp0 (candidate_username, username) == 0 && g_strcmp0 (candidate_seat_id, seat_id) == 0) { g_debug ("GdmManager: yes, found session %s", candidate_session_id); return candidate_session; } g_debug ("GdmManager: no, will not use session %s", candidate_session_id); } g_debug ("GdmManager: no matching sessions found"); return NULL; } static gboolean is_remote_session (GdmManager *self, const char *session_id, GError **error) { char *seat; int ret; gboolean is_remote; /* FIXME: The next release of logind is going to have explicit api for * checking remoteness. */ seat = NULL; ret = sd_session_get_seat (session_id, &seat); if (ret < 0 && ret != -ENXIO) { g_debug ("GdmManager: Error while retrieving seat for session %s: %s", session_id, strerror (-ret)); } if (seat != NULL) { is_remote = FALSE; free (seat); } else { is_remote = TRUE; } return is_remote; } static char * get_seat_id_for_session_id (const char *session_id, GError **error) { int ret; char *seat, *out_seat; seat = NULL; ret = sd_session_get_seat (session_id, &seat); if (ret == -ENXIO) { out_seat = NULL; } else if (ret < 0) { g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, "Error getting uid for session id %s from systemd: %s", session_id, g_strerror (-ret)); out_seat = NULL; } else { out_seat = g_strdup (seat); free (seat); } return out_seat; } static char * get_tty_for_session_id (const char *session_id, GError **error) { int ret; char *tty, *out_tty; ret = sd_session_get_tty (session_id, &tty); if (ret == -ENXIO) { out_tty = NULL; } else if (ret < 0) { g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GETTING_SESSION_INFO, "Error getting tty for session id %s from systemd: %s", session_id, g_strerror (-ret)); out_tty = NULL; } else { out_tty = g_strdup (tty); free (tty); } return out_tty; } static void get_display_and_details_for_bus_sender (GdmManager *self, GDBusConnection *connection, const char *sender, GdmDisplay **out_display, char **out_seat_id, char **out_session_id, char **out_tty, GPid *out_pid, uid_t *out_uid, gboolean *out_is_login_screen, gboolean *out_is_remote) { GdmDisplay *display = NULL; char *session_id = NULL; GError *error = NULL; int ret; GPid pid; uid_t caller_uid, session_uid; ret = gdm_dbus_get_pid_for_name (sender, &pid, &error); if (!ret) { g_debug ("GdmManager: Error while retrieving pid for sender: %s", error->message); g_error_free (error); goto out; } if (out_pid != NULL) { *out_pid = pid; } ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error); if (!ret) { g_debug ("GdmManager: Error while retrieving uid for sender: %s", error->message); g_error_free (error); goto out; } ret = gdm_find_display_session (pid, caller_uid, &session_id, &error); if (!ret) { g_debug ("GdmManager: Unable to find display session for uid %d: %s", (int) caller_uid, error->message); g_error_free (error); goto out; } if (out_session_id != NULL) { *out_session_id = g_strdup (session_id); } if (out_is_login_screen != NULL) { *out_is_login_screen = is_login_session (self, session_id, &error); if (error != NULL) { g_debug ("GdmManager: Error while checking if sender is login screen: %s", error->message); g_error_free (error); goto out; } } if (!get_uid_for_session_id (session_id, &session_uid, &error)) { g_debug ("GdmManager: Error while retrieving uid for session: %s", error->message); g_error_free (error); goto out; } if (out_uid != NULL) { *out_uid = caller_uid; } if (caller_uid != session_uid) { g_debug ("GdmManager: uid for sender and uid for session don't match"); goto out; } if (out_seat_id != NULL) { *out_seat_id = get_seat_id_for_session_id (session_id, &error); if (error != NULL) { g_debug ("GdmManager: Error while retrieving seat id for session: %s", error->message); g_clear_error (&error); } } if (out_is_remote != NULL) { *out_is_remote = is_remote_session (self, session_id, &error); if (error != NULL) { g_debug ("GdmManager: Error while retrieving remoteness for session: %s", error->message); g_clear_error (&error); } } if (out_tty != NULL) { *out_tty = get_tty_for_session_id (session_id, &error); if (error != NULL) { g_debug ("GdmManager: Error while retrieving tty for session: %s", error->message); g_clear_error (&error); } } display = gdm_display_store_find (self->priv->display_store, lookup_by_session_id, (gpointer) session_id); out: if (out_display != NULL) { *out_display = display; } g_free (session_id); } static gboolean switch_to_compatible_user_session (GdmManager *manager, GdmSession *session, gboolean fail_if_already_switched) { gboolean res; gboolean ret; const char *username; const char *seat_id; const char *ssid_to_activate; GdmSession *existing_session; ret = FALSE; username = gdm_session_get_username (session); seat_id = gdm_session_get_display_seat_id (session); if (!fail_if_already_switched) { session = NULL; } existing_session = find_session_for_user_on_seat (manager, username, seat_id, session); if (existing_session != NULL) { ssid_to_activate = gdm_session_get_session_id (existing_session); if (seat_id != NULL) { res = gdm_activate_session_by_id (manager->priv->connection, seat_id, ssid_to_activate); if (! res) { g_debug ("GdmManager: unable to activate session: %s", ssid_to_activate); goto out; } } res = session_unlock (manager, ssid_to_activate); if (!res) { /* this isn't fatal */ g_debug ("GdmManager: unable to unlock session: %s", ssid_to_activate); } } else { goto out; } ret = TRUE; out: return ret; } static GdmDisplay * get_display_for_user_session (GdmSession *session) { return g_object_get_data (G_OBJECT (session), "gdm-display"); } static GdmSession * get_user_session_for_display (GdmDisplay *display) { if (display == NULL) { return NULL; } return g_object_get_data (G_OBJECT (display), "gdm-user-session"); } static gboolean add_session_record (GdmManager *manager, GdmSession *session, GPid pid, SessionRecord record) { const char *username; char *display_name, *hostname, *display_device; gboolean recorded = FALSE; display_name = NULL; username = NULL; hostname = NULL; display_device = NULL; username = gdm_session_get_username (session); if (username == NULL) { goto out; } g_object_get (G_OBJECT (session), "display-name", &display_name, "display-hostname", &hostname, "display-device", &display_device, NULL); if (display_name == NULL && display_device == NULL) { goto out; } switch (record) { case SESSION_RECORD_LOGIN: gdm_session_record_login (pid, username, hostname, display_name, display_device); break; case SESSION_RECORD_LOGOUT: gdm_session_record_logout (pid, username, hostname, display_name, display_device); break; case SESSION_RECORD_FAILED: gdm_session_record_failed (pid, username, hostname, display_name, display_device); break; } recorded = TRUE; out: g_free (display_name); g_free (hostname); g_free (display_device); return recorded; } static GdmSession * find_user_session_for_display (GdmManager *self, GdmDisplay *display) { GList *node = self->priv->user_sessions; while (node != NULL) { GdmSession *session = node->data; GdmDisplay *candidate_display; GList *next_node = node->next; candidate_display = get_display_for_user_session (session); if (candidate_display == display) return session; node = next_node; } return NULL; } static gboolean gdm_manager_handle_register_display (GdmDBusManager *manager, GDBusMethodInvocation *invocation, GVariant *details) { GdmManager *self = GDM_MANAGER (manager); const char *sender; GDBusConnection *connection; GdmDisplay *display = NULL; GdmSession *session; GVariantIter iter; char *key = NULL; char *value = NULL; char *x11_display_name = NULL; char *tty = NULL; g_debug ("GdmManager: trying to register new display"); sender = g_dbus_method_invocation_get_sender (invocation); connection = g_dbus_method_invocation_get_connection (invocation); get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, &tty, NULL, NULL, NULL, NULL); if (display == NULL) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("No display available")); return TRUE; } g_variant_iter_init (&iter, details); while (g_variant_iter_loop (&iter, "{&s&s}", &key, &value)) { if (g_strcmp0 (key, "x11-display-name") == 0) { x11_display_name = g_strdup (value); break; } } session = find_user_session_for_display (self, display); if (session != NULL) { GPid pid; if (x11_display_name != NULL) { g_object_set (G_OBJECT (session), "display-name", x11_display_name, NULL); g_object_set (G_OBJECT (display), "x11-display-name", x11_display_name, NULL); } /* FIXME: this should happen in gdm-session.c when the session is opened */ if (tty != NULL) g_object_set (G_OBJECT (session), "display-device", tty, NULL); pid = gdm_session_get_pid (session); if (pid > 0) { add_session_record (self, session, pid, SESSION_RECORD_LOGIN); } } g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL); gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager), invocation); g_clear_pointer (&x11_display_name, g_free); g_clear_pointer (&tty, g_free); return TRUE; } static gboolean gdm_manager_handle_register_session (GdmDBusManager *manager, GDBusMethodInvocation *invocation, GVariant *details) { GdmManager *self = GDM_MANAGER (manager); GdmDisplay *display = NULL; const char *sender; GDBusConnection *connection; sender = g_dbus_method_invocation_get_sender (invocation); connection = g_dbus_method_invocation_get_connection (invocation); get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, NULL, NULL, NULL, NULL, NULL); g_debug ("GdmManager: trying to register new session on display %p", display); if (display != NULL) g_object_set (G_OBJECT (display), "session-registered", TRUE, NULL); else g_debug ("GdmManager: No display, not registering"); gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), invocation); return TRUE; } static gboolean gdm_manager_handle_open_session (GdmDBusManager *manager, GDBusMethodInvocation *invocation) { GdmManager *self = GDM_MANAGER (manager); const char *sender; GDBusConnection *connection; GdmDisplay *display = NULL; GdmSession *session = NULL; const char *address; GPid pid = 0; uid_t uid = (uid_t) -1; uid_t allowed_user; g_debug ("GdmManager: trying to open new session"); sender = g_dbus_method_invocation_get_sender (invocation); connection = g_dbus_method_invocation_get_connection (invocation); get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, NULL, &pid, &uid, NULL, NULL); if (display == NULL) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("No session available")); return TRUE; } #ifdef HAVE_LIBXDMCP if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) { GdmLaunchEnvironment *launch_environment; g_object_get (display, "launch-environment", &launch_environment, NULL); if (launch_environment != NULL) { session = gdm_launch_environment_get_session (launch_environment); } if (session == NULL) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("Chooser session unavailable")); return TRUE; } } #endif if (session == NULL) { session = get_user_session_for_display (display); g_debug ("GdmSession: Considering session %s for username %s", gdm_session_get_session_id (session), gdm_session_get_username (session)); if (gdm_session_is_running (session)) { g_debug ("GdmSession: the session is running, and therefore can't be used"); g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("Can only be called before user is logged in")); return TRUE; } } allowed_user = gdm_session_get_allowed_user (session); if (uid != allowed_user) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("Caller not GDM")); return TRUE; } address = gdm_session_get_server_address (session); if (address == NULL) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("Unable to open private communication channel")); return TRUE; } gdm_dbus_manager_complete_open_session (GDM_DBUS_MANAGER (manager), invocation, address); return TRUE; } static void close_transient_session (GdmManager *self, GdmSession *session) { GPid pid; pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid")); gdm_session_close (session); g_hash_table_remove (self->priv->transient_sessions, GUINT_TO_POINTER (pid)); } static void on_reauthentication_client_connected (GdmSession *session, GCredentials *credentials, GPid pid_of_client, GdmManager *self) { g_debug ("GdmManager: client connected to reauthentication server"); } static void on_reauthentication_client_disconnected (GdmSession *session, GCredentials *credentials, GPid pid_of_client, GdmManager *self) { g_debug ("GdmManger: client disconnected from reauthentication server"); close_transient_session (self, session); } static void on_reauthentication_client_rejected (GdmSession *session, GCredentials *credentials, GPid pid_of_client, GdmManager *self) { GPid pid; g_debug ("GdmManger: client with pid %ld rejected from reauthentication server", (long) pid_of_client); if (gdm_session_client_is_connected (session)) { /* we already have a client connected, ignore this rejected one */ return; } pid = (GPid) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid")); if (pid != pid_of_client) { const char *session_id; char *client_session_id; /* rejected client isn't the process that started the * transient reauthentication session. If it's not even from the * same audit session, ignore it since it doesn't "own" the * reauthentication session */ client_session_id = get_session_id_for_pid (pid_of_client, NULL); session_id = g_object_get_data (G_OBJECT (session), "caller-session-id"); if (g_strcmp0 (session_id, client_session_id) != 0) { return; } } /* client was rejected, so clean up its session object */ close_transient_session (self, session); } static void on_reauthentication_cancelled (GdmSession *session, GdmManager *self) { g_debug ("GdmManager: client cancelled reauthentication request"); close_transient_session (self, session); } static void on_reauthentication_conversation_started (GdmSession *session, const char *service_name, GdmManager *self) { g_debug ("GdmManager: reauthentication service '%s' started", service_name); } static void on_reauthentication_conversation_stopped (GdmSession *session, const char *service_name, GdmManager *self) { g_debug ("GdmManager: reauthentication service '%s' stopped", service_name); } static void on_reauthentication_verification_complete (GdmSession *session, const char *service_name, GdmManager *self) { const char *session_id; session_id = g_object_get_data (G_OBJECT (session), "caller-session-id"); g_debug ("GdmManager: reauthenticated user in unmanaged session '%s' with service '%s'", session_id, service_name); session_unlock (self, session_id); close_transient_session (self, session); } static char * open_temporary_reauthentication_channel (GdmManager *self, char *seat_id, char *session_id, GPid pid, uid_t uid, gboolean is_remote) { GdmSession *session; char **environment; const char *display, *auth_file; const char *address; /* Note we're just using a minimal environment here rather than the * session's environment because the caller is unprivileged and the * associated worker will be privileged */ environment = g_get_environ (); display = ""; auth_file = "/dev/null"; session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE, uid, display, NULL, NULL, seat_id, auth_file, is_remote == FALSE, (const char * const *) environment); g_strfreev (environment); g_debug ("GdmSession: Created session for temporary reauthentication channel for user %d (seat %s)", (int) uid, seat_id); g_object_set_data_full (G_OBJECT (session), "caller-session-id", g_strdup (session_id), (GDestroyNotify) g_free); g_object_set_data (G_OBJECT (session), "caller-pid", GUINT_TO_POINTER (pid)); g_hash_table_insert (self->priv->transient_sessions, GINT_TO_POINTER (pid), session); g_signal_connect (session, "client-connected", G_CALLBACK (on_reauthentication_client_connected), self); g_signal_connect (session, "client-disconnected", G_CALLBACK (on_reauthentication_client_disconnected), self); g_signal_connect (session, "client-rejected", G_CALLBACK (on_reauthentication_client_rejected), self); g_signal_connect (session, "cancelled", G_CALLBACK (on_reauthentication_cancelled), self); g_signal_connect (session, "conversation-started", G_CALLBACK (on_reauthentication_conversation_started), self); g_signal_connect (session, "conversation-stopped", G_CALLBACK (on_reauthentication_conversation_stopped), self); g_signal_connect (session, "verification-complete", G_CALLBACK (on_reauthentication_verification_complete), self); address = gdm_session_get_server_address (session); return g_strdup (address); } static gboolean gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager, GDBusMethodInvocation *invocation, const char *username) { GdmManager *self = GDM_MANAGER (manager); const char *sender; GdmDisplay *display = NULL; GdmSession *session; GDBusConnection *connection; char *seat_id = NULL; char *session_id = NULL; GPid pid = 0; uid_t uid = (uid_t) -1; gboolean is_login_screen = FALSE; gboolean is_remote = FALSE; g_debug ("GdmManager: trying to open reauthentication channel for user %s", username); sender = g_dbus_method_invocation_get_sender (invocation); connection = g_dbus_method_invocation_get_connection (invocation); get_display_and_details_for_bus_sender (self, connection, sender, &display, &seat_id, &session_id, NULL, &pid, &uid, &is_login_screen, &is_remote); if (session_id == NULL || pid == 0 || uid == (uid_t) -1) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, _("No session available")); return TRUE; } if (is_login_screen) { g_debug ("GdmManager: looking for login screen session for user %s on seat %s", username, seat_id); session = find_session_for_user_on_seat (self, username, seat_id, NULL); } else { g_debug ("GdmManager: looking for user session on display"); session = get_user_session_for_display (display); } if (session != NULL && gdm_session_is_running (session)) { gdm_session_start_reauthentication (session, pid, uid); g_hash_table_insert (self->priv->open_reauthentication_requests, GINT_TO_POINTER (pid), invocation); } else if (is_login_screen) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Login screen only allowed to open reauthentication channels for running sessions"); return TRUE; } else { char *address; address = open_temporary_reauthentication_channel (self, seat_id, session_id, pid, uid, is_remote); gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager), invocation, address); g_free (address); } return TRUE; } static void manager_interface_init (GdmDBusManagerIface *interface) { interface->handle_register_display = gdm_manager_handle_register_display; interface->handle_register_session = gdm_manager_handle_register_session; interface->handle_open_session = gdm_manager_handle_open_session; interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel; } static gboolean display_is_on_seat0 (GdmDisplay *display) { gboolean is_on_seat0 = TRUE; char *seat_id = NULL; g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL); if (g_strcmp0 (seat_id, "seat0") != 0) { is_on_seat0 = FALSE; } g_free (seat_id); return is_on_seat0; } static gboolean get_timed_login_details (GdmManager *manager, char **usernamep, int *delayp) { gboolean res; gboolean enabled; int delay; char *username = NULL; enabled = FALSE; username = NULL; delay = 0; res = gdm_settings_direct_get_boolean (GDM_KEY_TIMED_LOGIN_ENABLE, &enabled); if (res && ! enabled) { goto out; } res = gdm_settings_direct_get_string (GDM_KEY_TIMED_LOGIN_USER, &username); if (res && (username == NULL || username[0] == '\0')) { g_clear_pointer (&username, g_free); goto out; } delay = 0; res = gdm_settings_direct_get_int (GDM_KEY_TIMED_LOGIN_DELAY, &delay); if (res && delay <= 0) { /* we don't allow the timed login to have a zero delay */ delay = 10; } out: if (enabled) { g_debug ("GdmDisplay: Got timed login details for display: %d %s %d", enabled, username, delay); } else { g_debug ("GdmDisplay: Got timed login details for display: 0"); } if (usernamep != NULL) { *usernamep = username; } else { g_free (username); } if (delayp != NULL) { *delayp = delay; } return enabled; } static gboolean get_automatic_login_details (GdmManager *manager, char **usernamep) { gboolean res; gboolean enabled; char *username = NULL; enabled = FALSE; username = NULL; res = gdm_settings_direct_get_boolean (GDM_KEY_AUTO_LOGIN_ENABLE, &enabled); if (res && enabled) { res = gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username); } if (enabled && res && username != NULL && username[0] != '\0') { goto out; } g_free (username); username = NULL; enabled = FALSE; out: if (enabled) { g_debug ("GdmDisplay: Got automatic login details for display: %d %s", enabled, username); } else { g_debug ("GdmDisplay: Got automatic login details for display: 0"); } if (usernamep != NULL) { *usernamep = username; } else { g_free (username); } return enabled; } static const char * get_username_for_greeter_display (GdmManager *manager, GdmDisplay *display) { gboolean doing_initial_setup = FALSE; g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); if (doing_initial_setup) { return INITIAL_SETUP_USERNAME; } else { return GDM_USERNAME; } } static void set_up_automatic_login_session (GdmManager *manager, GdmDisplay *display) { GdmSession *session; g_auto (GStrv) supported_session_types = NULL; /* 0 is root user; since the daemon talks to the session object * directly, itself, for automatic login */ create_user_session_for_display (manager, display, 0); session = get_user_session_for_display (display); g_object_get (G_OBJECT (display), "supported-session-types", &supported_session_types, NULL); g_object_set (G_OBJECT (session), "display-is-initial", FALSE, "supported-session-types", supported_session_types, NULL); g_debug ("GdmManager: Starting automatic login conversation"); gdm_session_start_conversation (session, "gdm-autologin"); } static void set_up_chooser_session (GdmManager *manager, GdmDisplay *display) { const char *allowed_user; struct passwd *passwd_entry; allowed_user = get_username_for_greeter_display (manager, display); if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) { g_warning ("GdmManager: couldn't look up username %s", allowed_user); gdm_display_unmanage (display); gdm_display_finish (display); return; } gdm_display_start_greeter_session (display); } static void set_up_greeter_session (GdmManager *manager, GdmDisplay *display) { const char *allowed_user; struct passwd *passwd_entry; allowed_user = get_username_for_greeter_display (manager, display); if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) { g_warning ("GdmManager: couldn't look up username %s", allowed_user); gdm_display_unmanage (display); gdm_display_finish (display); return; } create_user_session_for_display (manager, display, passwd_entry->pw_uid); gdm_display_start_greeter_session (display); } static void set_up_automatic_login_session_if_user_exists (GdmManager *manager, GdmDisplay *display, ActUser *user) { if (act_user_is_nonexistent (user)) set_up_greeter_session (manager, display); else set_up_automatic_login_session (manager, display); } typedef struct { GdmManager *manager; GdmDisplay *display; char *username; } UsernameLookupOperation; static void destroy_username_lookup_operation (UsernameLookupOperation *operation) { g_object_unref (operation->manager); g_object_unref (operation->display); g_free (operation->username); g_free (operation); } static void on_user_is_loaded_changed (ActUser *user, GParamSpec *pspec, UsernameLookupOperation *operation) { if (act_user_is_loaded (user)) { set_up_automatic_login_session_if_user_exists (operation->manager, operation->display, user); g_signal_handlers_disconnect_by_func (G_OBJECT (user), G_CALLBACK (on_user_is_loaded_changed), operation); destroy_username_lookup_operation (operation); } } static void set_up_session (GdmManager *manager, GdmDisplay *display) { ActUserManager *user_manager; ActUser *user; gboolean loaded; gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE; gboolean autologin_enabled = FALSE; g_autofree char *seat_id = NULL; char *username = NULL; g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL); if (g_strcmp0 (seat_id, "seat0") == 0) seat_can_autologin = TRUE; if (manager->priv->did_automatic_login || manager->priv->automatic_login_display != NULL) seat_did_autologin = TRUE; if (seat_can_autologin && !seat_did_autologin) autologin_enabled = get_automatic_login_details (manager, &username); if (!autologin_enabled) { g_free (username); #ifdef HAVE_LIBXDMCP if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) { set_up_chooser_session (manager, display); return; } #endif set_up_greeter_session (manager, display); return; } /* Check whether the user really exists before committing to autologin. */ user_manager = act_user_manager_get_default (); user = act_user_manager_get_user (user_manager, username); g_object_get (user_manager, "is-loaded", &loaded, NULL); if (loaded) { set_up_automatic_login_session_if_user_exists (manager, display, user); } else { UsernameLookupOperation *operation; operation = g_new (UsernameLookupOperation, 1); operation->manager = g_object_ref (manager); operation->display = g_object_ref (display); operation->username = username; g_signal_connect (user, "notify::is-loaded", G_CALLBACK (on_user_is_loaded_changed), operation); } } static void on_display_status_changed (GdmDisplay *display, GParamSpec *arg1, GdmManager *manager) { int status; int display_number = -1; char *session_type = NULL; gboolean doing_initial_setup = FALSE; #ifdef WITH_PLYMOUTH gboolean display_is_local = FALSE; gboolean quit_plymouth = FALSE; g_object_get (display, "is-local", &display_is_local, NULL); quit_plymouth = display_is_local && manager->priv->plymouth_is_running; #endif g_object_get (display, "x11-display-number", &display_number, "session-type", &session_type, "doing-initial-setup", &doing_initial_setup, NULL); status = gdm_display_get_status (display); switch (status) { case GDM_DISPLAY_PREPARED: case GDM_DISPLAY_MANAGED: if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) || (display_number != -1 && status == GDM_DISPLAY_MANAGED)) { char *session_class; g_object_get (display, "session-class", &session_class, NULL); if (g_strcmp0 (session_class, "greeter") == 0) set_up_session (manager, display); g_free (session_class); } break; case GDM_DISPLAY_FAILED: case GDM_DISPLAY_UNMANAGED: case GDM_DISPLAY_FINISHED: #ifdef WITH_PLYMOUTH if (quit_plymouth) { plymouth_quit_without_transition (); manager->priv->plymouth_is_running = FALSE; } #endif g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); if (display == manager->priv->automatic_login_display) { g_clear_weak_pointer (&manager->priv->automatic_login_display); manager->priv->did_automatic_login = TRUE; #ifdef ENABLE_WAYLAND_SUPPORT if (g_strcmp0 (session_type, "wayland") != 0 && status == GDM_DISPLAY_FAILED) { /* we're going to fall back to X11, so try to autologin again */ manager->priv->did_automatic_login = FALSE; } #endif } break; default: break; } } static void on_display_removed (GdmDisplayStore *display_store, GdmDisplay *display, GdmManager *manager) { char *id; gdm_display_get_id (display, &id, NULL); g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); g_free (id); g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager); g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display); } static void destroy_start_user_session_operation (StartUserSessionOperation *operation) { g_object_set_data (G_OBJECT (operation->session), "start-user-session-operation", NULL); g_object_unref (operation->session); g_free (operation->service_name); g_slice_free (StartUserSessionOperation, operation); } static void start_user_session (GdmManager *manager, StartUserSessionOperation *operation) { GdmDisplay *display; display = get_display_for_user_session (operation->session); if (display != NULL) { char *auth_file; const char *username; gboolean is_connected = FALSE; g_object_get (G_OBJECT (display), "is-connected", &is_connected, NULL); if (is_connected) { auth_file = NULL; username = gdm_session_get_username (operation->session); gdm_display_add_user_authorization (display, username, &auth_file, NULL); g_assert (auth_file != NULL); g_object_set (operation->session, "user-x11-authority-file", auth_file, NULL); g_free (auth_file); } } gdm_session_start_session (operation->session, operation->service_name); destroy_start_user_session_operation (operation); } static void create_display_for_user_session (GdmManager *self, GdmSession *session, const char *session_id) { GdmDisplay *display; /* at the moment we only create GdmLocalDisplay objects on seat0 */ const char *seat_id = "seat0"; display = gdm_local_display_new (); g_object_set (G_OBJECT (display), "session-class", "user", "seat-id", seat_id, "session-id", session_id, NULL); gdm_display_store_add (self->priv->display_store, display); g_object_set_data (G_OBJECT (session), "gdm-display", display); g_object_set_data_full (G_OBJECT (display), "gdm-user-session", g_object_ref (session), (GDestroyNotify) clean_user_session); } static gboolean chown_file (GFile *file, uid_t uid, gid_t gid, GError **error) { if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_UID, uid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error)) { return FALSE; } if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_GID, gid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error)) { return FALSE; } return TRUE; } static gboolean chown_recursively (GFile *dir, uid_t uid, gid_t gid, GError **error) { GFile *file = NULL; GFileInfo *info = NULL; GFileEnumerator *enumerator = NULL; gboolean retval = FALSE; if (chown_file (dir, uid, gid, error) == FALSE) { goto out; } enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_TYPE"," G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); if (!enumerator) { goto out; } while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) { file = g_file_get_child (dir, g_file_info_get_name (info)); if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { if (chown_recursively (file, uid, gid, error) == FALSE) { goto out; } } else if (chown_file (file, uid, gid, error) == FALSE) { goto out; } g_clear_object (&file); g_clear_object (&info); } if (*error) { goto out; } retval = TRUE; out: g_clear_object (&file); g_clear_object (&info); g_clear_object (&enumerator); return retval; } static void chown_initial_setup_home_dir (void) { GFile *dir; GError *error; char *gis_dir_path; char *gis_uid_path; char *gis_uid_contents; struct passwd *pwe; uid_t uid; if (!gdm_get_pwent_for_name (INITIAL_SETUP_USERNAME, &pwe)) { g_warning ("Unknown user %s", INITIAL_SETUP_USERNAME); return; } gis_dir_path = g_strdup (pwe->pw_dir); gis_uid_path = g_build_filename (gis_dir_path, "gnome-initial-setup-uid", NULL); if (!g_file_get_contents (gis_uid_path, &gis_uid_contents, NULL, NULL)) { g_warning ("Unable to read %s", gis_uid_path); goto out; } uid = (uid_t) atoi (gis_uid_contents); pwe = getpwuid (uid); if (uid == 0 || pwe == NULL) { g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path); goto out; } error = NULL; dir = g_file_new_for_path (gis_dir_path); if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) { g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message); g_error_free (error); } g_object_unref (dir); out: g_free (gis_uid_contents); g_free (gis_uid_path); g_free (gis_dir_path); } static gboolean on_start_user_session (StartUserSessionOperation *operation) { GdmManager *self = operation->manager; gboolean migrated; gboolean fail_if_already_switched = TRUE; gboolean doing_initial_setup = FALSE; GdmDisplay *display; const char *session_id; g_debug ("GdmManager: start or jump to session"); /* If there's already a session running, jump to it. * If the only session running is the one we just opened, * start a session on it. */ migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched); g_debug ("GdmManager: migrated: %d", migrated); if (migrated) { /* We don't stop the manager here because when Xorg exits it switches to the VT it was started from. That interferes with fast user switching. */ gdm_session_reset (operation->session); destroy_start_user_session_operation (operation); goto out; } display = get_display_for_user_session (operation->session); g_object_get (G_OBJECT (display), "doing-initial-setup", &doing_initial_setup, NULL); if (doing_initial_setup) chown_initial_setup_home_dir (); session_id = gdm_session_get_conversation_session_id (operation->session, operation->service_name); if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { /* In this case, the greeter's display is morphing into * the user session display. Kill the greeter on this session * and let the user session follow the same display. */ gdm_display_stop_greeter_session (display); g_object_set (G_OBJECT (display), "session-class", "user", "session-id", session_id, NULL); } else { uid_t allowed_uid; g_object_ref (display); if (doing_initial_setup) { g_autoptr(GError) error = NULL; g_debug ("GdmManager: closing down initial setup display in background"); g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL); if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, "1", 1, &error)) { g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s", ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, error->message); g_clear_error (&error); } } else { g_debug ("GdmManager: session has its display server, reusing our server for another login screen"); } /* The user session is going to follow the session worker * into the new display. Untie it from this display and * create a new session for a future user login. */ allowed_uid = gdm_session_get_allowed_user (operation->session); g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL); g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL); create_user_session_for_display (operation->manager, display, allowed_uid); /* Give the user session a new display object for bookkeeping purposes */ create_display_for_user_session (operation->manager, operation->session, session_id); if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0 && !gdm_session_client_is_connected (operation->session)) { /* remove the unused prepared greeter display since we're not going * to have a greeter */ gdm_display_store_remove (self->priv->display_store, display); g_object_unref (display); self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display"); g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display); } } start_user_session (operation->manager, operation); out: return G_SOURCE_REMOVE; } static void queue_start_user_session (GdmManager *manager, GdmSession *session, const char *service_name) { StartUserSessionOperation *operation; operation = g_slice_new0 (StartUserSessionOperation); operation->manager = manager; operation->session = g_object_ref (session); operation->service_name = g_strdup (service_name); operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation); g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation); } static void start_user_session_if_ready (GdmManager *manager, GdmSession *session, const char *service_name) { gboolean start_when_ready; start_when_ready = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "start-when-ready")); if (start_when_ready) { g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (FALSE)); queue_start_user_session (manager, session, service_name); } else { g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (TRUE)); } } static void on_session_authentication_failed (GdmSession *session, const char *service_name, GPid conversation_pid, GdmManager *manager) { add_session_record (manager, session, conversation_pid, SESSION_RECORD_FAILED); } static void on_user_session_opened (GdmSession *session, const char *service_name, const char *session_id, GdmManager *manager) { manager->priv->user_sessions = g_list_append (manager->priv->user_sessions, g_object_ref (session)); if (g_strcmp0 (service_name, "gdm-autologin") == 0 && !gdm_session_client_is_connected (session)) { /* If we're auto logging in then don't wait for the go-ahead from a greeter, * (since there is no greeter) */ g_object_set_data (G_OBJECT (session), "start-when-ready", GINT_TO_POINTER (TRUE)); } start_user_session_if_ready (manager, session, service_name); } static void on_user_session_started (GdmSession *session, const char *service_name, GPid pid, GdmManager *manager) { g_debug ("GdmManager: session started %d", pid); add_session_record (manager, session, pid, SESSION_RECORD_LOGIN); #ifdef WITH_PLYMOUTH if (g_strcmp0 (service_name, "gdm-autologin") == 0) { if (manager->priv->plymouth_is_running) { g_timeout_add_seconds (20, (GSourceFunc) plymouth_quit_with_transition, NULL); manager->priv->plymouth_is_running = FALSE; } } #endif } static void remove_user_session (GdmManager *manager, GdmSession *session) { GList *node; GdmDisplay *display; display = get_display_for_user_session (session); if (display != NULL) { gdm_display_unmanage (display); gdm_display_finish (display); } node = g_list_find (manager->priv->user_sessions, session); if (node != NULL) { manager->priv->user_sessions = g_list_delete_link (manager->priv->user_sessions, node); gdm_session_close (session); g_object_unref (session); } } static void on_session_start_failed (GdmSession *session, const char *service_name, const char *message, GdmManager *manager) { g_debug ("GdmManager: session failed to start: %s", message); remove_user_session (manager, session); } static void on_user_session_exited (GdmSession *session, int code, GdmManager *manager) { GPid pid; g_debug ("GdmManager: session exited with status %d", code); pid = gdm_session_get_pid (session); if (pid > 0) { add_session_record (manager, session, pid, SESSION_RECORD_LOGOUT); } remove_user_session (manager, session); } static void on_user_session_died (GdmSession *session, int signal_number, GdmManager *manager) { g_debug ("GdmManager: session died with signal %s", strsignal (signal_number)); remove_user_session (manager, session); } static char * get_display_device (GdmManager *manager, GdmDisplay *display) { /* systemd finds the display device out on its own based on the display */ return NULL; } static void on_session_reauthenticated (GdmSession *session, const char *service_name, GdmManager *manager) { gboolean fail_if_already_switched = FALSE; if (gdm_session_get_display_mode (session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) { const char *seat_id; char *session_id; seat_id = gdm_session_get_display_seat_id (session); if (gdm_get_login_window_session_id (seat_id, &session_id)) { GdmDisplay *display = gdm_display_store_find (manager->priv->display_store, lookup_by_session_id, (gpointer) session_id); if (display != NULL) { gdm_display_stop_greeter_session (display); gdm_display_unmanage (display); gdm_display_finish (display); } g_free (session_id); } } /* There should already be a session running, so jump to its * VT. In the event we're already on the right VT, (i.e. user * used an unlock screen instead of a user switched login screen), * then silently succeed and unlock the session. */ switch_to_compatible_user_session (manager, session, fail_if_already_switched); } static void on_session_client_ready_for_session_to_start (GdmSession *session, const char *service_name, gboolean client_is_ready, GdmManager *manager) { gboolean waiting_to_start_user_session; if (client_is_ready) { g_debug ("GdmManager: Will start session when ready"); } else { g_debug ("GdmManager: Will start session when ready and told"); } waiting_to_start_user_session = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "waiting-to-start")); g_object_set_data (G_OBJECT (session), "start-when-ready", GINT_TO_POINTER (client_is_ready)); if (client_is_ready && waiting_to_start_user_session) { start_user_session_if_ready (manager, session, service_name); } } static void on_session_client_connected (GdmSession *session, GCredentials *credentials, GPid pid_of_client, GdmManager *manager) { GdmDisplay *display; char *username; int delay; gboolean enabled; gboolean allow_timed_login = FALSE; g_debug ("GdmManager: client with pid %d connected", (int) pid_of_client); if (gdm_session_is_running (session)) { const char *session_username; session_username = gdm_session_get_username (session); g_debug ("GdmManager: ignoring connection, since session already running (for user %s)", session_username); return; } display = get_display_for_user_session (session); if (display == NULL) { return; } if (!display_is_on_seat0 (display)) { return; } #ifdef WITH_PLYMOUTH if (manager->priv->plymouth_is_running) { plymouth_quit_with_transition (); manager->priv->plymouth_is_running = FALSE; } #endif g_object_get (G_OBJECT (display), "allow-timed-login", &allow_timed_login, NULL); if (!allow_timed_login) { return; } enabled = get_timed_login_details (manager, &username, &delay); if (! enabled) { return; } gdm_session_set_timed_login_details (session, username, delay); g_debug ("GdmManager: Starting automatic login conversation (for timed login)"); gdm_session_start_conversation (session, "gdm-autologin"); g_free (username); } static void on_session_client_disconnected (GdmSession *session, GCredentials *credentials, GPid pid_of_client, GdmManager *manager) { g_debug ("GdmManager: client with pid %d disconnected", (int) pid_of_client); } typedef struct { GdmManager *manager; GdmSession *session; guint idle_id; } ResetSessionOperation; static void destroy_reset_session_operation (ResetSessionOperation *operation) { g_object_set_data (G_OBJECT (operation->session), "reset-session-operation", NULL); g_object_unref (operation->session); g_slice_free (ResetSessionOperation, operation); } static gboolean on_reset_session (ResetSessionOperation *operation) { gdm_session_reset (operation->session); destroy_reset_session_operation (operation); return G_SOURCE_REMOVE; } static void queue_session_reset (GdmManager *manager, GdmSession *session) { ResetSessionOperation *operation; operation = g_object_get_data (G_OBJECT (session), "reset-session-operation"); if (operation != NULL) { return; } operation = g_slice_new0 (ResetSessionOperation); operation->manager = manager; operation->session = g_object_ref (session); operation->idle_id = g_idle_add ((GSourceFunc) on_reset_session, operation); g_object_set_data (G_OBJECT (session), "reset-session-operation", operation); } static void on_session_cancelled (GdmSession *session, GdmManager *manager) { g_debug ("GdmManager: Session was cancelled"); queue_session_reset (manager, session); } static void on_session_conversation_started (GdmSession *session, const char *service_name, GdmManager *manager) { GdmDisplay *display; gboolean enabled; char *username; g_debug ("GdmManager: session conversation started for service %s on session", service_name); if (g_strcmp0 (service_name, "gdm-autologin") != 0) { g_debug ("GdmManager: ignoring session conversation since its not automatic login conversation"); return; } display = get_display_for_user_session (session); if (display == NULL) { g_debug ("GdmManager: conversation has no associated display"); return; } if (!display_is_on_seat0 (display)) { return; } enabled = get_automatic_login_details (manager, &username); if (! enabled) { return; } g_debug ("GdmManager: begin auto login for user '%s'", username); /* service_name will be "gdm-autologin" */ gdm_session_setup_for_user (session, service_name, username); g_free (username); } static void on_session_conversation_stopped (GdmSession *session, const char *service_name, GdmManager *manager) { g_debug ("GdmManager: session conversation '%s' stopped", service_name); } static void on_session_reauthentication_started (GdmSession *session, int pid_of_caller, const char *address, GdmManager *manager) { GDBusMethodInvocation *invocation; gpointer source_tag; g_debug ("GdmManager: reauthentication started"); source_tag = GINT_TO_POINTER (pid_of_caller); invocation = g_hash_table_lookup (manager->priv->open_reauthentication_requests, source_tag); if (invocation != NULL) { g_hash_table_steal (manager->priv->open_reauthentication_requests, source_tag); gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager), invocation, address); } } static void clean_user_session (GdmSession *session) { g_object_set_data (G_OBJECT (session), "gdm-display", NULL); g_object_unref (session); } static void create_user_session_for_display (GdmManager *manager, GdmDisplay *display, uid_t allowed_user) { GdmSession *session; gboolean display_is_local = FALSE; char *display_name = NULL; char *display_device = NULL; char *remote_hostname = NULL; char *display_auth_file = NULL; char *display_seat_id = NULL; char *display_id = NULL; g_auto (GStrv) supported_session_types = NULL; g_object_get (G_OBJECT (display), "id", &display_id, "x11-display-name", &display_name, "is-local", &display_is_local, "remote-hostname", &remote_hostname, "x11-authority-file", &display_auth_file, "seat-id", &display_seat_id, "supported-session-types", &supported_session_types, NULL); display_device = get_display_device (manager, display); session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN, allowed_user, display_name, remote_hostname, display_device, display_seat_id, display_auth_file, display_is_local, NULL); g_object_set (G_OBJECT (session), "supported-session-types", supported_session_types, NULL); g_debug ("GdmSession: Created user session for user %d on display %s (seat %s)", (int) allowed_user, display_id, display_seat_id); g_free (display_name); g_free (remote_hostname); g_free (display_auth_file); g_free (display_seat_id); g_signal_connect (session, "reauthentication-started", G_CALLBACK (on_session_reauthentication_started), manager); g_signal_connect (session, "reauthenticated", G_CALLBACK (on_session_reauthenticated), manager); g_signal_connect (session, "client-ready-for-session-to-start", G_CALLBACK (on_session_client_ready_for_session_to_start), manager); g_signal_connect (session, "client-connected", G_CALLBACK (on_session_client_connected), manager); g_signal_connect (session, "client-disconnected", G_CALLBACK (on_session_client_disconnected), manager); g_signal_connect (session, "cancelled", G_CALLBACK (on_session_cancelled), manager); g_signal_connect (session, "conversation-started", G_CALLBACK (on_session_conversation_started), manager); g_signal_connect (session, "conversation-stopped", G_CALLBACK (on_session_conversation_stopped), manager); g_signal_connect (session, "authentication-failed", G_CALLBACK (on_session_authentication_failed), manager); g_signal_connect (session, "session-opened", G_CALLBACK (on_user_session_opened), manager); g_signal_connect (session, "session-started", G_CALLBACK (on_user_session_started), manager); g_signal_connect (session, "session-start-failed", G_CALLBACK (on_session_start_failed), manager); g_signal_connect (session, "session-exited", G_CALLBACK (on_user_session_exited), manager); g_signal_connect (session, "session-died", G_CALLBACK (on_user_session_died), manager); g_object_set_data (G_OBJECT (session), "gdm-display", display); g_object_set_data_full (G_OBJECT (display), "gdm-user-session", session, (GDestroyNotify) clean_user_session); } static void on_display_added (GdmDisplayStore *display_store, const char *id, GdmManager *manager) { GdmDisplay *display; display = gdm_display_store_lookup (display_store, id); if (display != NULL) { g_dbus_object_manager_server_export (manager->priv->object_manager, gdm_display_get_object_skeleton (display)); g_signal_connect (display, "notify::status", G_CALLBACK (on_display_status_changed), manager); g_signal_emit (manager, signals[DISPLAY_ADDED], 0, id); } } GQuark gdm_manager_error_quark (void) { static GQuark ret = 0; if (ret == 0) { ret = g_quark_from_static_string ("gdm_manager_error"); } return ret; } static void listify_display_ids (const char *id, GdmDisplay *display, GPtrArray **array) { g_ptr_array_add (*array, g_strdup (id)); } /* Example: dbus-send --system --dest=org.gnome.DisplayManager \ --type=method_call --print-reply --reply-timeout=2000 \ /org/gnome/DisplayManager/Displays \ org.freedesktop.ObjectManager.GetAll */ gboolean gdm_manager_get_displays (GdmManager *manager, GPtrArray **displays, GError **error) { g_return_val_if_fail (GDM_IS_MANAGER (manager), FALSE); if (displays == NULL) { return FALSE; } *displays = g_ptr_array_new (); gdm_display_store_foreach (manager->priv->display_store, (GdmDisplayStoreFunc)listify_display_ids, displays); return TRUE; } void gdm_manager_stop (GdmManager *manager) { g_debug ("GdmManager: GDM stopping"); if (manager->priv->local_factory != NULL) { gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->local_factory)); } #ifdef HAVE_LIBXDMCP if (manager->priv->xdmcp_factory != NULL) { gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory)); } #endif manager->priv->started = FALSE; } void gdm_manager_start (GdmManager *manager) { g_debug ("GdmManager: GDM starting to manage displays"); #ifdef WITH_PLYMOUTH manager->priv->plymouth_is_running = plymouth_is_running (); if (manager->priv->plymouth_is_running) { plymouth_prepare_for_transition (); } #endif if (!manager->priv->xdmcp_enabled || manager->priv->show_local_greeter) { gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->local_factory)); } #ifdef HAVE_LIBXDMCP /* Accept remote connections */ if (manager->priv->xdmcp_enabled) { #ifdef WITH_PLYMOUTH /* Quit plymouth if xdmcp is the only display */ if (!manager->priv->show_local_greeter && manager->priv->plymouth_is_running) { plymouth_quit_without_transition (); manager->priv->plymouth_is_running = FALSE; } #endif if (manager->priv->xdmcp_factory != NULL) { g_debug ("GdmManager: Accepting XDMCP connections..."); gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory)); } } #endif manager->priv->started = TRUE; } static gboolean register_manager (GdmManager *manager) { GError *error = NULL; GDBusObjectManagerServer *object_server; error = NULL; manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (manager->priv->connection == NULL) { g_critical ("error getting system bus: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } object_server = g_dbus_object_manager_server_new (GDM_MANAGER_DISPLAYS_PATH); g_dbus_object_manager_server_set_connection (object_server, manager->priv->connection); manager->priv->object_manager = object_server; if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager), manager->priv->connection, GDM_MANAGER_PATH, &error)) { g_critical ("error exporting interface to %s: %s", GDM_MANAGER_PATH, error->message); g_error_free (error); exit (EXIT_FAILURE); } return TRUE; } void gdm_manager_set_xdmcp_enabled (GdmManager *manager, gboolean enabled) { g_return_if_fail (GDM_IS_MANAGER (manager)); if (manager->priv->xdmcp_enabled != enabled) { manager->priv->xdmcp_enabled = enabled; #ifdef HAVE_LIBXDMCP if (manager->priv->xdmcp_enabled) { manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store); if (manager->priv->started) { gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory)); } } else { if (manager->priv->started) { gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory)); } g_object_unref (manager->priv->xdmcp_factory); manager->priv->xdmcp_factory = NULL; } #endif } } void gdm_manager_set_show_local_greeter (GdmManager *manager, gboolean show_local_greeter) { g_return_if_fail (GDM_IS_MANAGER (manager)); manager->priv->show_local_greeter = show_local_greeter; } static void gdm_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GdmManager *self; self = GDM_MANAGER (object); switch (prop_id) { case PROP_XDMCP_ENABLED: gdm_manager_set_xdmcp_enabled (self, g_value_get_boolean (value)); break; case PROP_SHOW_LOCAL_GREETER: gdm_manager_set_show_local_greeter (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gdm_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GdmManager *self; self = GDM_MANAGER (object); switch (prop_id) { case PROP_XDMCP_ENABLED: g_value_set_boolean (value, self->priv->xdmcp_enabled); break; case PROP_SHOW_LOCAL_GREETER: g_value_set_boolean (value, self->priv->show_local_greeter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gdm_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GdmManager *manager; manager = GDM_MANAGER (G_OBJECT_CLASS (gdm_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); gdm_dbus_manager_set_version (GDM_DBUS_MANAGER (manager), PACKAGE_VERSION); manager->priv->local_factory = gdm_local_display_factory_new (manager->priv->display_store); #ifdef HAVE_LIBXDMCP if (manager->priv->xdmcp_enabled) { manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store); } #endif return G_OBJECT (manager); } static void gdm_manager_class_init (GdmManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = gdm_manager_get_property; object_class->set_property = gdm_manager_set_property; object_class->constructor = gdm_manager_constructor; object_class->dispose = gdm_manager_dispose; signals [DISPLAY_ADDED] = g_signal_new ("display-added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdmManagerClass, display_added), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals [DISPLAY_REMOVED] = g_signal_new ("display-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GdmManagerClass, display_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); g_object_class_install_property (object_class, PROP_XDMCP_ENABLED, g_param_spec_boolean ("xdmcp-enabled", NULL, NULL, FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); } static void gdm_manager_init (GdmManager *manager) { manager->priv = GDM_MANAGER_GET_PRIVATE (manager); manager->priv->display_store = gdm_display_store_new (); manager->priv->user_sessions = NULL; manager->priv->open_reauthentication_requests = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) NULL, (GDestroyNotify) g_object_unref); manager->priv->transient_sessions = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) NULL, (GDestroyNotify) g_object_unref); g_signal_connect (G_OBJECT (manager->priv->display_store), "display-added", G_CALLBACK (on_display_added), manager); g_signal_connect (G_OBJECT (manager->priv->display_store), "display-removed", G_CALLBACK (on_display_removed), manager); } static void unexport_display (const char *id, GdmDisplay *display, GdmManager *manager) { if (!g_dbus_connection_is_closed (manager->priv->connection)) g_dbus_object_manager_server_unexport (manager->priv->object_manager, id); } static void finish_display (const char *id, GdmDisplay *display, GdmManager *manager) { gdm_display_stop_greeter_session (display); if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) gdm_display_unmanage (display); gdm_display_finish (display); } static void gdm_manager_dispose (GObject *object) { GdmManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GDM_IS_MANAGER (object)); manager = GDM_MANAGER (object); g_return_if_fail (manager->priv != NULL); gdm_manager_stop (manager); g_clear_weak_pointer (&manager->priv->automatic_login_display); #ifdef HAVE_LIBXDMCP g_clear_object (&manager->priv->xdmcp_factory); #endif g_clear_object (&manager->priv->local_factory); g_clear_pointer (&manager->priv->open_reauthentication_requests, g_hash_table_unref); g_clear_pointer (&manager->priv->transient_sessions, g_hash_table_unref); g_list_foreach (manager->priv->user_sessions, (GFunc) gdm_session_close, NULL); g_list_free_full (manager->priv->user_sessions, (GDestroyNotify) g_object_unref); manager->priv->user_sessions = NULL; g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store), G_CALLBACK (on_display_added), manager); g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store), G_CALLBACK (on_display_removed), manager); if (!g_dbus_connection_is_closed (manager->priv->connection)) { gdm_display_store_foreach (manager->priv->display_store, (GdmDisplayStoreFunc)unexport_display, manager); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (manager)); } gdm_display_store_foreach (manager->priv->display_store, (GdmDisplayStoreFunc) finish_display, manager); gdm_display_store_clear (manager->priv->display_store); g_dbus_object_manager_server_set_connection (manager->priv->object_manager, NULL); g_clear_object (&manager->priv->connection); g_clear_object (&manager->priv->object_manager); g_clear_object (&manager->priv->display_store); G_OBJECT_CLASS (gdm_manager_parent_class)->dispose (object); } GdmManager * gdm_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { gboolean res; manager_object = g_object_new (GDM_TYPE_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); res = register_manager (manager_object); if (! res) { g_object_unref (manager_object); return NULL; } } return GDM_MANAGER (manager_object); }