source: proiecte/PPPP/gdm/daemon/gdm-display-access-file.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: 21.2 KB
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * gdm-display-access-file.c - Abstraction around xauth cookies
4 *
5 * Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22
23#include "config.h"
24
25#include <errno.h>
26#include <limits.h>
27#include <pwd.h>
28#include <string.h>
29#include <sys/types.h>
30#include <unistd.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33
34#include <glib.h>
35#include <glib-object.h>
36#include <glib/gstdio.h>
37#include <glib/gi18n.h>
38
39#include <X11/Xauth.h>
40
41#include "gdm-display-access-file.h"
42#include "gdm-common.h"
43
44struct _GdmDisplayAccessFilePrivate
45{
46        char *username;
47        FILE *fp;
48        char *path;
49};
50
51#ifndef GDM_DISPLAY_ACCESS_COOKIE_SIZE
52#define GDM_DISPLAY_ACCESS_COOKIE_SIZE 16
53#endif
54
55#ifndef O_BINARY
56#define O_BINARY 0
57#endif
58
59static void gdm_display_access_file_finalize (GObject * object);
60
61enum
62{
63        PROP_0 = 0,
64        PROP_USERNAME,
65        PROP_PATH
66};
67
68G_DEFINE_TYPE (GdmDisplayAccessFile, gdm_display_access_file, G_TYPE_OBJECT)
69
70static void
71gdm_display_access_file_get_property (GObject    *object,
72                                      guint       prop_id,
73                                      GValue     *value,
74                                      GParamSpec *pspec)
75{
76        GdmDisplayAccessFile *access_file;
77
78        access_file = GDM_DISPLAY_ACCESS_FILE (object);
79
80        switch (prop_id) {
81            case PROP_USERNAME:
82                g_value_set_string (value, access_file->priv->username);
83                break;
84
85            case PROP_PATH:
86                g_value_set_string (value, access_file->priv->path);
87                break;
88
89            default:
90                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
91        }
92}
93
94static void
95gdm_display_access_file_set_property (GObject      *object,
96                                      guint         prop_id,
97                                      const GValue *value,
98                                      GParamSpec   *pspec)
99{
100        GdmDisplayAccessFile *access_file;
101
102        access_file = GDM_DISPLAY_ACCESS_FILE (object);
103
104        switch (prop_id) {
105            case PROP_USERNAME:
106                g_assert (access_file->priv->username == NULL);
107                access_file->priv->username = g_value_dup_string (value);
108                break;
109
110            default:
111                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
112        }
113}
114
115static void
116gdm_display_access_file_class_init (GdmDisplayAccessFileClass *access_file_class)
117{
118        GObjectClass *object_class;
119        GParamSpec   *param_spec;
120
121        object_class = G_OBJECT_CLASS (access_file_class);
122
123        object_class->finalize = gdm_display_access_file_finalize;
124        object_class->get_property = gdm_display_access_file_get_property;
125        object_class->set_property = gdm_display_access_file_set_property;
126
127        param_spec = g_param_spec_string ("username",
128                                          "Username",
129                                          "Owner of Xauthority file",
130                                          NULL,
131                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
132        g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
133        param_spec = g_param_spec_string ("path",
134                                          "Path",
135                                          "Path to Xauthority file",
136                                          NULL,
137                                          G_PARAM_READABLE);
138        g_object_class_install_property (object_class, PROP_PATH, param_spec);
139        g_type_class_add_private (access_file_class, sizeof (GdmDisplayAccessFilePrivate));
140}
141
142static void
143gdm_display_access_file_init (GdmDisplayAccessFile *access_file)
144{
145        access_file->priv = G_TYPE_INSTANCE_GET_PRIVATE (access_file,
146                                                         GDM_TYPE_DISPLAY_ACCESS_FILE,
147                                                         GdmDisplayAccessFilePrivate);
148}
149
150static void
151gdm_display_access_file_finalize (GObject *object)
152{
153        GdmDisplayAccessFile *file;
154        GObjectClass *parent_class;
155
156        file = GDM_DISPLAY_ACCESS_FILE (object);
157        parent_class = G_OBJECT_CLASS (gdm_display_access_file_parent_class);
158
159        if (file->priv->fp != NULL) {
160            gdm_display_access_file_close (file);
161        }
162        g_assert (file->priv->path == NULL);
163
164        if (file->priv->username != NULL) {
165                g_free (file->priv->username);
166                file->priv->username = NULL;
167                g_object_notify (object, "username");
168        }
169
170        if (parent_class->finalize != NULL) {
171                parent_class->finalize (object);
172        }
173}
174
175GQuark
176gdm_display_access_file_error_quark (void)
177{
178        static GQuark error_quark = 0;
179
180        if (error_quark == 0) {
181                error_quark = g_quark_from_static_string ("gdm-display-access-file");
182        }
183
184        return error_quark;
185}
186
187GdmDisplayAccessFile *
188gdm_display_access_file_new (const char *username)
189{
190        GdmDisplayAccessFile *access_file;
191        g_return_val_if_fail (username != NULL, NULL);
192
193        access_file = g_object_new (GDM_TYPE_DISPLAY_ACCESS_FILE,
194                                    "username", username,
195                                    NULL);
196
197        return access_file;
198}
199
200static gboolean
201_get_uid_and_gid_for_user (const char *username,
202                           uid_t      *uid,
203                           gid_t      *gid)
204{
205        struct passwd *passwd_entry;
206
207        g_assert (username != NULL);
208        g_assert (uid != NULL);
209        g_assert (gid != NULL);
210
211        errno = 0;
212        passwd_entry = getpwnam (username);
213
214        if (passwd_entry == NULL) {
215                return FALSE;
216        }
217
218        *uid = passwd_entry->pw_uid;
219        *gid = passwd_entry->pw_gid;
220
221        return TRUE;
222}
223
224static void
225clean_up_stale_auth_subdirs (void)
226{
227        GDir *dir;
228        const char *filename;
229
230        dir = g_dir_open (GDM_XAUTH_DIR, 0, NULL);
231
232        if (dir == NULL) {
233                return;
234        }
235
236        while ((filename = g_dir_read_name (dir)) != NULL) {
237                char *path;
238
239                path = g_build_filename (GDM_XAUTH_DIR, filename, NULL);
240
241                /* Will only succeed if the directory is empty
242                 */
243                g_rmdir (path);
244                g_free (path);
245        }
246        g_dir_close (dir);
247}
248
249static FILE *
250_create_xauth_file_for_user (const char  *username,
251                             char       **filename,
252                             GError     **error)
253{
254        char   *template;
255        const char *dir_name;
256        char   *auth_filename;
257        int     fd;
258        FILE   *fp;
259        uid_t   uid;
260        gid_t   gid;
261
262        g_assert (filename != NULL);
263
264        *filename = NULL;
265
266        template = NULL;
267        auth_filename = NULL;
268        fp = NULL;
269        fd = -1;
270
271        /* Create directory if not exist, then set permission 01775 and ownership root:gdm */
272        if (g_file_test (GDM_XAUTH_DIR, G_FILE_TEST_IS_DIR) == FALSE) {
273                g_unlink (GDM_XAUTH_DIR);
274                if (g_mkdir (GDM_XAUTH_DIR, S_ISVTX | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
275                        g_set_error (error,
276                                     G_FILE_ERROR,
277                                     g_file_error_from_errno (errno),
278                                     "%s", g_strerror (errno));
279                        goto out;
280                }
281
282                g_chmod (GDM_XAUTH_DIR, S_ISVTX | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
283                _get_uid_and_gid_for_user (GDM_USERNAME, &uid, &gid);
284                if (chown (GDM_XAUTH_DIR, 0, gid) != 0) {
285                        g_warning ("Unable to change owner of '%s'",
286                                   GDM_XAUTH_DIR);
287                }
288        } else {
289                /* if it does exist make sure it has correct mode 01775 */
290                g_chmod (GDM_XAUTH_DIR, S_ISVTX | S_IRWXU |S_IRWXG | S_IROTH | S_IXOTH);
291
292                /* and clean up any stale auth subdirs */
293                clean_up_stale_auth_subdirs ();
294        }
295
296        if (!_get_uid_and_gid_for_user (username, &uid, &gid)) {
297                g_set_error (error,
298                             GDM_DISPLAY_ERROR,
299                             GDM_DISPLAY_ERROR_GETTING_USER_INFO,
300                             _("could not find user \"%s\" on system"),
301                             username);
302                goto out;
303
304        }
305
306        template = g_strdup_printf (GDM_XAUTH_DIR
307                                    "/auth-for-%s-XXXXXX",
308                                    username);
309
310        g_debug ("GdmDisplayAccessFile: creating xauth directory %s", template);
311        /* Initially create with mode 01700 then later chmod after we create database */
312        errno = 0;
313        dir_name = gdm_make_temp_dir (template);
314        if (dir_name == NULL) {
315                g_set_error (error,
316                             G_FILE_ERROR,
317                             g_file_error_from_errno (errno),
318                             "Unable to create temp dir from tempalte '%s': %s",
319                             template,
320                             g_strerror (errno));
321                goto out;
322        }
323
324        g_debug ("GdmDisplayAccessFile: chowning %s to %u:%u",
325                 dir_name, (guint)uid, (guint)gid);
326        errno = 0;
327        if (chown (dir_name, uid, gid) < 0) {
328                g_set_error (error,
329                             G_FILE_ERROR,
330                             g_file_error_from_errno (errno),
331                             "Unable to change permission of '%s': %s",
332                             dir_name,
333                             g_strerror (errno));
334                goto out;
335        }
336
337        auth_filename = g_build_filename (dir_name, "database", NULL);
338
339        g_debug ("GdmDisplayAccessFile: creating %s", auth_filename);
340        /* mode 00600 */
341        errno = 0;
342        fd = g_open (auth_filename,
343                     O_RDWR | O_CREAT | O_EXCL | O_BINARY,
344                     S_IRUSR | S_IWUSR);
345
346        if (fd < 0) {
347                g_set_error (error,
348                             G_FILE_ERROR,
349                             g_file_error_from_errno (errno),
350                             "Unable to open '%s': %s",
351                             auth_filename,
352                             g_strerror (errno));
353                goto out;
354        }
355
356        g_debug ("GdmDisplayAccessFile: chowning %s to %u:%u", auth_filename, (guint)uid, (guint)gid);
357        errno = 0;
358        if (fchown (fd, uid, gid) < 0) {
359                g_set_error (error,
360                             G_FILE_ERROR,
361                             g_file_error_from_errno (errno),
362                             "Unable to change owner for '%s': %s",
363                             auth_filename,
364                             g_strerror (errno));
365                close (fd);
366                fd = -1;
367                goto out;
368        }
369
370        /* now open up permissions on per-session directory */
371        g_debug ("GdmDisplayAccessFile: chmoding %s to 1777", dir_name);
372        g_chmod (dir_name, S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
373
374        errno = 0;
375        fp = fdopen (fd, "w");
376        if (fp == NULL) {
377                g_set_error (error,
378                             G_FILE_ERROR,
379                             g_file_error_from_errno (errno),
380                             "%s", g_strerror (errno));
381                close (fd);
382                fd = -1;
383                goto out;
384        }
385
386        *filename = auth_filename;
387        auth_filename = NULL;
388
389        /* don't close it */
390        fd = -1;
391out:
392        g_free (template);
393        g_free (auth_filename);
394        if (fd != -1) {
395                close (fd);
396        }
397
398        return fp;
399}
400
401gboolean
402gdm_display_access_file_open (GdmDisplayAccessFile  *file,
403                              GError               **error)
404{
405        GError *create_error;
406
407        g_return_val_if_fail (file != NULL, FALSE);
408        g_return_val_if_fail (file->priv->fp == NULL, FALSE);
409        g_return_val_if_fail (file->priv->path == NULL, FALSE);
410
411        create_error = NULL;
412        file->priv->fp = _create_xauth_file_for_user (file->priv->username,
413                                                      &file->priv->path,
414                                                      &create_error);
415
416        if (file->priv->fp == NULL) {
417                g_propagate_error (error, create_error);
418                return FALSE;
419        }
420
421        return TRUE;
422}
423
424static void
425_get_auth_info_for_display (GdmDisplayAccessFile *file,
426                            GdmDisplay           *display,
427                            unsigned short       *family,
428                            unsigned short       *address_length,
429                            char                **address,
430                            unsigned short       *number_length,
431                            char                **number,
432                            unsigned short       *name_length,
433                            char                **name)
434{
435        int display_number;
436        gboolean is_local;
437
438        gdm_display_is_local (display, &is_local, NULL);
439
440        if (is_local) {
441                char localhost[HOST_NAME_MAX + 1] = "";
442                *family = FamilyLocal;
443                if (gethostname (localhost, HOST_NAME_MAX) == 0) {
444                        *address = g_strdup (localhost);
445                } else {
446                        *address = g_strdup ("localhost");
447                }
448        } else {
449                *family = FamilyWild;
450                gdm_display_get_remote_hostname (display, address, NULL);
451        }
452        *address_length = strlen (*address);
453
454        gdm_display_get_x11_display_number (display, &display_number, NULL);
455        *number = g_strdup_printf ("%d", display_number);
456        *number_length = strlen (*number);
457
458        *name = g_strdup ("MIT-MAGIC-COOKIE-1");
459        *name_length = strlen (*name);
460}
461
462gboolean
463gdm_display_access_file_add_display (GdmDisplayAccessFile  *file,
464                                     GdmDisplay            *display,
465                                     char                 **cookie,
466                                     gsize                 *cookie_size,
467                                     GError               **error)
468{
469        GError  *add_error;
470        gboolean display_added;
471
472        g_return_val_if_fail (file != NULL, FALSE);
473        g_return_val_if_fail (file->priv->path != NULL, FALSE);
474        g_return_val_if_fail (cookie != NULL, FALSE);
475
476        add_error = NULL;
477        *cookie = gdm_generate_random_bytes (GDM_DISPLAY_ACCESS_COOKIE_SIZE,
478                                             &add_error);
479
480        if (*cookie == NULL) {
481                g_propagate_error (error, add_error);
482                return FALSE;
483        }
484
485        *cookie_size = GDM_DISPLAY_ACCESS_COOKIE_SIZE;
486
487        display_added = gdm_display_access_file_add_display_with_cookie (file, display,
488                                                                         *cookie,
489                                                                         *cookie_size,
490                                                                         &add_error);
491        if (!display_added) {
492                g_free (*cookie);
493                *cookie = NULL;
494                g_propagate_error (error, add_error);
495                return FALSE;
496        }
497
498        return TRUE;
499}
500
501gboolean
502gdm_display_access_file_add_display_with_cookie (GdmDisplayAccessFile  *file,
503                                                 GdmDisplay            *display,
504                                                 const char            *cookie,
505                                                 gsize                  cookie_size,
506                                                 GError               **error)
507{
508        Xauth auth_entry;
509        gboolean display_added;
510
511        g_return_val_if_fail (file != NULL, FALSE);
512        g_return_val_if_fail (file->priv->path != NULL, FALSE);
513        g_return_val_if_fail (cookie != NULL, FALSE);
514
515        _get_auth_info_for_display (file, display,
516                                    &auth_entry.family,
517                                    &auth_entry.address_length,
518                                    &auth_entry.address,
519                                    &auth_entry.number_length,
520                                    &auth_entry.number,
521                                    &auth_entry.name_length,
522                                    &auth_entry.name);
523
524        auth_entry.data = (char *) cookie;
525        auth_entry.data_length = cookie_size;
526
527        /* FIXME: We should lock the file in case the X server is
528         * trying to use it, too.
529         */
530        if (!XauWriteAuth (file->priv->fp, &auth_entry)
531            || fflush (file->priv->fp) == EOF) {
532                g_set_error (error,
533                        G_FILE_ERROR,
534                        g_file_error_from_errno (errno),
535                        "%s", g_strerror (errno));
536                display_added = FALSE;
537        } else {
538                display_added = TRUE;
539        }
540
541
542        g_free (auth_entry.address);
543        g_free (auth_entry.number);
544        g_free (auth_entry.name);
545
546        return display_added;
547}
548
549gboolean
550gdm_display_access_file_remove_display (GdmDisplayAccessFile  *file,
551                                        GdmDisplay            *display,
552                                        GError               **error)
553{
554        Xauth           *auth_entry;
555        unsigned short  family;
556        unsigned short  address_length;
557        char           *address;
558        unsigned short  number_length;
559        char           *number;
560        unsigned short  name_length;
561        char           *name;
562
563
564        g_return_val_if_fail (file != NULL, FALSE);
565        g_return_val_if_fail (file->priv->path != NULL, FALSE);
566
567        _get_auth_info_for_display (file, display,
568                                    &family,
569                                    &address_length,
570                                    &address,
571                                    &number_length,
572                                    &number,
573                                    &name_length,
574                                    &name);
575
576        auth_entry = XauGetAuthByAddr (family,
577                                       address_length,
578                                       address,
579                                       number_length,
580                                       number,
581                                       name_length,
582                                       name);
583        g_free (address);
584        g_free (number);
585        g_free (name);
586
587        if (auth_entry == NULL) {
588                g_set_error (error,
589                             GDM_DISPLAY_ACCESS_FILE_ERROR,
590                             GDM_DISPLAY_ACCESS_FILE_ERROR_FINDING_AUTH_ENTRY,
591                             "could not find authorization entry");
592                return FALSE;
593        }
594
595        XauDisposeAuth (auth_entry);
596
597        if (fflush (file->priv->fp) == EOF) {
598                g_set_error (error,
599                             G_FILE_ERROR,
600                             g_file_error_from_errno (errno),
601                             "%s", g_strerror (errno));
602                return FALSE;
603        }
604
605        return TRUE;
606}
607
608void
609gdm_display_access_file_close (GdmDisplayAccessFile  *file)
610{
611        char *auth_dir;
612
613        g_return_if_fail (file != NULL);
614        g_return_if_fail (file->priv->fp != NULL);
615        g_return_if_fail (file->priv->path != NULL);
616
617        errno = 0;
618        if (g_unlink (file->priv->path) != 0) {
619                g_warning ("GdmDisplayAccessFile: Unable to remove X11 authority database '%s': %s",
620                           file->priv->path,
621                           g_strerror (errno));
622        }
623
624        /* still try to remove dir even if file remove failed,
625           may have already been removed by someone else */
626        /* we own the parent directory too */
627        auth_dir = g_path_get_dirname (file->priv->path);
628        if (auth_dir != NULL) {
629                errno = 0;
630                if (g_rmdir (auth_dir) != 0) {
631                        g_warning ("GdmDisplayAccessFile: Unable to remove X11 authority directory '%s': %s",
632                                   auth_dir,
633                                   g_strerror (errno));
634                }
635                g_free (auth_dir);
636        }
637
638        g_free (file->priv->path);
639        file->priv->path = NULL;
640        g_object_notify (G_OBJECT (file), "path");
641
642        fclose (file->priv->fp);
643        file->priv->fp = NULL;
644}
645
646char *
647gdm_display_access_file_get_path (GdmDisplayAccessFile *access_file)
648{
649        return g_strdup (access_file->priv->path);
650}
Note: See TracBrowser for help on using the repository browser.