GSettings: properly support 'extends'
[glib.git] / gio / gdbusauthmechanismsha1.c
blobe4f75eb0e8045cb76d0ae0f68d2182cfa2692b96
1 /* GDBus - GLib D-Bus Library
3 * Copyright (C) 2008-2010 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: David Zeuthen <davidz@redhat.com>
23 #include "config.h"
25 #include <string.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef _WIN32
33 #include <io.h>
34 #endif
36 #include <glib/gstdio.h>
38 #include "gdbusauthmechanismsha1.h"
39 #include "gcredentials.h"
40 #include "gdbuserror.h"
41 #include "gioenumtypes.h"
42 #include "gioerror.h"
43 #include "gdbusprivate.h"
45 #include "glibintl.h"
47 struct _GDBusAuthMechanismSha1Private
49 gboolean is_client;
50 gboolean is_server;
51 GDBusAuthMechanismState state;
53 /* used on the client side */
54 gchar *to_send;
56 /* used on the server side */
57 gchar *cookie;
58 gchar *server_challenge;
61 static gint mechanism_get_priority (void);
62 static const gchar *mechanism_get_name (void);
64 static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
65 static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
66 const gchar *data,
67 gsize data_len,
68 gsize *out_data_len);
69 static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
70 const gchar *data,
71 gsize data_len,
72 gsize *out_data_len);
73 static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
74 static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
75 const gchar *initial_response,
76 gsize initial_response_len);
77 static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
78 const gchar *data,
79 gsize data_len);
80 static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
81 gsize *out_data_len);
82 static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
83 static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
84 static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
85 static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
86 gsize *out_initial_response_len);
87 static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
88 const gchar *data,
89 gsize data_len);
90 static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
91 gsize *out_data_len);
92 static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
94 /* ---------------------------------------------------------------------------------------------------- */
96 G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM)
98 /* ---------------------------------------------------------------------------------------------------- */
100 static void
101 _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
103 GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
105 g_free (mechanism->priv->to_send);
107 g_free (mechanism->priv->cookie);
108 g_free (mechanism->priv->server_challenge);
110 if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL)
111 G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object);
114 static void
115 _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
117 GObjectClass *gobject_class;
118 GDBusAuthMechanismClass *mechanism_class;
120 gobject_class = G_OBJECT_CLASS (klass);
121 gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
123 mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
124 mechanism_class->get_priority = mechanism_get_priority;
125 mechanism_class->get_name = mechanism_get_name;
126 mechanism_class->is_supported = mechanism_is_supported;
127 mechanism_class->encode_data = mechanism_encode_data;
128 mechanism_class->decode_data = mechanism_decode_data;
129 mechanism_class->server_get_state = mechanism_server_get_state;
130 mechanism_class->server_initiate = mechanism_server_initiate;
131 mechanism_class->server_data_receive = mechanism_server_data_receive;
132 mechanism_class->server_data_send = mechanism_server_data_send;
133 mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
134 mechanism_class->server_shutdown = mechanism_server_shutdown;
135 mechanism_class->client_get_state = mechanism_client_get_state;
136 mechanism_class->client_initiate = mechanism_client_initiate;
137 mechanism_class->client_data_receive = mechanism_client_data_receive;
138 mechanism_class->client_data_send = mechanism_client_data_send;
139 mechanism_class->client_shutdown = mechanism_client_shutdown;
142 static void
143 _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
145 mechanism->priv = _g_dbus_auth_mechanism_sha1_get_instance_private (mechanism);
148 /* ---------------------------------------------------------------------------------------------------- */
150 static gint
151 mechanism_get_priority (void)
153 return 0;
156 static const gchar *
157 mechanism_get_name (void)
159 return "DBUS_COOKIE_SHA1";
162 static gboolean
163 mechanism_is_supported (GDBusAuthMechanism *mechanism)
165 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
166 return TRUE;
169 static gchar *
170 mechanism_encode_data (GDBusAuthMechanism *mechanism,
171 const gchar *data,
172 gsize data_len,
173 gsize *out_data_len)
175 return NULL;
179 static gchar *
180 mechanism_decode_data (GDBusAuthMechanism *mechanism,
181 const gchar *data,
182 gsize data_len,
183 gsize *out_data_len)
185 return NULL;
188 /* ---------------------------------------------------------------------------------------------------- */
190 static gint
191 random_ascii (void)
193 gint ret;
194 ret = g_random_int_range (0, 60);
195 if (ret < 25)
196 ret += 'A';
197 else if (ret < 50)
198 ret += 'a' - 25;
199 else
200 ret += '0' - 50;
201 return ret;
204 static gchar *
205 random_ascii_string (guint len)
207 GString *challenge;
208 guint n;
210 challenge = g_string_new (NULL);
211 for (n = 0; n < len; n++)
212 g_string_append_c (challenge, random_ascii ());
213 return g_string_free (challenge, FALSE);
216 static gchar *
217 random_blob (guint len)
219 GString *challenge;
220 guint n;
222 challenge = g_string_new (NULL);
223 for (n = 0; n < len; n++)
224 g_string_append_c (challenge, g_random_int_range (0, 256));
225 return g_string_free (challenge, FALSE);
228 /* ---------------------------------------------------------------------------------------------------- */
230 /* ensure keyring dir exists and permissions are correct */
231 static gchar *
232 ensure_keyring_directory (GError **error)
234 gchar *path;
235 const gchar *e;
237 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
239 e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
240 if (e != NULL)
242 path = g_strdup (e);
244 else
246 path = g_build_filename (g_get_home_dir (),
247 ".dbus-keyrings",
248 NULL);
251 if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
253 if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL)
255 #ifdef G_OS_UNIX
256 struct stat statbuf;
257 if (stat (path, &statbuf) != 0)
259 g_set_error (error,
260 G_IO_ERROR,
261 g_io_error_from_errno (errno),
262 _("Error when getting information for directory '%s': %s"),
263 path,
264 strerror (errno));
265 g_free (path);
266 path = NULL;
267 goto out;
269 if ((statbuf.st_mode & 0777) != 0700)
271 g_set_error (error,
272 G_IO_ERROR,
273 G_IO_ERROR_FAILED,
274 _("Permissions on directory '%s' are malformed. Expected mode 0700, got 0%o"),
275 path,
276 statbuf.st_mode & 0777);
277 g_free (path);
278 path = NULL;
279 goto out;
281 #else
282 #ifdef __GNUC__
283 #warning Please implement permission checking on this non-UNIX platform
284 #endif
285 #endif
287 goto out;
290 if (g_mkdir (path, 0700) != 0)
292 g_set_error (error,
293 G_IO_ERROR,
294 g_io_error_from_errno (errno),
295 _("Error creating directory '%s': %s"),
296 path,
297 strerror (errno));
298 g_free (path);
299 path = NULL;
300 goto out;
303 out:
304 return path;
307 /* ---------------------------------------------------------------------------------------------------- */
309 static void
310 append_nibble (GString *s, gint val)
312 g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
315 static gchar *
316 hexencode (const gchar *str,
317 gssize len)
319 guint n;
320 GString *s;
322 if (len == -1)
323 len = strlen (str);
325 s = g_string_new (NULL);
326 for (n = 0; n < len; n++)
328 gint val;
329 gint upper_nibble;
330 gint lower_nibble;
332 val = ((const guchar *) str)[n];
333 upper_nibble = val >> 4;
334 lower_nibble = val & 0x0f;
336 append_nibble (s, upper_nibble);
337 append_nibble (s, lower_nibble);
340 return g_string_free (s, FALSE);
343 /* ---------------------------------------------------------------------------------------------------- */
345 /* looks up an entry in the keyring */
346 static gchar *
347 keyring_lookup_entry (const gchar *cookie_context,
348 gint cookie_id,
349 GError **error)
351 gchar *ret;
352 gchar *keyring_dir;
353 gchar *contents;
354 gchar *path;
355 guint n;
356 gchar **lines;
358 g_return_val_if_fail (cookie_context != NULL, NULL);
359 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
361 ret = NULL;
362 path = NULL;
363 contents = NULL;
364 lines = NULL;
366 keyring_dir = ensure_keyring_directory (error);
367 if (keyring_dir == NULL)
368 goto out;
370 path = g_build_filename (keyring_dir, cookie_context, NULL);
372 if (!g_file_get_contents (path,
373 &contents,
374 NULL,
375 error))
377 g_prefix_error (error,
378 _("Error opening keyring '%s' for reading: "),
379 path);
380 goto out;
382 g_assert (contents != NULL);
384 lines = g_strsplit (contents, "\n", 0);
385 for (n = 0; lines[n] != NULL; n++)
387 const gchar *line = lines[n];
388 gchar **tokens;
389 gchar *endp;
390 gint line_id;
391 guint64 line_when;
393 if (line[0] == '\0')
394 continue;
396 tokens = g_strsplit (line, " ", 0);
397 if (g_strv_length (tokens) != 3)
399 g_set_error (error,
400 G_IO_ERROR,
401 G_IO_ERROR_FAILED,
402 _("Line %d of the keyring at '%s' with content '%s' is malformed"),
403 n + 1,
404 path,
405 line);
406 g_strfreev (tokens);
407 goto out;
410 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
411 if (*endp != '\0')
413 g_set_error (error,
414 G_IO_ERROR,
415 G_IO_ERROR_FAILED,
416 _("First token of line %d of the keyring at '%s' with content '%s' is malformed"),
417 n + 1,
418 path,
419 line);
420 g_strfreev (tokens);
421 goto out;
424 line_when = g_ascii_strtoll (tokens[1], &endp, 10);
425 line_when = line_when; /* To avoid -Wunused-but-set-variable */
426 if (*endp != '\0')
428 g_set_error (error,
429 G_IO_ERROR,
430 G_IO_ERROR_FAILED,
431 _("Second token of line %d of the keyring at '%s' with content '%s' is malformed"),
432 n + 1,
433 path,
434 line);
435 g_strfreev (tokens);
436 goto out;
439 if (line_id == cookie_id)
441 /* YAY, success */
442 ret = tokens[2]; /* steal pointer */
443 tokens[2] = NULL;
444 g_strfreev (tokens);
445 goto out;
448 g_strfreev (tokens);
451 /* BOOH, didn't find the cookie */
452 g_set_error (error,
453 G_IO_ERROR,
454 G_IO_ERROR_FAILED,
455 _("Didn't find cookie with id %d in the keyring at '%s'"),
456 cookie_id,
457 path);
459 out:
460 g_free (keyring_dir);
461 g_free (path);
462 g_free (contents);
463 g_strfreev (lines);
464 return ret;
467 /* function for logging important events that the system administrator should take notice of */
468 G_GNUC_PRINTF(1, 2)
469 static void
470 _log (const gchar *message,
471 ...)
473 gchar *s;
474 va_list var_args;
476 va_start (var_args, message);
477 s = g_strdup_vprintf (message, var_args);
478 va_end (var_args);
480 /* TODO: might want to send this to syslog instead */
481 g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
482 g_free (s);
485 static gint
486 keyring_acquire_lock (const gchar *path,
487 GError **error)
489 gchar *lock;
490 gint ret;
491 guint num_tries;
492 guint num_create_tries;
494 g_return_val_if_fail (path != NULL, FALSE);
495 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
497 ret = -1;
498 lock = g_strdup_printf ("%s.lock", path);
500 /* This is what the D-Bus spec says
502 * Create a lockfile name by appending ".lock" to the name of the
503 * cookie file. The server should attempt to create this file using
504 * O_CREAT | O_EXCL. If file creation fails, the lock
505 * fails. Servers should retry for a reasonable period of time,
506 * then they may choose to delete an existing lock to keep users
507 * from having to manually delete a stale lock. [1]
509 * [1] : Lockfiles are used instead of real file locking fcntl() because
510 * real locking implementations are still flaky on network filesystems
513 num_create_tries = 0;
514 #ifdef EEXISTS
515 again:
516 #endif
517 num_tries = 0;
518 while (g_file_test (lock, G_FILE_TEST_EXISTS))
520 /* sleep 10ms, then try again */
521 g_usleep (1000*10);
522 num_tries++;
523 if (num_tries == 50)
525 /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
526 * stale (nuke the it from orbit)
528 if (g_unlink (lock) != 0)
530 g_set_error (error,
531 G_IO_ERROR,
532 g_io_error_from_errno (errno),
533 _("Error deleting stale lock file '%s': %s"),
534 lock,
535 strerror (errno));
536 goto out;
538 _log ("Deleted stale lock file '%s'", lock);
539 break;
543 ret = g_open (lock, O_CREAT |
544 #ifdef O_EXCL
545 O_EXCL,
546 #else
548 #endif
549 0700);
550 if (ret == -1)
552 #ifdef EEXISTS
553 /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
554 if (errno == EEXISTS)
556 num_create_tries++;
557 if (num_create_tries < 5)
558 goto again;
560 #endif
561 num_create_tries = num_create_tries; /* To avoid -Wunused-but-set-variable */
562 g_set_error (error,
563 G_IO_ERROR,
564 g_io_error_from_errno (errno),
565 _("Error creating lock file '%s': %s"),
566 lock,
567 strerror (errno));
568 goto out;
571 out:
572 g_free (lock);
573 return ret;
576 static gboolean
577 keyring_release_lock (const gchar *path,
578 gint lock_fd,
579 GError **error)
581 gchar *lock;
582 gboolean ret;
584 g_return_val_if_fail (path != NULL, FALSE);
585 g_return_val_if_fail (lock_fd != -1, FALSE);
586 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
588 ret = FALSE;
589 lock = g_strdup_printf ("%s.lock", path);
590 if (close (lock_fd) != 0)
592 g_set_error (error,
593 G_IO_ERROR,
594 g_io_error_from_errno (errno),
595 _("Error closing (unlinked) lock file '%s': %s"),
596 lock,
597 strerror (errno));
598 goto out;
600 if (g_unlink (lock) != 0)
602 g_set_error (error,
603 G_IO_ERROR,
604 g_io_error_from_errno (errno),
605 _("Error unlinking lock file '%s': %s"),
606 lock,
607 strerror (errno));
608 goto out;
611 ret = TRUE;
613 out:
614 g_free (lock);
615 return ret;
619 /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
620 static gboolean
621 keyring_generate_entry (const gchar *cookie_context,
622 gint *out_id,
623 gchar **out_cookie,
624 GError **error)
626 gboolean ret;
627 gchar *keyring_dir;
628 gchar *path;
629 gchar *contents;
630 GError *local_error;
631 gchar **lines;
632 gint max_line_id;
633 GString *new_contents;
634 guint64 now;
635 gboolean have_id;
636 gint use_id;
637 gchar *use_cookie;
638 gboolean changed_file;
639 gint lock_fd;
641 g_return_val_if_fail (cookie_context != NULL, FALSE);
642 g_return_val_if_fail (out_id != NULL, FALSE);
643 g_return_val_if_fail (out_cookie != NULL, FALSE);
644 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
646 ret = FALSE;
647 path = NULL;
648 contents = NULL;
649 lines = NULL;
650 new_contents = NULL;
651 have_id = FALSE;
652 use_id = 0;
653 use_cookie = NULL;
654 lock_fd = -1;
656 keyring_dir = ensure_keyring_directory (error);
657 if (keyring_dir == NULL)
658 goto out;
660 path = g_build_filename (keyring_dir, cookie_context, NULL);
662 lock_fd = keyring_acquire_lock (path, error);
663 if (lock_fd == -1)
664 goto out;
666 local_error = NULL;
667 contents = NULL;
668 if (!g_file_get_contents (path,
669 &contents,
670 NULL,
671 &local_error))
673 if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
675 /* file doesn't have to exist */
676 g_error_free (local_error);
678 else
680 g_propagate_prefixed_error (error,
681 local_error,
682 _("Error opening keyring '%s' for writing: "),
683 path);
684 goto out;
688 new_contents = g_string_new (NULL);
689 now = time (NULL);
690 changed_file = FALSE;
692 max_line_id = 0;
693 if (contents != NULL)
695 guint n;
696 lines = g_strsplit (contents, "\n", 0);
697 for (n = 0; lines[n] != NULL; n++)
699 const gchar *line = lines[n];
700 gchar **tokens;
701 gchar *endp;
702 gint line_id;
703 guint64 line_when;
704 gboolean keep_entry;
706 if (line[0] == '\0')
707 continue;
709 tokens = g_strsplit (line, " ", 0);
710 if (g_strv_length (tokens) != 3)
712 g_set_error (error,
713 G_IO_ERROR,
714 G_IO_ERROR_FAILED,
715 _("Line %d of the keyring at '%s' with content '%s' is malformed"),
716 n + 1,
717 path,
718 line);
719 g_strfreev (tokens);
720 goto out;
723 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
724 if (*endp != '\0')
726 g_set_error (error,
727 G_IO_ERROR,
728 G_IO_ERROR_FAILED,
729 _("First token of line %d of the keyring at '%s' with content '%s' is malformed"),
730 n + 1,
731 path,
732 line);
733 g_strfreev (tokens);
734 goto out;
737 line_when = g_ascii_strtoll (tokens[1], &endp, 10);
738 if (*endp != '\0')
740 g_set_error (error,
741 G_IO_ERROR,
742 G_IO_ERROR_FAILED,
743 _("Second token of line %d of the keyring at '%s' with content '%s' is malformed"),
744 n + 1,
745 path,
746 line);
747 g_strfreev (tokens);
748 goto out;
750 line_when = line_when; /* To avoid -Wunused-but-set-variable */
753 /* D-Bus spec says:
755 * Once the lockfile has been created, the server loads the
756 * cookie file. It should then delete any cookies that are
757 * old (the timeout can be fairly short), or more than a
758 * reasonable time in the future (so that cookies never
759 * accidentally become permanent, if the clock was set far
760 * into the future at some point). If no recent keys remain,
761 * the server may generate a new key.
764 keep_entry = TRUE;
765 if (line_when > now)
767 /* Oddball case: entry is more recent than our current wall-clock time..
768 * This is OK, it means that another server on another machine but with
769 * same $HOME wrote the entry.
771 * So discard the entry if it's more than 1 day in the future ("reasonable
772 * time in the future").
774 if (line_when - now > 24*60*60)
776 keep_entry = FALSE;
777 _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
780 else
782 /* Discard entry if it's older than 15 minutes ("can be fairly short") */
783 if (now - line_when > 15*60)
785 keep_entry = FALSE;
789 if (!keep_entry)
791 changed_file = FALSE;
793 else
795 g_string_append_printf (new_contents,
796 "%d %" G_GUINT64_FORMAT " %s\n",
797 line_id,
798 line_when,
799 tokens[2]);
800 max_line_id = MAX (line_id, max_line_id);
801 /* Only reuse entry if not older than 10 minutes.
803 * (We need a bit of grace time compared to 15 minutes above.. otherwise
804 * there's a race where we reuse the 14min59.9 secs old entry and a
805 * split-second later another server purges the now 15 minute old entry.)
807 if (now - line_when < 10 * 60)
809 if (!have_id)
811 use_id = line_id;
812 use_cookie = tokens[2]; /* steal memory */
813 tokens[2] = NULL;
814 have_id = TRUE;
818 g_strfreev (tokens);
820 } /* for each line */
822 ret = TRUE;
824 if (have_id)
826 *out_id = use_id;
827 *out_cookie = use_cookie;
828 use_cookie = NULL;
830 else
832 gchar *raw_cookie;
833 *out_id = max_line_id + 1;
834 raw_cookie = random_blob (32);
835 *out_cookie = hexencode (raw_cookie, 32);
836 g_free (raw_cookie);
838 g_string_append_printf (new_contents,
839 "%d %" G_GUINT64_FORMAT " %s\n",
840 *out_id,
841 (guint64) time (NULL),
842 *out_cookie);
843 changed_file = TRUE;
846 /* and now actually write the cookie file if there are changes (this is atomic) */
847 if (changed_file)
849 if (!g_file_set_contents (path,
850 new_contents->str,
852 error))
854 *out_id = 0;
855 *out_cookie = 0;
856 g_free (*out_cookie);
857 ret = FALSE;
858 goto out;
862 out:
864 if (lock_fd != -1)
866 GError *local_error;
867 local_error = NULL;
868 if (!keyring_release_lock (path, lock_fd, &local_error))
870 if (error != NULL)
872 if (*error == NULL)
874 *error = local_error;
876 else
878 g_prefix_error (error,
879 _("(Additionally, releasing the lock for '%s' also failed: %s) "),
880 path,
881 local_error->message);
884 else
886 g_error_free (local_error);
891 g_free (keyring_dir);
892 g_free (path);
893 g_strfreev (lines);
894 g_free (contents);
895 if (new_contents != NULL)
896 g_string_free (new_contents, TRUE);
897 g_free (use_cookie);
898 return ret;
901 /* ---------------------------------------------------------------------------------------------------- */
903 static gchar *
904 generate_sha1 (const gchar *server_challenge,
905 const gchar *client_challenge,
906 const gchar *cookie)
908 GString *str;
909 gchar *sha1;
911 str = g_string_new (server_challenge);
912 g_string_append_c (str, ':');
913 g_string_append (str, client_challenge);
914 g_string_append_c (str, ':');
915 g_string_append (str, cookie);
916 sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
917 g_string_free (str, TRUE);
919 return sha1;
922 /* ---------------------------------------------------------------------------------------------------- */
924 static GDBusAuthMechanismState
925 mechanism_server_get_state (GDBusAuthMechanism *mechanism)
927 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
929 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
930 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
932 return m->priv->state;
935 static void
936 mechanism_server_initiate (GDBusAuthMechanism *mechanism,
937 const gchar *initial_response,
938 gsize initial_response_len)
940 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
942 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
943 g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
945 m->priv->is_server = TRUE;
946 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
948 if (initial_response != NULL && strlen (initial_response) > 0)
950 #ifdef G_OS_UNIX
951 gint64 uid;
952 gchar *endp;
954 uid = g_ascii_strtoll (initial_response, &endp, 10);
955 if (*endp == '\0')
957 if (uid == getuid ())
959 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
962 #elif defined(G_OS_WIN32)
963 gchar *sid;
964 sid = _g_dbus_win32_get_user_sid ();
965 if (g_strcmp0 (initial_response, sid) == 0)
966 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
967 g_free (sid);
968 #else
969 #error Please implement for your OS
970 #endif
974 static void
975 mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
976 const gchar *data,
977 gsize data_len)
979 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
980 gchar **tokens;
981 const gchar *client_challenge;
982 const gchar *alleged_sha1;
983 gchar *sha1;
985 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
986 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
987 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
989 tokens = NULL;
990 sha1 = NULL;
992 tokens = g_strsplit (data, " ", 0);
993 if (g_strv_length (tokens) != 2)
995 g_warning ("Malformed data '%s'", data);
996 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
997 goto out;
1000 client_challenge = tokens[0];
1001 alleged_sha1 = tokens[1];
1003 sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
1005 if (g_strcmp0 (sha1, alleged_sha1) == 0)
1007 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1009 else
1011 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1014 out:
1015 g_strfreev (tokens);
1016 g_free (sha1);
1019 static gchar *
1020 mechanism_server_data_send (GDBusAuthMechanism *mechanism,
1021 gsize *out_data_len)
1023 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1024 gchar *s;
1025 gint cookie_id;
1026 const gchar *cookie_context;
1027 GError *error;
1029 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1030 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1031 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1033 s = NULL;
1035 /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1036 cookie_context = "org_gtk_gdbus_general";
1038 cookie_id = -1;
1039 error = NULL;
1040 if (!keyring_generate_entry (cookie_context,
1041 &cookie_id,
1042 &m->priv->cookie,
1043 &error))
1045 g_warning ("Error adding entry to keyring: %s", error->message);
1046 g_error_free (error);
1047 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1048 goto out;
1051 m->priv->server_challenge = random_ascii_string (16);
1052 s = g_strdup_printf ("%s %d %s",
1053 cookie_context,
1054 cookie_id,
1055 m->priv->server_challenge);
1057 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1059 out:
1060 return s;
1063 static gchar *
1064 mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
1066 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1068 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1069 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1070 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1072 /* can never end up here because we are never in the REJECTED state */
1073 g_assert_not_reached ();
1075 return NULL;
1078 static void
1079 mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
1081 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1083 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1084 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1086 m->priv->is_server = FALSE;
1089 /* ---------------------------------------------------------------------------------------------------- */
1091 static GDBusAuthMechanismState
1092 mechanism_client_get_state (GDBusAuthMechanism *mechanism)
1094 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1096 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1097 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1099 return m->priv->state;
1102 static gchar *
1103 mechanism_client_initiate (GDBusAuthMechanism *mechanism,
1104 gsize *out_initial_response_len)
1106 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1107 gchar *initial_response;
1109 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1110 g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1112 m->priv->is_client = TRUE;
1113 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1115 *out_initial_response_len = -1;
1117 #ifdef G_OS_UNIX
1118 initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1119 #elif defined (G_OS_WIN32)
1120 initial_response = _g_dbus_win32_get_user_sid ();
1121 #else
1122 #error Please implement for your OS
1123 #endif
1124 g_assert (initial_response != NULL);
1126 return initial_response;
1129 static void
1130 mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
1131 const gchar *data,
1132 gsize data_len)
1134 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1135 gchar **tokens;
1136 const gchar *cookie_context;
1137 guint cookie_id;
1138 const gchar *server_challenge;
1139 gchar *client_challenge;
1140 gchar *endp;
1141 gchar *cookie;
1142 GError *error;
1143 gchar *sha1;
1145 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1146 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1147 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1149 tokens = NULL;
1150 cookie = NULL;
1151 client_challenge = NULL;
1153 tokens = g_strsplit (data, " ", 0);
1154 if (g_strv_length (tokens) != 3)
1156 g_warning ("Malformed data '%s'", data);
1157 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1158 goto out;
1161 cookie_context = tokens[0];
1162 cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1163 if (*endp != '\0')
1165 g_warning ("Malformed cookie_id '%s'", tokens[1]);
1166 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1167 goto out;
1169 server_challenge = tokens[2];
1171 error = NULL;
1172 cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1173 if (cookie == NULL)
1175 g_warning ("Problems looking up entry in keyring: %s", error->message);
1176 g_error_free (error);
1177 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1178 goto out;
1181 client_challenge = random_ascii_string (16);
1182 sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1183 m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1184 g_free (sha1);
1185 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1187 out:
1188 g_strfreev (tokens);
1189 g_free (cookie);
1190 g_free (client_challenge);
1193 static gchar *
1194 mechanism_client_data_send (GDBusAuthMechanism *mechanism,
1195 gsize *out_data_len)
1197 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1199 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1200 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1201 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1203 g_assert (m->priv->to_send != NULL);
1205 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1207 return g_strdup (m->priv->to_send);
1210 static void
1211 mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
1213 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1215 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1216 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1218 m->priv->is_client = FALSE;
1221 /* ---------------------------------------------------------------------------------------------------- */