Branch data Line data Source code
1 : : /* GDBus - GLib D-Bus Library
2 : : *
3 : : * Copyright (C) 2008-2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library 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 GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <string.h>
26 : : #include <fcntl.h>
27 : : #include <errno.h>
28 : : #include <sys/types.h>
29 : :
30 : : #include <glib/gstdio.h>
31 : :
32 : : #ifdef G_OS_UNIX
33 : : #include <unistd.h>
34 : : #endif
35 : : #ifdef G_OS_WIN32
36 : : #include <io.h>
37 : : #include "gwin32sid.h"
38 : : #endif
39 : :
40 : : #ifndef O_CLOEXEC
41 : : #define O_CLOEXEC 0
42 : : #endif
43 : :
44 : : #include "gdbusauthmechanismsha1.h"
45 : : #include "gcredentials.h"
46 : : #include "gdbuserror.h"
47 : : #include "glocalfileinfo.h"
48 : : #include "gioenumtypes.h"
49 : : #include "gioerror.h"
50 : : #include "gdbusprivate.h"
51 : : #include "glib-private.h"
52 : :
53 : : #include "glibintl.h"
54 : :
55 : : /*
56 : : * Arbitrary timeouts for keys in the keyring.
57 : : * For interoperability, these match the reference implementation, libdbus.
58 : : * To make them easier to compare, their names also match libdbus
59 : : * (see dbus/dbus-keyring.c).
60 : : */
61 : :
62 : : /*
63 : : * Maximum age of a key before we create a new key to use in challenges:
64 : : * 5 minutes.
65 : : */
66 : : #define NEW_KEY_TIMEOUT_SECONDS (60*5)
67 : :
68 : : /*
69 : : * Time before we drop a key from the keyring: 7 minutes.
70 : : * Authentication will succeed if it takes less than
71 : : * EXPIRE_KEYS_TIMEOUT_SECONDS - NEW_KEY_TIMEOUT_SECONDS (2 minutes)
72 : : * to complete.
73 : : * The spec says "delete any cookies that are old (the timeout can be
74 : : * fairly short)".
75 : : */
76 : : #define EXPIRE_KEYS_TIMEOUT_SECONDS (NEW_KEY_TIMEOUT_SECONDS + (60*2))
77 : :
78 : : /*
79 : : * Maximum amount of time a key can be in the future due to clock skew
80 : : * with a shared home directory: 5 minutes.
81 : : * The spec says "a reasonable time in the future".
82 : : */
83 : : #define MAX_TIME_TRAVEL_SECONDS (60*5)
84 : :
85 : :
86 : : struct _GDBusAuthMechanismSha1Private
87 : : {
88 : : gboolean is_client;
89 : : gboolean is_server;
90 : : GDBusAuthMechanismState state;
91 : : gchar *reject_reason; /* non-NULL iff (state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED) */
92 : :
93 : : /* used on the client side */
94 : : gchar *to_send;
95 : :
96 : : /* used on the server side */
97 : : gchar *cookie;
98 : : gchar *server_challenge;
99 : : };
100 : :
101 : : static gint mechanism_get_priority (void);
102 : : static const gchar *mechanism_get_name (void);
103 : :
104 : : static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
105 : : static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
106 : : const gchar *data,
107 : : gsize data_len,
108 : : gsize *out_data_len);
109 : : static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
110 : : const gchar *data,
111 : : gsize data_len,
112 : : gsize *out_data_len);
113 : : static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
114 : : static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
115 : : const gchar *initial_response,
116 : : gsize initial_response_len);
117 : : static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
118 : : const gchar *data,
119 : : gsize data_len);
120 : : static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
121 : : gsize *out_data_len);
122 : : static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism);
123 : : static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
124 : : static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
125 : : static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
126 : : GDBusConnectionFlags conn_flags,
127 : : gsize *out_initial_response_len);
128 : : static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
129 : : const gchar *data,
130 : : gsize data_len);
131 : : static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
132 : : gsize *out_data_len);
133 : : static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
134 : :
135 : : /* ---------------------------------------------------------------------------------------------------- */
136 : :
137 : 4753 : G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM)
138 : :
139 : : /* ---------------------------------------------------------------------------------------------------- */
140 : :
141 : : static void
142 : 92 : _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
143 : : {
144 : 92 : GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
145 : :
146 : 92 : g_free (mechanism->priv->reject_reason);
147 : 92 : g_free (mechanism->priv->to_send);
148 : :
149 : 92 : g_free (mechanism->priv->cookie);
150 : 92 : g_free (mechanism->priv->server_challenge);
151 : :
152 : 92 : if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL)
153 : 92 : G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object);
154 : 92 : }
155 : :
156 : : static void
157 : 101 : _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
158 : : {
159 : : GObjectClass *gobject_class;
160 : : GDBusAuthMechanismClass *mechanism_class;
161 : :
162 : 101 : gobject_class = G_OBJECT_CLASS (klass);
163 : 101 : gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
164 : :
165 : 101 : mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
166 : 101 : mechanism_class->get_priority = mechanism_get_priority;
167 : 101 : mechanism_class->get_name = mechanism_get_name;
168 : 101 : mechanism_class->is_supported = mechanism_is_supported;
169 : 101 : mechanism_class->encode_data = mechanism_encode_data;
170 : 101 : mechanism_class->decode_data = mechanism_decode_data;
171 : 101 : mechanism_class->server_get_state = mechanism_server_get_state;
172 : 101 : mechanism_class->server_initiate = mechanism_server_initiate;
173 : 101 : mechanism_class->server_data_receive = mechanism_server_data_receive;
174 : 101 : mechanism_class->server_data_send = mechanism_server_data_send;
175 : 101 : mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason;
176 : 101 : mechanism_class->server_shutdown = mechanism_server_shutdown;
177 : 101 : mechanism_class->client_get_state = mechanism_client_get_state;
178 : 101 : mechanism_class->client_initiate = mechanism_client_initiate;
179 : 101 : mechanism_class->client_data_receive = mechanism_client_data_receive;
180 : 101 : mechanism_class->client_data_send = mechanism_client_data_send;
181 : 101 : mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason;
182 : 101 : mechanism_class->client_shutdown = mechanism_client_shutdown;
183 : 101 : }
184 : :
185 : : static void
186 : 92 : _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
187 : : {
188 : 92 : mechanism->priv = _g_dbus_auth_mechanism_sha1_get_instance_private (mechanism);
189 : 92 : }
190 : :
191 : : /* ---------------------------------------------------------------------------------------------------- */
192 : :
193 : : static gint
194 : 3200 : mechanism_get_priority (void)
195 : : {
196 : 3200 : return 0;
197 : : }
198 : :
199 : : static const gchar *
200 : 3314 : mechanism_get_name (void)
201 : : {
202 : 3314 : return "DBUS_COOKIE_SHA1";
203 : : }
204 : :
205 : : static gboolean
206 : 24 : mechanism_is_supported (GDBusAuthMechanism *mechanism)
207 : : {
208 : 24 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
209 : 24 : return TRUE;
210 : : }
211 : :
212 : : static gchar *
213 : 0 : mechanism_encode_data (GDBusAuthMechanism *mechanism,
214 : : const gchar *data,
215 : : gsize data_len,
216 : : gsize *out_data_len)
217 : : {
218 : 0 : return NULL;
219 : : }
220 : :
221 : :
222 : : static gchar *
223 : 0 : mechanism_decode_data (GDBusAuthMechanism *mechanism,
224 : : const gchar *data,
225 : : gsize data_len,
226 : : gsize *out_data_len)
227 : : {
228 : 0 : return NULL;
229 : : }
230 : :
231 : : /* ---------------------------------------------------------------------------------------------------- */
232 : :
233 : : static gint
234 : 1232 : random_ascii (void)
235 : : {
236 : : gint ret;
237 : 1232 : ret = g_random_int_range (0, 60);
238 : 1232 : if (ret < 25)
239 : 498 : ret += 'A';
240 : 734 : else if (ret < 50)
241 : 529 : ret += 'a' - 25;
242 : : else
243 : 205 : ret += '0' - 50;
244 : 1232 : return ret;
245 : : }
246 : :
247 : : static gchar *
248 : 77 : random_ascii_string (guint len)
249 : : {
250 : : GString *challenge;
251 : : guint n;
252 : :
253 : 77 : challenge = g_string_new (NULL);
254 : 1309 : for (n = 0; n < len; n++)
255 : 1232 : g_string_append_c (challenge, random_ascii ());
256 : 77 : return g_string_free (challenge, FALSE);
257 : : }
258 : :
259 : : static gchar *
260 : 7 : random_blob (guint len)
261 : : {
262 : : GString *challenge;
263 : : guint n;
264 : :
265 : 7 : challenge = g_string_new (NULL);
266 : 231 : for (n = 0; n < len; n++)
267 : 224 : g_string_append_c (challenge, g_random_int_range (0, 256));
268 : 7 : return g_string_free (challenge, FALSE);
269 : : }
270 : :
271 : : /* ---------------------------------------------------------------------------------------------------- */
272 : :
273 : : /* ensure keyring dir exists and permissions are correct */
274 : : static gchar *
275 : 78 : ensure_keyring_directory (GError **error)
276 : : {
277 : : gchar *path;
278 : : const gchar *e;
279 : : #ifdef G_OS_UNIX
280 : : struct stat statbuf;
281 : : #endif
282 : :
283 : 78 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
284 : :
285 : : /* If running as setuid, we can’t trust the environment (as it’s provided by
286 : : * a user running at a lower privilege level), so we can’t check
287 : : * $G_DBUS_COOKIE_SHA1_KEYRING_DIR *and* we can’t build it using $HOME.
288 : : * Just bail out in that case. */
289 : 78 : if (GLIB_PRIVATE_CALL (g_check_setuid) ())
290 : : {
291 : 0 : g_set_error_literal (error,
292 : : G_IO_ERROR,
293 : : G_IO_ERROR_PERMISSION_DENIED,
294 : : _("Error accessing D-Bus keyring: keyring-based "
295 : : "authentication is not supported when running as setuid"));
296 : 0 : return NULL;
297 : : }
298 : :
299 : 78 : e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
300 : 78 : if (e != NULL)
301 : : {
302 : 4 : path = g_strdup (e);
303 : : }
304 : : else
305 : : {
306 : 74 : path = g_build_filename (g_get_home_dir (),
307 : : ".dbus-keyrings",
308 : : NULL);
309 : : }
310 : :
311 : : #ifdef G_OS_UNIX
312 : 78 : if (stat (path, &statbuf) != 0)
313 : : {
314 : 7 : int errsv = errno;
315 : :
316 : 7 : if (errsv != ENOENT)
317 : : {
318 : 0 : g_set_error (error,
319 : : G_IO_ERROR,
320 : 0 : g_io_error_from_errno (errsv),
321 : : _("Error when getting information for directory “%s”: %s"),
322 : : path,
323 : : g_strerror (errsv));
324 : 0 : g_clear_pointer (&path, g_free);
325 : 0 : return NULL;
326 : : }
327 : : }
328 : 71 : else if (S_ISDIR (statbuf.st_mode))
329 : : {
330 : 71 : if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL &&
331 : 67 : (statbuf.st_mode & 0777) != 0700)
332 : : {
333 : 0 : g_set_error (error,
334 : : G_IO_ERROR,
335 : : G_IO_ERROR_FAILED,
336 : : _("Permissions on directory “%s” are malformed. Expected mode 0700, got 0%o"),
337 : : path,
338 : 0 : (guint) (statbuf.st_mode & 0777));
339 : 0 : g_clear_pointer (&path, g_free);
340 : 0 : return NULL;
341 : : }
342 : :
343 : 71 : return g_steal_pointer (&path);
344 : : }
345 : : #else /* if !G_OS_UNIX */
346 : : /* On non-Unix platforms, check that it exists as a directory, but don’t do
347 : : * permissions checks at the moment. */
348 : : if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
349 : : {
350 : : #ifdef __GNUC__
351 : : #pragma GCC diagnostic push
352 : : #pragma GCC diagnostic warning "-Wcpp"
353 : : #warning Please implement permission checking on this non-UNIX platform
354 : : #pragma GCC diagnostic pop
355 : : #endif /* __GNUC__ */
356 : : return g_steal_pointer (&path);
357 : : }
358 : : #endif /* if !G_OS_UNIX */
359 : :
360 : : /* Only create the directory if not running as setuid */
361 : 7 : if (g_mkdir_with_parents (path, 0700) != 0)
362 : : {
363 : 0 : int errsv = errno;
364 : 0 : g_set_error (error,
365 : : G_IO_ERROR,
366 : 0 : g_io_error_from_errno (errsv),
367 : : _("Error creating directory “%s”: %s"),
368 : : path,
369 : : g_strerror (errsv));
370 : 0 : g_clear_pointer (&path, g_free);
371 : 0 : return NULL;
372 : : }
373 : :
374 : 7 : return g_steal_pointer (&path);
375 : : }
376 : :
377 : : /* ---------------------------------------------------------------------------------------------------- */
378 : :
379 : : /* looks up an entry in the keyring */
380 : : static gchar *
381 : 10 : keyring_lookup_entry (const gchar *cookie_context,
382 : : gint cookie_id,
383 : : GError **error)
384 : : {
385 : : gchar *ret;
386 : : gchar *keyring_dir;
387 : : gchar *contents;
388 : : gchar *path;
389 : : guint n;
390 : : gchar **lines;
391 : :
392 : 10 : g_return_val_if_fail (cookie_context != NULL, NULL);
393 : 10 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
394 : :
395 : 10 : ret = NULL;
396 : 10 : path = NULL;
397 : 10 : contents = NULL;
398 : 10 : lines = NULL;
399 : :
400 : 10 : keyring_dir = ensure_keyring_directory (error);
401 : 10 : if (keyring_dir == NULL)
402 : 0 : goto out;
403 : :
404 : 10 : path = g_build_filename (keyring_dir, cookie_context, NULL);
405 : :
406 : 10 : if (!g_file_get_contents (path,
407 : : &contents,
408 : : NULL,
409 : : error))
410 : : {
411 : 1 : g_prefix_error (error,
412 : : _("Error opening keyring “%s” for reading: "),
413 : : path);
414 : 1 : goto out;
415 : : }
416 : 9 : g_assert (contents != NULL);
417 : :
418 : 9 : lines = g_strsplit (contents, "\n", 0);
419 : 9 : for (n = 0; lines[n] != NULL; n++)
420 : : {
421 : 9 : const gchar *line = lines[n];
422 : : gchar **tokens;
423 : : gchar *endp;
424 : : gint line_id;
425 : :
426 : 9 : if (line[0] == '\0')
427 : 0 : continue;
428 : :
429 : 9 : tokens = g_strsplit (line, " ", 0);
430 : 9 : if (g_strv_length (tokens) != 3)
431 : : {
432 : 0 : g_set_error (error,
433 : : G_IO_ERROR,
434 : : G_IO_ERROR_FAILED,
435 : : _("Line %d of the keyring at “%s” with content “%s” is malformed"),
436 : : n + 1,
437 : : path,
438 : : line);
439 : 0 : g_strfreev (tokens);
440 : 9 : goto out;
441 : : }
442 : :
443 : 9 : line_id = g_ascii_strtoll (tokens[0], &endp, 10);
444 : 9 : if (*endp != '\0')
445 : : {
446 : 0 : g_set_error (error,
447 : : G_IO_ERROR,
448 : : G_IO_ERROR_FAILED,
449 : : _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
450 : : n + 1,
451 : : path,
452 : : line);
453 : 0 : g_strfreev (tokens);
454 : 0 : goto out;
455 : : }
456 : :
457 : 9 : (void)g_ascii_strtoll (tokens[1], &endp, 10); /* do not care what the timestamp is */
458 : 9 : if (*endp != '\0')
459 : : {
460 : 0 : g_set_error (error,
461 : : G_IO_ERROR,
462 : : G_IO_ERROR_FAILED,
463 : : _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
464 : : n + 1,
465 : : path,
466 : : line);
467 : 0 : g_strfreev (tokens);
468 : 0 : goto out;
469 : : }
470 : :
471 : 9 : if (line_id == cookie_id)
472 : : {
473 : : /* YAY, success */
474 : 9 : ret = tokens[2]; /* steal pointer */
475 : 9 : tokens[2] = NULL;
476 : 9 : g_strfreev (tokens);
477 : 9 : goto out;
478 : : }
479 : :
480 : 0 : g_strfreev (tokens);
481 : : }
482 : :
483 : : /* BOOH, didn't find the cookie */
484 : 0 : g_set_error (error,
485 : : G_IO_ERROR,
486 : : G_IO_ERROR_FAILED,
487 : : _("Didn’t find cookie with id %d in the keyring at “%s”"),
488 : : cookie_id,
489 : : path);
490 : :
491 : 10 : out:
492 : 10 : g_free (keyring_dir);
493 : 10 : g_free (path);
494 : 10 : g_free (contents);
495 : 10 : g_strfreev (lines);
496 : 10 : return ret;
497 : : }
498 : :
499 : : /* function for logging important events that the system administrator should take notice of */
500 : : G_GNUC_PRINTF(1, 2)
501 : : static void
502 : 0 : _log (const gchar *message,
503 : : ...)
504 : : {
505 : : gchar *s;
506 : : va_list var_args;
507 : :
508 : 0 : va_start (var_args, message);
509 : 0 : s = g_strdup_vprintf (message, var_args);
510 : 0 : va_end (var_args);
511 : :
512 : : /* TODO: might want to send this to syslog instead */
513 : 0 : g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
514 : 0 : g_free (s);
515 : 0 : }
516 : :
517 : : /* Returns FD for lock file, if it was created exclusively (didn't exist already,
518 : : * and was created successfully) */
519 : : static gint
520 : 68 : create_lock_exclusive (const gchar *lock_path,
521 : : gint64 *mtime_nsec,
522 : : GError **error)
523 : : {
524 : : int errsv;
525 : : gint ret;
526 : :
527 : 68 : ret = g_open (lock_path, O_CREAT | O_EXCL | O_CLOEXEC, 0600);
528 : 68 : errsv = errno;
529 : 68 : if (ret < 0)
530 : : {
531 : : GLocalFileStat stat_buf;
532 : :
533 : : /* Get the modification time to distinguish between the lock being stale
534 : : * or highly contested. */
535 : 0 : if (mtime_nsec != NULL &&
536 : 0 : g_local_file_stat (lock_path, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &stat_buf) == 0)
537 : 0 : *mtime_nsec = _g_stat_mtime (&stat_buf) * G_USEC_PER_SEC * 1000 + _g_stat_mtim_nsec (&stat_buf);
538 : 0 : else if (mtime_nsec != NULL)
539 : 0 : *mtime_nsec = 0;
540 : :
541 : 0 : g_set_error (error,
542 : : G_IO_ERROR,
543 : 0 : g_io_error_from_errno (errsv),
544 : : _("Error creating lock file “%s”: %s"),
545 : : lock_path,
546 : : g_strerror (errsv));
547 : 0 : return -1;
548 : : }
549 : :
550 : 68 : return ret;
551 : : }
552 : :
553 : : static gint
554 : 68 : keyring_acquire_lock (const gchar *path,
555 : : GError **error)
556 : : {
557 : 68 : gchar *lock = NULL;
558 : : gint ret;
559 : : guint num_tries;
560 : : int errsv;
561 : 68 : gint64 lock_mtime_nsec = 0, lock_mtime_nsec_prev = 0;
562 : :
563 : : /* Total possible sleep period = max_tries * timeout_usec = 0.5s */
564 : 68 : const guint max_tries = 50;
565 : 68 : const guint timeout_usec = 1000 * 10;
566 : :
567 : 68 : g_return_val_if_fail (path != NULL, -1);
568 : 68 : g_return_val_if_fail (error == NULL || *error == NULL, -1);
569 : :
570 : 68 : ret = -1;
571 : 68 : lock = g_strconcat (path, ".lock", NULL);
572 : :
573 : : /* This is what the D-Bus spec says
574 : : * (https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha)
575 : : *
576 : : * Create a lockfile name by appending ".lock" to the name of the
577 : : * cookie file. The server should attempt to create this file using
578 : : * O_CREAT | O_EXCL. If file creation fails, the lock
579 : : * fails. Servers should retry for a reasonable period of time,
580 : : * then they may choose to delete an existing lock to keep users
581 : : * from having to manually delete a stale lock. [1]
582 : : *
583 : : * [1] : Lockfiles are used instead of real file locking fcntl() because
584 : : * real locking implementations are still flaky on network filesystems
585 : : */
586 : :
587 : 68 : for (num_tries = 0; num_tries < max_tries; num_tries++)
588 : : {
589 : 68 : lock_mtime_nsec_prev = lock_mtime_nsec;
590 : :
591 : : /* Ignore the error until the final call. */
592 : 68 : ret = create_lock_exclusive (lock, &lock_mtime_nsec, NULL);
593 : 68 : if (ret >= 0)
594 : 68 : break;
595 : :
596 : : /* sleep 10ms, then try again */
597 : 0 : g_usleep (timeout_usec);
598 : :
599 : : /* If the mtime of the lock file changed, don’t count the retry, as it
600 : : * seems like there’s contention between processes for the lock file,
601 : : * rather than a stale lock file from a crashed process. */
602 : 0 : if (num_tries > 0 && lock_mtime_nsec != lock_mtime_nsec_prev)
603 : 0 : num_tries--;
604 : : }
605 : :
606 : 68 : if (num_tries == max_tries)
607 : : {
608 : : /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
609 : : * stale (nuke it from orbit)
610 : : */
611 : 0 : if (g_unlink (lock) != 0)
612 : : {
613 : 0 : errsv = errno;
614 : 0 : g_set_error (error,
615 : : G_IO_ERROR,
616 : 0 : g_io_error_from_errno (errsv),
617 : : _("Error deleting stale lock file “%s”: %s"),
618 : : lock,
619 : : g_strerror (errsv));
620 : 0 : goto out;
621 : : }
622 : :
623 : 0 : _log ("Deleted stale lock file '%s'", lock);
624 : :
625 : : /* Try one last time to create it, now that we've deleted the stale one */
626 : 0 : ret = create_lock_exclusive (lock, NULL, error);
627 : 0 : if (ret < 0)
628 : 0 : goto out;
629 : : }
630 : :
631 : 68 : out:
632 : 68 : g_free (lock);
633 : 68 : return ret;
634 : : }
635 : :
636 : : static gboolean
637 : 68 : keyring_release_lock (const gchar *path,
638 : : gint lock_fd,
639 : : GError **error)
640 : : {
641 : : gchar *lock;
642 : : gboolean ret;
643 : :
644 : 68 : g_return_val_if_fail (path != NULL, FALSE);
645 : 68 : g_return_val_if_fail (lock_fd != -1, FALSE);
646 : 68 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
647 : :
648 : 68 : ret = FALSE;
649 : 68 : lock = g_strdup_printf ("%s.lock", path);
650 : 68 : if (close (lock_fd) != 0)
651 : : {
652 : 0 : int errsv = errno;
653 : 0 : g_set_error (error,
654 : : G_IO_ERROR,
655 : 0 : g_io_error_from_errno (errsv),
656 : : _("Error closing (unlinked) lock file “%s”: %s"),
657 : : lock,
658 : : g_strerror (errsv));
659 : 0 : goto out;
660 : : }
661 : 68 : if (g_unlink (lock) != 0)
662 : : {
663 : 0 : int errsv = errno;
664 : 0 : g_set_error (error,
665 : : G_IO_ERROR,
666 : 0 : g_io_error_from_errno (errsv),
667 : : _("Error unlinking lock file “%s”: %s"),
668 : : lock,
669 : : g_strerror (errsv));
670 : 0 : goto out;
671 : : }
672 : :
673 : 68 : ret = TRUE;
674 : :
675 : 68 : out:
676 : 68 : g_free (lock);
677 : 68 : return ret;
678 : : }
679 : :
680 : :
681 : : /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
682 : : static gboolean
683 : 68 : keyring_generate_entry (const gchar *cookie_context,
684 : : gint *out_id,
685 : : gchar **out_cookie,
686 : : GError **error)
687 : : {
688 : : gboolean ret;
689 : : gchar *keyring_dir;
690 : : gchar *path;
691 : : gchar *contents;
692 : 68 : GError *local_error = NULL;
693 : : gchar **lines;
694 : : gint max_line_id;
695 : : GString *new_contents;
696 : : gint64 now;
697 : : gboolean have_id;
698 : : gint use_id;
699 : : gchar *use_cookie;
700 : : gboolean changed_file;
701 : : gint lock_fd;
702 : :
703 : 68 : g_return_val_if_fail (cookie_context != NULL, FALSE);
704 : 68 : g_return_val_if_fail (out_id != NULL, FALSE);
705 : 68 : g_return_val_if_fail (out_cookie != NULL, FALSE);
706 : 68 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
707 : :
708 : 68 : ret = FALSE;
709 : 68 : path = NULL;
710 : 68 : contents = NULL;
711 : 68 : lines = NULL;
712 : 68 : new_contents = NULL;
713 : 68 : have_id = FALSE;
714 : 68 : use_id = 0;
715 : 68 : use_cookie = NULL;
716 : 68 : lock_fd = -1;
717 : :
718 : 68 : keyring_dir = ensure_keyring_directory (error);
719 : 68 : if (keyring_dir == NULL)
720 : 0 : goto out;
721 : :
722 : 68 : path = g_build_filename (keyring_dir, cookie_context, NULL);
723 : :
724 : 68 : lock_fd = keyring_acquire_lock (path, error);
725 : 68 : if (lock_fd == -1)
726 : 0 : goto out;
727 : :
728 : 68 : contents = NULL;
729 : 68 : if (!g_file_get_contents (path,
730 : : &contents,
731 : : NULL,
732 : : &local_error))
733 : : {
734 : 7 : if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
735 : : {
736 : : /* file doesn't have to exist */
737 : 7 : g_clear_error (&local_error);
738 : : }
739 : : else
740 : : {
741 : 0 : g_propagate_prefixed_error (error,
742 : 0 : g_steal_pointer (&local_error),
743 : : _("Error opening keyring “%s” for writing: "),
744 : : path);
745 : 0 : goto out;
746 : : }
747 : : }
748 : :
749 : 68 : new_contents = g_string_new (NULL);
750 : 68 : now = g_get_real_time () / G_USEC_PER_SEC;
751 : 68 : changed_file = FALSE;
752 : :
753 : 68 : max_line_id = 0;
754 : 68 : if (contents != NULL)
755 : : {
756 : : guint n;
757 : 61 : lines = g_strsplit (contents, "\n", 0);
758 : 183 : for (n = 0; lines[n] != NULL; n++)
759 : : {
760 : 122 : const gchar *line = lines[n];
761 : : gchar **tokens;
762 : : gchar *endp;
763 : : gint line_id;
764 : : gint64 line_when;
765 : : gboolean keep_entry;
766 : :
767 : 122 : if (line[0] == '\0')
768 : 61 : continue;
769 : :
770 : 61 : tokens = g_strsplit (line, " ", 0);
771 : 61 : if (g_strv_length (tokens) != 3)
772 : : {
773 : 0 : g_set_error (error,
774 : : G_IO_ERROR,
775 : : G_IO_ERROR_FAILED,
776 : : _("Line %d of the keyring at “%s” with content “%s” is malformed"),
777 : : n + 1,
778 : : path,
779 : : line);
780 : 0 : g_strfreev (tokens);
781 : 0 : goto out;
782 : : }
783 : :
784 : 61 : line_id = g_ascii_strtoll (tokens[0], &endp, 10);
785 : 61 : if (*endp != '\0')
786 : : {
787 : 0 : g_set_error (error,
788 : : G_IO_ERROR,
789 : : G_IO_ERROR_FAILED,
790 : : _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
791 : : n + 1,
792 : : path,
793 : : line);
794 : 0 : g_strfreev (tokens);
795 : 0 : goto out;
796 : : }
797 : :
798 : 61 : line_when = g_ascii_strtoll (tokens[1], &endp, 10);
799 : 61 : if (*endp != '\0')
800 : : {
801 : 0 : g_set_error (error,
802 : : G_IO_ERROR,
803 : : G_IO_ERROR_FAILED,
804 : : _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
805 : : n + 1,
806 : : path,
807 : : line);
808 : 0 : g_strfreev (tokens);
809 : 0 : goto out;
810 : : }
811 : :
812 : :
813 : : /* D-Bus spec says:
814 : : *
815 : : * Once the lockfile has been created, the server loads the
816 : : * cookie file. It should then delete any cookies that are
817 : : * old (the timeout can be fairly short), or more than a
818 : : * reasonable time in the future (so that cookies never
819 : : * accidentally become permanent, if the clock was set far
820 : : * into the future at some point). If no recent keys remain,
821 : : * the server may generate a new key.
822 : : *
823 : : */
824 : 61 : keep_entry = TRUE;
825 : 61 : if (line_when > now)
826 : : {
827 : : /* Oddball case: entry is more recent than our current wall-clock time..
828 : : * This is OK, it means that another server on another machine but with
829 : : * same $HOME wrote the entry. */
830 : 0 : if (line_when - now > MAX_TIME_TRAVEL_SECONDS)
831 : : {
832 : 0 : keep_entry = FALSE;
833 : 0 : _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
834 : : }
835 : : }
836 : : else
837 : : {
838 : : /* Discard entry if it's too old. */
839 : 61 : if (now - line_when > EXPIRE_KEYS_TIMEOUT_SECONDS)
840 : : {
841 : 0 : keep_entry = FALSE;
842 : : }
843 : : }
844 : :
845 : 61 : if (!keep_entry)
846 : : {
847 : 0 : changed_file = FALSE;
848 : : }
849 : : else
850 : : {
851 : 61 : g_string_append_printf (new_contents,
852 : : "%d %" G_GUINT64_FORMAT " %s\n",
853 : : line_id,
854 : : line_when,
855 : 61 : tokens[2]);
856 : 61 : max_line_id = MAX (line_id, max_line_id);
857 : : /* Only reuse entry if not older than 5 minutes.
858 : : *
859 : : * (We need a bit of grace time compared to 7 minutes above.. otherwise
860 : : * there's a race where we reuse the 6min59.9 secs old entry and a
861 : : * split-second later another server purges the now 7 minute old entry.)
862 : : */
863 : 61 : if (now - line_when < NEW_KEY_TIMEOUT_SECONDS)
864 : : {
865 : 61 : if (!have_id)
866 : : {
867 : 61 : use_id = line_id;
868 : 61 : use_cookie = tokens[2]; /* steal memory */
869 : 61 : tokens[2] = NULL;
870 : 61 : have_id = TRUE;
871 : : }
872 : : }
873 : : }
874 : 61 : g_strfreev (tokens);
875 : : }
876 : : } /* for each line */
877 : :
878 : 68 : ret = TRUE;
879 : :
880 : 68 : if (have_id)
881 : : {
882 : 61 : *out_id = use_id;
883 : 61 : *out_cookie = use_cookie;
884 : 61 : use_cookie = NULL;
885 : : }
886 : : else
887 : : {
888 : : gchar *raw_cookie;
889 : 7 : *out_id = max_line_id + 1;
890 : 7 : raw_cookie = random_blob (32);
891 : 7 : *out_cookie = _g_dbus_hexencode (raw_cookie, 32);
892 : 7 : g_free (raw_cookie);
893 : :
894 : 7 : g_string_append_printf (new_contents,
895 : : "%d %" G_GINT64_FORMAT " %s\n",
896 : : *out_id,
897 : 7 : g_get_real_time () / G_USEC_PER_SEC,
898 : : *out_cookie);
899 : 7 : changed_file = TRUE;
900 : : }
901 : :
902 : : /* and now actually write the cookie file if there are changes (this is atomic) */
903 : 68 : if (changed_file)
904 : : {
905 : 7 : if (!g_file_set_contents_full (path,
906 : 7 : new_contents->str,
907 : : -1,
908 : : G_FILE_SET_CONTENTS_CONSISTENT,
909 : : 0600,
910 : : error))
911 : : {
912 : 0 : *out_id = 0;
913 : 0 : g_free (*out_cookie);
914 : 0 : *out_cookie = 0;
915 : 0 : ret = FALSE;
916 : 0 : goto out;
917 : : }
918 : : }
919 : :
920 : 68 : out:
921 : : /* Any error should have been propagated to @error by now */
922 : 68 : g_assert (local_error == NULL);
923 : :
924 : 68 : if (lock_fd != -1)
925 : : {
926 : 68 : if (!keyring_release_lock (path, lock_fd, &local_error))
927 : : {
928 : 0 : if (error != NULL)
929 : : {
930 : 0 : if (*error == NULL)
931 : : {
932 : 0 : *error = local_error;
933 : : }
934 : : else
935 : : {
936 : 0 : g_prefix_error (error,
937 : : _("(Additionally, releasing the lock for “%s” also failed: %s) "),
938 : : path,
939 : 0 : local_error->message);
940 : 0 : g_error_free (local_error);
941 : : }
942 : : }
943 : : else
944 : : {
945 : 0 : g_error_free (local_error);
946 : : }
947 : : }
948 : : }
949 : :
950 : 68 : g_free (keyring_dir);
951 : 68 : g_free (path);
952 : 68 : g_strfreev (lines);
953 : 68 : g_free (contents);
954 : 68 : if (new_contents != NULL)
955 : 68 : g_string_free (new_contents, TRUE);
956 : 68 : g_free (use_cookie);
957 : 68 : return ret;
958 : : }
959 : :
960 : : /* ---------------------------------------------------------------------------------------------------- */
961 : :
962 : : static gchar *
963 : 77 : generate_sha1 (const gchar *server_challenge,
964 : : const gchar *client_challenge,
965 : : const gchar *cookie)
966 : : {
967 : : GString *str;
968 : : gchar *sha1;
969 : :
970 : 77 : str = g_string_new (server_challenge);
971 : : g_string_append_c (str, ':');
972 : : g_string_append (str, client_challenge);
973 : : g_string_append_c (str, ':');
974 : : g_string_append (str, cookie);
975 : 77 : sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
976 : 77 : g_string_free (str, TRUE);
977 : :
978 : 77 : return sha1;
979 : : }
980 : :
981 : : /* ---------------------------------------------------------------------------------------------------- */
982 : :
983 : : static GDBusAuthMechanismState
984 : 204 : mechanism_server_get_state (GDBusAuthMechanism *mechanism)
985 : : {
986 : 204 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
987 : :
988 : 204 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
989 : 204 : g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
990 : :
991 : 204 : return m->priv->state;
992 : : }
993 : :
994 : : static void
995 : 68 : mechanism_server_initiate (GDBusAuthMechanism *mechanism,
996 : : const gchar *initial_response,
997 : : gsize initial_response_len)
998 : : {
999 : 68 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1000 : :
1001 : 68 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1002 : 68 : g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
1003 : :
1004 : 68 : m->priv->is_server = TRUE;
1005 : 68 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1006 : :
1007 : 68 : if (initial_response != NULL && initial_response_len > 0)
1008 : : {
1009 : : #ifdef G_OS_UNIX
1010 : : gint64 uid;
1011 : : gchar *endp;
1012 : :
1013 : 68 : uid = g_ascii_strtoll (initial_response, &endp, 10);
1014 : 68 : if (*endp == '\0')
1015 : : {
1016 : 68 : if (uid == getuid ())
1017 : : {
1018 : 68 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1019 : : }
1020 : : }
1021 : : #elif defined(G_OS_WIN32)
1022 : : gchar *sid;
1023 : :
1024 : : sid = _g_win32_current_process_sid_string (NULL);
1025 : :
1026 : : if (g_strcmp0 (initial_response, sid) == 0)
1027 : : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1028 : :
1029 : : g_free (sid);
1030 : : #else
1031 : : #error Please implement for your OS
1032 : : #endif
1033 : : }
1034 : : }
1035 : :
1036 : : static void
1037 : 68 : mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
1038 : : const gchar *data,
1039 : : gsize data_len)
1040 : : {
1041 : 68 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1042 : : gchar **tokens;
1043 : : const gchar *client_challenge;
1044 : : const gchar *alleged_sha1;
1045 : : gchar *sha1;
1046 : :
1047 : 68 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1048 : 68 : g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1049 : 68 : g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1050 : :
1051 : 68 : tokens = NULL;
1052 : 68 : sha1 = NULL;
1053 : :
1054 : 68 : tokens = g_strsplit (data, " ", 0);
1055 : 68 : if (g_strv_length (tokens) != 2)
1056 : : {
1057 : 0 : g_free (m->priv->reject_reason);
1058 : 0 : m->priv->reject_reason = g_strdup_printf ("Malformed data '%s'", data);
1059 : 0 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1060 : 0 : goto out;
1061 : : }
1062 : :
1063 : 68 : client_challenge = tokens[0];
1064 : 68 : alleged_sha1 = tokens[1];
1065 : :
1066 : 68 : sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
1067 : :
1068 : 68 : if (g_strcmp0 (sha1, alleged_sha1) == 0)
1069 : : {
1070 : 68 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1071 : : }
1072 : : else
1073 : : {
1074 : 0 : g_free (m->priv->reject_reason);
1075 : 0 : m->priv->reject_reason = g_strdup_printf ("SHA-1 mismatch");
1076 : 0 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1077 : : }
1078 : :
1079 : 68 : out:
1080 : 68 : g_strfreev (tokens);
1081 : 68 : g_free (sha1);
1082 : : }
1083 : :
1084 : : static gchar *
1085 : 68 : mechanism_server_data_send (GDBusAuthMechanism *mechanism,
1086 : : gsize *out_data_len)
1087 : : {
1088 : 68 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1089 : : gchar *s;
1090 : : gint cookie_id;
1091 : : const gchar *cookie_context;
1092 : : GError *error;
1093 : :
1094 : 68 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1095 : 68 : g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1096 : 68 : g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1097 : :
1098 : 68 : s = NULL;
1099 : 68 : *out_data_len = 0;
1100 : :
1101 : : /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1102 : 68 : cookie_context = "org_gtk_gdbus_general";
1103 : :
1104 : 68 : cookie_id = -1;
1105 : 68 : error = NULL;
1106 : 68 : if (!keyring_generate_entry (cookie_context,
1107 : : &cookie_id,
1108 : 68 : &m->priv->cookie,
1109 : : &error))
1110 : : {
1111 : 0 : g_free (m->priv->reject_reason);
1112 : 0 : m->priv->reject_reason = g_strdup_printf ("Error adding entry to keyring: %s", error->message);
1113 : 0 : g_error_free (error);
1114 : 0 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1115 : 0 : goto out;
1116 : : }
1117 : :
1118 : 68 : m->priv->server_challenge = random_ascii_string (16);
1119 : 68 : s = g_strdup_printf ("%s %d %s",
1120 : : cookie_context,
1121 : : cookie_id,
1122 : 68 : m->priv->server_challenge);
1123 : 68 : *out_data_len = strlen (s);
1124 : :
1125 : 68 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1126 : :
1127 : 68 : out:
1128 : 68 : return s;
1129 : : }
1130 : :
1131 : : static gchar *
1132 : 15 : mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism)
1133 : : {
1134 : 15 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1135 : :
1136 : 15 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1137 : 15 : g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1138 : :
1139 : 30 : return g_strdup (m->priv->reject_reason);
1140 : : }
1141 : :
1142 : : static void
1143 : 0 : mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
1144 : : {
1145 : 0 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1146 : :
1147 : 0 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1148 : 0 : g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1149 : :
1150 : 0 : m->priv->is_server = FALSE;
1151 : : }
1152 : :
1153 : : /* ---------------------------------------------------------------------------------------------------- */
1154 : :
1155 : : static GDBusAuthMechanismState
1156 : 33 : mechanism_client_get_state (GDBusAuthMechanism *mechanism)
1157 : : {
1158 : 33 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1159 : :
1160 : 33 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1161 : 33 : g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1162 : :
1163 : 33 : return m->priv->state;
1164 : : }
1165 : :
1166 : : static gchar *
1167 : 24 : mechanism_client_initiate (GDBusAuthMechanism *mechanism,
1168 : : GDBusConnectionFlags conn_flags,
1169 : : gsize *out_initial_response_len)
1170 : : {
1171 : 24 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1172 : : gchar *initial_response;
1173 : :
1174 : 24 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1175 : 24 : g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1176 : :
1177 : 24 : m->priv->is_client = TRUE;
1178 : :
1179 : 24 : *out_initial_response_len = 0;
1180 : :
1181 : : #ifdef G_OS_UNIX
1182 : 24 : initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1183 : : #elif defined (G_OS_WIN32)
1184 : : initial_response = _g_win32_current_process_sid_string (NULL);
1185 : : #else
1186 : : #error Please implement for your OS
1187 : : #endif
1188 : 24 : if (initial_response)
1189 : : {
1190 : 24 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1191 : 24 : *out_initial_response_len = strlen (initial_response);
1192 : : }
1193 : : else
1194 : : {
1195 : 0 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1196 : : }
1197 : :
1198 : 24 : return initial_response;
1199 : : }
1200 : :
1201 : : /* Context names must be valid ASCII, nonzero length, and may not contain the
1202 : : * characters slash ("/"), backslash ("\"), space (" "), newline ("\n"),
1203 : : * carriage return ("\r"), tab ("\t"), or period (".").
1204 : : *
1205 : : * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha */
1206 : : static gboolean
1207 : 22 : validate_cookie_context (const char *cookie_context)
1208 : : {
1209 : 22 : size_t i = 0;
1210 : :
1211 : 22 : g_return_val_if_fail (cookie_context != NULL, FALSE);
1212 : :
1213 : 318 : for (i = 0; cookie_context[i] != '\0'; i++)
1214 : : {
1215 : 303 : if ((uint8_t) cookie_context[i] >= 128 ||
1216 : 302 : cookie_context[i] == '/' ||
1217 : 301 : cookie_context[i] == '\\' ||
1218 : 300 : cookie_context[i] == ' ' ||
1219 : 300 : cookie_context[i] == '\n' ||
1220 : 299 : cookie_context[i] == '\r' ||
1221 : 298 : cookie_context[i] == '\t' ||
1222 : 297 : cookie_context[i] == '.')
1223 : 7 : return FALSE;
1224 : : }
1225 : :
1226 : 15 : return (i > 0);
1227 : : }
1228 : :
1229 : : static void
1230 : 24 : mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
1231 : : const gchar *data,
1232 : : gsize data_len)
1233 : : {
1234 : 24 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1235 : : gchar **tokens;
1236 : : const gchar *cookie_context;
1237 : : int64_t cookie_id;
1238 : : const gchar *server_challenge;
1239 : : gchar *client_challenge;
1240 : : gchar *endp;
1241 : : gchar *cookie;
1242 : : GError *error;
1243 : : gchar *sha1;
1244 : :
1245 : 24 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1246 : 24 : g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1247 : 24 : g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1248 : :
1249 : 24 : tokens = NULL;
1250 : 24 : cookie = NULL;
1251 : 24 : client_challenge = NULL;
1252 : :
1253 : 24 : tokens = g_strsplit (data, " ", 0);
1254 : 24 : if (g_strv_length (tokens) != 3)
1255 : : {
1256 : 2 : g_free (m->priv->reject_reason);
1257 : 2 : m->priv->reject_reason = g_strdup_printf ("Malformed data '%s'", data);
1258 : 2 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1259 : 2 : goto out;
1260 : : }
1261 : :
1262 : 22 : cookie_context = tokens[0];
1263 : 22 : if (!validate_cookie_context (tokens[0]))
1264 : : {
1265 : 8 : g_free (m->priv->reject_reason);
1266 : 8 : m->priv->reject_reason = g_strdup_printf ("Malformed cookie_context '%s'", tokens[0]);
1267 : 8 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1268 : 8 : goto out;
1269 : : }
1270 : :
1271 : 14 : cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1272 : 14 : if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > UINT32_MAX)
1273 : : {
1274 : 4 : g_free (m->priv->reject_reason);
1275 : 4 : m->priv->reject_reason = g_strdup_printf ("Malformed cookie_id '%s'", tokens[1]);
1276 : 4 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1277 : 4 : goto out;
1278 : : }
1279 : 10 : server_challenge = tokens[2];
1280 : :
1281 : 10 : error = NULL;
1282 : 10 : cookie = keyring_lookup_entry (cookie_context, (unsigned int) cookie_id, &error);
1283 : 10 : if (cookie == NULL)
1284 : : {
1285 : 1 : g_free (m->priv->reject_reason);
1286 : 1 : m->priv->reject_reason = g_strdup_printf ("Problems looking up entry in keyring: %s", error->message);
1287 : 1 : g_error_free (error);
1288 : 1 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1289 : 1 : goto out;
1290 : : }
1291 : :
1292 : 9 : client_challenge = random_ascii_string (16);
1293 : 9 : sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1294 : 9 : m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1295 : 9 : g_free (sha1);
1296 : 9 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1297 : :
1298 : 24 : out:
1299 : 24 : g_strfreev (tokens);
1300 : 24 : g_free (cookie);
1301 : 24 : g_free (client_challenge);
1302 : : }
1303 : :
1304 : : static gchar *
1305 : 9 : mechanism_client_data_send (GDBusAuthMechanism *mechanism,
1306 : : gsize *out_data_len)
1307 : : {
1308 : 9 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1309 : :
1310 : 9 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1311 : 9 : g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1312 : 9 : g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1313 : :
1314 : 9 : g_assert (m->priv->to_send != NULL);
1315 : :
1316 : 9 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1317 : :
1318 : 9 : *out_data_len = strlen (m->priv->to_send);
1319 : 18 : return g_strdup (m->priv->to_send);
1320 : : }
1321 : :
1322 : : static void
1323 : 15 : mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
1324 : : {
1325 : 15 : GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1326 : :
1327 : 15 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1328 : 15 : g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1329 : :
1330 : 15 : m->priv->is_client = FALSE;
1331 : : }
1332 : :
1333 : : /* ---------------------------------------------------------------------------------------------------- */
|