# # Description: Add a new D-Bus operation StartGuestSession(). # If setup/teardown scripts are available in # /usr/share/gdm/guest-session/ (shipped by separate gdm-guest-user # package), run a guest session without requiring a password. # Guest sessions call /usr/share/gdm/guest-session/Xsession instead of # /etc/gdm/Xsession, so that we can wrap AppArmor (or other MAC # system) rules around it. # Ubuntu: https://wiki.ubuntu.com/DesktopTeam/Specs/Intrepid/GuestAccount # diff -Nur -x '*.orig' -x '*~' gdm-2.30.0/daemon/gdm-local-display-factory.c gdm-2.30.0.new/daemon/gdm-local-display-factory.c --- gdm-2.30.0/daemon/gdm-local-display-factory.c 2010-03-29 23:42:03.000000000 +0200 +++ gdm-2.30.0.new/daemon/gdm-local-display-factory.c 2010-04-14 14:51:08.306602571 +0200 @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include @@ -45,6 +47,8 @@ #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory" #define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory" +#define GUEST_USERNAME "guest" + #define MAX_DISPLAY_FAILURES 5 struct GdmLocalDisplayFactoryPrivate @@ -228,6 +232,270 @@ return ret; } +/* GdmGuestDisplay */ + +typedef struct +{ + GdmTransientDisplayClass parent_class; +} GdmGuestDisplayClass; + +typedef struct +{ + GdmTransientDisplay parent; + GdmTransientDisplayPrivate *priv; +} GdmGuestDisplay; + +#define GDM_TYPE_GUEST_DISPLAY (gdm_guest_display_get_type ()) +#define GDM_GUEST_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GUEST_DISPLAY, GdmGuestDisplayClass)) +GType gdm_guest_display_get_type (void); +static void gdm_guest_display_class_init (GdmGuestDisplayClass *klass); +static void gdm_guest_display_init (GdmGuestDisplay *display) {} +GdmDisplay * gdm_guest_display_new (int display_number); +static gboolean gdm_guest_display_finish (GdmDisplay *display); + +G_DEFINE_TYPE (GdmGuestDisplay, gdm_guest_display, GDM_TYPE_TRANSIENT_DISPLAY); + +/* override timed_login_details for guest session */ +static void +gdm_guest_display_get_timed_login_details (GdmDisplay *display, + gboolean *enabledp, + char **usernamep, + int *delayp) +{ + g_debug ("GdmLocalDisplayFactory: Getting guest timed login details"); + *enabledp = TRUE; + *usernamep = g_strdup(GUEST_USERNAME); + *delayp = 0; +} + +static void +gdm_guest_display_class_init (GdmGuestDisplayClass *klass) +{ + GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass); + + display_class->get_timed_login_details = gdm_guest_display_get_timed_login_details; + display_class->finish = gdm_guest_display_finish; +} + +GdmDisplay * +gdm_guest_display_new (int display_number) +{ + GObject *object; + char *x11_display; + + x11_display = g_strdup_printf (":%d", display_number); + object = g_object_new (GDM_TYPE_GUEST_DISPLAY, + "x11-display-number", display_number, + "x11-display-name", x11_display, + NULL); + g_free (x11_display); + + return GDM_DISPLAY (object); +} + +static +gboolean +gdm_guest_display_finish (GdmDisplay *display) +{ + GError *err = NULL; + gboolean result; + gint status; + struct sigaction dfl, old_act; + const char* argv[] = { + "/usr/share/gdm/guest-session/guest-session-cleanup.sh", + GUEST_USERNAME, NULL}; + + /* temporarily reset SIGCHLD, we need it for g_spawn_sync */ + dfl.sa_handler = SIG_DFL; + dfl.sa_flags = SA_RESTART|SA_NOCLDSTOP; + sigemptyset (&dfl.sa_mask); + g_assert (sigaction (SIGCHLD, &dfl, &old_act) == 0); + + /* destroy guest user again */ + result = g_spawn_sync ("/", (gchar**) argv, NULL, 0, NULL, NULL, NULL, + NULL, &status, &err); + + g_assert (sigaction (SIGCHLD, &old_act, NULL) == 0); + + if (!result) { + g_warning ("gdm_guest_display_finish: Calling '%s %s' failed: %s", argv[0], + argv[1], err->message); + g_error_free (err); + } + + return GDM_DISPLAY_CLASS (gdm_guest_display_parent_class)->finish (display); +} + +/* End GdmGuestDisplay */ + +static gboolean +gdm_local_display_factory_setup_guest_account (const char *current_user_session) +{ + GError *err = NULL; + gboolean result; + gchar *sout, *serr; + char *username; + gint status; + int len; + struct sigaction dfl, old_act; + const char* argv[] = { + "/usr/share/gdm/guest-session/guest-session-setup.sh", + current_user_session, NULL, NULL}; /* leave enough room for a second argument */ + + g_debug ("gdm_local_display_factory_setup_guest_account: Calling guest-session-setup.sh %s", current_user_session); + + /* temporarily reset SIGCHLD, we need it for g_spawn_sync */ + dfl.sa_handler = SIG_DFL; + dfl.sa_flags = SA_RESTART|SA_NOCLDSTOP; + sigemptyset (&dfl.sa_mask); + if (sigaction (SIGCHLD, &dfl, &old_act) < 0) { + g_warning("gdm_local_display_factory_setup_guest_account: failure to temporarily restore SIGCHLD: %s", + strerror(errno)); + return FALSE; + } + + /* call guest setup script */ + result = g_spawn_sync ("/", (gchar**) argv, NULL, 0, NULL, NULL, &sout, + &serr, &status, &err); + g_assert (sigaction (SIGCHLD, &old_act, NULL) == 0); + if (!result) { + g_warning ("gdm_local_display_factory_setup_guest_account: Calling %s failed: %s", argv[0], + err->message); + g_error_free (err); + return FALSE; + } + if (status != 0) { + g_warning ("gdm_local_display_factory_setup_guest_account: %s failed with status %i:\n%s\n%s", + argv[0], status, sout, serr); + g_free(sout); + g_free(serr); + return FALSE; + } + g_free (serr); + + /* extract user name from stdout */ + len = strlen (sout); + if (sout[len-1] == '\n') + sout[len-1] = 0; + username = strrchr (sout, '\n'); + if (!username || strcmp (username + 1, GUEST_USERNAME)) { + g_warning ("gdm_local_display_factory_setup_guest_account: no output, last line of stdout must have username; or username is not 'guest'"); + g_free (sout); + return FALSE; + } + g_debug ("gdm_local_display_factory_setup_guest_account: %s succeeded, username: '%s'", argv[0], username+1); + /* if we ever need to pass it to outside: */ + /* username = g_strdup (username + 1); */ + g_free (sout); + + return TRUE; +} + +static gboolean +switch_to_guest_display (GdmLocalDisplayFactory *factory) +{ + struct passwd *password; + DBusGProxy *proxy; + GPtrArray *sessions = NULL; + GError *error = NULL; + gboolean result = FALSE; + + password = getpwnam (GUEST_USERNAME); + if (!password) { + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (factory->priv->connection, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager"); + + dbus_g_proxy_call (proxy, "GetSessionsForUnixUser", &error, + G_TYPE_UINT, password->pw_uid, G_TYPE_INVALID, + dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions, G_TYPE_INVALID); + g_object_unref(proxy); + if (error != NULL) { + g_warning ("Error getting guest sessions: %s", error->message); + g_error_free (error); + } + + if (sessions && sessions->len > 0) { + gchar *session_id = sessions->pdata[0]; + + g_debug ("GdmLocalDisplayFactory: Switching to guest session %s", session_id); + + proxy = dbus_g_proxy_new_for_name (factory->priv->connection, + "org.freedesktop.ConsoleKit", + session_id, + "org.freedesktop.ConsoleKit.Session"); + result = dbus_g_proxy_call (proxy, "Activate", &error, G_TYPE_INVALID, G_TYPE_INVALID); + g_object_unref (proxy); + if (error != NULL) + { + g_warning ("Error activating guest session: %s", error->message); + g_error_free (error); + } + } + + if (sessions != NULL) { + gint i; + for (i = 0; i < sessions->len; i++) { + g_free (sessions->pdata[i]); + } + g_ptr_array_free (sessions, TRUE); + } + + return result; +} + +gboolean +gdm_local_display_factory_start_guest_session (GdmLocalDisplayFactory *factory, + const char *current_user_session, + char **id, + GError **error) +{ + GdmDisplay *display = NULL; + guint32 num; + + g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); + + /* Switch to existing guest display */ + if (switch_to_guest_display (factory)) { + /* FIXME: How to return the ID of the guest display? It should + * be easy but I can't find how to get it */ + /*if (id != NULL) { + *id = g_strdup ("FIXME"); + }*/ + + /* FIXME: We should return TRUE here but this causes GDM to go + * crazy. Luckily we can return FALSE as we don't need + * any values returned from this call */ + return FALSE; + } + + if (!gdm_local_display_factory_setup_guest_account(current_user_session)) { + return FALSE; + } + + num = take_next_display_number (factory); + + g_debug ("GdmLocalDisplayFactory: Starting Guest %s Session %d", current_user_session, num); + + display = gdm_guest_display_new (num); + + /* FIXME: don't hardcode seat1? */ + g_object_set (display, "seat-id", CK_SEAT1_PATH, NULL); + + store_display (factory, num, display); + + if (! gdm_display_manage (display) || ! gdm_display_get_id (display, id, NULL)) { + return FALSE; + } else { + g_object_unref (display); + return TRUE; + } +} + gboolean gdm_local_display_factory_create_product_display (GdmLocalDisplayFactory *factory, const char *parent_display_id, diff -Nur -x '*.orig' -x '*~' gdm-2.30.0/daemon/gdm-local-display-factory.h gdm-2.30.0.new/daemon/gdm-local-display-factory.h --- gdm-2.30.0/daemon/gdm-local-display-factory.h 2010-03-29 23:42:03.000000000 +0200 +++ gdm-2.30.0.new/daemon/gdm-local-display-factory.h 2010-04-14 14:50:03.194778506 +0200 @@ -65,6 +65,11 @@ char **id, GError **error); +gboolean gdm_local_display_factory_start_guest_session (GdmLocalDisplayFactory *factory, + const char *current_user_session, + char **id, + GError **error); + gboolean gdm_local_display_factory_create_product_display (GdmLocalDisplayFactory *factory, const char *parent_display_id, const char *relay_address, diff -Nur -x '*.orig' -x '*~' gdm-2.30.0/daemon/gdm-local-display-factory.xml gdm-2.30.0.new/daemon/gdm-local-display-factory.xml --- gdm-2.30.0/daemon/gdm-local-display-factory.xml 2010-03-29 23:42:03.000000000 +0200 +++ gdm-2.30.0.new/daemon/gdm-local-display-factory.xml 2010-04-14 14:49:50.358604017 +0200 @@ -9,5 +9,9 @@ + + + + diff -Nur -x '*.orig' -x '*~' gdm-2.30.0/data/gdm.conf.in gdm-2.30.0.new/data/gdm.conf.in --- gdm-2.30.0/data/gdm.conf.in 2010-04-14 14:49:49.439607398 +0200 +++ gdm-2.30.0.new/data/gdm.conf.in 2010-04-14 14:49:50.358604017 +0200 @@ -68,6 +68,9 @@ +