]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-authority.c
Load all users only when really needed
[sojka/lightdm.git] / src / x-authority.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <string.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <glib/gstdio.h>
19
20 #include "x-authority.h"
21
22 struct XAuthorityPrivate
23 {
24     /* Protocol family */
25     guint16 family;
26
27     /* Address of the X server (format dependent on family) */
28     guint8 *address;
29     gsize address_length;
30
31     /* Display number of X server */
32     gchar *number;
33
34     /* Authorization scheme */
35     gchar *authorization_name;
36
37     /* Authorization data */
38     guint8 *authorization_data;
39     gsize authorization_data_length;
40 };
41
42 G_DEFINE_TYPE (XAuthority, x_authority, G_TYPE_OBJECT);
43
44 XAuthority *
45 x_authority_new (guint16 family, const guint8 *address, gsize address_length, const gchar *number, const gchar *name, const guint8 *data, gsize data_length)
46 {
47     XAuthority *auth = g_object_new (X_AUTHORITY_TYPE, NULL);
48
49     x_authority_set_family (auth, family);
50     x_authority_set_address (auth, address, address_length);
51     x_authority_set_number (auth, number);
52     x_authority_set_authorization_name (auth, name);
53     x_authority_set_authorization_data (auth, data, data_length);
54
55     return auth;
56 }
57
58 XAuthority *
59 x_authority_new_cookie (guint16 family, const guint8 *address, gsize address_length, const gchar *number)
60 {
61     guint8 cookie[16];
62     gint i;
63
64     for (i = 0; i < 16; i++)
65         cookie[i] = g_random_int () & 0xFF;
66
67     return x_authority_new (family, address, address_length, number, "MIT-MAGIC-COOKIE-1", cookie, 16);
68 }
69
70 XAuthority *
71 x_authority_new_local_cookie (const gchar *number)
72 {
73     gchar hostname[1024];
74
75     gethostname (hostname, 1024);
76     return x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8 *) hostname, strlen (hostname), number);
77 }
78
79 void
80 x_authority_set_family (XAuthority *auth, guint16 family)
81 {
82     g_return_if_fail (auth != NULL);
83     auth->priv->family = family;
84 }
85
86 guint16
87 x_authority_get_family (XAuthority *auth)
88 {
89     g_return_val_if_fail (auth != NULL, 0);
90     return auth->priv->family;
91 }
92
93 void
94 x_authority_set_address (XAuthority *auth, const guint8 *address, gsize address_length)
95 {
96     g_return_if_fail (auth != NULL);
97     g_free (auth->priv->address);
98     auth->priv->address = g_malloc (address_length);
99     memcpy (auth->priv->address, address, address_length);
100     auth->priv->address_length = address_length;
101 }
102
103 const guint8 *
104 x_authority_get_address (XAuthority *auth)
105 {
106     g_return_val_if_fail (auth != NULL, NULL);
107     return auth->priv->address;
108 }
109
110 const gsize
111 x_authority_get_address_length (XAuthority *auth)
112 {
113     g_return_val_if_fail (auth != NULL, 0);
114     return auth->priv->address_length;
115 }
116
117 void
118 x_authority_set_number (XAuthority *auth, const gchar *number)
119 {
120     g_return_if_fail (auth != NULL);
121     g_free (auth->priv->number);
122     auth->priv->number = g_strdup (number);
123 }
124
125 const gchar *
126 x_authority_get_number (XAuthority *auth)
127 {
128     g_return_val_if_fail (auth != NULL, NULL);
129     return auth->priv->number;
130 }
131
132 void
133 x_authority_set_authorization_name (XAuthority *auth, const gchar *name)
134 {
135     g_return_if_fail (auth != NULL);
136     g_free (auth->priv->authorization_name);
137     auth->priv->authorization_name = g_strdup (name);
138 }
139
140 const gchar *
141 x_authority_get_authorization_name (XAuthority *auth)
142 {
143     g_return_val_if_fail (auth != NULL, NULL);
144     return auth->priv->authorization_name;
145 }
146
147 void
148 x_authority_set_authorization_data (XAuthority *auth, const guint8 *data, gsize data_length)
149 {
150     g_return_if_fail (auth != NULL);
151     g_free (auth->priv->authorization_data);
152     auth->priv->authorization_data = g_malloc (data_length);
153     memcpy (auth->priv->authorization_data, data, data_length);
154     auth->priv->authorization_data_length = data_length;
155 }
156
157 const guint8 *
158 x_authority_get_authorization_data (XAuthority *auth)
159 {
160     g_return_val_if_fail (auth != NULL, NULL);
161     return auth->priv->authorization_data;
162 }
163
164 guint8 *
165 x_authority_copy_authorization_data (XAuthority *auth)
166 {
167     guint8 *data;
168
169     g_return_val_if_fail (auth != NULL, NULL);
170
171     data = g_malloc (auth->priv->authorization_data_length);
172     memcpy (data, auth->priv->authorization_data, auth->priv->authorization_data_length);
173     return data;
174 }
175
176 gsize
177 x_authority_get_authorization_data_length (XAuthority *auth)
178 {
179     g_return_val_if_fail (auth != NULL, 0);
180     return auth->priv->authorization_data_length;
181 }
182
183 static gboolean
184 read_uint16 (gchar *data, gsize data_length, gsize *offset, guint16 *value)
185 {
186     if (data_length - *offset < 2)
187         return FALSE;
188
189     *value = data[*offset] << 8 | data[*offset + 1];
190     *offset += 2;
191
192     return TRUE;
193 }
194
195 static gboolean
196 read_data (gchar *data, gsize data_length, gsize *offset, guint16 length, guint8 **value)
197 {
198     int i;
199
200     g_free (*value);
201     *value = NULL;
202
203     if (data_length - *offset < length)
204         return FALSE;
205
206     *value = g_malloc0 (length + 1);
207     for (i = 0; i < length; i++)
208         (*value)[i] = data[*offset + i];
209     *offset += length;
210     (*value)[length] = 0;
211
212     return TRUE;
213 }
214
215 static gboolean
216 read_string (gchar *data, gsize data_length, gsize *offset, gchar **value)
217 {
218     guint16 length;
219     if (!read_uint16 (data, data_length, offset, &length))
220         return FALSE;
221     return read_data (data, data_length, offset, length, (guint8 **) value);
222 }
223
224 static gboolean
225 write_uint16 (int fd, guint16 value)
226 {
227     guint8 v[2];
228     v[0] = value >> 8;
229     v[1] = value & 0xFF;
230     return write (fd, v, 2) == 2;
231 }
232
233 static gboolean
234 write_data (int fd, const guint8 *value, gsize value_length)
235 {
236     return write (fd, value, value_length) == value_length;
237 }
238
239 static gboolean
240 write_string (int fd, const gchar *value)
241 {
242     size_t value_length = strlen (value);
243     return write_uint16 (fd, value_length) && write_data (fd, (guint8 *) value, value_length);
244 }
245
246 gboolean
247 x_authority_write (XAuthority *auth, XAuthWriteMode mode, const gchar *filename, GError **error)
248 {
249     gchar *input = NULL;
250     gsize input_length = 0, input_offset = 0;
251     GList *link, *records = NULL;
252     XAuthority *a;
253     gboolean result = TRUE;
254     gboolean matched = FALSE;
255     int output_fd;
256
257     g_return_val_if_fail (auth != NULL, FALSE);
258     g_return_val_if_fail (filename != NULL, FALSE);
259
260     /* Read out existing records */
261     if (mode != XAUTH_WRITE_MODE_SET)
262     {
263         GError *read_error = NULL;
264
265         g_file_get_contents (filename, &input, &input_length, &read_error);
266         if (read_error && !g_error_matches (read_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
267             g_warning ("Error reading existing Xauthority: %s", read_error->message);
268         g_clear_error (&read_error);
269     }
270     while (input_offset != input_length)
271     {
272         gboolean address_matches = FALSE;
273         guint16 address_length = 0;
274         guint16 authorization_data_length = 0;
275
276         a = g_object_new (X_AUTHORITY_TYPE, NULL);
277
278         result = read_uint16 (input, input_length, &input_offset, &a->priv->family) &&
279                  read_uint16 (input, input_length, &input_offset, &address_length) &&
280                  read_data (input, input_length, &input_offset, address_length, &a->priv->address) &&
281                  read_string (input, input_length, &input_offset, &a->priv->number) &&
282                  read_string (input, input_length, &input_offset, &a->priv->authorization_name) &&
283                  read_uint16 (input, input_length, &input_offset, &authorization_data_length) &&
284                  read_data (input, input_length, &input_offset, authorization_data_length, &a->priv->authorization_data);
285         a->priv->address_length = address_length;
286         a->priv->authorization_data_length = authorization_data_length;
287
288         if (!result)
289         {
290             g_object_unref (a);
291             break;
292         }
293
294         if (auth->priv->address_length == a->priv->address_length)
295         {
296             guint16 i;
297             for (i = 0; i < auth->priv->address_length && auth->priv->address[i] == a->priv->address[i]; i++);
298             address_matches = i == auth->priv->address_length;
299         }
300
301         /* If this record matches, then update or delete it */
302         if (!matched &&
303             auth->priv->family == a->priv->family &&
304             address_matches &&
305             strcmp (auth->priv->number, a->priv->number) == 0)
306         {
307             matched = TRUE;
308             if (mode == XAUTH_WRITE_MODE_REMOVE)
309             {
310                 g_object_unref (a);
311                 continue;
312             }
313             else
314                 x_authority_set_authorization_data (a, auth->priv->authorization_data, auth->priv->authorization_data_length);
315         }
316
317         records = g_list_append (records, a);
318     }
319     g_free (input);
320
321     /* If didn't exist, then add a new one */
322     if (!matched)
323         records = g_list_append (records, g_object_ref (auth));
324
325     /* Write records back */
326     errno = 0;
327     output_fd = g_open (filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
328     if (output_fd < 0)
329     {
330         g_set_error (error,
331                      G_FILE_ERROR,
332                      g_file_error_from_errno (errno),
333                      "Failed to open X authority %s: %s",
334                      filename,
335                      g_strerror (errno));
336         return FALSE;
337     }
338
339     errno = 0;
340     result = TRUE;
341     for (link = records; link && result; link = link->next)
342     {
343         XAuthority *a = link->data;
344
345         result = write_uint16 (output_fd, a->priv->family) &&
346                  write_uint16 (output_fd, a->priv->address_length) &&
347                  write_data (output_fd, a->priv->address, a->priv->address_length) &&
348                  write_string (output_fd, a->priv->number) &&
349                  write_string (output_fd, a->priv->authorization_name) &&
350                  write_uint16 (output_fd, a->priv->authorization_data_length) &&
351                  write_data (output_fd, a->priv->authorization_data, a->priv->authorization_data_length);
352
353         g_object_unref (a);
354     }
355     g_list_free (records);
356
357     fsync (output_fd);
358     close (output_fd);
359
360     if (!result)
361     {
362         g_set_error (error,
363                      G_FILE_ERROR,
364                      g_file_error_from_errno (errno),
365                      "Failed to write X authority %s: %s",
366                      filename,
367                      g_strerror (errno));
368         return FALSE;
369     }
370
371     return TRUE;
372 }
373
374 static void
375 x_authority_init (XAuthority *auth)
376 {
377     auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, X_AUTHORITY_TYPE, XAuthorityPrivate);
378     auth->priv->number = g_strdup ("");
379 }
380
381 static void
382 x_authority_finalize (GObject *object)
383 {
384     XAuthority *self = X_AUTHORITY (object);
385
386     g_free (self->priv->address);
387     g_free (self->priv->number);
388     g_free (self->priv->authorization_name);
389     g_free (self->priv->authorization_data);
390
391     G_OBJECT_CLASS (x_authority_parent_class)->finalize (object);
392 }
393
394 static void
395 x_authority_class_init (XAuthorityClass *klass)
396 {
397     GObjectClass *object_class = G_OBJECT_CLASS (klass);
398
399     object_class->finalize = x_authority_finalize;
400
401     g_type_class_add_private (klass, sizeof (XAuthorityPrivate));
402 }