1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
4  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #ifdef WITH_SYSTEMD
32 #include <sys/ioctl.h>
33 #include <sys/vt.h>
34 #include <sys/kd.h>
35 #endif
36 #include <errno.h>
37 #include <grp.h>
38 #include <pwd.h>
39 
40 #include <security/pam_appl.h>
41 
42 #ifdef HAVE_LOGINCAP
43 #include <login_cap.h>
44 #endif
45 
46 #include <glib.h>
47 #include <glib/gi18n.h>
48 #include <glib/gstdio.h>
49 #include <glib-object.h>
50 #include <gio/gio.h>
51 
52 #include <X11/Xauth.h>
53 
54 #ifdef WITH_SYSTEMD
55 #include <systemd/sd-daemon.h>
56 #endif
57 
58 #ifdef ENABLE_SYSTEMD_JOURNAL
59 #include <systemd/sd-journal.h>
60 #endif
61 
62 #ifdef HAVE_SELINUX
63 #include <selinux/selinux.h>
64 #endif /* HAVE_SELINUX */
65 
66 #include "gdm-common.h"
67 #include "gdm-log.h"
68 
69 #ifdef SUPPORTS_PAM_EXTENSIONS
70 #include "gdm-pam-extensions.h"
71 #endif
72 
73 #include "gdm-session-worker.h"
74 #include "gdm-session-glue.h"
75 #include "gdm-session.h"
76 
77 #if defined (HAVE_ADT)
78 #include "gdm-session-solaris-auditor.h"
79 #elif defined (HAVE_LIBAUDIT)
80 #include "gdm-session-linux-auditor.h"
81 #else
82 #include "gdm-session-auditor.h"
83 #endif
84 
85 #include "gdm-session-settings.h"
86 
87 #define GDM_SESSION_WORKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerPrivate))
88 
89 #define GDM_SESSION_DBUS_PATH         "/org/gnome/DisplayManager/Session"
90 #define GDM_SESSION_DBUS_NAME         "org.gnome.DisplayManager.Session"
91 #define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
92 
93 #define GDM_WORKER_DBUS_PATH "/org/gnome/DisplayManager/Worker"
94 
95 #ifndef GDM_PASSWD_AUXILLARY_BUFFER_SIZE
96 #define GDM_PASSWD_AUXILLARY_BUFFER_SIZE 1024
97 #endif
98 
99 #ifndef GDM_SESSION_DEFAULT_PATH
100 #define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin"
101 #endif
102 
103 #ifndef GDM_SESSION_ROOT_UID
104 #define GDM_SESSION_ROOT_UID 0
105 #endif
106 
107 #ifndef GDM_SESSION_LOG_FILENAME
108 #define GDM_SESSION_LOG_FILENAME "session.log"
109 #endif
110 
111 #define MAX_FILE_SIZE     65536
112 #define MAX_LOGS          5
113 
114 #define RELEASE_DISPLAY_SIGNAL (SIGRTMAX)
115 #define ACQUIRE_DISPLAY_SIGNAL (SIGRTMAX - 1)
116 
117 enum {
118         GDM_SESSION_WORKER_STATE_NONE = 0,
119         GDM_SESSION_WORKER_STATE_SETUP_COMPLETE,
120         GDM_SESSION_WORKER_STATE_AUTHENTICATED,
121         GDM_SESSION_WORKER_STATE_AUTHORIZED,
122         GDM_SESSION_WORKER_STATE_ACCREDITED,
123         GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED,
124         GDM_SESSION_WORKER_STATE_SESSION_OPENED,
125         GDM_SESSION_WORKER_STATE_SESSION_STARTED
126 };
127 
128 typedef struct
129 {
130         GdmSessionWorker *worker;
131         GdmSession       *session;
132         GPid              pid_of_caller;
133         uid_t             uid_of_caller;
134 
135 } ReauthenticationRequest;
136 
137 struct GdmSessionWorkerPrivate
138 {
139         int               state;
140 
141         int               exit_code;
142 
143 #ifdef WITH_CONSOLE_KIT
144         char             *session_cookie;
145 #endif
146 
147         pam_handle_t     *pam_handle;
148 
149         GPid              child_pid;
150         guint             child_watch_id;
151 
152         /* from Setup */
153         char             *service;
154         char             *x11_display_name;
155         char             *x11_authority_file;
156         char             *display_device;
157         char             *display_seat_id;
158         char             *hostname;
159         char             *username;
160         char             *log_file;
161         char             *session_type;
162         char             *session_id;
163         uid_t             uid;
164         gid_t             gid;
165         gboolean          password_is_required;
166         char            **extensions;
167 
168         int               cred_flags;
169         int               login_vt;
170         int               session_vt;
171         int               session_tty_fd;
172 
173         char            **arguments;
174         guint32           cancelled : 1;
175         guint32           timed_out : 1;
176         guint32           is_program_session : 1;
177         guint32           is_reauth_session : 1;
178         guint32           display_is_local : 1;
179         guint32           display_is_initial : 1;
180         guint             state_change_idle_id;
181         GdmSessionDisplayMode display_mode;
182 
183         char                 *server_address;
184         GDBusConnection      *connection;
185         GdmDBusWorkerManager *manager;
186 
187         GHashTable         *reauthentication_requests;
188 
189         GdmSessionAuditor  *auditor;
190         GdmSessionSettings *user_settings;
191 
192         GDBusMethodInvocation *pending_invocation;
193 };
194 
195 #ifdef SUPPORTS_PAM_EXTENSIONS
196 static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
197 
198 static const char * const
199 gdm_supported_pam_extensions[] = {
200         GDM_PAM_EXTENSION_CHOICE_LIST,
201         NULL
202 };
203 #endif
204 
205 enum {
206         PROP_0,
207         PROP_SERVER_ADDRESS,
208         PROP_IS_REAUTH_SESSION,
209 };
210 
211 static void     gdm_session_worker_class_init   (GdmSessionWorkerClass *klass);
212 static void     gdm_session_worker_init         (GdmSessionWorker      *session_worker);
213 static void     gdm_session_worker_finalize     (GObject               *object);
214 
215 static void     gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
216                                                              const char       *key,
217                                                              const char       *value);
218 
219 static void     queue_state_change              (GdmSessionWorker      *worker);
220 
221 static void     worker_interface_init           (GdmDBusWorkerIface *iface);
222 
223 
224 typedef int (* GdmSessionWorkerPamNewMessagesFunc) (int,
225                                                     const struct pam_message **,
226                                                     struct pam_response **,
227                                                     gpointer);
228 
G_DEFINE_TYPE_WITH_CODE(GdmSessionWorker,gdm_session_worker,GDM_DBUS_TYPE_WORKER_SKELETON,G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_WORKER,worker_interface_init))229 G_DEFINE_TYPE_WITH_CODE (GdmSessionWorker,
230                          gdm_session_worker,
231                          GDM_DBUS_TYPE_WORKER_SKELETON,
232                          G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_WORKER,
233                                                 worker_interface_init))
234 
235 #ifdef WITH_CONSOLE_KIT
236 static gboolean
237 open_ck_session (GdmSessionWorker  *worker)
238 {
239         GDBusConnection  *system_bus;
240         GVariantBuilder   builder;
241         GVariant         *parameters;
242         GVariant         *in_args;
243         struct passwd    *pwent;
244         GVariant         *reply;
245         GError           *error = NULL;
246         const char       *display_name;
247         const char       *display_device;
248         const char       *display_hostname;
249         const char       *session_type;
250         gint32            uid;
251 
252         g_assert (worker->priv->session_cookie == NULL);
253 
254         if (worker->priv->x11_display_name != NULL) {
255                 display_name = worker->priv->x11_display_name;
256         } else {
257                 display_name = "";
258         }
259         if (worker->priv->hostname != NULL) {
260                 display_hostname = worker->priv->hostname;
261         } else {
262                 display_hostname = "";
263         }
264         if (worker->priv->display_device != NULL) {
265                 display_device = worker->priv->display_device;
266         } else {
267                 display_device = "";
268         }
269 
270         if (worker->priv->session_type != NULL) {
271                 session_type = worker->priv->session_type;
272         } else {
273                 session_type = "";
274         }
275 
276         g_assert (worker->priv->username != NULL);
277 
278         gdm_get_pwent_for_name (worker->priv->username, &pwent);
279         if (pwent == NULL) {
280                 goto out;
281         }
282 
283         uid = (gint32) pwent->pw_uid;
284 
285         error = NULL;
286         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
287 
288         if (system_bus == NULL) {
289                 g_warning ("Couldn't create connection to system bus: %s",
290                            error->message);
291 
292                 g_error_free (error);
293                 goto out;
294         }
295 
296         g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sv)"));
297         g_variant_builder_add_parsed (&builder, "('unix-user', <%i>)", uid);
298         g_variant_builder_add_parsed (&builder, "('x11-display-device', <%s>)", display_device);
299         g_variant_builder_add_parsed (&builder, "('x11-display', <%s>)", display_name);
300         g_variant_builder_add_parsed (&builder, "('remote-host-name', <%s>)", display_hostname);
301         g_variant_builder_add_parsed (&builder, "('is-local', <%b>)", worker->priv->display_is_local);
302         g_variant_builder_add_parsed (&builder, "('session-type', <%s>)", session_type);
303 
304         parameters = g_variant_builder_end (&builder);
305         in_args = g_variant_new_tuple (&parameters, 1);
306 
307         reply = g_dbus_connection_call_sync (system_bus,
308                                              "org.freedesktop.ConsoleKit",
309                                              "/org/freedesktop/ConsoleKit/Manager",
310                                              "org.freedesktop.ConsoleKit.Manager",
311                                              "OpenSessionWithParameters",
312                                              in_args,
313                                              G_VARIANT_TYPE ("(s)"),
314                                              G_DBUS_CALL_FLAGS_NONE,
315                                              -1,
316                                              NULL,
317                                              &error);
318 
319         if (! reply) {
320                 g_warning ("%s\n", error->message);
321                 g_clear_error (&error);
322                 goto out;
323         }
324 
325         g_variant_get (reply, "(s)", &worker->priv->session_cookie);
326 
327         g_variant_unref (reply);
328 
329 out:
330         return worker->priv->session_cookie != NULL;
331 }
332 
333 static void
close_ck_session(GdmSessionWorker * worker)334 close_ck_session (GdmSessionWorker *worker)
335 {
336         GDBusConnection  *system_bus;
337         GVariant         *reply;
338         GError           *error = NULL;
339         gboolean          was_closed;
340 
341         if (worker->priv->session_cookie == NULL) {
342                 return;
343         }
344 
345         error = NULL;
346         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
347 
348         if (system_bus == NULL) {
349                 g_warning ("Couldn't create connection to system bus: %s",
350                            error->message);
351 
352                 g_error_free (error);
353                 goto out;
354         }
355 
356         reply = g_dbus_connection_call_sync (system_bus,
357                                              "org.freedesktop.ConsoleKit",
358                                              "/org/freedesktop/ConsoleKit/Manager",
359                                              "org.freedesktop.ConsoleKit.Manager",
360                                              "CloseSession",
361                                              g_variant_new ("(s)", worker->priv->session_cookie),
362                                              G_VARIANT_TYPE ("(b)"),
363                                              G_DBUS_CALL_FLAGS_NONE,
364                                              -1,
365                                              NULL,
366                                              &error);
367 
368         if (! reply) {
369                 g_warning ("%s", error->message);
370                 g_clear_error (&error);
371                 goto out;
372         }
373 
374         g_variant_get (reply, "(b)", &was_closed);
375 
376         if (!was_closed) {
377                 g_warning ("Unable to close ConsoleKit session");
378         }
379 
380         g_variant_unref (reply);
381 
382 out:
383         g_clear_pointer (&worker->priv->session_cookie,
384                          (GDestroyNotify) g_free);
385 }
386 
387 static char *
get_ck_session_id(GdmSessionWorker * worker)388 get_ck_session_id (GdmSessionWorker *worker)
389 {
390         GDBusConnection  *system_bus;
391         GVariant         *reply;
392         GError           *error = NULL;
393         char             *session_id = NULL;
394 
395         error = NULL;
396         system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
397 
398         if (system_bus == NULL) {
399                 g_warning ("Couldn't create connection to system bus: %s",
400                            error->message);
401 
402                 g_error_free (error);
403                 goto out;
404         }
405 
406         reply = g_dbus_connection_call_sync (system_bus,
407                                              "org.freedesktop.ConsoleKit",
408                                              "/org/freedesktop/ConsoleKit/Manager",
409                                              "org.freedesktop.ConsoleKit.Manager",
410                                              "GetSessionForCookie",
411                                              g_variant_new ("(s)", worker->priv->session_cookie),
412                                              G_VARIANT_TYPE ("(o)"),
413                                              G_DBUS_CALL_FLAGS_NONE,
414                                              -1,
415                                              NULL,
416                                              &error);
417 
418         if (reply == NULL) {
419                 g_warning ("%s", error->message);
420                 g_clear_error (&error);
421                 goto out;
422         }
423 
424         g_variant_get (reply, "(o)", &session_id);
425 
426         g_variant_unref (reply);
427 
428 out:
429         return session_id;
430 }
431 #endif
432 
433 /* adapted from glib script_execute */
434 static void
script_execute(const gchar * file,char ** argv,char ** envp,gboolean search_path)435 script_execute (const gchar *file,
436                 char       **argv,
437                 char       **envp,
438                 gboolean     search_path)
439 {
440         /* Count the arguments.  */
441         int argc = 0;
442 
443         while (argv[argc]) {
444                 ++argc;
445         }
446 
447         /* Construct an argument list for the shell.  */
448         {
449                 char **new_argv;
450 
451                 new_argv = g_new0 (gchar*, argc + 2); /* /bin/sh and NULL */
452 
453                 new_argv[0] = (char *) "/bin/sh";
454                 new_argv[1] = (char *) file;
455                 while (argc > 0) {
456                         new_argv[argc + 1] = argv[argc];
457                         --argc;
458                 }
459 
460                 /* Execute the shell. */
461                 if (envp) {
462                         execve (new_argv[0], new_argv, envp);
463                 } else {
464                         execv (new_argv[0], new_argv);
465                 }
466 
467                 g_free (new_argv);
468         }
469 }
470 
471 static char *
my_strchrnul(const char * str,char c)472 my_strchrnul (const char *str, char c)
473 {
474         char *p = (char*) str;
475         while (*p && (*p != c)) {
476                 ++p;
477         }
478 
479         return p;
480 }
481 
482 /* adapted from glib g_execute */
483 static gint
gdm_session_execute(const char * file,char ** argv,char ** envp,gboolean search_path)484 gdm_session_execute (const char *file,
485                      char      **argv,
486                      char      **envp,
487                      gboolean    search_path)
488 {
489         if (*file == '\0') {
490                 /* We check the simple case first. */
491                 errno = ENOENT;
492                 return -1;
493         }
494 
495         if (!search_path || strchr (file, '/') != NULL) {
496                 /* Don't search when it contains a slash. */
497                 if (envp) {
498                         execve (file, argv, envp);
499                 } else {
500                         execv (file, argv);
501                 }
502 
503                 if (errno == ENOEXEC) {
504                         script_execute (file, argv, envp, FALSE);
505                 }
506         } else {
507                 gboolean got_eacces = 0;
508                 const char *path, *p;
509                 char *name, *freeme;
510                 gsize len;
511                 gsize pathlen;
512 
513                 path = g_getenv ("PATH");
514                 if (path == NULL) {
515                         /* There is no `PATH' in the environment.  The default
516                          * search path in libc is the current directory followed by
517                          * the path `confstr' returns for `_CS_PATH'.
518                          */
519 
520                         /* In GLib we put . last, for security, and don't use the
521                          * unportable confstr(); UNIX98 does not actually specify
522                          * what to search if PATH is unset. POSIX may, dunno.
523                          */
524 
525                         path = "/bin:/usr/bin:.";
526                 }
527 
528                 len = strlen (file) + 1;
529                 pathlen = strlen (path);
530                 freeme = name = g_malloc (pathlen + len + 1);
531 
532                 /* Copy the file name at the top, including '\0'  */
533                 memcpy (name + pathlen + 1, file, len);
534                 name = name + pathlen;
535                 /* And add the slash before the filename  */
536                 *name = '/';
537 
538                 p = path;
539                 do {
540                         char *startp;
541 
542                         path = p;
543                         p = my_strchrnul (path, ':');
544 
545                         if (p == path) {
546                                 /* Two adjacent colons, or a colon at the beginning or the end
547                                  * of `PATH' means to search the current directory.
548                                  */
549                                 startp = name + 1;
550                         } else {
551                                 startp = memcpy (name - (p - path), path, p - path);
552                         }
553 
554                         /* Try to execute this name.  If it works, execv will not return.  */
555                         if (envp) {
556                                 execve (startp, argv, envp);
557                         } else {
558                                 execv (startp, argv);
559                         }
560 
561                         if (errno == ENOEXEC) {
562                                 script_execute (startp, argv, envp, search_path);
563                         }
564 
565                         switch (errno) {
566                         case EACCES:
567                                 /* Record the we got a `Permission denied' error.  If we end
568                                  * up finding no executable we can use, we want to diagnose
569                                  * that we did find one but were denied access.
570                                  */
571                                 got_eacces = TRUE;
572 
573                                 /* FALL THRU */
574 
575                         case ENOENT:
576 #ifdef ESTALE
577                         case ESTALE:
578 #endif
579 #ifdef ENOTDIR
580                         case ENOTDIR:
581 #endif
582                                 /* Those errors indicate the file is missing or not executable
583                                  * by us, in which case we want to just try the next path
584                                  * directory.
585                                  */
586                                 break;
587 
588                         default:
589                                 /* Some other error means we found an executable file, but
590                                  * something went wrong executing it; return the error to our
591                                  * caller.
592                                  */
593                                 g_free (freeme);
594                                 return -1;
595                         }
596                 } while (*p++ != '\0');
597 
598                 /* We tried every element and none of them worked.  */
599                 if (got_eacces) {
600                         /* At least one failure was due to permissions, so report that
601                          * error.
602                          */
603                         errno = EACCES;
604                 }
605 
606                 g_free (freeme);
607         }
608 
609         /* Return the error from the last attempt (probably ENOENT).  */
610         return -1;
611 }
612 
613 /*
614  * This function is called with username set to NULL to update the
615  * auditor username value.
616  */
617 static gboolean
gdm_session_worker_get_username(GdmSessionWorker * worker,char ** username)618 gdm_session_worker_get_username (GdmSessionWorker  *worker,
619                                  char             **username)
620 {
621         gconstpointer item;
622 
623         g_assert (worker->priv->pam_handle != NULL);
624 
625         if (pam_get_item (worker->priv->pam_handle, PAM_USER, &item) == PAM_SUCCESS) {
626                 if (username != NULL) {
627                         *username = g_strdup ((char *) item);
628                         g_debug ("GdmSessionWorker: username is '%s'",
629                                  *username != NULL ? *username : "<unset>");
630                 }
631 
632                 if (worker->priv->auditor != NULL) {
633                         gdm_session_auditor_set_username (worker->priv->auditor, (char *)item);
634                 }
635 
636                 return TRUE;
637         }
638 
639         return FALSE;
640 }
641 
642 static void
attempt_to_load_user_settings(GdmSessionWorker * worker,const char * username)643 attempt_to_load_user_settings (GdmSessionWorker *worker,
644                                const char       *username)
645 {
646         g_debug ("GdmSessionWorker: attempting to load user settings");
647         gdm_session_settings_load (worker->priv->user_settings,
648                                    username);
649 }
650 
651 static void
gdm_session_worker_update_username(GdmSessionWorker * worker)652 gdm_session_worker_update_username (GdmSessionWorker *worker)
653 {
654         char    *username;
655         gboolean res;
656 
657         username = NULL;
658         res = gdm_session_worker_get_username (worker, &username);
659         if (res) {
660                 g_debug ("GdmSessionWorker: old-username='%s' new-username='%s'",
661                          worker->priv->username != NULL ? worker->priv->username : "<unset>",
662                          username != NULL ? username : "<unset>");
663 
664 
665                 gdm_session_auditor_set_username (worker->priv->auditor, worker->priv->username);
666 
667                 if ((worker->priv->username == username) ||
668                     ((worker->priv->username != NULL) && (username != NULL) &&
669                      (strcmp (worker->priv->username, username) == 0)))
670                         goto out;
671 
672                 g_debug ("GdmSessionWorker: setting username to '%s'", username);
673 
674                 g_free (worker->priv->username);
675                 worker->priv->username = username;
676                 username = NULL;
677 
678                 gdm_dbus_worker_emit_username_changed (GDM_DBUS_WORKER (worker),
679                                                        worker->priv->username);
680 
681                 /* We have a new username to try. If we haven't been able to
682                  * read user settings up until now, then give it a go now
683                  * (see the comment in do_setup for rationale on why it's useful
684                  * to keep trying to read settings)
685                  */
686                 if (worker->priv->username != NULL &&
687                     worker->priv->username[0] != '\0' &&
688                     !gdm_session_settings_is_loaded (worker->priv->user_settings)) {
689                         attempt_to_load_user_settings (worker, worker->priv->username);
690                 }
691         }
692 
693  out:
694         g_free (username);
695 }
696 
697 static gboolean
gdm_session_worker_ask_question(GdmSessionWorker * worker,const char * question,char ** answerp)698 gdm_session_worker_ask_question (GdmSessionWorker *worker,
699                                  const char       *question,
700                                  char            **answerp)
701 {
702         return gdm_dbus_worker_manager_call_info_query_sync (worker->priv->manager,
703                                                              worker->priv->service,
704                                                              question,
705                                                              answerp,
706                                                              NULL,
707                                                              NULL);
708 }
709 
710 static gboolean
gdm_session_worker_ask_for_secret(GdmSessionWorker * worker,const char * question,char ** answerp)711 gdm_session_worker_ask_for_secret (GdmSessionWorker *worker,
712                                    const char       *question,
713                                    char            **answerp)
714 {
715         return gdm_dbus_worker_manager_call_secret_info_query_sync (worker->priv->manager,
716                                                                     worker->priv->service,
717                                                                     question,
718                                                                     answerp,
719                                                                     NULL,
720                                                                     NULL);
721 }
722 
723 static gboolean
gdm_session_worker_report_info(GdmSessionWorker * worker,const char * info)724 gdm_session_worker_report_info (GdmSessionWorker *worker,
725                                 const char       *info)
726 {
727         return gdm_dbus_worker_manager_call_info_sync (worker->priv->manager,
728                                                        worker->priv->service,
729                                                        info,
730                                                        NULL,
731                                                        NULL);
732 }
733 
734 static gboolean
gdm_session_worker_report_problem(GdmSessionWorker * worker,const char * problem)735 gdm_session_worker_report_problem (GdmSessionWorker *worker,
736                                    const char       *problem)
737 {
738         return gdm_dbus_worker_manager_call_problem_sync (worker->priv->manager,
739                                                           worker->priv->service,
740                                                           problem,
741                                                           NULL,
742                                                           NULL);
743 }
744 
745 #ifdef SUPPORTS_PAM_EXTENSIONS
746 static gboolean
gdm_session_worker_ask_list_of_choices(GdmSessionWorker * worker,const char * prompt_message,GdmChoiceList * list,char ** answerp)747 gdm_session_worker_ask_list_of_choices (GdmSessionWorker *worker,
748                                         const char       *prompt_message,
749                                         GdmChoiceList    *list,
750                                         char            **answerp)
751 {
752         GVariantBuilder builder;
753         GVariant *choices_as_variant;
754         GError *error = NULL;
755         gboolean res;
756         size_t i;
757 
758         g_debug ("GdmSessionWorker: presenting user with list of choices:");
759 
760         g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
761 
762         for (i = 0; i < list->number_of_items; i++) {
763                 if (list->items[i].key == NULL) {
764                         g_warning ("choice list contains item with NULL key");
765                         g_variant_builder_clear (&builder);
766                         return FALSE;
767                 }
768                 g_debug ("GdmSessionWorker:        choices['%s'] = \"%s\"", list->items[i].key, list->items[i].text);
769                 g_variant_builder_add (&builder, "{ss}", list->items[i].key, list->items[i].text);
770         }
771         g_debug ("GdmSessionWorker: (and waiting for reply)");
772 
773         choices_as_variant = g_variant_builder_end (&builder);
774 
775         res = gdm_dbus_worker_manager_call_choice_list_query_sync (worker->priv->manager,
776                                                                     worker->priv->service,
777                                                                     prompt_message,
778                                                                     choices_as_variant,
779                                                                     answerp,
780                                                                     NULL,
781                                                                     &error);
782 
783         if (! res) {
784                 g_debug ("GdmSessionWorker: list request failed: %s", error->message);
785                 g_clear_error (&error);
786         } else {
787                 g_debug ("GdmSessionWorker: user selected '%s'", *answerp);
788         }
789 
790         return res;
791 }
792 
793 static gboolean
gdm_session_worker_process_choice_list_request(GdmSessionWorker * worker,GdmPamExtensionChoiceListRequest * request,GdmPamExtensionChoiceListResponse * response)794 gdm_session_worker_process_choice_list_request (GdmSessionWorker                   *worker,
795                                                 GdmPamExtensionChoiceListRequest  *request,
796                                                 GdmPamExtensionChoiceListResponse *response)
797 {
798         return gdm_session_worker_ask_list_of_choices (worker, request->prompt_message, &request->list, &response->key);
799 }
800 
801 static gboolean
gdm_session_worker_process_extended_pam_message(GdmSessionWorker * worker,const struct pam_message * query,char ** response)802 gdm_session_worker_process_extended_pam_message (GdmSessionWorker          *worker,
803                                                  const struct pam_message  *query,
804                                                  char                     **response)
805 {
806         GdmPamExtensionMessage *extended_message;
807         gboolean res;
808 
809         extended_message = GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE (query);
810 
811         if (GDM_PAM_EXTENSION_MESSAGE_TRUNCATED (extended_message)) {
812                 g_warning ("PAM service requested binary response for truncated query");
813                 return FALSE;
814         }
815 
816         if (GDM_PAM_EXTENSION_MESSAGE_INVALID_TYPE (extended_message)) {
817                 g_warning ("PAM service requested binary response for unadvertised query type");
818                 return FALSE;
819         }
820 
821         if (GDM_PAM_EXTENSION_MESSAGE_MATCH (extended_message, worker->priv->extensions, GDM_PAM_EXTENSION_CHOICE_LIST)) {
822                 GdmPamExtensionChoiceListRequest *list_request = (GdmPamExtensionChoiceListRequest *) extended_message;
823                 GdmPamExtensionChoiceListResponse *list_response = malloc (GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE);
824 
825                 g_debug ("GdmSessionWorker: received extended pam message '%s'", GDM_PAM_EXTENSION_CHOICE_LIST);
826 
827                 GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_INIT (list_response);
828 
829                 res = gdm_session_worker_process_choice_list_request (worker, list_request, list_response);
830 
831                 if (! res) {
832                         g_free (list_response);
833                         return FALSE;
834                 }
835 
836                 *response = GDM_PAM_EXTENSION_MESSAGE_TO_PAM_REPLY (list_response);
837                 return TRUE;
838         } else {
839                 g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned int) extended_message->type);
840                 return FALSE;
841 
842         }
843 
844         return TRUE;
845 }
846 #endif
847 
848 static char *
convert_to_utf8(const char * str)849 convert_to_utf8 (const char *str)
850 {
851         char *utf8;
852         utf8 = g_locale_to_utf8 (str,
853                                  -1,
854                                  NULL,
855                                  NULL,
856                                  NULL);
857 
858         /* if we couldn't convert text from locale then
859          * assume utf-8 and hope for the best */
860         if (utf8 == NULL) {
861                 char *p;
862                 char *q;
863 
864                 utf8 = g_strdup (str);
865 
866                 p = utf8;
867                 while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q)) {
868                         *q = '?';
869                         p = q + 1;
870                 }
871         }
872 
873         return utf8;
874 }
875 
876 static gboolean
gdm_session_worker_process_pam_message(GdmSessionWorker * worker,const struct pam_message * query,char ** response)877 gdm_session_worker_process_pam_message (GdmSessionWorker          *worker,
878                                         const struct pam_message  *query,
879                                         char                     **response)
880 {
881         char    *user_answer;
882         gboolean res;
883         char    *utf8_msg;
884 #ifdef PAM_RADIO_TYPE
885         char	*msg;
886 #endif
887 
888         if (response != NULL) {
889                 *response = NULL;
890         }
891 
892         gdm_session_worker_update_username (worker);
893 
894 #ifdef SUPPORTS_PAM_EXTENSIONS
895         if (query->msg_style == PAM_BINARY_PROMPT)
896                 return gdm_session_worker_process_extended_pam_message (worker, query, response);
897 #endif
898 
899         g_debug ("GdmSessionWorker: received pam message of type %u with payload '%s'",
900                  query->msg_style, query->msg);
901 
902         utf8_msg = convert_to_utf8 (query->msg);
903 
904         worker->priv->cancelled = FALSE;
905         worker->priv->timed_out = FALSE;
906 
907         user_answer = NULL;
908         res = FALSE;
909         switch (query->msg_style) {
910         case PAM_PROMPT_ECHO_ON:
911                 res = gdm_session_worker_ask_question (worker, utf8_msg, &user_answer);
912                 break;
913         case PAM_PROMPT_ECHO_OFF:
914                 res = gdm_session_worker_ask_for_secret (worker, utf8_msg, &user_answer);
915                 break;
916         case PAM_TEXT_INFO:
917                 res = gdm_session_worker_report_info (worker, utf8_msg);
918                 break;
919         case PAM_ERROR_MSG:
920                 res = gdm_session_worker_report_problem (worker, utf8_msg);
921                 break;
922 #ifdef PAM_RADIO_TYPE
923         case PAM_RADIO_TYPE:
924                 msg = g_strdup_printf ("%s (yes/no)", utf8_msg);
925                 res = gdm_session_worker_ask_question (worker, msg, &user_answer);
926                 g_free (msg);
927                 break;
928 #endif
929         default:
930                 res = FALSE;
931                 g_warning ("Unknown and unhandled message type %d\n",
932                            query->msg_style);
933 
934                 break;
935         }
936 
937         if (worker->priv->timed_out) {
938                 gdm_dbus_worker_emit_cancel_pending_query (GDM_DBUS_WORKER (worker));
939                 worker->priv->timed_out = FALSE;
940         }
941 
942         if (user_answer != NULL) {
943                 /* we strndup and g_free to make sure we return malloc'd
944                  * instead of g_malloc'd memory.  PAM_MAX_RESP_SIZE includes
945                  * the '\0' terminating character, thus the "- 1".
946                  */
947                 if (res && response != NULL) {
948                         *response = strndup (user_answer, PAM_MAX_RESP_SIZE - 1);
949                 }
950 
951                 memset (user_answer, '\0', strlen (user_answer));
952                 g_free (user_answer);
953 
954                 g_debug ("GdmSessionWorker: trying to get updated username");
955 
956                 res = TRUE;
957         }
958 
959         g_free (utf8_msg);
960 
961         return res;
962 }
963 
964 static const char *
get_friendly_error_message(int error_code)965 get_friendly_error_message (int error_code)
966 {
967         switch (error_code) {
968             case PAM_SUCCESS:
969             case PAM_IGNORE:
970                 return "";
971                 break;
972 
973             case PAM_ACCT_EXPIRED:
974             case PAM_AUTHTOK_EXPIRED:
975                 return _("Your account was given a time limit that’s now passed.");
976                 break;
977 
978             default:
979                 break;
980         }
981 
982         return _("Sorry, that didn’t work. Please try again.");
983 }
984 
985 static int
gdm_session_worker_pam_new_messages_handler(int number_of_messages,const struct pam_message ** messages,struct pam_response ** responses,GdmSessionWorker * worker)986 gdm_session_worker_pam_new_messages_handler (int                        number_of_messages,
987                                              const struct pam_message **messages,
988                                              struct pam_response      **responses,
989                                              GdmSessionWorker          *worker)
990 {
991         struct pam_response *replies;
992         int                  return_value;
993         int                  i;
994 
995         g_debug ("GdmSessionWorker: %d new messages received from PAM\n", number_of_messages);
996 
997         return_value = PAM_CONV_ERR;
998 
999         if (number_of_messages < 0) {
1000                 return PAM_CONV_ERR;
1001         }
1002 
1003         if (number_of_messages == 0) {
1004                 if (responses) {
1005                         *responses = NULL;
1006                 }
1007 
1008                 return PAM_SUCCESS;
1009         }
1010 
1011         /* we want to generate one reply for every question
1012          */
1013         replies = (struct pam_response *) calloc (number_of_messages,
1014                                                   sizeof (struct pam_response));
1015         for (i = 0; i < number_of_messages; i++) {
1016                 gboolean got_response;
1017                 char    *response;
1018 
1019                 response = NULL;
1020                 got_response = gdm_session_worker_process_pam_message (worker,
1021                                                                        messages[i],
1022                                                                        &response);
1023                 if (!got_response) {
1024                         goto out;
1025                 }
1026 
1027                 replies[i].resp = response;
1028                 replies[i].resp_retcode = PAM_SUCCESS;
1029         }
1030 
1031         return_value = PAM_SUCCESS;
1032 
1033  out:
1034         if (return_value != PAM_SUCCESS) {
1035                 for (i = 0; i < number_of_messages; i++) {
1036                         if (replies[i].resp != NULL) {
1037                                 memset (replies[i].resp, 0, strlen (replies[i].resp));
1038                                 free (replies[i].resp);
1039                         }
1040                         memset (&replies[i], 0, sizeof (replies[i]));
1041                 }
1042                 free (replies);
1043                 replies = NULL;
1044         }
1045 
1046         if (responses) {
1047                 *responses = replies;
1048         }
1049 
1050         g_debug ("GdmSessionWorker: PAM conversation returning %d: %s",
1051                  return_value,
1052                  pam_strerror (worker->priv->pam_handle, return_value));
1053 
1054         return return_value;
1055 }
1056 
1057 static void
gdm_session_worker_start_auditor(GdmSessionWorker * worker)1058 gdm_session_worker_start_auditor (GdmSessionWorker *worker)
1059 {
1060     /* Use dummy auditor so program session doesn't pollute user audit logs
1061      */
1062     if (worker->priv->is_program_session) {
1063             worker->priv->auditor = gdm_session_auditor_new (worker->priv->hostname,
1064                                                              worker->priv->display_device);
1065             return;
1066     }
1067 
1068 /* FIXME: it may make sense at some point to keep a list of
1069  * auditors, instead of assuming they are mutually exclusive
1070  */
1071 #if defined (HAVE_ADT)
1072         worker->priv->auditor = gdm_session_solaris_auditor_new (worker->priv->hostname,
1073                                                                  worker->priv->display_device);
1074 #elif defined (HAVE_LIBAUDIT)
1075         worker->priv->auditor = gdm_session_linux_auditor_new (worker->priv->hostname,
1076                                                                worker->priv->display_device);
1077 #else
1078         worker->priv->auditor = gdm_session_auditor_new (worker->priv->hostname,
1079                                                          worker->priv->display_device);
1080 #endif
1081 }
1082 
1083 static void
gdm_session_worker_stop_auditor(GdmSessionWorker * worker)1084 gdm_session_worker_stop_auditor (GdmSessionWorker *worker)
1085 {
1086         g_object_unref (worker->priv->auditor);
1087         worker->priv->auditor = NULL;
1088 }
1089 
1090 #ifdef WITH_SYSTEMD
1091 static void
on_release_display(int signal)1092 on_release_display (int signal)
1093 {
1094         int fd;
1095 
1096         fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
1097         ioctl(fd, VT_RELDISP, 1);
1098         close(fd);
1099 }
1100 
1101 static void
on_acquire_display(int signal)1102 on_acquire_display (int signal)
1103 {
1104         int fd;
1105 
1106         fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
1107         ioctl(fd, VT_RELDISP, VT_ACKACQ);
1108         close(fd);
1109 }
1110 
1111 static gboolean
handle_terminal_vt_switches(GdmSessionWorker * worker,int tty_fd)1112 handle_terminal_vt_switches (GdmSessionWorker *worker,
1113                              int               tty_fd)
1114 {
1115         struct vt_mode setmode_request = { 0 };
1116         gboolean succeeded = TRUE;
1117 
1118         setmode_request.mode = VT_PROCESS;
1119         setmode_request.relsig = RELEASE_DISPLAY_SIGNAL;
1120         setmode_request.acqsig = ACQUIRE_DISPLAY_SIGNAL;
1121 
1122         if (ioctl (tty_fd, VT_SETMODE, &setmode_request) < 0) {
1123                 g_debug ("GdmSessionWorker: couldn't manage VTs manually: %m");
1124                 succeeded = FALSE;
1125         }
1126 
1127         signal (RELEASE_DISPLAY_SIGNAL, on_release_display);
1128         signal (ACQUIRE_DISPLAY_SIGNAL, on_acquire_display);
1129 
1130         return succeeded;
1131 }
1132 
1133 static void
fix_terminal_vt_mode(GdmSessionWorker * worker,int tty_fd)1134 fix_terminal_vt_mode (GdmSessionWorker  *worker,
1135                       int                tty_fd)
1136 {
1137         struct vt_mode getmode_reply = { 0 };
1138         int kernel_display_mode = 0;
1139         gboolean mode_fixed = FALSE;
1140         gboolean succeeded = TRUE;
1141 
1142         if (ioctl (tty_fd, VT_GETMODE, &getmode_reply) < 0) {
1143                 g_debug ("GdmSessionWorker: couldn't query VT mode: %m");
1144                 succeeded = FALSE;
1145         }
1146 
1147         if (getmode_reply.mode != VT_AUTO) {
1148                 goto out;
1149         }
1150 
1151         if (ioctl (tty_fd, KDGETMODE, &kernel_display_mode) < 0) {
1152                 g_debug ("GdmSessionWorker: couldn't query kernel display mode: %m");
1153                 succeeded = FALSE;
1154         }
1155 
1156         if (kernel_display_mode == KD_TEXT) {
1157                 goto out;
1158         }
1159 
1160         /* VT is in the anti-social state of VT_AUTO + KD_GRAPHICS,
1161          * fix it.
1162          */
1163         succeeded = handle_terminal_vt_switches (worker, tty_fd);
1164         mode_fixed = TRUE;
1165 out:
1166         if (!succeeded) {
1167                 g_error ("GdmSessionWorker: couldn't set up terminal, aborting...");
1168                 return;
1169         }
1170 
1171         g_debug ("GdmSessionWorker: VT mode did %sneed to be fixed",
1172                  mode_fixed? "" : "not ");
1173 }
1174 
1175 static void
jump_to_vt(GdmSessionWorker * worker,int vt_number)1176 jump_to_vt (GdmSessionWorker  *worker,
1177             int                vt_number)
1178 {
1179         int fd;
1180         int active_vt_tty_fd;
1181 
1182         g_debug ("GdmSessionWorker: jumping to VT %d", vt_number);
1183         active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
1184 
1185         if (worker->priv->session_tty_fd != -1) {
1186                 fd = worker->priv->session_tty_fd;
1187 
1188                 g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker");
1189                 if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) {
1190                         g_debug ("GdmSessionWorker: couldn't set graphics mode: %m");
1191                 }
1192 
1193                 /* It's possible that the current VT was left in a broken
1194                  * combination of states (KD_GRAPHICS with VT_AUTO), that
1195                  * can't be switched away from.  This call makes sure things
1196                  * are set in a way that VT_ACTIVATE should work and
1197                  * VT_WAITACTIVE shouldn't hang.
1198                  */
1199                 fix_terminal_vt_mode (worker, active_vt_tty_fd);
1200         } else {
1201                 fd = active_vt_tty_fd;
1202         }
1203 
1204         handle_terminal_vt_switches (worker, fd);
1205 
1206         if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) {
1207                 g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m",
1208                          vt_number);
1209         } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) {
1210                 g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m",
1211                          vt_number);
1212         }
1213 
1214         close (active_vt_tty_fd);
1215 }
1216 #endif
1217 
1218 static void
gdm_session_worker_uninitialize_pam(GdmSessionWorker * worker,int status)1219 gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
1220                                      int               status)
1221 {
1222         g_debug ("GdmSessionWorker: uninitializing PAM");
1223 
1224         if (worker->priv->pam_handle == NULL)
1225                 return;
1226 
1227         gdm_session_worker_get_username (worker, NULL);
1228 
1229         if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) {
1230                 pam_close_session (worker->priv->pam_handle, 0);
1231                 gdm_session_auditor_report_logout (worker->priv->auditor);
1232         } else {
1233                 gdm_session_auditor_report_login_failure (worker->priv->auditor,
1234                                                           status,
1235                                                           pam_strerror (worker->priv->pam_handle, status));
1236         }
1237 
1238         if (worker->priv->state >= GDM_SESSION_WORKER_STATE_ACCREDITED) {
1239                 pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED);
1240         }
1241 
1242         pam_end (worker->priv->pam_handle, status);
1243         worker->priv->pam_handle = NULL;
1244 
1245         gdm_session_worker_stop_auditor (worker);
1246 
1247 #ifdef WITH_SYSTEMD
1248         if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
1249                 if (worker->priv->login_vt != worker->priv->session_vt) {
1250                         jump_to_vt (worker, worker->priv->login_vt);
1251                 }
1252         }
1253 #endif
1254 
1255         worker->priv->login_vt = 0;
1256         worker->priv->session_vt = 0;
1257 
1258         g_debug ("GdmSessionWorker: state NONE");
1259         worker->priv->state = GDM_SESSION_WORKER_STATE_NONE;
1260 }
1261 
1262 static char *
_get_tty_for_pam(const char * x11_display_name,const char * display_device)1263 _get_tty_for_pam (const char *x11_display_name,
1264                   const char *display_device)
1265 {
1266 #ifdef __sun
1267         return g_strdup (display_device);
1268 #else
1269         return g_strdup (x11_display_name);
1270 #endif
1271 }
1272 
1273 #ifdef PAM_XAUTHDATA
1274 static struct pam_xauth_data *
_get_xauth_for_pam(const char * x11_authority_file)1275 _get_xauth_for_pam (const char *x11_authority_file)
1276 {
1277         FILE                  *fh;
1278         Xauth                 *auth = NULL;
1279         struct pam_xauth_data *retval = NULL;
1280         gsize                  len = sizeof (*retval) + 1;
1281 
1282         fh = fopen (x11_authority_file, "r");
1283         if (fh) {
1284                 auth = XauReadAuth (fh);
1285                 fclose (fh);
1286         }
1287         if (auth) {
1288                 len += auth->name_length + auth->data_length;
1289                 retval = g_malloc0 (len);
1290         }
1291         if (retval) {
1292                 retval->namelen = auth->name_length;
1293                 retval->name = (char *) (retval + 1);
1294                 memcpy (retval->name, auth->name, auth->name_length);
1295                 retval->datalen = auth->data_length;
1296                 retval->data = retval->name + auth->name_length + 1;
1297                 memcpy (retval->data, auth->data, auth->data_length);
1298         }
1299         XauDisposeAuth (auth);
1300         return retval;
1301 }
1302 #endif
1303 
1304 #ifdef WITH_SYSTEMD
1305 static gboolean
ensure_login_vt(GdmSessionWorker * worker)1306 ensure_login_vt (GdmSessionWorker *worker)
1307 {
1308         int fd;
1309         struct vt_stat vt_state = { 0 };
1310         gboolean got_login_vt = FALSE;
1311 
1312         fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
1313 
1314         if (fd < 0) {
1315                 g_debug ("GdmSessionWorker: couldn't open VT master: %m");
1316                return FALSE;
1317         }
1318 
1319         if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
1320                 g_debug ("GdmSessionWorker: couldn't get current VT: %m");
1321                 goto out;
1322         }
1323 
1324         worker->priv->login_vt = vt_state.v_active;
1325         got_login_vt = TRUE;
1326 out:
1327         close (fd);
1328         return got_login_vt;
1329 }
1330 #endif
1331 
1332 static gboolean
gdm_session_worker_initialize_pam(GdmSessionWorker * worker,const char * service,const char * const * extensions,const char * username,const char * hostname,gboolean display_is_local,const char * x11_display_name,const char * x11_authority_file,const char * display_device,const char * seat_id,GError ** error)1333 gdm_session_worker_initialize_pam (GdmSessionWorker   *worker,
1334                                    const char         *service,
1335                                    const char * const *extensions,
1336                                    const char         *username,
1337                                    const char         *hostname,
1338                                    gboolean            display_is_local,
1339                                    const char         *x11_display_name,
1340                                    const char         *x11_authority_file,
1341                                    const char         *display_device,
1342                                    const char         *seat_id,
1343                                    GError            **error)
1344 {
1345         struct pam_conv        pam_conversation;
1346         int                    error_code;
1347 
1348         g_assert (worker->priv->pam_handle == NULL);
1349 
1350         g_debug ("GdmSessionWorker: initializing PAM; service=%s username=%s seat=%s",
1351                  service ? service : "(null)",
1352                  username ? username : "(null)",
1353                  seat_id ? seat_id : "(null)");
1354 
1355 #ifdef SUPPORTS_PAM_EXTENSIONS
1356         if (extensions != NULL) {
1357                 GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS (gdm_pam_extension_environment_block, extensions);
1358         }
1359 #endif
1360 
1361         pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
1362         pam_conversation.appdata_ptr = worker;
1363 
1364         gdm_session_worker_start_auditor (worker);
1365         error_code = pam_start (service,
1366                                 username,
1367                                 &pam_conversation,
1368                                 &worker->priv->pam_handle);
1369         if (error_code != PAM_SUCCESS) {
1370                 g_debug ("GdmSessionWorker: could not initialize PAM: (error code %d)", error_code);
1371                 /* we don't use pam_strerror here because it requires a valid
1372                  * pam handle, and if pam_start fails pam_handle is undefined
1373                  */
1374                 g_set_error (error,
1375                              GDM_SESSION_WORKER_ERROR,
1376                              GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
1377                              "%s", "");
1378 
1379                 goto out;
1380         }
1381 
1382         /* set USER PROMPT */
1383         if (username == NULL) {
1384                 error_code = pam_set_item (worker->priv->pam_handle, PAM_USER_PROMPT, _("Username:"));
1385 
1386                 if (error_code != PAM_SUCCESS) {
1387                         g_debug ("GdmSessionWorker: error informing authentication system of preferred username prompt: %s",
1388                                 pam_strerror (worker->priv->pam_handle, error_code));
1389                         g_set_error (error,
1390                                      GDM_SESSION_WORKER_ERROR,
1391                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1392                                      "%s", "");
1393                         goto out;
1394                 }
1395         }
1396 
1397         /* set RHOST */
1398         if (hostname != NULL && hostname[0] != '\0') {
1399                 error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname);
1400                 g_debug ("error informing authentication system of user's hostname %s: %s",
1401                          hostname,
1402                          pam_strerror (worker->priv->pam_handle, error_code));
1403 
1404                 if (error_code != PAM_SUCCESS) {
1405                         g_set_error (error,
1406                                      GDM_SESSION_WORKER_ERROR,
1407                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1408                                      "%s", "");
1409                         goto out;
1410                 }
1411         }
1412 
1413 #ifdef WITH_SYSTEMD
1414         /* set seat ID */
1415         if (seat_id != NULL && seat_id[0] != '\0' && LOGIND_RUNNING()) {
1416                 gdm_session_worker_set_environment_variable (worker, "XDG_SEAT", seat_id);
1417         }
1418 #endif
1419 
1420         if (strcmp (service, "gdm-launch-environment") == 0) {
1421                 gdm_session_worker_set_environment_variable (worker, "XDG_SESSION_CLASS", "greeter");
1422         }
1423 
1424         g_debug ("GdmSessionWorker: state SETUP_COMPLETE");
1425         worker->priv->state = GDM_SESSION_WORKER_STATE_SETUP_COMPLETE;
1426 
1427 #ifdef WITH_SYSTEMD
1428         /* Temporarily set PAM_TTY with the currently active VT (login screen)
1429            PAM_TTY will be reset with the users VT right before the user session is opened */
1430         ensure_login_vt (worker);
1431         g_snprintf (tty_string, 256, "/dev/tty%d", worker->priv->login_vt);
1432         pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);
1433         if (!display_is_local)
1434                 worker->priv->password_is_required = TRUE;
1435 #endif
1436 
1437  out:
1438         if (error_code != PAM_SUCCESS) {
1439                 gdm_session_worker_uninitialize_pam (worker, error_code);
1440                 return FALSE;
1441         }
1442 
1443         return TRUE;
1444 }
1445 
1446 static gboolean
gdm_session_worker_authenticate_user(GdmSessionWorker * worker,gboolean password_is_required,GError ** error)1447 gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
1448                                       gboolean          password_is_required,
1449                                       GError          **error)
1450 {
1451         int error_code;
1452         int authentication_flags;
1453 
1454         g_debug ("GdmSessionWorker: authenticating user %s", worker->priv->username);
1455 
1456         authentication_flags = 0;
1457 
1458         if (password_is_required) {
1459                 authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
1460         }
1461 
1462         /* blocking call, does the actual conversation */
1463         error_code = pam_authenticate (worker->priv->pam_handle, authentication_flags);
1464 
1465         if (error_code == PAM_AUTHINFO_UNAVAIL) {
1466                 g_debug ("GdmSessionWorker: authentication service unavailable");
1467 
1468                 g_set_error (error,
1469                              GDM_SESSION_WORKER_ERROR,
1470                              GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
1471                              "%s", "");
1472                 goto out;
1473         } else if (error_code != PAM_SUCCESS) {
1474                 g_debug ("GdmSessionWorker: authentication returned %d: %s", error_code, pam_strerror (worker->priv->pam_handle, error_code));
1475 
1476                 /*
1477                  * Do not display a different message for user unknown versus
1478                  * a failed password for a valid user.
1479                  */
1480                 if (error_code == PAM_USER_UNKNOWN) {
1481                         error_code = PAM_AUTH_ERR;
1482                 }
1483 
1484                 g_set_error (error,
1485                              GDM_SESSION_WORKER_ERROR,
1486                              GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1487                              "%s", get_friendly_error_message (error_code));
1488                 goto out;
1489         }
1490 
1491         g_debug ("GdmSessionWorker: state AUTHENTICATED");
1492         worker->priv->state = GDM_SESSION_WORKER_STATE_AUTHENTICATED;
1493 
1494  out:
1495         if (error_code != PAM_SUCCESS) {
1496                 gdm_session_worker_uninitialize_pam (worker, error_code);
1497                 return FALSE;
1498         }
1499 
1500         return TRUE;
1501 }
1502 
1503 static gboolean
gdm_session_worker_authorize_user(GdmSessionWorker * worker,gboolean password_is_required,GError ** error)1504 gdm_session_worker_authorize_user (GdmSessionWorker *worker,
1505                                    gboolean          password_is_required,
1506                                    GError          **error)
1507 {
1508         int error_code;
1509         int authentication_flags;
1510 
1511         g_debug ("GdmSessionWorker: determining if authenticated user (password required:%d) is authorized to session",
1512                  password_is_required);
1513 
1514         authentication_flags = 0;
1515 
1516         if (password_is_required) {
1517                 authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
1518         }
1519 
1520         /* check that the account isn't disabled or expired
1521          */
1522         error_code = pam_acct_mgmt (worker->priv->pam_handle, authentication_flags);
1523 
1524         /* it's possible that the user needs to change their password or pin code
1525          */
1526         if (error_code == PAM_NEW_AUTHTOK_REQD && !worker->priv->is_program_session) {
1527                 g_debug ("GdmSessionWorker: authenticated user requires new auth token");
1528                 error_code = pam_chauthtok (worker->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1529 
1530                 gdm_session_worker_get_username (worker, NULL);
1531 
1532                 if (error_code != PAM_SUCCESS) {
1533                         gdm_session_auditor_report_password_change_failure (worker->priv->auditor);
1534                 } else {
1535                         gdm_session_auditor_report_password_changed (worker->priv->auditor);
1536                 }
1537         }
1538 
1539         /* If the user is reauthenticating, then authorization isn't required to
1540          * proceed, the user is already logged in after all.
1541          */
1542         if (worker->priv->is_reauth_session) {
1543                 error_code = PAM_SUCCESS;
1544         }
1545 
1546         if (error_code != PAM_SUCCESS) {
1547                 g_debug ("GdmSessionWorker: user is not authorized to log in: %s",
1548                          pam_strerror (worker->priv->pam_handle, error_code));
1549                 g_set_error (error,
1550                              GDM_SESSION_WORKER_ERROR,
1551                              GDM_SESSION_WORKER_ERROR_AUTHORIZING,
1552                              "%s", get_friendly_error_message (error_code));
1553                 goto out;
1554         }
1555 
1556         g_debug ("GdmSessionWorker: state AUTHORIZED");
1557         worker->priv->state = GDM_SESSION_WORKER_STATE_AUTHORIZED;
1558 
1559  out:
1560         if (error_code != PAM_SUCCESS) {
1561                 gdm_session_worker_uninitialize_pam (worker, error_code);
1562                 return FALSE;
1563         }
1564 
1565         return TRUE;
1566 }
1567 
1568 static void
gdm_session_worker_set_environment_variable(GdmSessionWorker * worker,const char * key,const char * value)1569 gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
1570                                              const char       *key,
1571                                              const char       *value)
1572 {
1573         int error_code;
1574         char *environment_entry;
1575 
1576         if (value != NULL) {
1577                 environment_entry = g_strdup_printf ("%s=%s", key, value);
1578         } else {
1579                 /* empty value means "remove from environment" */
1580                 environment_entry = g_strdup (key);
1581         }
1582 
1583         error_code = pam_putenv (worker->priv->pam_handle,
1584                                  environment_entry);
1585 
1586         if (error_code != PAM_SUCCESS) {
1587                 g_warning ("cannot put %s in pam environment: %s\n",
1588                            environment_entry,
1589                            pam_strerror (worker->priv->pam_handle, error_code));
1590         }
1591         g_debug ("GdmSessionWorker: Set PAM environment variable: '%s'", environment_entry);
1592         g_free (environment_entry);
1593 }
1594 
1595 static char *
gdm_session_worker_get_environment_variable(GdmSessionWorker * worker,const char * key)1596 gdm_session_worker_get_environment_variable (GdmSessionWorker *worker,
1597                                              const char       *key)
1598 {
1599         return g_strdup (pam_getenv (worker->priv->pam_handle, key));
1600 }
1601 
1602 static void
gdm_session_worker_update_environment_from_passwd_info(GdmSessionWorker * worker,uid_t uid,gid_t gid,const char * home,const char * shell)1603 gdm_session_worker_update_environment_from_passwd_info (GdmSessionWorker *worker,
1604                                                         uid_t             uid,
1605                                                         gid_t             gid,
1606                                                         const char       *home,
1607                                                         const char       *shell)
1608 {
1609         gdm_session_worker_set_environment_variable (worker, "LOGNAME", worker->priv->username);
1610         gdm_session_worker_set_environment_variable (worker, "USER", worker->priv->username);
1611         gdm_session_worker_set_environment_variable (worker, "USERNAME", worker->priv->username);
1612         gdm_session_worker_set_environment_variable (worker, "HOME", home);
1613         gdm_session_worker_set_environment_variable (worker, "PWD", home);
1614         gdm_session_worker_set_environment_variable (worker, "SHELL", shell);
1615 }
1616 
1617 static gboolean
gdm_session_worker_environment_variable_is_set(GdmSessionWorker * worker,const char * key)1618 gdm_session_worker_environment_variable_is_set (GdmSessionWorker *worker,
1619                                                 const char       *key)
1620 {
1621         return pam_getenv (worker->priv->pam_handle, key) != NULL;
1622 }
1623 
1624 static gboolean
_change_user(GdmSessionWorker * worker,uid_t uid,gid_t gid)1625 _change_user (GdmSessionWorker  *worker,
1626               uid_t              uid,
1627               gid_t              gid)
1628 {
1629 #ifdef THE_MAN_PAGE_ISNT_LYING
1630         /* pam_setcred wants to be called as the authenticated user
1631          * but pam_open_session needs to be called as super-user.
1632          *
1633          * Set the real uid and gid to the user and give the user a
1634          * temporary super-user effective id.
1635          */
1636         if (setreuid (uid, GDM_SESSION_ROOT_UID) < 0) {
1637                 return FALSE;
1638         }
1639 #endif
1640         worker->priv->uid = uid;
1641         worker->priv->gid = gid;
1642 
1643         if (setgid (gid) < 0) {
1644                 return FALSE;
1645         }
1646 
1647         if (initgroups (worker->priv->username, gid) < 0) {
1648                 return FALSE;
1649         }
1650 
1651         return TRUE;
1652 }
1653 
1654 static gboolean
_lookup_passwd_info(const char * username,uid_t * uidp,gid_t * gidp,char ** homep,char ** shellp)1655 _lookup_passwd_info (const char *username,
1656                      uid_t      *uidp,
1657                      gid_t      *gidp,
1658                      char      **homep,
1659                      char      **shellp)
1660 {
1661         gboolean       ret;
1662         struct passwd *passwd_entry;
1663         struct passwd  passwd_buffer;
1664         char          *aux_buffer;
1665         long           required_aux_buffer_size;
1666         gsize          aux_buffer_size;
1667 
1668         ret = FALSE;
1669         aux_buffer = NULL;
1670         aux_buffer_size = 0;
1671 
1672         required_aux_buffer_size = sysconf (_SC_GETPW_R_SIZE_MAX);
1673 
1674         if (required_aux_buffer_size < 0) {
1675                 aux_buffer_size = GDM_PASSWD_AUXILLARY_BUFFER_SIZE;
1676         } else {
1677                 aux_buffer_size = (gsize) required_aux_buffer_size;
1678         }
1679 
1680         aux_buffer = g_slice_alloc0 (aux_buffer_size);
1681 
1682         /* we use the _r variant of getpwnam()
1683          * (with its weird semantics) so that the
1684          * passwd_entry doesn't potentially get stomped on
1685          * by a PAM module
1686          */
1687  again:
1688         passwd_entry = NULL;
1689 #ifdef HAVE_POSIX_GETPWNAM_R
1690         errno = getpwnam_r (username,
1691                             &passwd_buffer,
1692                             aux_buffer,
1693                             (size_t) aux_buffer_size,
1694                             &passwd_entry);
1695 #else
1696         passwd_entry = getpwnam_r (username,
1697                                    &passwd_buffer,
1698                                    aux_buffer,
1699                                    (size_t) aux_buffer_size);
1700         errno = 0;
1701 #endif /* !HAVE_POSIX_GETPWNAM_R */
1702         if (errno == EINTR) {
1703                 g_debug ("%s", g_strerror (errno));
1704                 goto again;
1705         } else if (errno != 0) {
1706                 g_warning ("%s", g_strerror (errno));
1707                 goto out;
1708         }
1709 
1710         if (passwd_entry == NULL) {
1711                 goto out;
1712         }
1713 
1714         if (uidp != NULL) {
1715                 *uidp = passwd_entry->pw_uid;
1716         }
1717         if (gidp != NULL) {
1718                 *gidp = passwd_entry->pw_gid;
1719         }
1720         if (homep != NULL) {
1721                 if (passwd_entry->pw_dir != NULL && passwd_entry->pw_dir[0] != '\0') {
1722                         *homep = g_strdup (passwd_entry->pw_dir);
1723                 } else {
1724                         *homep = g_strdup ("/");
1725                 }
1726         }
1727         if (shellp != NULL) {
1728                 if (passwd_entry->pw_shell != NULL && passwd_entry->pw_shell[0] != '\0') {
1729                         *shellp = g_strdup (passwd_entry->pw_shell);
1730                 } else {
1731                         *shellp = g_strdup ("/bin/sh");
1732                 }
1733         }
1734         ret = TRUE;
1735  out:
1736         if (aux_buffer != NULL) {
1737                 g_assert (aux_buffer_size > 0);
1738                 g_slice_free1 (aux_buffer_size, aux_buffer);
1739         }
1740 
1741         return ret;
1742 }
1743 
1744 static char *
get_var_cb(const char * key,gpointer user_data)1745 get_var_cb (const char *key,
1746             gpointer user_data)
1747 {
1748         return gdm_session_worker_get_environment_variable (user_data, key);
1749 }
1750 
1751 static void
load_env_file(GdmSessionWorker * worker,GFile * file)1752 load_env_file (GdmSessionWorker *worker,
1753                GFile   *file)
1754 {
1755         gchar *contents;
1756         gchar **lines;
1757         gchar *line, *p;
1758         gchar *var, *var_end;
1759         gchar *expanded;
1760         char *filename;
1761         int i;
1762 
1763         filename = g_file_get_path (file);
1764         g_debug ("Loading env vars from %s\n", filename);
1765         g_free (filename);
1766 
1767         if (g_file_load_contents (file, NULL, &contents, NULL, NULL, NULL)) {
1768                 lines = g_strsplit (contents, "\n", -1);
1769                 g_free (contents);
1770                 for (i = 0; lines[i] != NULL; i++) {
1771                         line = lines[i];
1772                         p = line;
1773                         while (g_ascii_isspace (*p))
1774                                 p++;
1775                         if (*p == '#' || *p == '\0')
1776                                 continue;
1777                         var = p;
1778                         while (gdm_shell_var_is_valid_char (*p, p == var))
1779                                 p++;
1780                         var_end = p;
1781                         while (g_ascii_isspace (*p))
1782                                 p++;
1783                         if (var == var_end || *p != '=') {
1784                                 g_warning ("Invalid env.d line '%s'\n", line);
1785                                 continue;
1786                         }
1787                         *var_end = 0;
1788                         p++; /* Skip = */
1789                         while (g_ascii_isspace (*p))
1790                                 p++;
1791 
1792                         expanded = gdm_shell_expand (p, get_var_cb, worker);
1793                         expanded = g_strchomp (expanded);
1794                         gdm_session_worker_set_environment_variable (worker, var, expanded);
1795                         g_free (expanded);
1796                 }
1797                 g_strfreev (lines);
1798         }
1799 }
1800 
1801 static gint
compare_str(gconstpointer a,gconstpointer b)1802 compare_str (gconstpointer  a,
1803              gconstpointer  b)
1804 {
1805   return strcmp (*(const char **)a, *(const char **)b);
1806 }
1807 
1808 static void
gdm_session_worker_load_env_dir(GdmSessionWorker * worker,GFile * dir)1809 gdm_session_worker_load_env_dir (GdmSessionWorker *worker,
1810                                  GFile *dir)
1811 {
1812         GFileInfo *info = NULL;
1813         GFileEnumerator *enumerator = NULL;
1814         GPtrArray *names = NULL;
1815         GFile *file;
1816         const gchar *name;
1817         int i;
1818 
1819         enumerator = g_file_enumerate_children (dir,
1820                                                 G_FILE_ATTRIBUTE_STANDARD_TYPE","
1821                                                 G_FILE_ATTRIBUTE_STANDARD_NAME","
1822                                                 G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN","
1823                                                 G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
1824                                                 G_FILE_QUERY_INFO_NONE,
1825                                                 NULL, NULL);
1826         if (!enumerator) {
1827                 goto out;
1828         }
1829 
1830         names = g_ptr_array_new_with_free_func (g_free);
1831         while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
1832                 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR &&
1833                     !g_file_info_get_is_hidden (info) &&
1834                     g_str_has_suffix (g_file_info_get_name (info), ".env"))
1835                   g_ptr_array_add (names, g_strdup (g_file_info_get_name (info)));
1836 
1837                 g_clear_object (&info);
1838         }
1839 
1840         g_ptr_array_sort (names, compare_str);
1841 
1842         for (i = 0; i < names->len; i++) {
1843                 name = g_ptr_array_index (names, i);
1844                 file = g_file_get_child (dir, name);
1845                 load_env_file (worker, file);
1846                 g_object_unref (file);
1847         }
1848 
1849  out:
1850         g_clear_pointer (&names, g_ptr_array_unref);
1851         g_clear_object (&enumerator);
1852 }
1853 
1854 static void
gdm_session_worker_load_env_d(GdmSessionWorker * worker)1855 gdm_session_worker_load_env_d (GdmSessionWorker *worker)
1856 {
1857         GFile *dir;
1858 
1859         dir = g_file_new_for_path (DATADIR "/gdm/env.d");
1860         gdm_session_worker_load_env_dir (worker, dir);
1861         g_object_unref (dir);
1862 
1863         dir = g_file_new_for_path (GDMCONFDIR "/env.d");
1864         gdm_session_worker_load_env_dir (worker, dir);
1865         g_object_unref (dir);
1866 }
1867 
1868 static gboolean
gdm_session_worker_accredit_user(GdmSessionWorker * worker,GError ** error)1869 gdm_session_worker_accredit_user (GdmSessionWorker  *worker,
1870                                   GError           **error)
1871 {
1872         gboolean ret;
1873         gboolean res;
1874         uid_t    uid;
1875         gid_t    gid;
1876         char    *shell;
1877         char    *home;
1878         int      error_code;
1879 
1880         ret = FALSE;
1881 
1882         home = NULL;
1883         shell = NULL;
1884 
1885         if (worker->priv->username == NULL) {
1886                 g_debug ("GdmSessionWorker: Username not set");
1887                 error_code = PAM_USER_UNKNOWN;
1888                 g_set_error (error,
1889                              GDM_SESSION_WORKER_ERROR,
1890                              GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1891                              _("no user account available"));
1892                 goto out;
1893         }
1894 
1895         uid = 0;
1896         gid = 0;
1897         res = _lookup_passwd_info (worker->priv->username,
1898                                    &uid,
1899                                    &gid,
1900                                    &home,
1901                                    &shell);
1902         if (! res) {
1903                 g_debug ("GdmSessionWorker: Unable to lookup account info");
1904                 error_code = PAM_AUTHINFO_UNAVAIL;
1905                 g_set_error (error,
1906                              GDM_SESSION_WORKER_ERROR,
1907                              GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1908                              _("no user account available"));
1909                 goto out;
1910         }
1911 
1912         gdm_session_worker_update_environment_from_passwd_info (worker,
1913                                                                 uid,
1914                                                                 gid,
1915                                                                 home,
1916                                                                 shell);
1917 
1918         /* Let's give the user a default PATH if he doesn't already have one
1919          */
1920         if (!gdm_session_worker_environment_variable_is_set (worker, "PATH")) {
1921                 if (strcmp (BINDIR, "/usr/bin") == 0) {
1922                         gdm_session_worker_set_environment_variable (worker, "PATH",
1923                                                                      GDM_SESSION_DEFAULT_PATH);
1924                 } else {
1925                         gdm_session_worker_set_environment_variable (worker, "PATH",
1926                                                                      BINDIR ":" GDM_SESSION_DEFAULT_PATH);
1927                 }
1928         }
1929 
1930         if (! _change_user (worker, uid, gid)) {
1931                 g_debug ("GdmSessionWorker: Unable to change to user");
1932                 error_code = PAM_SYSTEM_ERR;
1933                 g_set_error (error, GDM_SESSION_WORKER_ERROR,
1934                              GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1935                              "%s", _("Unable to change to user"));
1936                 goto out;
1937         }
1938 
1939         error_code = pam_setcred (worker->priv->pam_handle, worker->priv->cred_flags);
1940 
1941         /* If the user is reauthenticating and they've made it this far, then there
1942          * is no reason we should lock them out of their session.  They've already
1943          * proved they are they same person who logged in, and that's all we care
1944          * about.
1945          */
1946         if (worker->priv->is_reauth_session) {
1947                 error_code = PAM_SUCCESS;
1948         }
1949 
1950         if (error_code != PAM_SUCCESS) {
1951                 g_set_error (error,
1952                              GDM_SESSION_WORKER_ERROR,
1953                              GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1954                              "%s",
1955                              pam_strerror (worker->priv->pam_handle, error_code));
1956                 goto out;
1957         }
1958 
1959         ret = TRUE;
1960 
1961  out:
1962         g_free (home);
1963         g_free (shell);
1964         if (ret) {
1965                 g_debug ("GdmSessionWorker: state ACCREDITED");
1966                 ret = TRUE;
1967 
1968                 gdm_session_worker_get_username (worker, NULL);
1969                 gdm_session_auditor_report_user_accredited (worker->priv->auditor);
1970                 worker->priv->state = GDM_SESSION_WORKER_STATE_ACCREDITED;
1971         } else {
1972                 gdm_session_worker_uninitialize_pam (worker, error_code);
1973         }
1974 
1975         return ret;
1976 }
1977 
1978 static const char * const *
gdm_session_worker_get_environment(GdmSessionWorker * worker)1979 gdm_session_worker_get_environment (GdmSessionWorker *worker)
1980 {
1981         return (const char * const *) pam_getenvlist (worker->priv->pam_handle);
1982 }
1983 
1984 #ifdef WITH_CONSOLE_KIT
1985 static void
register_ck_session(GdmSessionWorker * worker)1986 register_ck_session (GdmSessionWorker *worker)
1987 {
1988 #ifdef WITH_SYSTEMD
1989         if (LOGIND_RUNNING()) {
1990                 return;
1991         }
1992 #endif
1993 
1994         open_ck_session (worker);
1995 
1996         if (worker->priv->session_cookie != NULL) {
1997                 gdm_session_worker_set_environment_variable (worker,
1998                                                              "XDG_SESSION_COOKIE",
1999                                                              worker->priv->session_cookie);
2000         }
2001 }
2002 #endif
2003 
2004 static gboolean
run_script(GdmSessionWorker * worker,const char * dir)2005 run_script (GdmSessionWorker *worker,
2006             const char       *dir)
2007 {
2008         /* scripts are for non-program sessions only */
2009         if (worker->priv->is_program_session) {
2010                 return TRUE;
2011         }
2012 
2013         return gdm_run_script (dir,
2014                                worker->priv->username,
2015                                worker->priv->x11_display_name,
2016                                worker->priv->display_is_local? NULL : worker->priv->hostname,
2017                                worker->priv->x11_authority_file);
2018 }
2019 
2020 static void
session_worker_child_watch(GPid pid,int status,GdmSessionWorker * worker)2021 session_worker_child_watch (GPid              pid,
2022                             int               status,
2023                             GdmSessionWorker *worker)
2024 {
2025         g_debug ("GdmSessionWorker: child (pid:%d) done (%s:%d)",
2026                  (int) pid,
2027                  WIFEXITED (status) ? "status"
2028                  : WIFSIGNALED (status) ? "signal"
2029                  : "unknown",
2030                  WIFEXITED (status) ? WEXITSTATUS (status)
2031                  : WIFSIGNALED (status) ? WTERMSIG (status)
2032                  : -1);
2033 
2034 #ifdef WITH_CONSOLE_KIT
2035         close_ck_session (worker);
2036 #endif
2037 
2038         gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS);
2039 
2040         gdm_dbus_worker_emit_session_exited (GDM_DBUS_WORKER (worker),
2041                                              worker->priv->service,
2042                                              status);
2043 
2044         killpg (pid, SIGHUP);
2045 
2046         worker->priv->child_pid = -1;
2047         worker->priv->child_watch_id = 0;
2048         run_script (worker, GDMCONFDIR "/PostSession");
2049 }
2050 
2051 static void
gdm_session_worker_watch_child(GdmSessionWorker * worker)2052 gdm_session_worker_watch_child (GdmSessionWorker *worker)
2053 {
2054         g_debug ("GdmSession worker: watching pid %d", worker->priv->child_pid);
2055         worker->priv->child_watch_id = g_child_watch_add (worker->priv->child_pid,
2056                                                           (GChildWatchFunc)session_worker_child_watch,
2057                                                           worker);
2058 
2059 }
2060 
2061 static gboolean
_is_loggable_file(const char * filename)2062 _is_loggable_file (const char* filename)
2063 {
2064         struct stat file_info;
2065 
2066         if (g_lstat (filename, &file_info) < 0) {
2067                 return FALSE;
2068         }
2069 
2070         return S_ISREG (file_info.st_mode) && g_access (filename, R_OK | W_OK) == 0;
2071 }
2072 
2073 static void
rotate_logs(const char * path,guint n_copies)2074 rotate_logs (const char *path,
2075              guint       n_copies)
2076 {
2077         int i;
2078 
2079         for (i = n_copies - 1; i > 0; i--) {
2080                 char *name_n;
2081                 char *name_n1;
2082 
2083                 name_n = g_strdup_printf ("%s.%d", path, i);
2084                 if (i > 1) {
2085                         name_n1 = g_strdup_printf ("%s.%d", path, i - 1);
2086                 } else {
2087                         name_n1 = g_strdup (path);
2088                 }
2089 
2090                 g_unlink (name_n);
2091                 g_rename (name_n1, name_n);
2092 
2093                 g_free (name_n1);
2094                 g_free (name_n);
2095         }
2096 
2097         g_unlink (path);
2098 }
2099 
2100 static int
_open_program_session_log(const char * filename)2101 _open_program_session_log (const char *filename)
2102 {
2103         int   fd;
2104 
2105         rotate_logs (filename, MAX_LOGS);
2106 
2107         fd = g_open (filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
2108 
2109         if (fd < 0) {
2110                 char *temp_name;
2111 
2112                 temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);
2113 
2114                 fd = g_mkstemp (temp_name);
2115 
2116                 if (fd < 0) {
2117                         g_free (temp_name);
2118                         goto out;
2119                 }
2120 
2121                 g_warning ("session log '%s' is not appendable, logging session to '%s' instead.\n", filename,
2122                            temp_name);
2123                 g_free (temp_name);
2124         } else {
2125                 if (ftruncate (fd, 0) < 0) {
2126                         close (fd);
2127                         fd = -1;
2128                         goto out;
2129                 }
2130         }
2131 
2132         if (fchmod (fd, 0644) < 0) {
2133                 close (fd);
2134                 fd = -1;
2135                 goto out;
2136         }
2137 
2138 
2139 out:
2140         if (fd < 0) {
2141                 g_warning ("unable to log program session");
2142                 fd = g_open ("/dev/null", O_RDWR);
2143         }
2144 
2145         return fd;
2146 }
2147 
2148 static int
_open_user_session_log(const char * dir)2149 _open_user_session_log (const char *dir)
2150 {
2151         int   fd;
2152         char *filename;
2153 
2154         filename = g_build_filename (dir, GDM_SESSION_LOG_FILENAME, NULL);
2155 
2156         if (g_access (dir, R_OK | W_OK | X_OK) == 0 && _is_loggable_file (filename)) {
2157                 char *filename_old;
2158 
2159                 filename_old = g_strdup_printf ("%s.old", filename);
2160                 g_rename (filename, filename_old);
2161                 g_free (filename_old);
2162         }
2163 
2164         fd = g_open (filename, O_RDWR | O_APPEND | O_CREAT, 0600);
2165 
2166         if (fd < 0) {
2167                 char *temp_name;
2168 
2169                 temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);
2170 
2171                 fd = g_mkstemp (temp_name);
2172 
2173                 if (fd < 0) {
2174                         g_free (temp_name);
2175                         goto out;
2176                 }
2177 
2178                 g_warning ("session log '%s' is not appendable, logging session to '%s' instead.\n", filename,
2179                            temp_name);
2180                 g_free (filename);
2181                 filename = temp_name;
2182         } else {
2183                 if (ftruncate (fd, 0) < 0) {
2184                         close (fd);
2185                         fd = -1;
2186                         goto out;
2187                 }
2188         }
2189 
2190         if (fchmod (fd, 0600) < 0) {
2191                 close (fd);
2192                 fd = -1;
2193                 goto out;
2194         }
2195 
2196 
2197 out:
2198         g_free (filename);
2199 
2200         if (fd < 0) {
2201                 g_warning ("unable to log session");
2202                 fd = g_open ("/dev/null", O_RDWR);
2203         }
2204 
2205         return fd;
2206 }
2207 
2208 static gboolean
gdm_session_worker_start_session(GdmSessionWorker * worker,GError ** error)2209 gdm_session_worker_start_session (GdmSessionWorker  *worker,
2210                                   GError           **error)
2211 {
2212         struct passwd *passwd_entry;
2213         pid_t session_pid;
2214         int   error_code;
2215 
2216         gdm_get_pwent_for_name (worker->priv->username, &passwd_entry);
2217         if (worker->priv->is_program_session) {
2218                 g_debug ("GdmSessionWorker: opening session for program '%s'",
2219                          worker->priv->arguments[0]);
2220         } else {
2221                 g_debug ("GdmSessionWorker: opening user session with program '%s'",
2222                          worker->priv->arguments[0]);
2223         }
2224 
2225         error_code = PAM_SUCCESS;
2226 
2227 #ifdef WITH_SYSTEMD
2228         /* If we're in new vt mode, jump to the new vt now. There's no need to jump for
2229          * the other two modes: in the logind case, the session will activate itself when
2230          * ready, and in the reuse server case, we're already on the correct VT. */
2231         if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
2232                 if (worker->priv->display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
2233                         jump_to_vt (worker, worker->priv->session_vt);
2234                 }
2235         }
2236 #endif
2237 
2238         if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PostLogin")) {
2239                 g_set_error (error,
2240                              GDM_SESSION_WORKER_ERROR,
2241                              GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2242                              "Failed to execute PostLogin script");
2243                 error_code = PAM_ABORT;
2244                 goto out;
2245         }
2246 
2247         if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PreSession")) {
2248                 g_set_error (error,
2249                              GDM_SESSION_WORKER_ERROR,
2250                              GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2251                              "Failed to execute PreSession script");
2252                 error_code = PAM_ABORT;
2253                 goto out;
2254         }
2255 
2256         session_pid = fork ();
2257 
2258         if (session_pid < 0) {
2259                 g_set_error (error,
2260                              GDM_SESSION_WORKER_ERROR,
2261                              GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2262                              "%s", g_strerror (errno));
2263                 error_code = PAM_ABORT;
2264                 goto out;
2265         }
2266 
2267         if (session_pid == 0) {
2268                 const char * const * environment;
2269                 char  *home_dir;
2270                 int    stdin_fd = -1, stdout_fd = -1, stderr_fd = -1;
2271                 gboolean has_journald = FALSE, needs_controlling_terminal = FALSE;
2272                 /* Leak the TTY into the session as stdin so that it stays open
2273                  * without any races. */
2274                 if (worker->priv->session_tty_fd > 0) {
2275                         dup2 (worker->priv->session_tty_fd, STDIN_FILENO);
2276                         close (worker->priv->session_tty_fd);
2277                         worker->priv->session_tty_fd = -1;
2278                         needs_controlling_terminal = TRUE;
2279                 } else {
2280                         stdin_fd = open ("/dev/null", O_RDWR);
2281                         dup2 (stdin_fd, STDIN_FILENO);
2282                         close (stdin_fd);
2283                 }
2284 
2285 #ifdef ENABLE_SYSTEMD_JOURNAL
2286                 has_journald = sd_booted() > 0;
2287 #endif
2288                 if (!has_journald && worker->priv->is_program_session) {
2289                         stdout_fd = _open_program_session_log (worker->priv->log_file);
2290                         stderr_fd = dup (stdout_fd);
2291                 }
2292 
2293                 if (setsid () < 0) {
2294                         g_debug ("GdmSessionWorker: could not set pid '%u' as leader of new session and process group: %s",
2295                                  (guint) getpid (), g_strerror (errno));
2296                         _exit (EXIT_FAILURE);
2297                 }
2298 
2299 #ifdef WITH_SYSTEMD
2300                 /* Take control of the tty
2301                  */
2302                 if (needs_controlling_terminal) {
2303                         if (ioctl (STDIN_FILENO, TIOCSCTTY, 0) < 0) {
2304                                 g_debug ("GdmSessionWorker: could not take control of tty: %m");
2305                         }
2306                 }
2307 #endif
2308 
2309 #ifdef HAVE_LOGINCAP
2310                 if (setusercontext (NULL, passwd_entry, passwd_entry->pw_uid, LOGIN_SETALL) < 0) {
2311                         g_debug ("GdmSessionWorker: setusercontext() failed for user %s: %s",
2312                                  passwd_entry->pw_name, g_strerror (errno));
2313                         _exit (EXIT_FAILURE);
2314                 }
2315 #else
2316                 if (setuid (worker->priv->uid) < 0) {
2317                         g_debug ("GdmSessionWorker: could not reset uid: %s", g_strerror (errno));
2318                         _exit (EXIT_FAILURE);
2319                 }
2320 #endif
2321 
2322                 if (!worker->priv->is_program_session) {
2323                         gdm_session_worker_load_env_d (worker);
2324                 }
2325 
2326                 environment = gdm_session_worker_get_environment (worker);
2327 
2328                 g_assert (geteuid () == getuid ());
2329 
2330                 home_dir = gdm_session_worker_get_environment_variable (worker, "HOME");
2331                 if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
2332                         g_chdir ("/");
2333                 }
2334 
2335 #ifdef ENABLE_SYSTEMD_JOURNAL
2336                 if (has_journald) {
2337                         stdout_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_INFO, FALSE);
2338                         stderr_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_WARNING, FALSE);
2339 
2340                         /* Unset the CLOEXEC flags, because sd_journal_stream_fd
2341                          * gives it to us by default.
2342                          */
2343                         gdm_clear_close_on_exec_flag (stdout_fd);
2344                         gdm_clear_close_on_exec_flag (stderr_fd);
2345                 }
2346 #endif
2347                 if (!has_journald && !worker->priv->is_program_session) {
2348                         if (home_dir != NULL && home_dir[0] != '\0') {
2349                                 char *cache_dir;
2350                                 char *log_dir;
2351 
2352                                 cache_dir = gdm_session_worker_get_environment_variable (worker, "XDG_CACHE_HOME");
2353                                 if (cache_dir == NULL || cache_dir[0] == '\0') {
2354                                         cache_dir = g_build_filename (home_dir, ".cache", NULL);
2355                                 }
2356 
2357                                 log_dir = g_build_filename (cache_dir, "gdm", NULL);
2358                                 g_free (cache_dir);
2359 
2360                                 if (g_mkdir_with_parents (log_dir, S_IRWXU) == 0) {
2361                                         stdout_fd = _open_user_session_log (log_dir);
2362                                         stderr_fd = dup (stdout_fd);
2363                                 } else {
2364                                         stdout_fd = open ("/dev/null", O_RDWR);
2365                                         stderr_fd = dup (stdout_fd);
2366                                 }
2367                                 g_free (log_dir);
2368                         } else {
2369                                 stdout_fd = open ("/dev/null", O_RDWR);
2370                                 stderr_fd = dup (stdout_fd);
2371                         }
2372                 }
2373                 g_free (home_dir);
2374 
2375                 if (stdout_fd != -1) {
2376                         dup2 (stdout_fd, STDOUT_FILENO);
2377                         close (stdout_fd);
2378                 }
2379 
2380                 if (stderr_fd != -1) {
2381                         dup2 (stderr_fd, STDERR_FILENO);
2382                         close (stderr_fd);
2383                 }
2384 
2385                 gdm_log_shutdown ();
2386 
2387                 /*
2388                  * Reset SIGPIPE to default so that any process in the user
2389                  * session get the default SIGPIPE behavior instead of ignoring
2390                  * SIGPIPE.
2391                  */
2392                 signal (SIGPIPE, SIG_DFL);
2393 
2394                 gdm_session_execute (worker->priv->arguments[0],
2395                                      worker->priv->arguments,
2396                                      (char **)
2397                                      environment,
2398                                      TRUE);
2399 
2400                 gdm_log_init ();
2401                 g_debug ("GdmSessionWorker: child '%s' could not be started: %s",
2402                          worker->priv->arguments[0],
2403                          g_strerror (errno));
2404 
2405                 _exit (EXIT_FAILURE);
2406         }
2407 
2408         if (worker->priv->session_tty_fd > 0) {
2409                 close (worker->priv->session_tty_fd);
2410                 worker->priv->session_tty_fd = -1;
2411         }
2412 
2413         /* If we end up execing again, make sure we don't use the executable context set up
2414          * by pam_selinux durin pam_open_session
2415          */
2416 #ifdef HAVE_SELINUX
2417         setexeccon (NULL);
2418 #endif
2419 
2420         worker->priv->child_pid = session_pid;
2421 
2422         g_debug ("GdmSessionWorker: session opened creating reply...");
2423         g_assert (sizeof (GPid) <= sizeof (int));
2424 
2425         g_debug ("GdmSessionWorker: state SESSION_STARTED");
2426         worker->priv->state = GDM_SESSION_WORKER_STATE_SESSION_STARTED;
2427 
2428         gdm_session_worker_watch_child (worker);
2429 
2430  out:
2431         if (error_code != PAM_SUCCESS) {
2432                 gdm_session_worker_uninitialize_pam (worker, error_code);
2433                 return FALSE;
2434         }
2435 
2436         return TRUE;
2437 }
2438 
2439 #ifdef WITH_SYSTEMD
2440 static gboolean
set_up_for_new_vt(GdmSessionWorker * worker)2441 set_up_for_new_vt (GdmSessionWorker *worker)
2442 {
2443         int fd;
2444         char vt_string[256], tty_string[256];
2445         struct vt_stat vt_state = { 0 };
2446         int session_vt = 0;
2447 
2448         fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
2449 
2450         if (fd < 0) {
2451                 g_debug ("GdmSessionWorker: couldn't open VT master: %m");
2452                 return FALSE;
2453         }
2454 
2455         if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
2456                 g_debug ("GdmSessionWorker: couldn't get current VT: %m");
2457                 goto fail;
2458         }
2459 
2460         if (worker->priv->display_is_initial) {
2461                 session_vt = atoi (GDM_INITIAL_VT);
2462         } else {
2463                 if (ioctl(fd, VT_OPENQRY, &session_vt) < 0) {
2464                         g_debug ("GdmSessionWorker: couldn't open new VT: %m");
2465                         goto fail;
2466                 }
2467         }
2468 
2469         worker->priv->login_vt = vt_state.v_active;
2470         worker->priv->session_vt = session_vt;
2471 
2472         close (fd);
2473         fd = -1;
2474 
2475         g_assert (session_vt > 0);
2476 
2477         g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt);
2478 
2479         /* Set the VTNR. This is used by logind to configure a session in
2480          * the logind-managed case, but it doesn't hurt to set it always.
2481          * When logind gains support for XDG_VTNR=auto, we can make the
2482          * OPENQRY and this whole path only used by the new VT code. */
2483         gdm_session_worker_set_environment_variable (worker,
2484                                                      "XDG_VTNR",
2485                                                      vt_string);
2486 
2487         g_snprintf (tty_string, 256, "/dev/tty%d", session_vt);
2488         worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY);
2489         pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);
2490 
2491         return TRUE;
2492 
2493 fail:
2494         close (fd);
2495         return FALSE;
2496 }
2497 
2498 static gboolean
set_xdg_vtnr_to_current_vt(GdmSessionWorker * worker)2499 set_xdg_vtnr_to_current_vt (GdmSessionWorker *worker)
2500 {
2501         int fd;
2502         char vt_string[256];
2503         struct vt_stat vt_state = { 0 };
2504 
2505         fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
2506 
2507         if (fd < 0) {
2508                 g_debug ("GdmSessionWorker: couldn't open VT master: %m");
2509                 return FALSE;
2510         }
2511 
2512         if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
2513                 g_debug ("GdmSessionWorker: couldn't get current VT: %m");
2514                 goto fail;
2515         }
2516 
2517         close (fd);
2518         fd = -1;
2519 
2520         g_snprintf (vt_string, sizeof (vt_string), "%d", vt_state.v_active);
2521 
2522         gdm_session_worker_set_environment_variable (worker,
2523                                                      "XDG_VTNR",
2524                                                      vt_string);
2525 
2526         return TRUE;
2527 
2528 fail:
2529         close (fd);
2530         return FALSE;
2531 }
2532 #endif
2533 
2534 static gboolean
set_up_for_current_vt(GdmSessionWorker * worker,GError ** error)2535 set_up_for_current_vt (GdmSessionWorker  *worker,
2536                        GError           **error)
2537 {
2538 #ifdef PAM_XAUTHDATA
2539         struct pam_xauth_data *pam_xauth;
2540 #endif
2541         int                    error_code = PAM_SUCCESS;
2542         char                  *pam_tty;
2543 
2544         /* set TTY */
2545         pam_tty = _get_tty_for_pam (worker->priv->x11_display_name, worker->priv->display_device);
2546         if (pam_tty != NULL && pam_tty[0] != '\0') {
2547                 error_code = pam_set_item (worker->priv->pam_handle, PAM_TTY, pam_tty);
2548 
2549                 if (error_code != PAM_SUCCESS) {
2550                         g_debug ("error informing authentication system of user's console %s: %s",
2551                                  pam_tty,
2552                                  pam_strerror (worker->priv->pam_handle, error_code));
2553                         g_free (pam_tty);
2554                         g_set_error (error,
2555                                      GDM_SESSION_WORKER_ERROR,
2556                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
2557                                      "%s", "");
2558                         goto out;
2559                 }
2560         }
2561         g_free (pam_tty);
2562 
2563 #ifdef PAM_XDISPLAY
2564         /* set XDISPLAY */
2565         if (worker->priv->x11_display_name != NULL && worker->priv->x11_display_name[0] != '\0') {
2566                 error_code = pam_set_item (worker->priv->pam_handle, PAM_XDISPLAY, worker->priv->x11_display_name);
2567                 if (error_code != PAM_SUCCESS) {
2568                         g_debug ("error informing authentication system of display string %s: %s",
2569                                  worker->priv->x11_display_name,
2570                                  pam_strerror (worker->priv->pam_handle, error_code));
2571                         g_set_error (error,
2572                                      GDM_SESSION_WORKER_ERROR,
2573                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
2574                                      "%s", "");
2575                         goto out;
2576                 }
2577         }
2578 #endif
2579 #ifdef PAM_XAUTHDATA
2580         /* set XAUTHDATA */
2581         pam_xauth = _get_xauth_for_pam (worker->priv->x11_authority_file);
2582         if (pam_xauth != NULL) {
2583                 error_code = pam_set_item (worker->priv->pam_handle, PAM_XAUTHDATA, pam_xauth);
2584                 if (error_code != PAM_SUCCESS) {
2585                         g_debug ("error informing authentication system of display string %s: %s",
2586                                  worker->priv->x11_display_name,
2587                                  pam_strerror (worker->priv->pam_handle, error_code));
2588                         g_free (pam_xauth);
2589 
2590                         g_set_error (error,
2591                                      GDM_SESSION_WORKER_ERROR,
2592                                      GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
2593                                      "%s", "");
2594                         goto out;
2595                 }
2596                 g_free (pam_xauth);
2597          }
2598 #endif
2599 
2600 #ifdef WITH_SYSTEMD
2601         if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
2602                 g_debug ("GdmSessionWorker: setting XDG_VTNR to current vt");
2603                 set_xdg_vtnr_to_current_vt (worker);
2604         } else {
2605                 g_debug ("GdmSessionWorker: not setting XDG_VTNR since not seat0");
2606         }
2607 #endif
2608 
2609         return TRUE;
2610 out:
2611         return FALSE;
2612 }
2613 
2614 static gboolean
gdm_session_worker_open_session(GdmSessionWorker * worker,GError ** error)2615 gdm_session_worker_open_session (GdmSessionWorker  *worker,
2616                                  GError           **error)
2617 {
2618         int error_code;
2619         int flags;
2620         char *session_id = NULL;
2621 
2622         g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
2623         g_assert (geteuid () == 0);
2624 
2625         switch (worker->priv->display_mode) {
2626         case GDM_SESSION_DISPLAY_MODE_REUSE_VT:
2627                 if (!set_up_for_current_vt (worker, error)) {
2628                         return FALSE;
2629                 }
2630                 break;
2631 #ifdef WITH_SYSTEMD
2632         case GDM_SESSION_DISPLAY_MODE_NEW_VT:
2633         case GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED:
2634                 if (!set_up_for_new_vt (worker)) {
2635                         g_set_error (error,
2636                                      GDM_SESSION_WORKER_ERROR,
2637                                      GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2638                                      "Unable to open VT");
2639                         return FALSE;
2640                 }
2641                 break;
2642 #endif
2643         }
2644 
2645         flags = 0;
2646 
2647         if (worker->priv->is_program_session) {
2648                 flags |= PAM_SILENT;
2649         }
2650 
2651         error_code = pam_open_session (worker->priv->pam_handle, flags);
2652 
2653         if (error_code != PAM_SUCCESS) {
2654                 g_set_error (error,
2655                              GDM_SESSION_WORKER_ERROR,
2656                              GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2657                              "%s", pam_strerror (worker->priv->pam_handle, error_code));
2658                 goto out;
2659         }
2660 
2661         g_debug ("GdmSessionWorker: state SESSION_OPENED");
2662         worker->priv->state = GDM_SESSION_WORKER_STATE_SESSION_OPENED;
2663 
2664 #ifdef WITH_SYSTEMD
2665         session_id = gdm_session_worker_get_environment_variable (worker, "XDG_SESSION_ID");
2666 #endif
2667 
2668 #ifdef WITH_CONSOLE_KIT
2669         register_ck_session (worker);
2670 
2671         if (session_id == NULL) {
2672                 session_id = get_ck_session_id (worker);
2673         }
2674 #endif
2675 
2676         if (session_id != NULL) {
2677                 g_free (worker->priv->session_id);
2678                 worker->priv->session_id = session_id;
2679         }
2680 
2681  out:
2682         if (error_code != PAM_SUCCESS) {
2683                 gdm_session_worker_uninitialize_pam (worker, error_code);
2684                 return FALSE;
2685         }
2686 
2687         gdm_session_worker_get_username (worker, NULL);
2688         gdm_session_auditor_report_login (worker->priv->auditor);
2689 
2690         return TRUE;
2691 }
2692 
2693 static void
gdm_session_worker_set_server_address(GdmSessionWorker * worker,const char * address)2694 gdm_session_worker_set_server_address (GdmSessionWorker *worker,
2695                                        const char       *address)
2696 {
2697         g_free (worker->priv->server_address);
2698         worker->priv->server_address = g_strdup (address);
2699 }
2700 
2701 static void
gdm_session_worker_set_is_reauth_session(GdmSessionWorker * worker,gboolean is_reauth_session)2702 gdm_session_worker_set_is_reauth_session (GdmSessionWorker *worker,
2703                                           gboolean          is_reauth_session)
2704 {
2705         worker->priv->is_reauth_session = is_reauth_session;
2706 }
2707 
2708 static void
gdm_session_worker_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2709 gdm_session_worker_set_property (GObject      *object,
2710                                 guint         prop_id,
2711                                 const GValue *value,
2712                                 GParamSpec   *pspec)
2713 {
2714         GdmSessionWorker *self;
2715 
2716         self = GDM_SESSION_WORKER (object);
2717 
2718         switch (prop_id) {
2719         case PROP_SERVER_ADDRESS:
2720                 gdm_session_worker_set_server_address (self, g_value_get_string (value));
2721                 break;
2722         case PROP_IS_REAUTH_SESSION:
2723                 gdm_session_worker_set_is_reauth_session (self, g_value_get_boolean (value));
2724                 break;
2725         default:
2726                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2727                 break;
2728         }
2729 }
2730 
2731 static void
gdm_session_worker_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2732 gdm_session_worker_get_property (GObject    *object,
2733                                 guint       prop_id,
2734                                 GValue     *value,
2735                                 GParamSpec *pspec)
2736 {
2737         GdmSessionWorker *self;
2738 
2739         self = GDM_SESSION_WORKER (object);
2740 
2741         switch (prop_id) {
2742         case PROP_SERVER_ADDRESS:
2743                 g_value_set_string (value, self->priv->server_address);
2744                 break;
2745         case PROP_IS_REAUTH_SESSION:
2746                 g_value_set_boolean (value, self->priv->is_reauth_session);
2747                 break;
2748         default:
2749                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2750                 break;
2751         }
2752 }
2753 
2754 static gboolean
gdm_session_worker_handle_set_environment_variable(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * key,const char * value)2755 gdm_session_worker_handle_set_environment_variable (GdmDBusWorker         *object,
2756                                                     GDBusMethodInvocation *invocation,
2757                                                     const char            *key,
2758                                                     const char            *value)
2759 {
2760         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
2761         gdm_session_worker_set_environment_variable (worker, key, value);
2762         gdm_dbus_worker_complete_set_environment_variable (object, invocation);
2763         return TRUE;
2764 }
2765 
2766 static gboolean
gdm_session_worker_handle_set_session_name(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * session_name)2767 gdm_session_worker_handle_set_session_name (GdmDBusWorker         *object,
2768                                             GDBusMethodInvocation *invocation,
2769                                             const char            *session_name)
2770 {
2771         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
2772         g_debug ("GdmSessionWorker: session name set to %s", session_name);
2773         gdm_session_settings_set_session_name (worker->priv->user_settings,
2774                                                session_name);
2775         gdm_dbus_worker_complete_set_session_name (object, invocation);
2776         return TRUE;
2777 }
2778 
2779 static gboolean
gdm_session_worker_handle_set_session_type(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * session_type)2780 gdm_session_worker_handle_set_session_type (GdmDBusWorker         *object,
2781                                             GDBusMethodInvocation *invocation,
2782                                             const char            *session_type)
2783 {
2784         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
2785         g_debug ("GdmSessionWorker: session type set to %s", session_type);
2786         g_free (worker->priv->session_type);
2787         worker->priv->session_type = g_strdup (session_type);
2788         gdm_dbus_worker_complete_set_session_type (object, invocation);
2789         return TRUE;
2790 }
2791 
2792 static gboolean
gdm_session_worker_handle_set_session_display_mode(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * str)2793 gdm_session_worker_handle_set_session_display_mode (GdmDBusWorker         *object,
2794                                                     GDBusMethodInvocation *invocation,
2795                                                     const char            *str)
2796 {
2797         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
2798         g_debug ("GdmSessionWorker: session display mode set to %s", str);
2799         worker->priv->display_mode = gdm_session_display_mode_from_string (str);
2800         gdm_dbus_worker_complete_set_session_display_mode (object, invocation);
2801         return TRUE;
2802 }
2803 
2804 static gboolean
gdm_session_worker_handle_set_language_name(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * language_name)2805 gdm_session_worker_handle_set_language_name (GdmDBusWorker         *object,
2806                                              GDBusMethodInvocation *invocation,
2807                                              const char            *language_name)
2808 {
2809         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
2810         g_debug ("GdmSessionWorker: language name set to %s", language_name);
2811         gdm_session_settings_set_language_name (worker->priv->user_settings,
2812                                                 language_name);
2813         gdm_dbus_worker_complete_set_language_name (object, invocation);
2814         return TRUE;
2815 }
2816 
2817 static void
on_saved_language_name_read(GdmSessionWorker * worker)2818 on_saved_language_name_read (GdmSessionWorker *worker)
2819 {
2820         char *language_name;
2821 
2822         language_name = gdm_session_settings_get_language_name (worker->priv->user_settings);
2823 
2824         g_debug ("GdmSessionWorker: Saved language is %s", language_name);
2825         gdm_dbus_worker_emit_saved_language_name_read (GDM_DBUS_WORKER (worker),
2826                                                        language_name);
2827         g_free (language_name);
2828 }
2829 
2830 static void
on_saved_session_name_read(GdmSessionWorker * worker)2831 on_saved_session_name_read (GdmSessionWorker *worker)
2832 {
2833         char *session_name;
2834 
2835         session_name = gdm_session_settings_get_session_name (worker->priv->user_settings);
2836 
2837         g_debug ("GdmSessionWorker: Saved session is %s", session_name);
2838         gdm_dbus_worker_emit_saved_session_name_read (GDM_DBUS_WORKER (worker),
2839                                                       session_name);
2840         g_free (session_name);
2841 }
2842 
2843 static void
do_setup(GdmSessionWorker * worker)2844 do_setup (GdmSessionWorker *worker)
2845 {
2846         GError  *error;
2847         gboolean res;
2848 
2849         error = NULL;
2850         res = gdm_session_worker_initialize_pam (worker,
2851                                                  worker->priv->service,
2852                                                  (const char **) worker->priv->extensions,
2853                                                  worker->priv->username,
2854                                                  worker->priv->hostname,
2855                                                  worker->priv->display_is_local,
2856                                                  worker->priv->x11_display_name,
2857                                                  worker->priv->x11_authority_file,
2858                                                  worker->priv->display_device,
2859                                                  worker->priv->display_seat_id,
2860                                                  &error);
2861 
2862         if (res) {
2863                 g_dbus_method_invocation_return_value (worker->priv->pending_invocation, NULL);
2864         } else {
2865                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
2866         }
2867         worker->priv->pending_invocation = NULL;
2868 }
2869 
2870 static void
do_authenticate(GdmSessionWorker * worker)2871 do_authenticate (GdmSessionWorker *worker)
2872 {
2873         GError  *error;
2874         gboolean res;
2875 
2876         /* find out who the user is and ensure they are who they say they are
2877          */
2878         error = NULL;
2879         res = gdm_session_worker_authenticate_user (worker,
2880                                                     worker->priv->password_is_required,
2881                                                     &error);
2882         if (res) {
2883                 /* we're authenticated.  Let's make sure we've been given
2884                  * a valid username for the system
2885                  */
2886                 if (!worker->priv->is_program_session) {
2887                         g_debug ("GdmSessionWorker: trying to get updated username");
2888                         gdm_session_worker_update_username (worker);
2889                 }
2890 
2891                 gdm_dbus_worker_complete_authenticate (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
2892         } else {
2893                 g_debug ("GdmSessionWorker: Unable to verify user");
2894                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
2895         }
2896         worker->priv->pending_invocation = NULL;
2897 }
2898 
2899 static void
do_authorize(GdmSessionWorker * worker)2900 do_authorize (GdmSessionWorker *worker)
2901 {
2902         GError  *error;
2903         gboolean res;
2904 
2905         /* make sure the user is allowed to log in to this system
2906          */
2907         error = NULL;
2908         res = gdm_session_worker_authorize_user (worker,
2909                                                  worker->priv->password_is_required,
2910                                                  &error);
2911         if (res) {
2912                 gdm_dbus_worker_complete_authorize (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
2913         } else {
2914                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
2915         }
2916         worker->priv->pending_invocation = NULL;
2917 }
2918 
2919 static void
do_accredit(GdmSessionWorker * worker)2920 do_accredit (GdmSessionWorker *worker)
2921 {
2922         GError  *error;
2923         gboolean res;
2924 
2925         /* get kerberos tickets, setup group lists, etc
2926          */
2927         error = NULL;
2928         res = gdm_session_worker_accredit_user (worker, &error);
2929 
2930         if (res) {
2931                 gdm_dbus_worker_complete_establish_credentials (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
2932         } else {
2933                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
2934         }
2935         worker->priv->pending_invocation = NULL;
2936 }
2937 
2938 static void
save_account_details_now(GdmSessionWorker * worker)2939 save_account_details_now (GdmSessionWorker *worker)
2940 {
2941         g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED);
2942 
2943         g_debug ("GdmSessionWorker: saving account details for user %s", worker->priv->username);
2944         worker->priv->state = GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED;
2945         if (!gdm_session_settings_save (worker->priv->user_settings,
2946                                         worker->priv->username)) {
2947                 g_warning ("could not save session and language settings");
2948         }
2949         queue_state_change (worker);
2950 }
2951 
2952 static void
on_settings_is_loaded_changed(GdmSessionSettings * user_settings,GParamSpec * pspec,GdmSessionWorker * worker)2953 on_settings_is_loaded_changed (GdmSessionSettings *user_settings,
2954                                GParamSpec         *pspec,
2955                                GdmSessionWorker   *worker)
2956 {
2957         if (!gdm_session_settings_is_loaded (worker->priv->user_settings)) {
2958                 return;
2959         }
2960 
2961         /* These signal handlers should be disconnected after the loading,
2962          * so that gdm_session_settings_set_* APIs don't cause the emitting
2963          * of Saved*NameRead D-Bus signals any more.
2964          */
2965         g_signal_handlers_disconnect_by_func (worker->priv->user_settings,
2966                                               G_CALLBACK (on_saved_session_name_read),
2967                                               worker);
2968 
2969         g_signal_handlers_disconnect_by_func (worker->priv->user_settings,
2970                                               G_CALLBACK (on_saved_language_name_read),
2971                                               worker);
2972 
2973         if (worker->priv->state == GDM_SESSION_WORKER_STATE_NONE) {
2974                 g_debug ("GdmSessionWorker: queuing setup for user: %s %s",
2975                          worker->priv->username, worker->priv->display_device);
2976                 queue_state_change (worker);
2977         } else if (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED) {
2978                 save_account_details_now (worker);
2979         } else {
2980                 return;
2981         }
2982 
2983         g_signal_handlers_disconnect_by_func (G_OBJECT (worker->priv->user_settings),
2984                                               G_CALLBACK (on_settings_is_loaded_changed),
2985                                               worker);
2986 }
2987 
2988 static void
do_save_account_details_when_ready(GdmSessionWorker * worker)2989 do_save_account_details_when_ready (GdmSessionWorker *worker)
2990 {
2991         g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED);
2992 
2993         if (!gdm_session_settings_is_loaded (worker->priv->user_settings)) {
2994                 g_signal_connect (G_OBJECT (worker->priv->user_settings),
2995                                   "notify::is-loaded",
2996                                   G_CALLBACK (on_settings_is_loaded_changed),
2997                                   worker);
2998                 g_debug ("GdmSessionWorker: user %s, not fully loaded yet, will save account details later",
2999                          worker->priv->username);
3000                 gdm_session_settings_load (worker->priv->user_settings,
3001                                            worker->priv->username);
3002                 return;
3003         }
3004 
3005         save_account_details_now (worker);
3006 }
3007 
3008 static void
do_open_session(GdmSessionWorker * worker)3009 do_open_session (GdmSessionWorker *worker)
3010 {
3011         GError  *error;
3012         gboolean res;
3013 
3014         error = NULL;
3015         res = gdm_session_worker_open_session (worker, &error);
3016 
3017         if (res) {
3018                 char *session_id = worker->priv->session_id;
3019                 if (session_id == NULL) {
3020                         session_id = "";
3021                 }
3022 
3023                 gdm_dbus_worker_complete_open (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation, session_id);
3024         } else {
3025                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
3026         }
3027         worker->priv->pending_invocation = NULL;
3028 }
3029 
3030 static void
do_start_session(GdmSessionWorker * worker)3031 do_start_session (GdmSessionWorker *worker)
3032 {
3033         GError  *error;
3034         gboolean res;
3035 
3036         error = NULL;
3037         res = gdm_session_worker_start_session (worker, &error);
3038         if (res) {
3039                 gdm_dbus_worker_complete_start_program (GDM_DBUS_WORKER (worker),
3040                                                         worker->priv->pending_invocation,
3041                                                         worker->priv->child_pid);
3042         } else {
3043                 g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
3044         }
3045         worker->priv->pending_invocation = NULL;
3046 }
3047 
3048 static const char *
get_state_name(int state)3049 get_state_name (int state)
3050 {
3051         const char *name;
3052 
3053         name = NULL;
3054 
3055         switch (state) {
3056         case GDM_SESSION_WORKER_STATE_NONE:
3057                 name = "NONE";
3058                 break;
3059         case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
3060                 name = "SETUP_COMPLETE";
3061                 break;
3062         case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
3063                 name = "AUTHENTICATED";
3064                 break;
3065         case GDM_SESSION_WORKER_STATE_AUTHORIZED:
3066                 name = "AUTHORIZED";
3067                 break;
3068         case GDM_SESSION_WORKER_STATE_ACCREDITED:
3069                 name = "ACCREDITED";
3070                 break;
3071         case GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED:
3072                 name = "ACCOUNT_DETAILS_SAVED";
3073                 break;
3074         case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
3075                 name = "SESSION_OPENED";
3076                 break;
3077         case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
3078                 name = "SESSION_STARTED";
3079                 break;
3080         default:
3081                 g_assert_not_reached ();
3082                 break;
3083         }
3084 
3085         return name;
3086 }
3087 
3088 static gboolean
state_change_idle(GdmSessionWorker * worker)3089 state_change_idle (GdmSessionWorker *worker)
3090 {
3091         int new_state;
3092 
3093         new_state = worker->priv->state + 1;
3094         g_debug ("GdmSessionWorker: attempting to change state to %s",
3095                  get_state_name (new_state));
3096 
3097         worker->priv->state_change_idle_id = 0;
3098 
3099         switch (new_state) {
3100         case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
3101                 do_setup (worker);
3102                 break;
3103         case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
3104                 do_authenticate (worker);
3105                 break;
3106         case GDM_SESSION_WORKER_STATE_AUTHORIZED:
3107                 do_authorize (worker);
3108                 break;
3109         case GDM_SESSION_WORKER_STATE_ACCREDITED:
3110                 do_accredit (worker);
3111                 break;
3112         case GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED:
3113                 do_save_account_details_when_ready (worker);
3114                 break;
3115         case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
3116                 do_open_session (worker);
3117                 break;
3118         case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
3119                 do_start_session (worker);
3120                 break;
3121         case GDM_SESSION_WORKER_STATE_NONE:
3122         default:
3123                 g_assert_not_reached ();
3124         }
3125         return FALSE;
3126 }
3127 
3128 static void
queue_state_change(GdmSessionWorker * worker)3129 queue_state_change (GdmSessionWorker *worker)
3130 {
3131         if (worker->priv->state_change_idle_id > 0) {
3132                 return;
3133         }
3134 
3135         worker->priv->state_change_idle_id = g_idle_add ((GSourceFunc)state_change_idle, worker);
3136 }
3137 
3138 static gboolean
validate_state_change(GdmSessionWorker * worker,GDBusMethodInvocation * invocation,int new_state)3139 validate_state_change (GdmSessionWorker      *worker,
3140                        GDBusMethodInvocation *invocation,
3141                        int                    new_state)
3142 {
3143         if (worker->priv->pending_invocation != NULL) {
3144                 g_dbus_method_invocation_return_error (invocation,
3145                                                        GDM_SESSION_WORKER_ERROR,
3146                                                        GDM_SESSION_WORKER_ERROR_OUTSTANDING_REQUEST,
3147                                                        "Cannot process state change to %s, as there is already an outstanding request to move to state %s",
3148                                                        get_state_name (new_state),
3149                                                        get_state_name (worker->priv->state + 1));
3150                 return FALSE;
3151         } else if (worker->priv->state != new_state - 1) {
3152                 g_dbus_method_invocation_return_error (invocation,
3153                                                        GDM_SESSION_WORKER_ERROR,
3154                                                        GDM_SESSION_WORKER_ERROR_WRONG_STATE,
3155                                                        "Cannot move to state %s, in state %s, not %s",
3156                                                        get_state_name (new_state),
3157                                                        get_state_name (worker->priv->state),
3158                                                        get_state_name (new_state - 1));
3159                 return FALSE;
3160         }
3161 
3162         return TRUE;
3163 }
3164 
3165 static void
validate_and_queue_state_change(GdmSessionWorker * worker,GDBusMethodInvocation * invocation,int new_state)3166 validate_and_queue_state_change (GdmSessionWorker      *worker,
3167                                  GDBusMethodInvocation *invocation,
3168                                  int                    new_state)
3169 {
3170         if (validate_state_change (worker, invocation, new_state)) {
3171                 worker->priv->pending_invocation = invocation;
3172                 queue_state_change (worker);
3173         }
3174 }
3175 
3176 static gboolean
gdm_session_worker_handle_authenticate(GdmDBusWorker * object,GDBusMethodInvocation * invocation)3177 gdm_session_worker_handle_authenticate (GdmDBusWorker         *object,
3178                                         GDBusMethodInvocation *invocation)
3179 {
3180         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3181         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_AUTHENTICATED);
3182         return TRUE;
3183 }
3184 
3185 static gboolean
gdm_session_worker_handle_authorize(GdmDBusWorker * object,GDBusMethodInvocation * invocation)3186 gdm_session_worker_handle_authorize (GdmDBusWorker         *object,
3187                                      GDBusMethodInvocation *invocation)
3188 {
3189         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3190         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_AUTHORIZED);
3191         return TRUE;
3192 }
3193 
3194 static gboolean
gdm_session_worker_handle_establish_credentials(GdmDBusWorker * object,GDBusMethodInvocation * invocation)3195 gdm_session_worker_handle_establish_credentials (GdmDBusWorker         *object,
3196                                                  GDBusMethodInvocation *invocation)
3197 {
3198         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3199         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_ACCREDITED);
3200 
3201         if (!worker->priv->is_reauth_session) {
3202                 worker->priv->cred_flags = PAM_ESTABLISH_CRED;
3203         } else {
3204                 worker->priv->cred_flags = PAM_REINITIALIZE_CRED;
3205         }
3206 
3207         return TRUE;
3208 }
3209 
3210 static gboolean
gdm_session_worker_handle_open(GdmDBusWorker * object,GDBusMethodInvocation * invocation)3211 gdm_session_worker_handle_open (GdmDBusWorker         *object,
3212                                 GDBusMethodInvocation *invocation)
3213 {
3214         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3215         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
3216         return TRUE;
3217 }
3218 
3219 #ifdef SUPPORTS_PAM_EXTENSIONS
3220 static char **
filter_extensions(const char * const * extensions)3221 filter_extensions (const char * const *extensions)
3222 {
3223         size_t i, j;
3224         GPtrArray *array = NULL;
3225         char **filtered_extensions = NULL;
3226 
3227         array = g_ptr_array_new ();
3228 
3229         for (i = 0; extensions[i] != NULL; i++) {
3230                 for (j = 0; gdm_supported_pam_extensions[j] != NULL; j++) {
3231                         if (g_strcmp0 (extensions[i], gdm_supported_pam_extensions[j]) == 0) {
3232                                 g_ptr_array_add (array, g_strdup (gdm_supported_pam_extensions[j]));
3233                                 break;
3234                         }
3235                 }
3236         }
3237         g_ptr_array_add (array, NULL);
3238 
3239         filtered_extensions = g_strdupv ((char **) array->pdata);
3240 
3241         g_ptr_array_free (array, TRUE);
3242 
3243         return filtered_extensions;
3244 }
3245 #endif
3246 
3247 static gboolean
gdm_session_worker_handle_initialize(GdmDBusWorker * object,GDBusMethodInvocation * invocation,GVariant * details)3248 gdm_session_worker_handle_initialize (GdmDBusWorker         *object,
3249                                       GDBusMethodInvocation *invocation,
3250                                       GVariant              *details)
3251 {
3252         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3253         GVariantIter      iter;
3254         char             *key;
3255         GVariant         *value;
3256         gboolean          wait_for_settings = FALSE;
3257 
3258         if (!validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE))
3259                 return TRUE;
3260 
3261         g_variant_iter_init (&iter, details);
3262         while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
3263                 if (g_strcmp0 (key, "service") == 0) {
3264                         worker->priv->service = g_variant_dup_string (value, NULL);
3265 #ifdef SUPPORTS_PAM_EXTENSIONS
3266                 } else if (g_strcmp0 (key, "extensions") == 0) {
3267                         worker->priv->extensions = filter_extensions (g_variant_get_strv (value, NULL));
3268 #endif
3269                 } else if (g_strcmp0 (key, "username") == 0) {
3270                         worker->priv->username = g_variant_dup_string (value, NULL);
3271                 } else if (g_strcmp0 (key, "is-program-session") == 0) {
3272                         worker->priv->is_program_session = g_variant_get_boolean (value);
3273                 } else if (g_strcmp0 (key, "log-file") == 0) {
3274                         worker->priv->log_file = g_variant_dup_string (value, NULL);
3275                 } else if (g_strcmp0 (key, "x11-display-name") == 0) {
3276                         worker->priv->x11_display_name = g_variant_dup_string (value, NULL);
3277                 } else if (g_strcmp0 (key, "x11-authority-file") == 0) {
3278                         worker->priv->x11_authority_file = g_variant_dup_string (value, NULL);
3279                 } else if (g_strcmp0 (key, "console") == 0) {
3280                         worker->priv->display_device = g_variant_dup_string (value, NULL);
3281                 } else if (g_strcmp0 (key, "seat-id") == 0) {
3282                         worker->priv->display_seat_id = g_variant_dup_string (value, NULL);
3283                 } else if (g_strcmp0 (key, "hostname") == 0) {
3284                         worker->priv->hostname = g_variant_dup_string (value, NULL);
3285                 } else if (g_strcmp0 (key, "display-is-local") == 0) {
3286                         worker->priv->display_is_local = g_variant_get_boolean (value);
3287                 } else if (g_strcmp0 (key, "display-is-initial") == 0) {
3288                         worker->priv->display_is_initial = g_variant_get_boolean (value);
3289                 }
3290         }
3291 
3292         worker->priv->pending_invocation = invocation;
3293 
3294         if (!worker->priv->is_program_session) {
3295                 g_signal_connect_swapped (worker->priv->user_settings,
3296                                           "notify::language-name",
3297                                           G_CALLBACK (on_saved_language_name_read),
3298                                           worker);
3299 
3300                 g_signal_connect_swapped (worker->priv->user_settings,
3301                                           "notify::session-name",
3302                                           G_CALLBACK (on_saved_session_name_read),
3303                                           worker);
3304 
3305                 if (worker->priv->username) {
3306                         wait_for_settings = !gdm_session_settings_load (worker->priv->user_settings,
3307                                                                         worker->priv->username);
3308                 }
3309         }
3310 
3311         if (wait_for_settings) {
3312                 /* Load settings from accounts daemon before continuing
3313                  */
3314                 g_signal_connect (G_OBJECT (worker->priv->user_settings),
3315                                   "notify::is-loaded",
3316                                   G_CALLBACK (on_settings_is_loaded_changed),
3317                                   worker);
3318         } else {
3319                 queue_state_change (worker);
3320         }
3321 
3322         return TRUE;
3323 }
3324 
3325 static gboolean
gdm_session_worker_handle_setup(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * service,const char * x11_display_name,const char * x11_authority_file,const char * console,const char * seat_id,const char * hostname,gboolean display_is_local,gboolean display_is_initial)3326 gdm_session_worker_handle_setup (GdmDBusWorker         *object,
3327                                  GDBusMethodInvocation *invocation,
3328                                  const char            *service,
3329                                  const char            *x11_display_name,
3330                                  const char            *x11_authority_file,
3331                                  const char            *console,
3332                                  const char            *seat_id,
3333                                  const char            *hostname,
3334                                  gboolean               display_is_local,
3335                                  gboolean               display_is_initial)
3336 {
3337         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3338         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
3339 
3340         worker->priv->service = g_strdup (service);
3341         worker->priv->x11_display_name = g_strdup (x11_display_name);
3342         worker->priv->x11_authority_file = g_strdup (x11_authority_file);
3343         worker->priv->display_device = g_strdup (console);
3344         worker->priv->display_seat_id = g_strdup (seat_id);
3345         worker->priv->hostname = g_strdup (hostname);
3346         worker->priv->display_is_local = display_is_local;
3347         worker->priv->display_is_initial = display_is_initial;
3348         worker->priv->username = NULL;
3349 
3350         g_signal_connect_swapped (worker->priv->user_settings,
3351                                   "notify::language-name",
3352                                   G_CALLBACK (on_saved_language_name_read),
3353                                   worker);
3354 
3355         g_signal_connect_swapped (worker->priv->user_settings,
3356                                   "notify::session-name",
3357                                   G_CALLBACK (on_saved_session_name_read),
3358                                   worker);
3359         return TRUE;
3360 }
3361 
3362 static gboolean
gdm_session_worker_handle_setup_for_user(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * service,const char * username,const char * x11_display_name,const char * x11_authority_file,const char * console,const char * seat_id,const char * hostname,gboolean display_is_local,gboolean display_is_initial)3363 gdm_session_worker_handle_setup_for_user (GdmDBusWorker         *object,
3364                                           GDBusMethodInvocation *invocation,
3365                                           const char            *service,
3366                                           const char            *username,
3367                                           const char            *x11_display_name,
3368                                           const char            *x11_authority_file,
3369                                           const char            *console,
3370                                           const char            *seat_id,
3371                                           const char            *hostname,
3372                                           gboolean               display_is_local,
3373                                           gboolean               display_is_initial)
3374 {
3375         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3376 
3377         if (!validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE))
3378                 return TRUE;
3379 
3380         worker->priv->service = g_strdup (service);
3381         worker->priv->x11_display_name = g_strdup (x11_display_name);
3382         worker->priv->x11_authority_file = g_strdup (x11_authority_file);
3383         worker->priv->display_device = g_strdup (console);
3384         worker->priv->display_seat_id = g_strdup (seat_id);
3385         worker->priv->hostname = g_strdup (hostname);
3386         worker->priv->display_is_local = display_is_local;
3387         worker->priv->display_is_initial = display_is_initial;
3388         worker->priv->username = g_strdup (username);
3389 
3390         g_signal_connect_swapped (worker->priv->user_settings,
3391                                   "notify::language-name",
3392                                   G_CALLBACK (on_saved_language_name_read),
3393                                   worker);
3394 
3395         g_signal_connect_swapped (worker->priv->user_settings,
3396                                   "notify::session-name",
3397                                   G_CALLBACK (on_saved_session_name_read),
3398                                   worker);
3399 
3400         /* Load settings from accounts daemon before continuing
3401          */
3402         worker->priv->pending_invocation = invocation;
3403         if (gdm_session_settings_load (worker->priv->user_settings, username)) {
3404                 queue_state_change (worker);
3405         } else {
3406                 g_signal_connect (G_OBJECT (worker->priv->user_settings),
3407                                   "notify::is-loaded",
3408                                   G_CALLBACK (on_settings_is_loaded_changed),
3409                                   worker);
3410         }
3411 
3412         return TRUE;
3413 }
3414 
3415 static gboolean
gdm_session_worker_handle_setup_for_program(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * service,const char * username,const char * x11_display_name,const char * x11_authority_file,const char * console,const char * seat_id,const char * hostname,gboolean display_is_local,gboolean display_is_initial,const char * log_file)3416 gdm_session_worker_handle_setup_for_program (GdmDBusWorker         *object,
3417                                              GDBusMethodInvocation *invocation,
3418                                              const char            *service,
3419                                              const char            *username,
3420                                              const char            *x11_display_name,
3421                                              const char            *x11_authority_file,
3422                                              const char            *console,
3423                                              const char            *seat_id,
3424                                              const char            *hostname,
3425                                              gboolean               display_is_local,
3426                                              gboolean               display_is_initial,
3427                                              const char            *log_file)
3428 {
3429         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3430         validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
3431 
3432         worker->priv->service = g_strdup (service);
3433         worker->priv->x11_display_name = g_strdup (x11_display_name);
3434         worker->priv->x11_authority_file = g_strdup (x11_authority_file);
3435         worker->priv->display_device = g_strdup (console);
3436         worker->priv->display_seat_id = g_strdup (seat_id);
3437         worker->priv->hostname = g_strdup (hostname);
3438         worker->priv->display_is_local = display_is_local;
3439         worker->priv->display_is_initial = display_is_initial;
3440         worker->priv->username = g_strdup (username);
3441         worker->priv->log_file = g_strdup (log_file);
3442         worker->priv->is_program_session = TRUE;
3443 
3444         return TRUE;
3445 }
3446 
3447 static gboolean
gdm_session_worker_handle_start_program(GdmDBusWorker * object,GDBusMethodInvocation * invocation,const char * text)3448 gdm_session_worker_handle_start_program (GdmDBusWorker         *object,
3449                                          GDBusMethodInvocation *invocation,
3450                                          const char            *text)
3451 {
3452         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3453         GError *parse_error = NULL;
3454         validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SESSION_STARTED);
3455 
3456         if (worker->priv->is_reauth_session) {
3457                 g_dbus_method_invocation_return_error (invocation,
3458                                                        GDM_SESSION_WORKER_ERROR,
3459                                                        GDM_SESSION_WORKER_ERROR_IN_REAUTH_SESSION,
3460                                                        "Cannot start a program while in a reauth session");
3461                 return TRUE;
3462         }
3463 
3464         g_debug ("GdmSessionWorker: start program: %s", text);
3465 
3466         g_clear_pointer (&worker->priv->arguments, (GDestroyNotify) g_strfreev);
3467         if (! g_shell_parse_argv (text, NULL, &worker->priv->arguments, &parse_error)) {
3468                 g_dbus_method_invocation_take_error (invocation, parse_error);
3469                 return TRUE;
3470         }
3471 
3472         worker->priv->pending_invocation = invocation;
3473         queue_state_change (worker);
3474 
3475         return TRUE;
3476 }
3477 
3478 static void
on_reauthentication_client_connected(GdmSession * session,GCredentials * credentials,GPid pid_of_client,ReauthenticationRequest * request)3479 on_reauthentication_client_connected (GdmSession              *session,
3480                                       GCredentials            *credentials,
3481                                       GPid                     pid_of_client,
3482                                       ReauthenticationRequest *request)
3483 {
3484         g_debug ("GdmSessionWorker: client connected to reauthentication server");
3485 }
3486 
3487 static void
on_reauthentication_client_disconnected(GdmSession * session,GCredentials * credentials,GPid pid_of_client,ReauthenticationRequest * request)3488 on_reauthentication_client_disconnected (GdmSession              *session,
3489                                          GCredentials            *credentials,
3490                                          GPid                     pid_of_client,
3491                                          ReauthenticationRequest *request)
3492 {
3493         GdmSessionWorker *worker;
3494 
3495         g_debug ("GdmSessionWorker: client disconnected from reauthentication server");
3496 
3497         worker = request->worker;
3498         g_hash_table_remove (worker->priv->reauthentication_requests,
3499                              GINT_TO_POINTER (pid_of_client));
3500 }
3501 
3502 static void
on_reauthentication_cancelled(GdmSession * session,ReauthenticationRequest * request)3503 on_reauthentication_cancelled (GdmSession              *session,
3504                                ReauthenticationRequest *request)
3505 {
3506         g_debug ("GdmSessionWorker: client cancelled reauthentication request");
3507         gdm_session_reset (session);
3508 }
3509 
3510 static void
on_reauthentication_conversation_started(GdmSession * session,const char * service_name,ReauthenticationRequest * request)3511 on_reauthentication_conversation_started (GdmSession              *session,
3512                                           const char              *service_name,
3513                                           ReauthenticationRequest *request)
3514 {
3515         g_debug ("GdmSessionWorker: reauthentication service '%s' started",
3516                  service_name);
3517 }
3518 
3519 static void
on_reauthentication_conversation_stopped(GdmSession * session,const char * service_name,ReauthenticationRequest * request)3520 on_reauthentication_conversation_stopped (GdmSession              *session,
3521                                           const char              *service_name,
3522                                           ReauthenticationRequest *request)
3523 {
3524         g_debug ("GdmSessionWorker: reauthentication service '%s' stopped",
3525                  service_name);
3526 }
3527 
3528 static void
on_reauthentication_verification_complete(GdmSession * session,const char * service_name,ReauthenticationRequest * request)3529 on_reauthentication_verification_complete (GdmSession              *session,
3530                                            const char              *service_name,
3531                                            ReauthenticationRequest *request)
3532 {
3533         GdmSessionWorker *worker;
3534 
3535         worker = request->worker;
3536 
3537         g_debug ("GdmSessionWorker: pid %d reauthenticated user %d with service '%s'",
3538                  (int) request->pid_of_caller,
3539                  (int) request->uid_of_caller,
3540                  service_name);
3541         gdm_session_reset (session);
3542 
3543         gdm_dbus_worker_emit_reauthenticated (GDM_DBUS_WORKER (worker), service_name);
3544 }
3545 
3546 static ReauthenticationRequest *
reauthentication_request_new(GdmSessionWorker * worker,GPid pid_of_caller,uid_t uid_of_caller,GDBusMethodInvocation * invocation)3547 reauthentication_request_new (GdmSessionWorker      *worker,
3548                               GPid                   pid_of_caller,
3549                               uid_t                  uid_of_caller,
3550                               GDBusMethodInvocation *invocation)
3551 {
3552         ReauthenticationRequest *request;
3553         const char * const * environment;
3554         const char *address;
3555 
3556         environment = gdm_session_worker_get_environment (worker);
3557 
3558         request = g_slice_new (ReauthenticationRequest);
3559 
3560         request->worker = worker;
3561         request->pid_of_caller = pid_of_caller;
3562         request->uid_of_caller = uid_of_caller;
3563         request->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE,
3564                                             uid_of_caller,
3565                                             worker->priv->x11_display_name,
3566                                             worker->priv->hostname,
3567                                             worker->priv->display_device,
3568                                             worker->priv->display_seat_id,
3569                                             worker->priv->x11_authority_file,
3570                                             worker->priv->display_is_local,
3571                                             environment);
3572 
3573         g_signal_connect (request->session,
3574                           "client-connected",
3575                           G_CALLBACK (on_reauthentication_client_connected),
3576                           request);
3577         g_signal_connect (request->session,
3578                           "client-disconnected",
3579                           G_CALLBACK (on_reauthentication_client_disconnected),
3580                           request);
3581         g_signal_connect (request->session,
3582                           "cancelled",
3583                           G_CALLBACK (on_reauthentication_cancelled),
3584                           request);
3585         g_signal_connect (request->session,
3586                           "conversation-started",
3587                           G_CALLBACK (on_reauthentication_conversation_started),
3588                           request);
3589         g_signal_connect (request->session,
3590                           "conversation-stopped",
3591                           G_CALLBACK (on_reauthentication_conversation_stopped),
3592                           request);
3593         g_signal_connect (request->session,
3594                           "verification-complete",
3595                           G_CALLBACK (on_reauthentication_verification_complete),
3596                           request);
3597 
3598         address = gdm_session_get_server_address (request->session);
3599 
3600         gdm_dbus_worker_complete_start_reauthentication (GDM_DBUS_WORKER (worker),
3601                                                          invocation,
3602                                                          address);
3603 
3604         return request;
3605 }
3606 
3607 static gboolean
gdm_session_worker_handle_start_reauthentication(GdmDBusWorker * object,GDBusMethodInvocation * invocation,int pid_of_caller,int uid_of_caller)3608 gdm_session_worker_handle_start_reauthentication (GdmDBusWorker         *object,
3609                                                   GDBusMethodInvocation *invocation,
3610                                                   int                    pid_of_caller,
3611                                                   int                    uid_of_caller)
3612 {
3613         GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
3614         ReauthenticationRequest *request;
3615 
3616         if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_STARTED) {
3617                 g_dbus_method_invocation_return_error (invocation,
3618                                                        GDM_SESSION_WORKER_ERROR,
3619                                                        GDM_SESSION_WORKER_ERROR_WRONG_STATE,
3620                                                        "Cannot reauthenticate while in state %s",
3621                                                        get_state_name (worker->priv->state));
3622                 return TRUE;
3623         }
3624 
3625         g_debug ("GdmSessionWorker: start reauthentication");
3626 
3627         request = reauthentication_request_new (worker, pid_of_caller, uid_of_caller, invocation);
3628         g_hash_table_replace (worker->priv->reauthentication_requests,
3629                               GINT_TO_POINTER (pid_of_caller),
3630                               request);
3631         return TRUE;
3632 }
3633 
3634 static GObject *
gdm_session_worker_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)3635 gdm_session_worker_constructor (GType                  type,
3636                                 guint                  n_construct_properties,
3637                                 GObjectConstructParam *construct_properties)
3638 {
3639         GdmSessionWorker  *worker;
3640         GError            *error;
3641 
3642         worker = GDM_SESSION_WORKER (G_OBJECT_CLASS (gdm_session_worker_parent_class)->constructor (type,
3643                                                                                                     n_construct_properties,
3644                                                                                                     construct_properties));
3645 
3646         g_debug ("GdmSessionWorker: connecting to address: %s", worker->priv->server_address);
3647 
3648         error = NULL;
3649         worker->priv->connection = g_dbus_connection_new_for_address_sync (worker->priv->server_address,
3650                                                                            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
3651                                                                            NULL,
3652                                                                            NULL,
3653                                                                            &error);
3654         if (worker->priv->connection == NULL) {
3655                 g_warning ("error opening connection: %s", error->message);
3656                 g_clear_error (&error);
3657 
3658                 exit (EXIT_FAILURE);
3659         }
3660 
3661         worker->priv->manager = GDM_DBUS_WORKER_MANAGER (gdm_dbus_worker_manager_proxy_new_sync (worker->priv->connection,
3662                                                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
3663                                                                                                  NULL, /* dbus name */
3664                                                                                                  GDM_SESSION_DBUS_PATH,
3665                                                                                                  NULL,
3666                                                                                                  &error));
3667         if (worker->priv->manager == NULL) {
3668                 g_warning ("error creating session proxy: %s", error->message);
3669                 g_clear_error (&error);
3670 
3671                 exit (EXIT_FAILURE);
3672         }
3673 
3674         if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker),
3675                                                worker->priv->connection,
3676                                                GDM_WORKER_DBUS_PATH,
3677                                                &error)) {
3678                 g_warning ("Error while exporting object: %s", error->message);
3679                 exit (EXIT_FAILURE);
3680         }
3681 
3682         g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (worker->priv->manager), G_MAXINT);
3683 
3684         /* Send an initial Hello message so that the session can associate
3685          * the conversation we manage with our pid.
3686          */
3687         gdm_dbus_worker_manager_call_hello_sync (worker->priv->manager,
3688                                                  NULL,
3689                                                  NULL);
3690 
3691         return G_OBJECT (worker);
3692 }
3693 
3694 static void
worker_interface_init(GdmDBusWorkerIface * interface)3695 worker_interface_init (GdmDBusWorkerIface *interface)
3696 {
3697         interface->handle_initialize = gdm_session_worker_handle_initialize;
3698         /* The next three are for backward compat only */
3699         interface->handle_setup = gdm_session_worker_handle_setup;
3700         interface->handle_setup_for_user = gdm_session_worker_handle_setup_for_user;
3701         interface->handle_setup_for_program = gdm_session_worker_handle_setup_for_program;
3702         interface->handle_authenticate = gdm_session_worker_handle_authenticate;
3703         interface->handle_authorize = gdm_session_worker_handle_authorize;
3704         interface->handle_establish_credentials = gdm_session_worker_handle_establish_credentials;
3705         interface->handle_open = gdm_session_worker_handle_open;
3706         interface->handle_set_language_name = gdm_session_worker_handle_set_language_name;
3707         interface->handle_set_session_name = gdm_session_worker_handle_set_session_name;
3708         interface->handle_set_session_type = gdm_session_worker_handle_set_session_type;
3709         interface->handle_set_session_display_mode = gdm_session_worker_handle_set_session_display_mode;
3710         interface->handle_set_environment_variable = gdm_session_worker_handle_set_environment_variable;
3711         interface->handle_start_program = gdm_session_worker_handle_start_program;
3712         interface->handle_start_reauthentication = gdm_session_worker_handle_start_reauthentication;
3713 }
3714 
3715 static void
gdm_session_worker_class_init(GdmSessionWorkerClass * klass)3716 gdm_session_worker_class_init (GdmSessionWorkerClass *klass)
3717 {
3718         GObjectClass    *object_class = G_OBJECT_CLASS (klass);
3719 
3720         object_class->get_property = gdm_session_worker_get_property;
3721         object_class->set_property = gdm_session_worker_set_property;
3722         object_class->constructor = gdm_session_worker_constructor;
3723         object_class->finalize = gdm_session_worker_finalize;
3724 
3725         g_type_class_add_private (klass, sizeof (GdmSessionWorkerPrivate));
3726 
3727         g_object_class_install_property (object_class,
3728                                          PROP_SERVER_ADDRESS,
3729                                          g_param_spec_string ("server-address",
3730                                                               "server address",
3731                                                               "server address",
3732                                                               NULL,
3733                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
3734 
3735         g_object_class_install_property (object_class,
3736                                          PROP_IS_REAUTH_SESSION,
3737                                          g_param_spec_boolean ("is-reauth-session",
3738                                                                "is reauth session",
3739                                                                "is reauth session",
3740                                                               FALSE,
3741                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
3742 }
3743 
3744 static void
reauthentication_request_free(ReauthenticationRequest * request)3745 reauthentication_request_free (ReauthenticationRequest *request)
3746 {
3747 
3748         g_signal_handlers_disconnect_by_func (request->session,
3749                                               G_CALLBACK (on_reauthentication_client_connected),
3750                                               request);
3751         g_signal_handlers_disconnect_by_func (request->session,
3752                                               G_CALLBACK (on_reauthentication_client_disconnected),
3753                                               request);
3754         g_signal_handlers_disconnect_by_func (request->session,
3755                                               G_CALLBACK (on_reauthentication_cancelled),
3756                                               request);
3757         g_signal_handlers_disconnect_by_func (request->session,
3758                                               G_CALLBACK (on_reauthentication_conversation_started),
3759                                               request);
3760         g_signal_handlers_disconnect_by_func (request->session,
3761                                               G_CALLBACK (on_reauthentication_conversation_stopped),
3762                                               request);
3763         g_signal_handlers_disconnect_by_func (request->session,
3764                                               G_CALLBACK (on_reauthentication_verification_complete),
3765                                               request);
3766         g_clear_object (&request->session);
3767         g_slice_free (ReauthenticationRequest, request);
3768 }
3769 
3770 static void
gdm_session_worker_init(GdmSessionWorker * worker)3771 gdm_session_worker_init (GdmSessionWorker *worker)
3772 {
3773         worker->priv = GDM_SESSION_WORKER_GET_PRIVATE (worker);
3774 
3775         worker->priv->user_settings = gdm_session_settings_new ();
3776         worker->priv->reauthentication_requests = g_hash_table_new_full (NULL,
3777                                                                          NULL,
3778                                                                          NULL,
3779                                                                          (GDestroyNotify)
3780                                                                          reauthentication_request_free);
3781 }
3782 
3783 static void
gdm_session_worker_unwatch_child(GdmSessionWorker * worker)3784 gdm_session_worker_unwatch_child (GdmSessionWorker *worker)
3785 {
3786         if (worker->priv->child_watch_id == 0)
3787                 return;
3788 
3789         g_source_remove (worker->priv->child_watch_id);
3790         worker->priv->child_watch_id = 0;
3791 }
3792 
3793 
3794 static void
gdm_session_worker_finalize(GObject * object)3795 gdm_session_worker_finalize (GObject *object)
3796 {
3797         GdmSessionWorker *worker;
3798 
3799         g_return_if_fail (object != NULL);
3800         g_return_if_fail (GDM_IS_SESSION_WORKER (object));
3801 
3802         worker = GDM_SESSION_WORKER (object);
3803 
3804         g_return_if_fail (worker->priv != NULL);
3805 
3806         gdm_session_worker_unwatch_child (worker);
3807 
3808         if (worker->priv->child_pid > 0) {
3809                 gdm_signal_pid (worker->priv->child_pid, SIGTERM);
3810                 gdm_wait_on_pid (worker->priv->child_pid);
3811         }
3812 
3813         g_object_unref (worker->priv->user_settings);
3814         g_free (worker->priv->service);
3815         g_free (worker->priv->x11_display_name);
3816         g_free (worker->priv->x11_authority_file);
3817         g_free (worker->priv->display_device);
3818         g_free (worker->priv->display_seat_id);
3819         g_free (worker->priv->hostname);
3820         g_free (worker->priv->username);
3821         g_free (worker->priv->server_address);
3822         g_strfreev (worker->priv->arguments);
3823         g_strfreev (worker->priv->extensions);
3824 
3825         g_hash_table_unref (worker->priv->reauthentication_requests);
3826 
3827         G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object);
3828 }
3829 
3830 GdmSessionWorker *
gdm_session_worker_new(const char * address,gboolean is_reauth_session)3831 gdm_session_worker_new (const char *address,
3832                         gboolean    is_reauth_session)
3833 {
3834         GObject *object;
3835 
3836         object = g_object_new (GDM_TYPE_SESSION_WORKER,
3837                                "server-address", address,
3838                                "is-reauth-session", is_reauth_session,
3839                                NULL);
3840 
3841         return GDM_SESSION_WORKER (object);
3842 }
3843