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 typedef struct
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 } XAuthorityPrivate;
41 
G_DEFINE_TYPE_WITH_PRIVATE(XAuthority,x_authority,G_TYPE_OBJECT)42 G_DEFINE_TYPE_WITH_PRIVATE (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 *
x_authority_new_cookie(guint16 family,const guint8 * address,gsize address_length,const gchar * number)59 x_authority_new_cookie (guint16 family, const guint8 *address, gsize address_length, const gchar *number)
60 {
61     guint8 cookie[16];
62     for (gint i = 0; i < 16; i++)
63         cookie[i] = g_random_int () & 0xFF;
64 
65     return x_authority_new (family, address, address_length, number, "MIT-MAGIC-COOKIE-1", cookie, 16);
66 }
67 
68 XAuthority *
x_authority_new_local_cookie(const gchar * number)69 x_authority_new_local_cookie (const gchar *number)
70 {
71     gchar hostname[1024];
72     gethostname (hostname, 1024);
73     return x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8 *) hostname, strlen (hostname), number);
74 }
75 
76 void
x_authority_set_family(XAuthority * auth,guint16 family)77 x_authority_set_family (XAuthority *auth, guint16 family)
78 {
79     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
80     g_return_if_fail (auth != NULL);
81     priv->family = family;
82 }
83 
84 guint16
x_authority_get_family(XAuthority * auth)85 x_authority_get_family (XAuthority *auth)
86 {
87     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
88     g_return_val_if_fail (auth != NULL, 0);
89     return priv->family;
90 }
91 
92 void
x_authority_set_address(XAuthority * auth,const guint8 * address,gsize address_length)93 x_authority_set_address (XAuthority *auth, const guint8 *address, gsize address_length)
94 {
95     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
96     g_return_if_fail (auth != NULL);
97     g_free (priv->address);
98     priv->address = g_malloc (address_length);
99     memcpy (priv->address, address, address_length);
100     priv->address_length = address_length;
101 }
102 
103 const guint8 *
x_authority_get_address(XAuthority * auth)104 x_authority_get_address (XAuthority *auth)
105 {
106     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
107     g_return_val_if_fail (auth != NULL, NULL);
108     return priv->address;
109 }
110 
111 gsize
x_authority_get_address_length(XAuthority * auth)112 x_authority_get_address_length (XAuthority *auth)
113 {
114     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
115     g_return_val_if_fail (auth != NULL, 0);
116     return priv->address_length;
117 }
118 
119 void
x_authority_set_number(XAuthority * auth,const gchar * number)120 x_authority_set_number (XAuthority *auth, const gchar *number)
121 {
122     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
123     g_return_if_fail (auth != NULL);
124     g_free (priv->number);
125     priv->number = g_strdup (number);
126 }
127 
128 const gchar *
x_authority_get_number(XAuthority * auth)129 x_authority_get_number (XAuthority *auth)
130 {
131     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
132     g_return_val_if_fail (auth != NULL, NULL);
133     return priv->number;
134 }
135 
136 void
x_authority_set_authorization_name(XAuthority * auth,const gchar * name)137 x_authority_set_authorization_name (XAuthority *auth, const gchar *name)
138 {
139     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
140     g_return_if_fail (auth != NULL);
141     g_free (priv->authorization_name);
142     priv->authorization_name = g_strdup (name);
143 }
144 
145 const gchar *
x_authority_get_authorization_name(XAuthority * auth)146 x_authority_get_authorization_name (XAuthority *auth)
147 {
148     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
149     g_return_val_if_fail (auth != NULL, NULL);
150     return priv->authorization_name;
151 }
152 
153 void
x_authority_set_authorization_data(XAuthority * auth,const guint8 * data,gsize data_length)154 x_authority_set_authorization_data (XAuthority *auth, const guint8 *data, gsize data_length)
155 {
156     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
157     g_return_if_fail (auth != NULL);
158     g_free (priv->authorization_data);
159     priv->authorization_data = g_malloc (data_length);
160     memcpy (priv->authorization_data, data, data_length);
161     priv->authorization_data_length = data_length;
162 }
163 
164 const guint8 *
x_authority_get_authorization_data(XAuthority * auth)165 x_authority_get_authorization_data (XAuthority *auth)
166 {
167     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
168     g_return_val_if_fail (auth != NULL, NULL);
169     return priv->authorization_data;
170 }
171 
172 guint8 *
x_authority_copy_authorization_data(XAuthority * auth)173 x_authority_copy_authorization_data (XAuthority *auth)
174 {
175     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
176 
177     g_return_val_if_fail (auth != NULL, NULL);
178 
179     guint8 *data = g_malloc (priv->authorization_data_length);
180     memcpy (data, priv->authorization_data, priv->authorization_data_length);
181     return data;
182 }
183 
184 gsize
x_authority_get_authorization_data_length(XAuthority * auth)185 x_authority_get_authorization_data_length (XAuthority *auth)
186 {
187     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
188     g_return_val_if_fail (auth != NULL, 0);
189     return priv->authorization_data_length;
190 }
191 
192 static gboolean
read_uint16(gchar * data,gsize data_length,gsize * offset,guint16 * value)193 read_uint16 (gchar *data, gsize data_length, gsize *offset, guint16 *value)
194 {
195     if (data_length - *offset < 2)
196         return FALSE;
197 
198     *value = data[*offset] << 8 | data[*offset + 1];
199     *offset += 2;
200 
201     return TRUE;
202 }
203 
204 static gboolean
read_data(gchar * data,gsize data_length,gsize * offset,guint16 length,guint8 ** value)205 read_data (gchar *data, gsize data_length, gsize *offset, guint16 length, guint8 **value)
206 {
207     g_free (*value);
208     *value = NULL;
209 
210     if (data_length - *offset < length)
211         return FALSE;
212 
213     *value = g_malloc0 (length + 1);
214     for (int i = 0; i < length; i++)
215         (*value)[i] = data[*offset + i];
216     *offset += length;
217     (*value)[length] = 0;
218 
219     return TRUE;
220 }
221 
222 static gboolean
read_string(gchar * data,gsize data_length,gsize * offset,gchar ** value)223 read_string (gchar *data, gsize data_length, gsize *offset, gchar **value)
224 {
225     guint16 length;
226     if (!read_uint16 (data, data_length, offset, &length))
227         return FALSE;
228     return read_data (data, data_length, offset, length, (guint8 **) value);
229 }
230 
231 static gboolean
write_uint16(int fd,guint16 value)232 write_uint16 (int fd, guint16 value)
233 {
234     guint8 v[2];
235     v[0] = value >> 8;
236     v[1] = value & 0xFF;
237     return write (fd, v, 2) == 2;
238 }
239 
240 static gboolean
write_data(int fd,const guint8 * value,gsize value_length)241 write_data (int fd, const guint8 *value, gsize value_length)
242 {
243     return write (fd, value, value_length) == value_length;
244 }
245 
246 static gboolean
write_string(int fd,const gchar * value)247 write_string (int fd, const gchar *value)
248 {
249     size_t value_length = strlen (value);
250     return write_uint16 (fd, value_length) && write_data (fd, (guint8 *) value, value_length);
251 }
252 
253 gboolean
x_authority_write(XAuthority * auth,XAuthWriteMode mode,const gchar * filename,GError ** error)254 x_authority_write (XAuthority *auth, XAuthWriteMode mode, const gchar *filename, GError **error)
255 {
256     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
257 
258     g_return_val_if_fail (auth != NULL, FALSE);
259     g_return_val_if_fail (filename != NULL, FALSE);
260 
261     /* Read out existing records */
262     g_autofree gchar *input = NULL;
263     gsize input_length = 0, input_offset = 0;
264     if (mode != XAUTH_WRITE_MODE_SET)
265     {
266         g_autoptr(GError) read_error = NULL;
267         g_file_get_contents (filename, &input, &input_length, &read_error);
268         if (read_error && !g_error_matches (read_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
269             g_warning ("Error reading existing Xauthority: %s", read_error->message);
270     }
271     GList *records = NULL;
272     gboolean matched = FALSE;
273     while (input_offset != input_length)
274     {
275         g_autoptr(XAuthority) a = g_object_new (X_AUTHORITY_TYPE, NULL);
276         XAuthorityPrivate *a_priv = x_authority_get_instance_private (a);
277 
278         guint16 address_length = 0;
279         guint16 authorization_data_length = 0;
280         gboolean result = read_uint16 (input, input_length, &input_offset, &a_priv->family) &&
281                           read_uint16 (input, input_length, &input_offset, &address_length) &&
282                           read_data (input, input_length, &input_offset, address_length, &a_priv->address) &&
283                           read_string (input, input_length, &input_offset, &a_priv->number) &&
284                           read_string (input, input_length, &input_offset, &a_priv->authorization_name) &&
285                           read_uint16 (input, input_length, &input_offset, &authorization_data_length) &&
286                           read_data (input, input_length, &input_offset, authorization_data_length, &a_priv->authorization_data);
287         a_priv->address_length = address_length;
288         a_priv->authorization_data_length = authorization_data_length;
289 
290         if (!result)
291             break;
292 
293         gboolean address_matches = FALSE;
294         if (priv->address_length == a_priv->address_length)
295         {
296             guint16 i;
297             for (i = 0; i < priv->address_length && priv->address[i] == a_priv->address[i]; i++);
298             address_matches = i == priv->address_length;
299         }
300 
301         /* If this record matches, then update or delete it */
302         if (!matched &&
303             priv->family == a_priv->family &&
304             address_matches &&
305             strcmp (priv->number, a_priv->number) == 0)
306         {
307             matched = TRUE;
308             if (mode == XAUTH_WRITE_MODE_REMOVE)
309                 continue;
310             else
311                 x_authority_set_authorization_data (a, priv->authorization_data, priv->authorization_data_length);
312         }
313 
314         records = g_list_append (records, g_steal_pointer (&a));
315     }
316 
317     /* If didn't exist, then add a new one */
318     if (!matched)
319         records = g_list_append (records, g_object_ref (auth));
320 
321     /* Write records back */
322     errno = 0;
323     int output_fd = g_open (filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
324     if (output_fd < 0)
325     {
326         g_set_error (error,
327                      G_FILE_ERROR,
328                      g_file_error_from_errno (errno),
329                      "Failed to open X authority %s: %s",
330                      filename,
331                      g_strerror (errno));
332         return FALSE;
333     }
334 
335     errno = 0;
336     gboolean result = TRUE;
337     for (GList *link = records; link && result; link = link->next)
338     {
339         XAuthority *a = link->data;
340         XAuthorityPrivate *a_priv = x_authority_get_instance_private (a);
341 
342         result = write_uint16 (output_fd, a_priv->family) &&
343                  write_uint16 (output_fd, a_priv->address_length) &&
344                  write_data (output_fd, a_priv->address, a_priv->address_length) &&
345                  write_string (output_fd, a_priv->number) &&
346                  write_string (output_fd, a_priv->authorization_name) &&
347                  write_uint16 (output_fd, a_priv->authorization_data_length) &&
348                  write_data (output_fd, a_priv->authorization_data, a_priv->authorization_data_length);
349     }
350     g_list_free_full (records, g_object_unref);
351 
352     fsync (output_fd);
353     close (output_fd);
354 
355     if (!result)
356     {
357         g_set_error (error,
358                      G_FILE_ERROR,
359                      g_file_error_from_errno (errno),
360                      "Failed to write X authority %s: %s",
361                      filename,
362                      g_strerror (errno));
363         return FALSE;
364     }
365 
366     return TRUE;
367 }
368 
369 static void
x_authority_init(XAuthority * auth)370 x_authority_init (XAuthority *auth)
371 {
372     XAuthorityPrivate *priv = x_authority_get_instance_private (auth);
373     priv->number = g_strdup ("");
374 }
375 
376 static void
x_authority_finalize(GObject * object)377 x_authority_finalize (GObject *object)
378 {
379     XAuthority *self = X_AUTHORITY (object);
380     XAuthorityPrivate *priv = x_authority_get_instance_private (self);
381 
382     g_clear_pointer (&priv->address, g_free);
383     g_clear_pointer (&priv->number, g_free);
384     g_clear_pointer (&priv->authorization_name, g_free);
385     g_clear_pointer (&priv->authorization_data, g_free);
386 
387     G_OBJECT_CLASS (x_authority_parent_class)->finalize (object);
388 }
389 
390 static void
x_authority_class_init(XAuthorityClass * klass)391 x_authority_class_init (XAuthorityClass *klass)
392 {
393     GObjectClass *object_class = G_OBJECT_CLASS (klass);
394 
395     object_class->finalize = x_authority_finalize;
396 }
397