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