source: proiecte/PPPP/gdm/daemon/gdm-session-worker.c @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 102.0 KB
Line 
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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#include <errno.h>
32#include <grp.h>
33#include <pwd.h>
34
35#ifdef  HAVE_LOGINDEVPERM
36#include <libdevinfo.h>
37#endif  /* HAVE_LOGINDEVPERM */
38
39#include <security/pam_appl.h>
40
41#include <glib.h>
42#include <glib/gi18n.h>
43#include <glib/gstdio.h>
44#include <glib-object.h>
45#include <gio/gio.h>
46#define DBUS_API_SUBJECT_TO_CHANGE
47#include <dbus/dbus.h>
48#include <dbus/dbus-glib.h>
49#include <dbus/dbus-glib-lowlevel.h>
50
51#include <X11/Xauth.h>
52
53#include "ck-connector.h"
54
55#include "gdm-session-worker.h"
56#include "gdm-marshal.h"
57
58#if defined (HAVE_ADT)
59#include "gdm-session-solaris-auditor.h"
60#elif defined (HAVE_LIBAUDIT)
61#include "gdm-session-linux-auditor.h"
62#else
63#include "gdm-session-auditor.h"
64#endif
65
66#include "gdm-session-settings.h"
67
68#define GDM_SESSION_WORKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerPrivate))
69
70#define GDM_SESSION_DBUS_PATH         "/org/gnome/DisplayManager/Session"
71#define GDM_SESSION_DBUS_INTERFACE    "org.gnome.DisplayManager.Session"
72#define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
73
74#ifndef GDM_PASSWD_AUXILLARY_BUFFER_SIZE
75#define GDM_PASSWD_AUXILLARY_BUFFER_SIZE 1024
76#endif
77
78#ifndef GDM_SESSION_DEFAULT_PATH
79#define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin"
80#endif
81
82#ifndef GDM_SESSION_ROOT_UID
83#define GDM_SESSION_ROOT_UID 0
84#endif
85
86#ifndef GDM_SESSION_LOG_FILENAME
87#define GDM_SESSION_LOG_FILENAME ".xsession-errors"
88#endif
89
90#define MESSAGE_REPLY_TIMEOUT (10 * 60 * 1000)
91
92#define MAX_FILE_SIZE     65536
93
94enum {
95        GDM_SESSION_WORKER_STATE_NONE = 0,
96        GDM_SESSION_WORKER_STATE_SETUP_COMPLETE,
97        GDM_SESSION_WORKER_STATE_AUTHENTICATED,
98        GDM_SESSION_WORKER_STATE_AUTHORIZED,
99        GDM_SESSION_WORKER_STATE_ACCREDITED,
100        GDM_SESSION_WORKER_STATE_SESSION_OPENED,
101        GDM_SESSION_WORKER_STATE_SESSION_STARTED,
102        GDM_SESSION_WORKER_STATE_REAUTHENTICATED,
103        GDM_SESSION_WORKER_STATE_REAUTHORIZED,
104        GDM_SESSION_WORKER_STATE_REACCREDITED,
105};
106
107struct GdmSessionWorkerPrivate
108{
109        int               state;
110
111        int               exit_code;
112
113        CkConnector      *ckc;
114        pam_handle_t     *pam_handle;
115
116        GPid              child_pid;
117        guint             child_watch_id;
118
119        /* from Setup */
120        char             *service;
121        char             *x11_display_name;
122        char             *x11_authority_file;
123        char             *display_device;
124        char             *hostname;
125        char             *username;
126        uid_t             uid;
127        gid_t             gid;
128        gboolean          password_is_required;
129
130        int               cred_flags;
131
132        char            **arguments;
133        GHashTable       *environment;
134        guint32           cancelled : 1;
135        guint32           timed_out : 1;
136        guint             state_change_idle_id;
137
138        char             *server_address;
139        DBusConnection   *connection;
140
141        GdmSessionAuditor  *auditor;
142        GdmSessionSettings *user_settings;
143};
144
145enum {
146        PROP_0,
147        PROP_SERVER_ADDRESS,
148};
149
150static void     gdm_session_worker_class_init   (GdmSessionWorkerClass *klass);
151static void     gdm_session_worker_init         (GdmSessionWorker      *session_worker);
152static void     gdm_session_worker_finalize     (GObject               *object);
153
154static void     queue_state_change              (GdmSessionWorker      *worker);
155
156typedef int (* GdmSessionWorkerPamNewMessagesFunc) (int,
157                                                    const struct pam_message **,
158                                                    struct pam_response **,
159                                                    gpointer);
160
161G_DEFINE_TYPE (GdmSessionWorker, gdm_session_worker, G_TYPE_OBJECT)
162
163GQuark
164gdm_session_worker_error_quark (void)
165{
166        static GQuark error_quark = 0;
167
168        if (error_quark == 0)
169                error_quark = g_quark_from_static_string ("gdm-session-worker");
170
171        return error_quark;
172}
173
174static gboolean
175open_ck_session (GdmSessionWorker  *worker)
176{
177        struct passwd *pwent;
178        gboolean       ret;
179        int            res;
180        DBusError      error;
181        const char     *display_name;
182        const char     *display_device;
183        const char     *display_hostname;
184        gboolean        is_local;
185
186        ret = FALSE;
187
188        if (worker->priv->x11_display_name != NULL) {
189                display_name = worker->priv->x11_display_name;
190        } else {
191                display_name = "";
192        }
193        if (worker->priv->hostname != NULL) {
194                display_hostname = worker->priv->hostname;
195        } else {
196                display_hostname = "";
197        }
198        if (worker->priv->display_device != NULL) {
199                display_device = worker->priv->display_device;
200        } else {
201                display_device = "";
202        }
203
204        g_assert (worker->priv->username != NULL);
205
206        /* FIXME: this isn't very good */
207        if (display_hostname == NULL
208            || display_hostname[0] == '\0'
209            || strcmp (display_hostname, "localhost") == 0) {
210                is_local = TRUE;
211        } else {
212                is_local = FALSE;
213        }
214
215        pwent = getpwnam (worker->priv->username);
216        if (pwent == NULL) {
217                goto out;
218        }
219
220        worker->priv->ckc = ck_connector_new ();
221        if (worker->priv->ckc == NULL) {
222                g_warning ("Couldn't create new ConsoleKit connector");
223                goto out;
224        }
225
226        dbus_error_init (&error);
227        res = ck_connector_open_session_with_parameters (worker->priv->ckc,
228                                                         &error,
229                                                         "unix-user", &pwent->pw_uid,
230                                                         "x11-display", &display_name,
231                                                         "x11-display-device", &display_device,
232                                                         "remote-host-name", &display_hostname,
233                                                         "is-local", &is_local,
234                                                         NULL);
235
236        if (! res) {
237                if (dbus_error_is_set (&error)) {
238                        g_warning ("%s\n", error.message);
239                        dbus_error_free (&error);
240                } else {
241                        g_warning ("cannot open CK session: OOM, D-Bus system bus not available,\n"
242                                   "ConsoleKit not available or insufficient privileges.\n");
243                }
244                goto out;
245        }
246
247        ret = TRUE;
248
249 out:
250        return ret;
251}
252
253/* adapted from glib script_execute */
254static void
255script_execute (const gchar *file,
256                char       **argv,
257                char       **envp,
258                gboolean     search_path)
259{
260        /* Count the arguments.  */
261        int argc = 0;
262
263        while (argv[argc]) {
264                ++argc;
265        }
266
267        /* Construct an argument list for the shell.  */
268        {
269                char **new_argv;
270
271                new_argv = g_new0 (gchar*, argc + 2); /* /bin/sh and NULL */
272
273                new_argv[0] = (char *) "/bin/sh";
274                new_argv[1] = (char *) file;
275                while (argc > 0) {
276                        new_argv[argc + 1] = argv[argc];
277                        --argc;
278                }
279
280                /* Execute the shell. */
281                if (envp) {
282                        execve (new_argv[0], new_argv, envp);
283                } else {
284                        execv (new_argv[0], new_argv);
285                }
286
287                g_free (new_argv);
288        }
289}
290
291static char *
292my_strchrnul (const char *str, char c)
293{
294        char *p = (char*) str;
295        while (*p && (*p != c)) {
296                ++p;
297        }
298
299        return p;
300}
301
302/* adapted from glib g_execute */
303static gint
304gdm_session_execute (const char *file,
305                     char      **argv,
306                     char      **envp,
307                     gboolean    search_path)
308{
309        if (*file == '\0') {
310                /* We check the simple case first. */
311                errno = ENOENT;
312                return -1;
313        }
314
315        if (!search_path || strchr (file, '/') != NULL) {
316                /* Don't search when it contains a slash. */
317                if (envp) {
318                        execve (file, argv, envp);
319                } else {
320                        execv (file, argv);
321                }
322
323                if (errno == ENOEXEC) {
324                        script_execute (file, argv, envp, FALSE);
325                }
326        } else {
327                gboolean got_eacces = 0;
328                const char *path, *p;
329                char *name, *freeme;
330                gsize len;
331                gsize pathlen;
332
333                path = g_getenv ("PATH");
334                if (path == NULL) {
335                        /* There is no `PATH' in the environment.  The default
336                         * search path in libc is the current directory followed by
337                         * the path `confstr' returns for `_CS_PATH'.
338                         */
339
340                        /* In GLib we put . last, for security, and don't use the
341                         * unportable confstr(); UNIX98 does not actually specify
342                         * what to search if PATH is unset. POSIX may, dunno.
343                         */
344
345                        path = "/bin:/usr/bin:.";
346                }
347
348                len = strlen (file) + 1;
349                pathlen = strlen (path);
350                freeme = name = g_malloc (pathlen + len + 1);
351
352                /* Copy the file name at the top, including '\0'  */
353                memcpy (name + pathlen + 1, file, len);
354                name = name + pathlen;
355                /* And add the slash before the filename  */
356                *name = '/';
357
358                p = path;
359                do {
360                        char *startp;
361
362                        path = p;
363                        p = my_strchrnul (path, ':');
364
365                        if (p == path) {
366                                /* Two adjacent colons, or a colon at the beginning or the end
367                                 * of `PATH' means to search the current directory.
368                                 */
369                                startp = name + 1;
370                        } else {
371                                startp = memcpy (name - (p - path), path, p - path);
372                        }
373
374                        /* Try to execute this name.  If it works, execv will not return.  */
375                        if (envp) {
376                                execve (startp, argv, envp);
377                        } else {
378                                execv (startp, argv);
379                        }
380
381                        if (errno == ENOEXEC) {
382                                script_execute (startp, argv, envp, search_path);
383                        }
384
385                        switch (errno) {
386                        case EACCES:
387                                /* Record the we got a `Permission denied' error.  If we end
388                                 * up finding no executable we can use, we want to diagnose
389                                 * that we did find one but were denied access.
390                                 */
391                                got_eacces = TRUE;
392
393                                /* FALL THRU */
394
395                        case ENOENT:
396#ifdef ESTALE
397                        case ESTALE:
398#endif
399#ifdef ENOTDIR
400                        case ENOTDIR:
401#endif
402                                /* Those errors indicate the file is missing or not executable
403                                 * by us, in which case we want to just try the next path
404                                 * directory.
405                                 */
406                                break;
407
408                        default:
409                                /* Some other error means we found an executable file, but
410                                 * something went wrong executing it; return the error to our
411                                 * caller.
412                                 */
413                                g_free (freeme);
414                                return -1;
415                        }
416                } while (*p++ != '\0');
417
418                /* We tried every element and none of them worked.  */
419                if (got_eacces) {
420                        /* At least one failure was due to permissions, so report that
421                         * error.
422                         */
423                        errno = EACCES;
424                }
425
426                g_free (freeme);
427        }
428
429        /* Return the error from the last attempt (probably ENOENT).  */
430        return -1;
431}
432
433static gboolean
434send_dbus_string_method (DBusConnection *connection,
435                         const char     *method,
436                         const char     *payload)
437{
438        DBusError       error;
439        DBusMessage    *message;
440        DBusMessage    *reply;
441        DBusMessageIter iter;
442        const char     *str;
443
444        if (payload != NULL) {
445                str = payload;
446        } else {
447                str = "";
448        }
449
450        g_debug ("GdmSessionWorker: Calling %s", method);
451        message = dbus_message_new_method_call (NULL,
452                                                GDM_SESSION_DBUS_PATH,
453                                                GDM_SESSION_DBUS_INTERFACE,
454                                                method);
455        if (message == NULL) {
456                g_warning ("Couldn't allocate the D-Bus message");
457                return FALSE;
458        }
459
460        dbus_message_iter_init_append (message, &iter);
461        dbus_message_iter_append_basic (&iter,
462                                        DBUS_TYPE_STRING,
463                                        &str);
464
465        dbus_error_init (&error);
466        reply = dbus_connection_send_with_reply_and_block (connection,
467                                                           message,
468                                                           -1,
469                                                           &error);
470
471        dbus_message_unref (message);
472
473        if (dbus_error_is_set (&error)) {
474                g_debug ("%s %s raised: %s\n",
475                         method,
476                         error.name,
477                         error.message);
478                return FALSE;
479        }
480        if (reply != NULL) {
481                dbus_message_unref (reply);
482        }
483        dbus_connection_flush (connection);
484
485        return TRUE;
486}
487
488static gboolean
489send_dbus_int_method (DBusConnection *connection,
490                      const char     *method,
491                      int             payload)
492{
493        DBusError       error;
494        DBusMessage    *message;
495        DBusMessage    *reply;
496        DBusMessageIter iter;
497
498        g_debug ("GdmSessionWorker: Calling %s", method);
499        message = dbus_message_new_method_call (NULL,
500                                                GDM_SESSION_DBUS_PATH,
501                                                GDM_SESSION_DBUS_INTERFACE,
502                                                method);
503        if (message == NULL) {
504                g_warning ("Couldn't allocate the D-Bus message");
505                return FALSE;
506        }
507
508        dbus_message_iter_init_append (message, &iter);
509        dbus_message_iter_append_basic (&iter,
510                                        DBUS_TYPE_INT32,
511                                        &payload);
512
513        dbus_error_init (&error);
514        reply = dbus_connection_send_with_reply_and_block (connection,
515                                                           message,
516                                                           -1,
517                                                           &error);
518        dbus_message_unref (message);
519        if (reply != NULL) {
520                dbus_message_unref (reply);
521        }
522        dbus_connection_flush (connection);
523
524        if (dbus_error_is_set (&error)) {
525                g_debug ("%s %s raised: %s\n",
526                         method,
527                         error.name,
528                         error.message);
529                return FALSE;
530        }
531
532        return TRUE;
533}
534
535static gboolean
536send_dbus_void_method (DBusConnection *connection,
537                       const char     *method)
538{
539        DBusError       error;
540        DBusMessage    *message;
541        DBusMessage    *reply;
542
543        g_debug ("GdmSessionWorker: Calling %s", method);
544        message = dbus_message_new_method_call (NULL,
545                                                GDM_SESSION_DBUS_PATH,
546                                                GDM_SESSION_DBUS_INTERFACE,
547                                                method);
548        if (message == NULL) {
549                g_warning ("Couldn't allocate the D-Bus message");
550                return FALSE;
551        }
552
553        dbus_error_init (&error);
554        reply = dbus_connection_send_with_reply_and_block (connection,
555                                                           message,
556                                                           -1,
557                                                           &error);
558        dbus_message_unref (message);
559        if (reply != NULL) {
560                dbus_message_unref (reply);
561        }
562        dbus_connection_flush (connection);
563
564        if (dbus_error_is_set (&error)) {
565                g_debug ("%s %s raised: %s\n",
566                         method,
567                         error.name,
568                         error.message);
569                return FALSE;
570        }
571
572        return TRUE;
573}
574
575static gboolean
576gdm_session_worker_get_username (GdmSessionWorker  *worker,
577                                 char             **username)
578{
579        gconstpointer item;
580
581        g_assert (worker->priv->pam_handle != NULL);
582
583        if (pam_get_item (worker->priv->pam_handle, PAM_USER, &item) == PAM_SUCCESS) {
584                if (username != NULL) {
585                        *username = g_strdup ((char *) item);
586                        g_debug ("GdmSessionWorker: username is '%s'",
587                                 *username != NULL ? *username : "<unset>");
588                }
589                return TRUE;
590        }
591
592        return FALSE;
593}
594
595static void
596attempt_to_load_user_settings (GdmSessionWorker *worker,
597                               const char       *username)
598{
599        gdm_session_settings_load (worker->priv->user_settings,
600                                   username,
601                                   NULL);
602}
603
604static void
605gdm_session_worker_update_username (GdmSessionWorker *worker)
606{
607        char    *username;
608        gboolean res;
609
610        username = NULL;
611        res = gdm_session_worker_get_username (worker, &username);
612        if (res) {
613                g_debug ("GdmSessionWorker: old-username='%s' new-username='%s'",
614                         worker->priv->username != NULL ? worker->priv->username : "<unset>",
615                         username != NULL ? username : "<unset>");
616
617
618                gdm_session_auditor_set_username (worker->priv->auditor, worker->priv->username);
619
620                if ((worker->priv->username == username) ||
621                    ((worker->priv->username != NULL) && (username != NULL) &&
622                     (strcmp (worker->priv->username, username) == 0)))
623                        goto out;
624
625                g_debug ("GdmSessionWorker: setting username to '%s'", username);
626
627                g_free (worker->priv->username);
628                worker->priv->username = username;
629                username = NULL;
630
631                send_dbus_string_method (worker->priv->connection,
632                                         "UsernameChanged",
633                                         worker->priv->username);
634
635                /* We have a new username to try. If we haven't been able to
636                 * read user settings up until now, then give it a go now
637                 * (see the comment in do_setup for rationale on why it's useful
638                 * to keep trying to read settings)
639                 */
640                if (worker->priv->username != NULL &&
641                    !gdm_session_settings_is_loaded (worker->priv->user_settings)) {
642                        attempt_to_load_user_settings (worker, worker->priv->username);
643                }
644        }
645
646 out:
647        g_free (username);
648}
649
650static gboolean
651send_question_method (GdmSessionWorker *worker,
652                      const char       *method,
653                      const char       *question,
654                      char            **answerp)
655{
656        DBusError       error;
657        DBusMessage    *message;
658        DBusMessage    *reply;
659        DBusMessageIter iter;
660        gboolean        ret;
661        const char     *answer;
662
663        ret = FALSE;
664
665        g_debug ("GdmSessionWorker: Calling %s", method);
666        message = dbus_message_new_method_call (NULL,
667                                                GDM_SESSION_DBUS_PATH,
668                                                GDM_SESSION_DBUS_INTERFACE,
669                                                method);
670        if (message == NULL) {
671                g_warning ("Couldn't allocate the D-Bus message");
672                return FALSE;
673        }
674
675        dbus_message_iter_init_append (message, &iter);
676        dbus_message_iter_append_basic (&iter,
677                                        DBUS_TYPE_STRING,
678                                        &question);
679
680        dbus_error_init (&error);
681        reply = dbus_connection_send_with_reply_and_block (worker->priv->connection,
682                                                           message,
683                                                           MESSAGE_REPLY_TIMEOUT,
684                                                           &error);
685        dbus_message_unref (message);
686
687        if (dbus_error_is_set (&error)) {
688                if (dbus_error_has_name (&error, GDM_SESSION_DBUS_ERROR_CANCEL)) {
689                        worker->priv->cancelled = TRUE;
690                } else if (dbus_error_has_name (&error, DBUS_ERROR_NO_REPLY)) {
691                        worker->priv->timed_out = TRUE;
692                }
693                g_debug ("%s %s raised: %s\n",
694                         method,
695                         error.name,
696                         error.message);
697                goto out;
698        }
699
700        dbus_message_iter_init (reply, &iter);
701        dbus_message_iter_get_basic (&iter, &answer);
702        if (answerp != NULL) {
703                *answerp = g_strdup (answer);
704        }
705        ret = TRUE;
706
707        dbus_message_unref (reply);
708        dbus_connection_flush (worker->priv->connection);
709
710 out:
711
712        return ret;
713}
714
715static gboolean
716gdm_session_worker_ask_question (GdmSessionWorker *worker,
717                                 const char       *question,
718                                 char            **answerp)
719{
720        return send_question_method (worker, "InfoQuery", question, answerp);
721}
722
723static gboolean
724gdm_session_worker_ask_for_secret (GdmSessionWorker *worker,
725                                   const char       *question,
726                                   char            **answerp)
727{
728        return send_question_method (worker, "SecretInfoQuery", question, answerp);
729}
730
731static gboolean
732gdm_session_worker_report_info (GdmSessionWorker *worker,
733                                const char       *info)
734{
735        return send_dbus_string_method (worker->priv->connection,
736                                        "Info",
737                                        info);
738}
739
740static gboolean
741gdm_session_worker_report_problem (GdmSessionWorker *worker,
742                                   const char       *problem)
743{
744        return send_dbus_string_method (worker->priv->connection,
745                                        "Problem",
746                                        problem);
747}
748
749static char *
750convert_to_utf8 (const char *str)
751{
752        char *utf8;
753        utf8 = g_locale_to_utf8 (str,
754                                 -1,
755                                 NULL,
756                                 NULL,
757                                 NULL);
758
759        /* if we couldn't convert text from locale then
760         * assume utf-8 and hope for the best */
761        if (utf8 == NULL) {
762                char *p;
763                char *q;
764
765                utf8 = g_strdup (str);
766
767                p = utf8;
768                while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q)) {
769                        *q = '?';
770                        p = q + 1;
771                }
772        }
773
774        return utf8;
775}
776
777static gboolean
778gdm_session_worker_process_pam_message (GdmSessionWorker          *worker,
779                                        const struct pam_message  *query,
780                                        char                     **response_text)
781{
782        char    *user_answer;
783        gboolean res;
784        char    *utf8_msg;
785
786        if (response_text != NULL) {
787                *response_text = NULL;
788        }
789
790        gdm_session_worker_update_username (worker);
791
792        g_debug ("GdmSessionWorker: received pam message of type %u with payload '%s'",
793                 query->msg_style, query->msg);
794
795        utf8_msg = convert_to_utf8 (query->msg);
796
797        worker->priv->cancelled = FALSE;
798        worker->priv->timed_out = FALSE;
799
800        user_answer = NULL;
801        res = FALSE;
802        switch (query->msg_style) {
803        case PAM_PROMPT_ECHO_ON:
804                res = gdm_session_worker_ask_question (worker, utf8_msg, &user_answer);
805                break;
806        case PAM_PROMPT_ECHO_OFF:
807                res = gdm_session_worker_ask_for_secret (worker, utf8_msg, &user_answer);
808                break;
809        case PAM_TEXT_INFO:
810                res = gdm_session_worker_report_info (worker, utf8_msg);
811                break;
812        case PAM_ERROR_MSG:
813                res = gdm_session_worker_report_problem (worker, utf8_msg);
814                break;
815        default:
816                g_assert_not_reached ();
817                break;
818        }
819
820        if (worker->priv->timed_out) {
821                send_dbus_void_method (worker->priv->connection, "CancelPendingQuery");
822                worker->priv->timed_out = FALSE;
823        }
824
825        if (user_answer != NULL) {
826                /* we strdup and g_free to make sure we return malloc'd
827                 * instead of g_malloc'd memory
828                 */
829                if (res && response_text != NULL) {
830                        *response_text = strdup (user_answer);
831                }
832
833                memset (user_answer, '\0', strlen (user_answer));
834                g_free (user_answer);
835
836                g_debug ("GdmSessionWorker: trying to get updated username");
837
838                res = TRUE;
839        }
840
841        g_free (utf8_msg);
842
843        return res;
844}
845
846static int
847gdm_session_worker_pam_new_messages_handler (int                        number_of_messages,
848                                             const struct pam_message **messages,
849                                             struct pam_response      **responses,
850                                             GdmSessionWorker          *worker)
851{
852        struct pam_response *replies;
853        int                  return_value;
854        int                  i;
855
856        g_debug ("GdmSessionWorker: %d new messages received from PAM\n", number_of_messages);
857
858        return_value = PAM_CONV_ERR;
859
860        if (number_of_messages < 0) {
861                return PAM_CONV_ERR;
862        }
863
864        if (number_of_messages == 0) {
865                if (responses) {
866                        *responses = NULL;
867                }
868
869                return PAM_SUCCESS;
870        }
871
872        /* we want to generate one reply for every question
873         */
874        replies = (struct pam_response *) calloc (number_of_messages,
875                                                  sizeof (struct pam_response));
876        for (i = 0; i < number_of_messages; i++) {
877                gboolean got_response;
878                char    *response_text;
879
880                response_text = NULL;
881                got_response = gdm_session_worker_process_pam_message (worker,
882                                                                       messages[i],
883                                                                       &response_text);
884                if (!got_response) {
885                        if (response_text != NULL) {
886                                memset (response_text, '\0', strlen (response_text));
887                                g_free (response_text);
888                        }
889                        goto out;
890                }
891
892                replies[i].resp = response_text;
893                replies[i].resp_retcode = PAM_SUCCESS;
894        }
895
896        return_value = PAM_SUCCESS;
897
898 out:
899        if (return_value != PAM_SUCCESS) {
900                for (i = 0; i < number_of_messages; i++) {
901                        if (replies[i].resp != NULL) {
902                                memset (replies[i].resp, 0, strlen (replies[i].resp));
903                                free (replies[i].resp);
904                        }
905                        memset (&replies[i], 0, sizeof (replies[i]));
906                }
907                free (replies);
908                replies = NULL;
909        }
910
911        if (responses) {
912                *responses = replies;
913        }
914
915        g_debug ("GdmSessionWorker: PAM conversation returning %d: %s",
916                 return_value,
917                 pam_strerror (worker->priv->pam_handle, return_value));
918
919        return return_value;
920}
921
922static void
923gdm_session_worker_start_auditor (GdmSessionWorker *worker)
924{
925
926/* FIXME: it may make sense at some point to keep a list of
927 * auditors, instead of assuming they are mutually exclusive
928 */
929#if defined (HAVE_ADT)
930        worker->priv->auditor = gdm_session_solaris_auditor_new (worker->priv->hostname,
931                                                                 worker->priv->display_device);
932#elif defined (HAVE_LIBAUDIT)
933        worker->priv->auditor = gdm_session_linux_auditor_new (worker->priv->hostname,
934                                                               worker->priv->display_device);
935#else
936        worker->priv->auditor = gdm_session_auditor_new (worker->priv->hostname,
937                                                         worker->priv->display_device);
938#endif
939}
940
941static void
942gdm_session_worker_stop_auditor (GdmSessionWorker *worker)
943{
944        g_object_unref (worker->priv->auditor);
945        worker->priv->auditor = NULL;
946}
947
948static gboolean
949check_user_copy_file (const char *srcfile,
950                      const char *destfile,
951                      uid_t       user,
952                      gssize      max_file_size)
953{
954        struct stat srcfileinfo;
955        struct stat destfileinfo;
956
957        if (max_file_size < 0) {
958                max_file_size = G_MAXSIZE;
959        }
960
961        /* Exists/Readable? */
962        if (g_stat (srcfile, &srcfileinfo) < 0) {
963                g_debug ("File does not exist");
964                return FALSE;
965        }
966
967        /* Is newer than the file already in the cache? */
968        if (destfile != NULL && g_stat (destfile, &destfileinfo) == 0) {
969                if (srcfileinfo.st_mtime <= destfileinfo.st_mtime) {
970                        g_debug ("Destination file is newer");
971                        return FALSE;
972                }
973        }
974
975        /* Is a regular file */
976        if (G_UNLIKELY (!S_ISREG (srcfileinfo.st_mode))) {
977                g_debug ("File is not a regular file");
978                return FALSE;
979        }
980
981        /* Owned by user? */
982        if (G_UNLIKELY (srcfileinfo.st_uid != user)) {
983                g_debug ("File is not owned by user");
984                return FALSE;
985        }
986
987        /* Size is kosher? */
988        if (G_UNLIKELY (srcfileinfo.st_size > max_file_size)) {
989                g_debug ("File is too large");
990                return FALSE;
991        }
992
993        return TRUE;
994}
995
996static gboolean
997gdm_cache_copy_file (GdmSessionWorker *worker,
998                     const char *userfilename,
999                     const char *cachefilename)
1000{
1001        gboolean res;
1002
1003        g_debug ("Checking if %s should be copied to cache %s",
1004                 userfilename, cachefilename);
1005
1006        res = check_user_copy_file (userfilename,
1007                                    cachefilename,
1008                                    worker->priv->uid,
1009                                    MAX_FILE_SIZE);
1010
1011        if (res) {
1012                 GFile  *src_file;
1013                 GFile  *dst_file;
1014                 GError *error;
1015
1016                 src_file = g_file_new_for_path (userfilename);
1017                 dst_file = g_file_new_for_path (cachefilename);
1018
1019                 error = NULL;
1020                 res = g_file_copy (src_file,
1021                                    dst_file, 
1022                                    G_FILE_COPY_OVERWRITE |
1023                                    G_FILE_COPY_NOFOLLOW_SYMLINKS,
1024                                    NULL,
1025                                    NULL,
1026                                    NULL,
1027                                    &error);
1028
1029                 if (! res) {
1030                        g_warning ("Could not copy file to cache: %s",
1031                                   error->message);
1032                        g_error_free (error);
1033                 } else {
1034                        chown (cachefilename,
1035                               worker->priv->uid,
1036                               worker->priv->gid);
1037                        g_chmod (cachefilename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1038                        g_debug ("Copy successful");
1039                }
1040
1041                g_object_unref (src_file);
1042                g_object_unref (dst_file);
1043        } else {
1044                g_debug ("Not copying file %s to cache",
1045                         userfilename);
1046        }
1047        return res;
1048}
1049
1050static char *
1051gdm_session_worker_create_cachedir (GdmSessionWorker *worker)
1052{
1053        struct stat statbuf;
1054        char       *cachedir;
1055        int         r;
1056
1057        cachedir = g_build_filename (GDM_CACHE_DIR,
1058                                     worker->priv->username,
1059                                     NULL);
1060
1061        /* Verify user cache directory exists, create if needed */
1062        r = g_stat (cachedir, &statbuf);
1063        if (r < 0) {
1064                g_debug ("Making user cache directory %s", cachedir);
1065                g_mkdir (cachedir,
1066                         S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH);
1067                g_chmod (cachedir,
1068                         S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH);
1069        }
1070        chown (cachedir, worker->priv->uid, worker->priv->gid);
1071
1072        return cachedir;
1073}
1074
1075static void
1076gdm_session_worker_cache_userfiles (GdmSessionWorker *worker)
1077{
1078        struct passwd *passwd_entry;
1079        char          *cachedir;
1080        char          *cachefile;
1081        char          *userfile;
1082        gboolean       res;
1083
1084        passwd_entry = getpwnam (worker->priv->username);
1085        if (passwd_entry == NULL)
1086                return;
1087
1088        cachedir = gdm_session_worker_create_cachedir (worker);
1089
1090        g_debug ("Copying user dmrc file to cache");
1091        cachefile = g_build_filename (cachedir, "dmrc", NULL);
1092        userfile = g_build_filename (passwd_entry->pw_dir, ".dmrc", NULL);
1093
1094        gdm_cache_copy_file (worker, userfile, cachefile);
1095        g_free (cachefile);
1096        g_free (userfile);
1097
1098        g_debug ("Copying user face file to cache");
1099        cachefile = g_build_filename (cachedir,
1100                                          "face",
1101                                          NULL);
1102
1103        /* First, try "~/.face" */
1104        userfile = g_build_filename (passwd_entry->pw_dir, ".face", NULL);
1105        res = gdm_cache_copy_file (worker, userfile, cachefile);
1106
1107        /* Next, try "~/.face.icon" */
1108        if (!res) {
1109                g_free (userfile);
1110                userfile = g_build_filename (passwd_entry->pw_dir,
1111                                             ".face.icon",
1112                                             NULL);
1113                res = gdm_cache_copy_file (worker,
1114                                           userfile,
1115                                           cachefile);
1116        }
1117
1118        /* Still nothing, try the user's personal GDM config */
1119        if (!res) {
1120                char *tempfilename;
1121
1122                tempfilename = g_build_filename (passwd_entry->pw_dir,
1123                                                 ".gnome",
1124                                                 "gdm",
1125                                                 NULL);
1126
1127                g_debug ("Checking user's ~/.gnome/gdm file");
1128                res = check_user_copy_file (tempfilename,
1129                                            NULL,
1130                                            worker->priv->uid,
1131                                            MAX_FILE_SIZE);
1132                if (res) {
1133                        GKeyFile *keyfile;
1134
1135                        g_free (userfile);
1136
1137                        keyfile = g_key_file_new ();
1138                        g_key_file_load_from_file (keyfile,
1139                                                   userfile,
1140                                                   G_KEY_FILE_NONE,
1141                                                   NULL);
1142
1143                        userfile = g_key_file_get_string (keyfile,
1144                                                          "face",
1145                                                          "picture",
1146                                                          NULL);
1147                        res = gdm_cache_copy_file (worker,
1148                                                   userfile,
1149                                                   cachefile);
1150
1151                        g_key_file_free (keyfile);
1152                  }
1153                  g_free (tempfilename);
1154        }
1155
1156        g_free (cachedir);
1157        g_free (cachefile);
1158        g_free (userfile);
1159}
1160
1161static void
1162gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
1163                                     int               status)
1164{
1165        g_debug ("GdmSessionWorker: uninitializing PAM");
1166
1167        if (worker->priv->pam_handle == NULL)
1168                return;
1169
1170        if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) {
1171                gdm_session_worker_cache_userfiles (worker);
1172                pam_close_session (worker->priv->pam_handle, 0);
1173                gdm_session_auditor_report_logout (worker->priv->auditor);
1174
1175#ifdef  HAVE_LOGINDEVPERM
1176                /*
1177                 * Only do logindevperm processing if /dev/console or
1178                 * a device associated with a VT
1179                 */
1180                if (worker->priv->display_device != NULL &&
1181                   (strncmp (worker->priv->display_device, "/dev/vt/", strlen ("/dev/vt/")) == 0 ||
1182                    strcmp  (worker->priv->display_device, "/dev/console") == 0)) {
1183                        g_debug ("Logindevperm logout for user %s, device %s",
1184                                 worker->priv->username,
1185                                 worker->priv->display_device);
1186                        (void) di_devperm_logout (worker->priv->display_device);
1187                }
1188#endif  /* HAVE_LOGINDEVPERM */
1189
1190        } else {
1191                void *p;
1192
1193                if ((pam_get_item (worker->priv->pam_handle, PAM_USER, &p)) == PAM_SUCCESS) {
1194                        gdm_session_auditor_set_username (worker->priv->auditor, (const char *)p);
1195                }
1196
1197                gdm_session_auditor_report_login_failure (worker->priv->auditor,
1198                                                          status,
1199                                                          pam_strerror (worker->priv->pam_handle, status));
1200        }
1201
1202        if (worker->priv->state >= GDM_SESSION_WORKER_STATE_ACCREDITED) {
1203                pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED);
1204        }
1205
1206        pam_end (worker->priv->pam_handle, status);
1207        worker->priv->pam_handle = NULL;
1208
1209        gdm_session_worker_stop_auditor (worker);
1210
1211        g_debug ("GdmSessionWorker: state NONE");
1212        worker->priv->state = GDM_SESSION_WORKER_STATE_NONE;
1213}
1214
1215static char *
1216_get_tty_for_pam (const char *x11_display_name,
1217                  const char *display_device)
1218{
1219#ifdef __sun
1220        return g_strdup (display_device);
1221#else
1222        return g_strdup (x11_display_name);
1223#endif
1224}
1225
1226#ifdef PAM_XAUTHDATA
1227static struct pam_xauth_data *
1228_get_xauth_for_pam (const char *x11_authority_file)
1229{
1230        FILE                  *fh;
1231        Xauth                 *auth = NULL;
1232        struct pam_xauth_data *retval = NULL;
1233        gsize                  len = sizeof (*retval) + 1;
1234
1235        fh = fopen (x11_authority_file, "r");
1236        if (fh) {
1237                auth = XauReadAuth (fh);
1238                fclose (fh);
1239        }
1240        if (auth) {
1241                len += auth->name_length + auth->data_length;
1242                retval = g_malloc0 (len);
1243        }
1244        if (retval) {
1245                retval->namelen = auth->name_length;
1246                retval->name = (char *) (retval + 1);
1247                memcpy (retval->name, auth->name, auth->name_length);
1248                retval->datalen = auth->data_length;
1249                retval->data = retval->name + auth->name_length + 1;
1250                memcpy (retval->data, auth->data, auth->data_length);
1251        }
1252        XauDisposeAuth (auth);
1253        return retval;
1254}
1255#endif
1256
1257static gboolean
1258gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
1259                                   const char       *service,
1260                                   const char       *username,
1261                                   const char       *hostname,
1262                                   const char       *x11_display_name,
1263                                   const char       *x11_authority_file,
1264                                   const char       *display_device,
1265                                   GError          **error)
1266{
1267        struct pam_conv        pam_conversation;
1268#ifdef PAM_XAUTHDATA
1269        struct pam_xauth_data *pam_xauth;
1270#endif
1271        int                    error_code;
1272        char                  *pam_tty;
1273
1274        g_assert (worker->priv->pam_handle == NULL);
1275
1276        g_debug ("GdmSessionWorker: initializing PAM");
1277
1278        pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
1279        pam_conversation.appdata_ptr = worker;
1280
1281        gdm_session_worker_start_auditor (worker);
1282        error_code = pam_start (service,
1283                                username,
1284                                &pam_conversation,
1285                                &worker->priv->pam_handle);
1286
1287        if (error_code != PAM_SUCCESS) {
1288                g_debug ("GdmSessionWorker: could not initialize PAM");
1289                /* we don't use pam_strerror here because it requires a valid
1290                 * pam handle, and if pam_start fails pam_handle is undefined
1291                 */
1292                g_set_error (error,
1293                             GDM_SESSION_WORKER_ERROR,
1294                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1295                             _("error initiating conversation with authentication system - %s"),
1296                             error_code == PAM_ABORT? _("general failure") :
1297                             error_code == PAM_BUF_ERR? _("out of memory") :
1298                             error_code == PAM_SYSTEM_ERR? _("application programmer error") :
1299                             _("unknown error"));
1300
1301                goto out;
1302        }
1303
1304        /* set USER PROMPT */
1305        if (username == NULL) {
1306                error_code = pam_set_item (worker->priv->pam_handle, PAM_USER_PROMPT, _("Username:"));
1307
1308                if (error_code != PAM_SUCCESS) {
1309                        g_set_error (error,
1310                                     GDM_SESSION_WORKER_ERROR,
1311                                     GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1312                                     _("error informing authentication system of preferred username prompt - %s"),
1313                                     pam_strerror (worker->priv->pam_handle, error_code));
1314                        goto out;
1315                }
1316        }
1317
1318        /* set RHOST */
1319        if (hostname != NULL && hostname[0] != '\0') {
1320                error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname);
1321
1322                if (error_code != PAM_SUCCESS) {
1323                        g_set_error (error,
1324                                     GDM_SESSION_WORKER_ERROR,
1325                                     GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1326                                     _("error informing authentication system of user's hostname - %s"),
1327                                     pam_strerror (worker->priv->pam_handle, error_code));
1328                        goto out;
1329                }
1330        }
1331
1332        /* set TTY */
1333        pam_tty = _get_tty_for_pam (x11_display_name, display_device);
1334        error_code = pam_set_item (worker->priv->pam_handle, PAM_TTY, pam_tty);
1335        g_free (pam_tty);
1336
1337        if (error_code != PAM_SUCCESS) {
1338                g_set_error (error,
1339                             GDM_SESSION_WORKER_ERROR,
1340                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1341                             _("error informing authentication system of user's console - %s"),
1342                             pam_strerror (worker->priv->pam_handle, error_code));
1343                goto out;
1344        }
1345
1346#ifdef PAM_XDISPLAY
1347        /* set XDISPLAY */
1348        error_code = pam_set_item (worker->priv->pam_handle, PAM_XDISPLAY, x11_display_name);
1349
1350        if (error_code != PAM_SUCCESS) {
1351                g_set_error (error,
1352                             GDM_SESSION_WORKER_ERROR,
1353                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1354                             _("error informing authentication system of display string - %s"),
1355                             pam_strerror (worker->priv->pam_handle, error_code));
1356                goto out;
1357        }
1358#endif
1359#ifdef PAM_XAUTHDATA
1360        /* set XAUTHDATA */
1361        pam_xauth = _get_xauth_for_pam (x11_authority_file);
1362        error_code = pam_set_item (worker->priv->pam_handle, PAM_XAUTHDATA, pam_xauth);
1363        g_free (pam_xauth);
1364
1365        if (error_code != PAM_SUCCESS) {
1366                g_set_error (error,
1367                             GDM_SESSION_WORKER_ERROR,
1368                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1369                             _("error informing authentication system of display xauth credentials - %s"),
1370                             pam_strerror (worker->priv->pam_handle, error_code));
1371                goto out;
1372        }
1373#endif
1374
1375        g_debug ("GdmSessionWorker: state SETUP_COMPLETE");
1376        worker->priv->state = GDM_SESSION_WORKER_STATE_SETUP_COMPLETE;
1377
1378 out:
1379        if (error_code != PAM_SUCCESS) {
1380                gdm_session_worker_uninitialize_pam (worker, error_code);
1381                return FALSE;
1382        }
1383
1384        return TRUE;
1385}
1386
1387static gboolean
1388gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
1389                                      gboolean          password_is_required,
1390                                      GError          **error)
1391{
1392        int error_code;
1393        int authentication_flags;
1394
1395        g_debug ("GdmSessionWorker: authenticating user");
1396
1397        authentication_flags = 0;
1398
1399        if (password_is_required) {
1400                authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
1401        }
1402
1403        /* blocking call, does the actual conversation */
1404        error_code = pam_authenticate (worker->priv->pam_handle, authentication_flags);
1405
1406        if (error_code != PAM_SUCCESS) {
1407                g_debug ("GdmSessionWorker: authentication returned %d: %s", error_code, pam_strerror (worker->priv->pam_handle, error_code));
1408
1409                g_set_error (error,
1410                             GDM_SESSION_WORKER_ERROR,
1411                             GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
1412                             "%s", pam_strerror (worker->priv->pam_handle, error_code));
1413                goto out;
1414        }
1415
1416        g_debug ("GdmSessionWorker: state AUTHENTICATED");
1417        worker->priv->state = GDM_SESSION_WORKER_STATE_AUTHENTICATED;
1418
1419 out:
1420        if (error_code != PAM_SUCCESS) {
1421                gdm_session_worker_uninitialize_pam (worker, error_code);
1422                return FALSE;
1423        }
1424
1425        return TRUE;
1426}
1427
1428static gboolean
1429gdm_session_worker_authorize_user (GdmSessionWorker *worker,
1430                                   gboolean          password_is_required,
1431                                   GError          **error)
1432{
1433        int error_code;
1434        int authentication_flags;
1435
1436        g_debug ("GdmSessionWorker: determining if authenticated user is authorized to session");
1437
1438        authentication_flags = 0;
1439
1440        if (password_is_required) {
1441                authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
1442        }
1443
1444        /* check that the account isn't disabled or expired
1445         */
1446        error_code = pam_acct_mgmt (worker->priv->pam_handle, authentication_flags);
1447
1448        /* it's possible that the user needs to change their password or pin code
1449         */
1450        if (error_code == PAM_NEW_AUTHTOK_REQD) {
1451                error_code = pam_chauthtok (worker->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
1452
1453                if (error_code != PAM_SUCCESS) {
1454                        gdm_session_auditor_report_password_change_failure (worker->priv->auditor);
1455                } else {
1456                        gdm_session_auditor_report_password_changed (worker->priv->auditor);
1457                }
1458        }
1459
1460        if (error_code != PAM_SUCCESS) {
1461                g_debug ("GdmSessionWorker: user is not authorized to log in: %s",
1462                         pam_strerror (worker->priv->pam_handle, error_code));
1463                g_set_error (error,
1464                             GDM_SESSION_WORKER_ERROR,
1465                             GDM_SESSION_WORKER_ERROR_AUTHORIZING,
1466                             "%s", pam_strerror (worker->priv->pam_handle, error_code));
1467                goto out;
1468        }
1469
1470        g_debug ("GdmSessionWorker: state AUTHORIZED");
1471        worker->priv->state = GDM_SESSION_WORKER_STATE_AUTHORIZED;
1472
1473 out:
1474        if (error_code != PAM_SUCCESS) {
1475                gdm_session_worker_uninitialize_pam (worker, error_code);
1476                return FALSE;
1477        }
1478
1479        return TRUE;
1480}
1481
1482static void
1483gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
1484                                             const char       *key,
1485                                             const char       *value)
1486{
1487        /* FIXME: maybe we should use use pam_putenv instead of our
1488         * own hash table, so pam can override our choices if it knows
1489         * better?
1490         */
1491        g_hash_table_replace (worker->priv->environment,
1492                              g_strdup (key),
1493                              g_strdup (value));
1494}
1495
1496static void
1497gdm_session_worker_update_environment_from_passwd_info (GdmSessionWorker *worker,
1498                                                        uid_t             uid,
1499                                                        gid_t             gid,
1500                                                        const char       *home,
1501                                                        const char       *shell)
1502{
1503        gdm_session_worker_set_environment_variable (worker, "LOGNAME", worker->priv->username);
1504        gdm_session_worker_set_environment_variable (worker, "USER", worker->priv->username);
1505        gdm_session_worker_set_environment_variable (worker, "USERNAME", worker->priv->username);
1506        gdm_session_worker_set_environment_variable (worker, "HOME", home);
1507        gdm_session_worker_set_environment_variable (worker, "SHELL", shell);
1508}
1509
1510static gboolean
1511gdm_session_worker_environment_variable_is_set (GdmSessionWorker *worker,
1512                                                const char       *name)
1513{
1514        return g_hash_table_lookup (worker->priv->environment, name) != NULL;
1515}
1516
1517static gboolean
1518_change_user (GdmSessionWorker  *worker,
1519              uid_t              uid,
1520              gid_t              gid)
1521{
1522        gboolean ret;
1523
1524        ret = FALSE;
1525
1526#ifdef THE_MAN_PAGE_ISNT_LYING
1527        /* pam_setcred wants to be called as the authenticated user
1528         * but pam_open_session needs to be called as super-user.
1529         *
1530         * Set the real uid and gid to the user and give the user a
1531         * temporary super-user effective id.
1532         */
1533        if (setreuid (uid, GDM_SESSION_ROOT_UID) < 0) {
1534                return FALSE;
1535        }
1536#endif
1537        worker->priv->uid = uid;
1538        worker->priv->gid = gid;
1539
1540        if (setgid (gid) < 0) {
1541                return FALSE;
1542        }
1543
1544        if (initgroups (worker->priv->username, gid) < 0) {
1545                return FALSE;
1546        }
1547
1548        return TRUE;
1549}
1550
1551static gboolean
1552_lookup_passwd_info (const char *username,
1553                     uid_t      *uidp,
1554                     gid_t      *gidp,
1555                     char      **homep,
1556                     char      **shellp)
1557{
1558        gboolean       ret;
1559        struct passwd *passwd_entry;
1560        struct passwd  passwd_buffer;
1561        char          *aux_buffer;
1562        long           required_aux_buffer_size;
1563        gsize          aux_buffer_size;
1564
1565        ret = FALSE;
1566        aux_buffer = NULL;
1567        aux_buffer_size = 0;
1568
1569        required_aux_buffer_size = sysconf (_SC_GETPW_R_SIZE_MAX);
1570
1571        if (required_aux_buffer_size < 0) {
1572                aux_buffer_size = GDM_PASSWD_AUXILLARY_BUFFER_SIZE;
1573        } else {
1574                aux_buffer_size = (gsize) required_aux_buffer_size;
1575        }
1576
1577        aux_buffer = g_slice_alloc0 (aux_buffer_size);
1578
1579        /* we use the _r variant of getpwnam()
1580         * (with its weird semantics) so that the
1581         * passwd_entry doesn't potentially get stomped on
1582         * by a PAM module
1583         */
1584        passwd_entry = NULL;
1585#ifdef HAVE_POSIX_GETPWNAM_R
1586        errno = getpwnam_r (username,
1587                            &passwd_buffer,
1588                            aux_buffer,
1589                            (size_t) aux_buffer_size,
1590                            &passwd_entry);
1591#else
1592        passwd_entry = getpwnam_r (username,
1593                                   &passwd_buffer,
1594                                   aux_buffer,
1595                                   (size_t) aux_buffer_size);
1596        errno = 0;
1597#endif /* !HAVE_POSIX_GETPWNAM_R */
1598
1599        if (errno != 0) {
1600                g_warning ("%s", g_strerror (errno));
1601                goto out;
1602        }
1603
1604        if (passwd_entry == NULL) {
1605                goto out;
1606        }
1607
1608        if (uidp != NULL) {
1609                *uidp = passwd_entry->pw_uid;
1610        }
1611        if (gidp != NULL) {
1612                *gidp = passwd_entry->pw_gid;
1613        }
1614        if (homep != NULL) {
1615                *homep = g_strdup (passwd_entry->pw_dir);
1616        }
1617        if (shellp != NULL) {
1618                *shellp = g_strdup (passwd_entry->pw_shell);
1619        }
1620        ret = TRUE;
1621 out:
1622        if (aux_buffer != NULL) {
1623                g_assert (aux_buffer_size > 0);
1624                g_slice_free1 (aux_buffer_size, aux_buffer);
1625        }
1626
1627        return ret;
1628}
1629
1630static gboolean
1631gdm_session_worker_accredit_user (GdmSessionWorker  *worker,
1632                                  GError           **error)
1633{
1634        gboolean ret;
1635        gboolean res;
1636        uid_t    uid;
1637        gid_t    gid;
1638        char    *shell;
1639        char    *home;
1640        int      error_code;
1641
1642        ret = FALSE;
1643
1644        if (worker->priv->username == NULL) {
1645                g_debug ("GdmSessionWorker: Username not set");
1646                error_code = PAM_USER_UNKNOWN;
1647                g_set_error (error,
1648                             GDM_SESSION_WORKER_ERROR,
1649                             GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1650                             _("no user account available"));
1651                goto out;
1652        }
1653
1654        home = NULL;
1655        shell = NULL;
1656        uid = 0;
1657        gid = 0;
1658        res = _lookup_passwd_info (worker->priv->username,
1659                                   &uid,
1660                                   &gid,
1661                                   &home,
1662                                   &shell);
1663        if (! res) {
1664                g_debug ("GdmSessionWorker: Unable to lookup account info");
1665                error_code = PAM_AUTHINFO_UNAVAIL;
1666                g_set_error (error,
1667                             GDM_SESSION_WORKER_ERROR,
1668                             GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1669                             _("no user account available"));
1670                goto out;
1671        }
1672
1673        gdm_session_worker_update_environment_from_passwd_info (worker,
1674                                                                uid,
1675                                                                gid,
1676                                                                home,
1677                                                                shell);
1678
1679        /* Let's give the user a default PATH if he doesn't already have one
1680         */
1681        if (!gdm_session_worker_environment_variable_is_set (worker, "PATH")) {
1682                if (strcmp (BINDIR, "/usr/bin") == 0) {
1683                        gdm_session_worker_set_environment_variable (worker, "PATH",
1684                                                                     GDM_SESSION_DEFAULT_PATH);
1685                } else {
1686                        gdm_session_worker_set_environment_variable (worker, "PATH",
1687                                                                     BINDIR ":" GDM_SESSION_DEFAULT_PATH);
1688                }
1689        }
1690
1691        if (! _change_user (worker, uid, gid)) {
1692                g_debug ("GdmSessionWorker: Unable to change to user");
1693                error_code = PAM_SYSTEM_ERR;
1694                g_set_error (error, GDM_SESSION_WORKER_ERROR,
1695                             GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1696                             "%s", _("Unable to change to user"));
1697                goto out;
1698        }
1699
1700        error_code = pam_setcred (worker->priv->pam_handle, worker->priv->cred_flags);
1701
1702        if (error_code != PAM_SUCCESS) {
1703                g_set_error (error,
1704                             GDM_SESSION_WORKER_ERROR,
1705                             GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
1706                             "%s",
1707                             pam_strerror (worker->priv->pam_handle, error_code));
1708                goto out;
1709        }
1710
1711        ret = TRUE;
1712
1713 out:
1714        if (ret) {
1715                g_debug ("GdmSessionWorker: state ACCREDITED");
1716                ret = TRUE;
1717                gdm_session_auditor_report_user_accredited (worker->priv->auditor);
1718                worker->priv->state = GDM_SESSION_WORKER_STATE_ACCREDITED;
1719        } else {
1720                gdm_session_worker_uninitialize_pam (worker, error_code);
1721        }
1722
1723        return ret;
1724}
1725
1726static void
1727gdm_session_worker_update_environment_from_pam (GdmSessionWorker *worker)
1728{
1729        char **environment;
1730        gsize i;
1731
1732        environment = pam_getenvlist (worker->priv->pam_handle);
1733
1734        for (i = 0; environment[i] != NULL; i++) {
1735                char **key_and_value;
1736
1737                key_and_value = g_strsplit (environment[i], "=", 2);
1738
1739                gdm_session_worker_set_environment_variable (worker, key_and_value[0], key_and_value[1]);
1740
1741                g_strfreev (key_and_value);
1742        }
1743
1744        for (i = 0; environment[i]; i++) {
1745                free (environment[i]);
1746        }
1747
1748        free (environment);
1749}
1750
1751static void
1752gdm_session_worker_fill_environment_array (const char *key,
1753                                           const char *value,
1754                                           GPtrArray  *environment)
1755{
1756        char *variable;
1757
1758        if (value == NULL)
1759                return;
1760
1761        variable = g_strdup_printf ("%s=%s", key, value);
1762
1763        g_ptr_array_add (environment, variable);
1764}
1765
1766static char **
1767gdm_session_worker_get_environment (GdmSessionWorker *worker)
1768{
1769        GPtrArray *environment;
1770
1771        environment = g_ptr_array_new ();
1772        g_hash_table_foreach (worker->priv->environment,
1773                              (GHFunc) gdm_session_worker_fill_environment_array,
1774                              environment);
1775        g_ptr_array_add (environment, NULL);
1776
1777        return (char **) g_ptr_array_free (environment, FALSE);
1778}
1779
1780static void
1781register_ck_session (GdmSessionWorker *worker)
1782{
1783        const char *session_cookie;
1784        gboolean    res;
1785
1786        session_cookie = NULL;
1787        res = open_ck_session (worker);
1788        if (res) {
1789                session_cookie = ck_connector_get_cookie (worker->priv->ckc);
1790        }
1791        if (session_cookie != NULL) {
1792                gdm_session_worker_set_environment_variable (worker,
1793                                                             "XDG_SESSION_COOKIE",
1794                                                             session_cookie);
1795        }
1796}
1797
1798static void
1799session_worker_child_watch (GPid              pid,
1800                            int               status,
1801                            GdmSessionWorker *worker)
1802{
1803        g_debug ("GdmSessionWorker: child (pid:%d) done (%s:%d)",
1804                 (int) pid,
1805                 WIFEXITED (status) ? "status"
1806                 : WIFSIGNALED (status) ? "signal"
1807                 : "unknown",
1808                 WIFEXITED (status) ? WEXITSTATUS (status)
1809                 : WIFSIGNALED (status) ? WTERMSIG (status)
1810                 : -1);
1811
1812        if (WIFEXITED (status)) {
1813                int code = WEXITSTATUS (status);
1814
1815                send_dbus_int_method (worker->priv->connection,
1816                                      "SessionExited",
1817                                      code);
1818        } else if (WIFSIGNALED (status)) {
1819                int num = WTERMSIG (status);
1820
1821                send_dbus_int_method (worker->priv->connection,
1822                                      "SessionDied",
1823                                      num);
1824        }
1825
1826        if (worker->priv->ckc != NULL) {
1827                ck_connector_close_session (worker->priv->ckc, NULL);
1828                ck_connector_unref (worker->priv->ckc);
1829                worker->priv->ckc = NULL;
1830        }
1831
1832        gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS);
1833
1834        worker->priv->child_pid = -1;
1835}
1836
1837static void
1838gdm_session_worker_watch_child (GdmSessionWorker *worker)
1839{
1840
1841        worker->priv->child_watch_id = g_child_watch_add (worker->priv->child_pid,
1842                                                          (GChildWatchFunc)session_worker_child_watch,
1843                                                          worker);
1844
1845}
1846
1847static gboolean
1848_fd_is_normal_file (int fd)
1849{
1850        struct stat file_info;
1851
1852        if (fstat (fd, &file_info) < 0) {
1853                return FALSE;
1854        }
1855
1856        return S_ISREG (file_info.st_mode);
1857}
1858
1859static int
1860_open_session_log (const char *dir)
1861{
1862        int   fd;
1863        char *filename;
1864
1865        filename = g_build_filename (dir, GDM_SESSION_LOG_FILENAME, NULL);
1866
1867        if (g_access (dir, R_OK | W_OK | X_OK) == 0 && g_access (filename, R_OK | W_OK) == 0) {
1868                char *filename_old;
1869
1870                filename_old = g_strdup_printf ("%s.old", filename);
1871                g_rename (filename, filename_old);
1872                g_free (filename_old);
1873        }
1874
1875        fd = g_open (filename, O_RDWR | O_APPEND | O_CREAT, 0600);
1876
1877        if (fd < 0 || !_fd_is_normal_file (fd)) {
1878                char *temp_name;
1879
1880                close (fd);
1881
1882                temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);
1883
1884                fd = g_mkstemp (temp_name);
1885
1886                if (fd < 0) {
1887                        g_free (temp_name);
1888                        goto out;
1889                }
1890
1891                g_warning ("session log '%s' is not a normal file, logging session to '%s' instead.\n", filename,
1892                           temp_name);
1893                g_free (filename);
1894                filename = temp_name;
1895        } else {
1896                if (ftruncate (fd, 0) < 0) {
1897                        close (fd);
1898                        fd = -1;
1899                        goto out;
1900                }
1901        }
1902
1903        if (fchmod (fd, 0600) < 0) {
1904                close (fd);
1905                fd = -1;
1906                goto out;
1907        }
1908
1909
1910out:
1911        g_free (filename);
1912
1913        if (fd < 0) {
1914                g_warning ("unable to log session");
1915                fd = g_open ("/dev/null", O_RDWR);
1916        }
1917
1918        return fd;
1919}
1920
1921static void
1922_save_user_settings (GdmSessionWorker *worker,
1923                     const char       *home_dir)
1924{
1925        GError *error;
1926
1927        if (!gdm_session_settings_is_loaded (worker->priv->user_settings)) {
1928                /*
1929                 * Even if the user did not change the defaults, there may
1930                 * be files to cache
1931                 */
1932                goto out;
1933        }
1934
1935        error = NULL;
1936        if (!gdm_session_settings_save (worker->priv->user_settings,
1937                                        home_dir, &error)) {
1938                g_warning ("could not save session and language settings: %s",
1939                           error->message);
1940                g_error_free (error);
1941        }
1942
1943out:
1944        gdm_session_worker_cache_userfiles (worker);
1945}
1946
1947static gboolean
1948gdm_session_worker_start_user_session (GdmSessionWorker  *worker,
1949                                       GError           **error)
1950{
1951        struct passwd *passwd_entry;
1952        pid_t session_pid;
1953        int   error_code;
1954
1955        g_debug ("GdmSessionWorker: querying pam for user environment");
1956        gdm_session_worker_update_environment_from_pam (worker);
1957
1958        register_ck_session (worker);
1959
1960        passwd_entry = getpwnam (worker->priv->username);
1961
1962#ifdef  HAVE_LOGINDEVPERM
1963        /*
1964         * Only do logindevperm processing if /dev/console or
1965         * a device associated with a VT
1966         */
1967        if (worker->priv->display_device != NULL &&
1968           (strncmp (worker->priv->display_device, "/dev/vt/", strlen ("/dev/vt/")) == 0 ||
1969            strcmp  (worker->priv->display_device, "/dev/console") == 0)) {
1970                g_debug ("Logindevperm login for user %s, device %s",
1971                         worker->priv->username,
1972                         worker->priv->display_device);
1973                (void) di_devperm_login (worker->priv->display_device,
1974                                         passwd_entry->pw_uid,
1975                                         passwd_entry->pw_gid,
1976                                         NULL);
1977        }
1978#endif  /* HAVE_LOGINDEVPERM */
1979
1980        g_debug ("GdmSessionWorker: opening user session with program '%s'",
1981                 worker->priv->arguments[0]);
1982
1983        error_code = PAM_SUCCESS;
1984
1985        session_pid = fork ();
1986
1987        if (session_pid < 0) {
1988                g_set_error (error,
1989                             GDM_SESSION_WORKER_ERROR,
1990                             GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
1991                             "%s", g_strerror (errno));
1992                error_code = PAM_ABORT;
1993                goto out;
1994        }
1995
1996        if (session_pid == 0) {
1997                char **environment;
1998                char  *cachedirname;
1999                char  *home_dir;
2000                int    fd;
2001
2002                /* Make sure cachedir gets created before we drop to user */
2003                cachedirname = gdm_session_worker_create_cachedir (worker);
2004                g_free (cachedirname);
2005
2006                if (setuid (worker->priv->uid) < 0) {
2007                        g_debug ("GdmSessionWorker: could not reset uid - %s", g_strerror (errno));
2008                        _exit (1);
2009                }
2010
2011                if (setsid () < 0) {
2012                        g_debug ("GdmSessionWorker: could not set pid '%u' as leader of new session and process group - %s",
2013                                 (guint) getpid (), g_strerror (errno));
2014                        _exit (2);
2015                }
2016
2017                environment = gdm_session_worker_get_environment (worker);
2018
2019                g_assert (geteuid () == getuid ());
2020
2021                home_dir = g_hash_table_lookup (worker->priv->environment,
2022                                                "HOME");
2023
2024                if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
2025                        g_chdir ("/");
2026                }
2027
2028                fd = open ("/dev/null", O_RDWR);
2029                dup2 (fd, STDIN_FILENO);
2030                close (fd);
2031
2032                fd = _open_session_log (home_dir);
2033                dup2 (fd, STDOUT_FILENO);
2034                dup2 (fd, STDERR_FILENO);
2035                close (fd);
2036
2037                _save_user_settings (worker, home_dir);
2038
2039                gdm_session_execute (worker->priv->arguments[0],
2040                                     worker->priv->arguments,
2041                                     environment,
2042                                     TRUE);
2043
2044                g_debug ("GdmSessionWorker: child '%s' could not be started - %s",
2045                         worker->priv->arguments[0],
2046                         g_strerror (errno));
2047                g_strfreev (environment);
2048
2049                _exit (127);
2050        }
2051
2052        worker->priv->child_pid = session_pid;
2053
2054        g_debug ("GdmSessionWorker: session opened creating reply...");
2055        g_assert (sizeof (GPid) <= sizeof (int));
2056
2057        g_debug ("GdmSessionWorker: state SESSION_STARTED");
2058        worker->priv->state = GDM_SESSION_WORKER_STATE_SESSION_STARTED;
2059
2060        gdm_session_worker_watch_child (worker);
2061
2062 out:
2063        if (error_code != PAM_SUCCESS) {
2064                gdm_session_worker_uninitialize_pam (worker, error_code);
2065                return FALSE;
2066        }
2067
2068        return TRUE;
2069}
2070
2071static gboolean
2072gdm_session_worker_open_user_session (GdmSessionWorker  *worker,
2073                                      GError           **error)
2074{
2075        int error_code;
2076
2077        g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED);
2078        g_assert (geteuid () == 0);
2079
2080        error_code = pam_open_session (worker->priv->pam_handle, 0);
2081
2082        if (error_code != PAM_SUCCESS) {
2083                g_set_error (error,
2084                             GDM_SESSION_WORKER_ERROR,
2085                             GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
2086                             "%s", pam_strerror (worker->priv->pam_handle, error_code));
2087                goto out;
2088        }
2089
2090        g_debug ("GdmSessionWorker: state SESSION_OPENED");
2091        worker->priv->state = GDM_SESSION_WORKER_STATE_SESSION_OPENED;
2092
2093 out:
2094        if (error_code != PAM_SUCCESS) {
2095                gdm_session_worker_uninitialize_pam (worker, error_code);
2096                return FALSE;
2097        }
2098
2099        gdm_session_auditor_report_login (worker->priv->auditor);
2100
2101        return TRUE;
2102}
2103
2104static void
2105gdm_session_worker_set_server_address (GdmSessionWorker *worker,
2106                                       const char       *address)
2107{
2108        g_free (worker->priv->server_address);
2109        worker->priv->server_address = g_strdup (address);
2110}
2111
2112static void
2113gdm_session_worker_set_property (GObject      *object,
2114                                guint         prop_id,
2115                                const GValue *value,
2116                                GParamSpec   *pspec)
2117{
2118        GdmSessionWorker *self;
2119
2120        self = GDM_SESSION_WORKER (object);
2121
2122        switch (prop_id) {
2123        case PROP_SERVER_ADDRESS:
2124                gdm_session_worker_set_server_address (self, g_value_get_string (value));
2125                break;
2126        default:
2127                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2128                break;
2129        }
2130}
2131
2132static void
2133gdm_session_worker_get_property (GObject    *object,
2134                                guint       prop_id,
2135                                GValue     *value,
2136                                GParamSpec *pspec)
2137{
2138        GdmSessionWorker *self;
2139
2140        self = GDM_SESSION_WORKER (object);
2141
2142        switch (prop_id) {
2143        case PROP_SERVER_ADDRESS:
2144                g_value_set_string (value, self->priv->server_address);
2145                break;
2146        default:
2147                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2148                break;
2149        }
2150}
2151
2152static void
2153on_set_environment_variable (GdmSessionWorker *worker,
2154                             DBusMessage      *message)
2155{
2156        DBusError   error;
2157        const char *key;
2158        const char *value;
2159        dbus_bool_t res;
2160
2161        dbus_error_init (&error);
2162        res = dbus_message_get_args (message,
2163                                     &error,
2164                                     DBUS_TYPE_STRING, &key,
2165                                     DBUS_TYPE_STRING, &value,
2166                                     DBUS_TYPE_INVALID);
2167        if (res) {
2168                g_debug ("GdmSessionWorker: set env: %s = %s", key, value);
2169                gdm_session_worker_set_environment_variable (worker, key, value);
2170        } else {
2171                g_warning ("Unable to get arguments: %s", error.message);
2172                dbus_error_free (&error);
2173        }
2174}
2175
2176static void
2177gdm_session_worker_set_session_name (GdmSessionWorker *worker,
2178                                     const char       *session_name)
2179{
2180        gdm_session_settings_set_session_name (worker->priv->user_settings,
2181                                               session_name);
2182}
2183
2184static void
2185on_set_session_name (GdmSessionWorker *worker,
2186                     DBusMessage      *message)
2187{
2188        DBusError   error;
2189        const char *session_name;
2190        dbus_bool_t res;
2191
2192        dbus_error_init (&error);
2193        res = dbus_message_get_args (message,
2194                                     &error,
2195                                     DBUS_TYPE_STRING, &session_name,
2196                                     DBUS_TYPE_INVALID);
2197        if (res) {
2198                g_debug ("GdmSessionWorker: session name set to %s", session_name);
2199                gdm_session_worker_set_session_name (worker, session_name);
2200        } else {
2201                g_warning ("Unable to get arguments: %s", error.message);
2202                dbus_error_free (&error);
2203        }
2204}
2205
2206static void
2207gdm_session_worker_set_language_name (GdmSessionWorker *worker,
2208                                      const char       *language_name)
2209{
2210        gdm_session_settings_set_language_name (worker->priv->user_settings,
2211                                                language_name);
2212}
2213
2214static void
2215gdm_session_worker_set_layout_name (GdmSessionWorker *worker,
2216                                    const char       *layout_name)
2217{
2218        gdm_session_settings_set_layout_name (worker->priv->user_settings,
2219                                              layout_name);
2220}
2221
2222static void
2223on_set_language_name (GdmSessionWorker *worker,
2224                      DBusMessage      *message)
2225{
2226        DBusError   error;
2227        const char *language_name;
2228        dbus_bool_t res;
2229
2230        dbus_error_init (&error);
2231        res = dbus_message_get_args (message,
2232                                     &error,
2233                                     DBUS_TYPE_STRING, &language_name,
2234                                     DBUS_TYPE_INVALID);
2235        if (res) {
2236                g_debug ("GdmSessionWorker: language name set to %s", language_name);
2237                gdm_session_worker_set_language_name (worker, language_name);
2238        } else {
2239                g_warning ("Unable to get arguments: %s", error.message);
2240                dbus_error_free (&error);
2241        }
2242}
2243
2244static void
2245on_set_layout_name (GdmSessionWorker *worker,
2246                    DBusMessage      *message)
2247{
2248        DBusError   error;
2249        const char *layout_name;
2250        dbus_bool_t res;
2251
2252        dbus_error_init (&error);
2253        res = dbus_message_get_args (message,
2254                                     &error,
2255                                     DBUS_TYPE_STRING, &layout_name,
2256                                     DBUS_TYPE_INVALID);
2257        if (res) {
2258                g_debug ("GdmSessionWorker: layout name set to %s", layout_name);
2259                gdm_session_worker_set_layout_name (worker, layout_name);
2260        } else {
2261                g_warning ("Unable to get arguments: %s", error.message);
2262                dbus_error_free (&error);
2263        }
2264}
2265
2266static void
2267on_saved_language_name_read (GdmSessionWorker *worker)
2268{
2269        char *language_name;
2270
2271        language_name = gdm_session_settings_get_language_name (worker->priv->user_settings);
2272        send_dbus_string_method (worker->priv->connection,
2273                                 "SavedLanguageNameRead",
2274                                 language_name);
2275        g_free (language_name);
2276}
2277
2278static void
2279on_saved_layout_name_read (GdmSessionWorker *worker)
2280{
2281        char *layout_name;
2282
2283        layout_name = gdm_session_settings_get_layout_name (worker->priv->user_settings);
2284        send_dbus_string_method (worker->priv->connection,
2285                                 "SavedLayoutNameRead",
2286                                 layout_name);
2287        g_free (layout_name);
2288}
2289
2290static void
2291on_saved_session_name_read (GdmSessionWorker *worker)
2292{
2293        char *session_name;
2294
2295        session_name = gdm_session_settings_get_session_name (worker->priv->user_settings);
2296        send_dbus_string_method (worker->priv->connection,
2297                                 "SavedSessionNameRead",
2298                                 session_name);
2299        g_free (session_name);
2300}
2301
2302static void
2303do_setup (GdmSessionWorker *worker)
2304{
2305        GError  *error;
2306        gboolean res;
2307
2308        worker->priv->user_settings = gdm_session_settings_new ();
2309
2310        g_signal_connect_swapped (worker->priv->user_settings,
2311                                  "notify::language-name",
2312                                  G_CALLBACK (on_saved_language_name_read),
2313                                  worker);
2314
2315        g_signal_connect_swapped (worker->priv->user_settings,
2316                                  "notify::layout-name",
2317                                  G_CALLBACK (on_saved_layout_name_read),
2318                                  worker);
2319
2320        g_signal_connect_swapped (worker->priv->user_settings,
2321                                  "notify::session-name",
2322                                  G_CALLBACK (on_saved_session_name_read),
2323                                  worker);
2324
2325        /* In some setups the user can read ~/.dmrc at this point.
2326         * In some other setups the user can only read ~/.dmrc after completing
2327         * the pam conversation.
2328         *
2329         * The user experience is better if we can read .dmrc now since we can
2330         * prefill in the language and session combo boxes in the greeter with
2331         * the right values.
2332         *
2333         * We'll try now, and if it doesn't work out, try later.
2334         */
2335        if (worker->priv->username != NULL) {
2336                attempt_to_load_user_settings (worker,
2337                                               worker->priv->username);
2338        }
2339
2340        error = NULL;
2341        res = gdm_session_worker_initialize_pam (worker,
2342                                                 worker->priv->service,
2343                                                 worker->priv->username,
2344                                                 worker->priv->hostname,
2345                                                 worker->priv->x11_display_name,
2346                                                 worker->priv->x11_authority_file,
2347                                                 worker->priv->display_device,
2348                                                 &error);
2349        if (! res) {
2350                send_dbus_string_method (worker->priv->connection,
2351                                         "SetupFailed",
2352                                         error->message);
2353                g_error_free (error);
2354                return;
2355        }
2356
2357        send_dbus_void_method (worker->priv->connection, "SetupComplete");
2358}
2359
2360static void
2361do_authenticate (GdmSessionWorker *worker)
2362{
2363        GError  *error;
2364        gboolean res;
2365
2366        /* find out who the user is and ensure they are who they say they are
2367         */
2368        error = NULL;
2369        res = gdm_session_worker_authenticate_user (worker,
2370                                                    worker->priv->password_is_required,
2371                                                    &error);
2372        if (! res) {
2373                g_debug ("GdmSessionWorker: Unable to verify user");
2374                send_dbus_string_method (worker->priv->connection,
2375                                         "AuthenticationFailed",
2376                                         error->message);
2377                g_error_free (error);
2378                return;
2379        }
2380
2381        /* we're authenticated.  Let's make sure we've been given
2382         * a valid username for the system
2383         */
2384        g_debug ("GdmSessionWorker: trying to get updated username");
2385        gdm_session_worker_update_username (worker);
2386
2387        send_dbus_void_method (worker->priv->connection, "Authenticated");
2388}
2389
2390static void
2391do_authorize (GdmSessionWorker *worker)
2392{
2393        GError  *error;
2394        gboolean res;
2395
2396        /* make sure the user is allowed to log in to this system
2397         */
2398        error = NULL;
2399        res = gdm_session_worker_authorize_user (worker,
2400                                                 worker->priv->password_is_required,
2401                                                 &error);
2402        if (! res) {
2403                send_dbus_string_method (worker->priv->connection,
2404                                         "AuthorizationFailed",
2405                                         error->message);
2406                g_error_free (error);
2407                return;
2408        }
2409
2410        send_dbus_void_method (worker->priv->connection, "Authorized");
2411}
2412
2413static void
2414do_accredit (GdmSessionWorker *worker)
2415{
2416        GError  *error;
2417        gboolean res;
2418
2419        /* get kerberos tickets, setup group lists, etc
2420         */
2421        error = NULL;
2422        res = gdm_session_worker_accredit_user (worker, &error);
2423
2424        if (! res) {
2425                send_dbus_string_method (worker->priv->connection,
2426                                         "AccreditationFailed",
2427                                         error->message);
2428                g_error_free (error);
2429                return;
2430        }
2431
2432        send_dbus_void_method (worker->priv->connection, "Accredited");
2433}
2434
2435static void
2436do_open_session (GdmSessionWorker *worker)
2437{
2438        GError  *error;
2439        gboolean res;
2440
2441        error = NULL;
2442        res = gdm_session_worker_open_user_session (worker, &error);
2443        if (! res) {
2444                send_dbus_string_method (worker->priv->connection,
2445                                         "StartFailed",
2446                                         error->message);
2447                g_error_free (error);
2448                return;
2449        }
2450
2451        queue_state_change (worker);
2452}
2453
2454static void
2455do_start_session (GdmSessionWorker *worker)
2456{
2457        GError  *error;
2458        gboolean res;
2459
2460        error = NULL;
2461        res = gdm_session_worker_start_user_session (worker, &error);
2462        if (! res) {
2463                send_dbus_string_method (worker->priv->connection,
2464                                         "StartFailed",
2465                                         error->message);
2466                g_error_free (error);
2467                return;
2468        }
2469
2470        send_dbus_int_method (worker->priv->connection,
2471                              "SessionStarted",
2472                              (int)worker->priv->child_pid);
2473}
2474
2475static const char *
2476get_state_name (int state)
2477{
2478        const char *name;
2479
2480        name = NULL;
2481
2482        switch (state) {
2483        case GDM_SESSION_WORKER_STATE_NONE:
2484                name = "NONE";
2485                break;
2486        case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
2487                name = "SETUP_COMPLETE";
2488                break;
2489        case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
2490                name = "AUTHENTICATED";
2491                break;
2492        case GDM_SESSION_WORKER_STATE_AUTHORIZED:
2493                name = "AUTHORIZED";
2494                break;
2495        case GDM_SESSION_WORKER_STATE_ACCREDITED:
2496                name = "ACCREDITED";
2497                break;
2498        case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
2499                name = "SESSION_OPENED";
2500                break;
2501        case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
2502                name = "SESSION_STARTED";
2503                break;
2504        default:
2505                g_assert_not_reached ();
2506                break;
2507        }
2508
2509        return name;
2510}
2511
2512static gboolean
2513state_change_idle (GdmSessionWorker *worker)
2514{
2515        int new_state;
2516
2517        new_state = worker->priv->state + 1;
2518        g_debug ("GdmSessionWorker: attempting to change state to %s",
2519                 get_state_name (new_state));
2520
2521        worker->priv->state_change_idle_id = 0;
2522
2523        switch (new_state) {
2524        case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
2525                do_setup (worker);
2526                break;
2527        case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
2528                do_authenticate (worker);
2529                break;
2530        case GDM_SESSION_WORKER_STATE_AUTHORIZED:
2531                do_authorize (worker);
2532                break;
2533        case GDM_SESSION_WORKER_STATE_ACCREDITED:
2534                do_accredit (worker);
2535                break;
2536        case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
2537                do_open_session (worker);
2538                break;
2539        case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
2540                do_start_session (worker);
2541                break;
2542        case GDM_SESSION_WORKER_STATE_NONE:
2543        default:
2544                g_assert_not_reached ();
2545        }
2546        return FALSE;
2547}
2548
2549static void
2550queue_state_change (GdmSessionWorker *worker)
2551{
2552        if (worker->priv->state_change_idle_id > 0) {
2553                return;
2554        }
2555
2556        worker->priv->state_change_idle_id = g_idle_add ((GSourceFunc)state_change_idle, worker);
2557}
2558
2559static void
2560on_start_program (GdmSessionWorker *worker,
2561                  DBusMessage      *message)
2562{
2563        DBusError   error;
2564        const char *text;
2565        dbus_bool_t res;
2566
2567        if (worker->priv->state != GDM_SESSION_WORKER_STATE_ACCREDITED) {
2568                g_debug ("GdmSessionWorker: ignoring spurious start program while in state %s", get_state_name (worker->priv->state));
2569                return;
2570        }
2571
2572        dbus_error_init (&error);
2573        res = dbus_message_get_args (message,
2574                                     &error,
2575                                     DBUS_TYPE_STRING, &text,
2576                                     DBUS_TYPE_INVALID);
2577        if (res) {
2578                GError *parse_error;
2579
2580                g_debug ("GdmSessionWorker: start program: %s", text);
2581
2582                if (worker->priv->arguments != NULL) {
2583                        g_strfreev (worker->priv->arguments);
2584                        worker->priv->arguments = NULL;
2585                }
2586
2587                parse_error = NULL;
2588                if (! g_shell_parse_argv (text, NULL, &worker->priv->arguments, &parse_error)) {
2589                        g_warning ("Unable to parse command: %s", parse_error->message);
2590                        g_error_free (parse_error);
2591                        return;
2592                }
2593
2594                queue_state_change (worker);
2595        } else {
2596                g_warning ("Unable to get arguments: %s", error.message);
2597                dbus_error_free (&error);
2598        }
2599}
2600
2601static void
2602on_setup (GdmSessionWorker *worker,
2603          DBusMessage      *message)
2604{
2605        DBusError   error;
2606        const char *service;
2607        const char *x11_display_name;
2608        const char *x11_authority_file;
2609        const char *console;
2610        const char *hostname;
2611        dbus_bool_t res;
2612
2613        if (worker->priv->state != GDM_SESSION_WORKER_STATE_NONE) {
2614                g_debug ("GdmSessionWorker: ignoring spurious setup while in state %s", get_state_name (worker->priv->state));
2615                return;
2616        }
2617
2618        dbus_error_init (&error);
2619        res = dbus_message_get_args (message,
2620                                     &error,
2621                                     DBUS_TYPE_STRING, &service,
2622                                     DBUS_TYPE_STRING, &x11_display_name,
2623                                     DBUS_TYPE_STRING, &console,
2624                                     DBUS_TYPE_STRING, &hostname,
2625                                     DBUS_TYPE_STRING, &x11_authority_file,
2626                                     DBUS_TYPE_INVALID);
2627        if (res) {
2628                worker->priv->service = g_strdup (service);
2629                worker->priv->x11_display_name = g_strdup (x11_display_name);
2630                worker->priv->x11_authority_file = g_strdup (x11_authority_file);
2631                worker->priv->display_device = g_strdup (console);
2632                worker->priv->hostname = g_strdup (hostname);
2633                worker->priv->username = NULL;
2634
2635                g_debug ("GdmSessionWorker: queuing setup: %s %s", service, console);
2636                queue_state_change (worker);
2637        } else {
2638                g_warning ("Unable to get arguments: %s", error.message);
2639                dbus_error_free (&error);
2640        }
2641}
2642
2643static void
2644on_setup_for_user (GdmSessionWorker *worker,
2645                   DBusMessage      *message)
2646{
2647        DBusError   error;
2648        const char *service;
2649        const char *x11_display_name;
2650        const char *x11_authority_file;
2651        const char *console;
2652        const char *hostname;
2653        const char *username;
2654        dbus_bool_t res;
2655
2656        if (worker->priv->state != GDM_SESSION_WORKER_STATE_NONE) {
2657                g_debug ("GdmSessionWorker: ignoring spurious setup for user while in state %s", get_state_name (worker->priv->state));
2658                return;
2659        }
2660
2661        dbus_error_init (&error);
2662        res = dbus_message_get_args (message,
2663                                     &error,
2664                                     DBUS_TYPE_STRING, &service,
2665                                     DBUS_TYPE_STRING, &x11_display_name,
2666                                     DBUS_TYPE_STRING, &console,
2667                                     DBUS_TYPE_STRING, &hostname,
2668                                     DBUS_TYPE_STRING, &x11_authority_file,
2669                                     DBUS_TYPE_STRING, &username,
2670                                     DBUS_TYPE_INVALID);
2671        if (res) {
2672                worker->priv->service = g_strdup (service);
2673                worker->priv->x11_display_name = g_strdup (x11_display_name);
2674                worker->priv->x11_authority_file = g_strdup (x11_authority_file);
2675                worker->priv->display_device = g_strdup (console);
2676                worker->priv->hostname = g_strdup (hostname);
2677                worker->priv->username = g_strdup (username);
2678
2679                g_debug ("GdmSessionWorker: queuing setup for user: %s %s", service, console);
2680                queue_state_change (worker);
2681        } else {
2682                g_warning ("Unable to get arguments: %s", error.message);
2683                dbus_error_free (&error);
2684        }
2685}
2686
2687static void
2688on_authenticate (GdmSessionWorker *worker,
2689                 DBusMessage      *message)
2690{
2691        if (worker->priv->state != GDM_SESSION_WORKER_STATE_SETUP_COMPLETE) {
2692                g_debug ("GdmSessionWorker: ignoring spurious authenticate for user while in state %s", get_state_name (worker->priv->state));
2693                return;
2694        }
2695
2696        queue_state_change (worker);
2697}
2698
2699static void
2700on_authorize (GdmSessionWorker *worker,
2701              DBusMessage      *message)
2702{
2703        if (worker->priv->state != GDM_SESSION_WORKER_STATE_AUTHENTICATED) {
2704                g_debug ("GdmSessionWorker: ignoring spurious authorize for user while in state %s", get_state_name (worker->priv->state));
2705                return;
2706        }
2707
2708        queue_state_change (worker);
2709}
2710
2711static void
2712on_establish_credentials (GdmSessionWorker *worker,
2713                          DBusMessage      *message)
2714{
2715        if (worker->priv->state != GDM_SESSION_WORKER_STATE_AUTHORIZED) {
2716                g_debug ("GdmSessionWorker: ignoring spurious establish credentials for user while in state %s", get_state_name (worker->priv->state));
2717                return;
2718        }
2719
2720        worker->priv->cred_flags = PAM_ESTABLISH_CRED;
2721
2722        queue_state_change (worker);
2723}
2724
2725static void
2726on_reauthenticate (GdmSessionWorker *worker,
2727                   DBusMessage      *message)
2728{
2729        if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_STARTED) {
2730                g_debug ("GdmSessionWorker: ignoring spurious reauthenticate for user while in state %s", get_state_name (worker->priv->state));
2731                return;
2732        }
2733
2734        queue_state_change (worker);
2735}
2736
2737static void
2738on_reauthorize (GdmSessionWorker *worker,
2739                DBusMessage      *message)
2740{
2741        if (worker->priv->state != GDM_SESSION_WORKER_STATE_REAUTHENTICATED) {
2742                g_debug ("GdmSessionWorker: ignoring spurious reauthorize for user while in state %s", get_state_name (worker->priv->state));
2743                return;
2744        }
2745
2746        queue_state_change (worker);
2747}
2748
2749static void
2750on_refresh_credentials (GdmSessionWorker *worker,
2751                        DBusMessage      *message)
2752{
2753        int error_code;
2754
2755        if (worker->priv->state != GDM_SESSION_WORKER_STATE_REAUTHORIZED) {
2756                g_debug ("GdmSessionWorker: ignoring spurious refreshing credentials for user while in state %s", get_state_name (worker->priv->state));
2757                return;
2758        }
2759
2760        g_debug ("GdmSessionWorker: refreshing credentials");
2761
2762        error_code = pam_setcred (worker->priv->pam_handle, PAM_REFRESH_CRED);
2763        if (error_code != PAM_SUCCESS) {
2764                g_debug ("GdmSessionWorker: %s", pam_strerror (worker->priv->pam_handle, error_code));
2765        }
2766}
2767
2768static DBusHandlerResult
2769worker_dbus_handle_message (DBusConnection *connection,
2770                            DBusMessage    *message,
2771                            void           *user_data,
2772                            dbus_bool_t     local_interface)
2773{
2774        GdmSessionWorker *worker = GDM_SESSION_WORKER (user_data);
2775
2776#if 0
2777        g_message ("obj_path=%s interface=%s method=%s destination=%s",
2778                   dbus_message_get_path (message),
2779                   dbus_message_get_interface (message),
2780                   dbus_message_get_member (message),
2781                   dbus_message_get_destination (message));
2782#endif
2783
2784        g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
2785        g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
2786
2787        if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Setup")) {
2788                on_setup (worker, message);
2789        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "SetupForUser")) {
2790                on_setup_for_user (worker, message);
2791        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Authenticate")) {
2792                on_authenticate (worker, message);
2793        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Authorize")) {
2794                on_authorize (worker, message);
2795        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "EstablishCredentials")) {
2796                on_establish_credentials (worker, message);
2797        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "StartProgram")) {
2798                on_start_program (worker, message);
2799        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Reauthenticate")) {
2800                on_reauthenticate (worker, message);
2801        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Reauthorize")) {
2802                on_reauthorize (worker, message);
2803        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "RefreshCredentials")) {
2804                on_refresh_credentials (worker, message);
2805        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "SetEnvironmentVariable")) {
2806                on_set_environment_variable (worker, message);
2807        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "SetLanguageName")) {
2808                on_set_language_name (worker, message);
2809        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "SetLayoutName")) {
2810                on_set_layout_name (worker, message);
2811        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "SetSessionName")) {
2812                on_set_session_name (worker, message);
2813        } else {
2814                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2815        }
2816
2817        return DBUS_HANDLER_RESULT_HANDLED;
2818}
2819
2820static DBusHandlerResult
2821worker_dbus_filter_function (DBusConnection *connection,
2822                             DBusMessage    *message,
2823                             void           *user_data)
2824{
2825        GdmSessionWorker *worker = GDM_SESSION_WORKER (user_data);
2826        const char       *path;
2827
2828        path = dbus_message_get_path (message);
2829
2830        g_debug ("GdmSessionWorker: obj_path=%s interface=%s method=%s",
2831                 dbus_message_get_path (message),
2832                 dbus_message_get_interface (message),
2833                 dbus_message_get_member (message));
2834
2835        if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
2836            && strcmp (path, DBUS_PATH_LOCAL) == 0) {
2837
2838                g_debug ("GdmSessionWorker: Got disconnected from the server");
2839
2840                dbus_connection_unref (connection);
2841                worker->priv->connection = NULL;
2842
2843        } else if (dbus_message_is_signal (message,
2844                                           DBUS_INTERFACE_DBUS,
2845                                           "NameOwnerChanged")) {
2846                g_debug ("GdmSessionWorker: Name owner changed?");
2847        } else {
2848                return worker_dbus_handle_message (connection, message, user_data, FALSE);
2849        }
2850
2851        return DBUS_HANDLER_RESULT_HANDLED;
2852}
2853
2854static GObject *
2855gdm_session_worker_constructor (GType                  type,
2856                                guint                  n_construct_properties,
2857                                GObjectConstructParam *construct_properties)
2858{
2859        GdmSessionWorker      *worker;
2860        DBusError              error;
2861
2862        worker = GDM_SESSION_WORKER (G_OBJECT_CLASS (gdm_session_worker_parent_class)->constructor (type,
2863                                                                                                    n_construct_properties,
2864                                                                                                    construct_properties));
2865
2866        g_debug ("GdmSessionWorker: connecting to address: %s", worker->priv->server_address);
2867
2868        dbus_error_init (&error);
2869        worker->priv->connection = dbus_connection_open (worker->priv->server_address, &error);
2870        if (worker->priv->connection == NULL) {
2871                if (dbus_error_is_set (&error)) {
2872                        g_warning ("error opening connection: %s", error.message);
2873                        dbus_error_free (&error);
2874                } else {
2875                        g_warning ("Unable to open connection");
2876                }
2877                exit (1);
2878        }
2879
2880        dbus_connection_setup_with_g_main (worker->priv->connection, NULL);
2881        dbus_connection_set_exit_on_disconnect (worker->priv->connection, TRUE);
2882
2883        dbus_connection_add_filter (worker->priv->connection,
2884                                    worker_dbus_filter_function,
2885                                    worker,
2886                                    NULL);
2887
2888        return G_OBJECT (worker);
2889}
2890
2891static void
2892gdm_session_worker_class_init (GdmSessionWorkerClass *klass)
2893{
2894        GObjectClass    *object_class = G_OBJECT_CLASS (klass);
2895
2896        object_class->get_property = gdm_session_worker_get_property;
2897        object_class->set_property = gdm_session_worker_set_property;
2898        object_class->constructor = gdm_session_worker_constructor;
2899        object_class->finalize = gdm_session_worker_finalize;
2900
2901        g_type_class_add_private (klass, sizeof (GdmSessionWorkerPrivate));
2902
2903        g_object_class_install_property (object_class,
2904                                         PROP_SERVER_ADDRESS,
2905                                         g_param_spec_string ("server-address",
2906                                                              "server address",
2907                                                              "server address",
2908                                                              NULL,
2909                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
2910}
2911
2912static void
2913gdm_session_worker_init (GdmSessionWorker *worker)
2914{
2915
2916        worker->priv = GDM_SESSION_WORKER_GET_PRIVATE (worker);
2917        worker->priv->environment = g_hash_table_new_full (g_str_hash,
2918                                                           g_str_equal,
2919                                                           (GDestroyNotify) g_free,
2920                                                           (GDestroyNotify) g_free);
2921}
2922
2923static void
2924gdm_session_worker_unwatch_child (GdmSessionWorker *worker)
2925{
2926        if (worker->priv->child_watch_id == 0)
2927                return;
2928
2929        g_source_remove (worker->priv->child_watch_id);
2930        worker->priv->child_watch_id = 0;
2931}
2932
2933
2934static void
2935gdm_session_worker_finalize (GObject *object)
2936{
2937        GdmSessionWorker *worker;
2938
2939        g_return_if_fail (object != NULL);
2940        g_return_if_fail (GDM_IS_SESSION_WORKER (object));
2941
2942        worker = GDM_SESSION_WORKER (object);
2943
2944        g_return_if_fail (worker->priv != NULL);
2945
2946        gdm_session_worker_unwatch_child (worker);
2947
2948        if (worker->priv->username != NULL) {
2949                g_free (worker->priv->username);
2950                worker->priv->username = NULL;
2951        }
2952
2953        if (worker->priv->arguments != NULL) {
2954                g_strfreev (worker->priv->arguments);
2955                worker->priv->arguments = NULL;
2956        }
2957
2958        if (worker->priv->environment != NULL) {
2959                g_hash_table_destroy (worker->priv->environment);
2960                worker->priv->environment = NULL;
2961        }
2962
2963        G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object);
2964}
2965
2966GdmSessionWorker *
2967gdm_session_worker_new (const char *address)
2968{
2969        GObject *object;
2970
2971        object = g_object_new (GDM_TYPE_SESSION_WORKER,
2972                               "server-address", address,
2973                               NULL);
2974
2975        return GDM_SESSION_WORKER (object);
2976}
2977
Note: See TracBrowser for help on using the repository browser.