Add some more cases to the app-id unit tests
[glib.git] / gio / gdbusauthmechanismsha1.c
blob0aa5c251671c6428a2b72dbb3a8cebb1ded6fadf
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, see <http://www.gnu.org/licenses/>.
18 * Author: David Zeuthen <davidz@redhat.com>
21 #include "config.h"
23 #include <string.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <sys/types.h>
28 #include <glib/gstdio.h>
30 #ifdef G_OS_UNIX
31 #include <unistd.h>
32 #endif
33 #ifdef G_OS_WIN32
34 #include <io.h>
35 #endif
37 #include "gdbusauthmechanismsha1.h"
38 #include "gcredentials.h"
39 #include "gdbuserror.h"
40 #include "gioenumtypes.h"
41 #include "gioerror.h"
42 #include "gdbusprivate.h"
44 #include "glibintl.h"
46 struct _GDBusAuthMechanismSha1Private
48 gboolean is_client;
49 gboolean is_server;
50 GDBusAuthMechanismState state;
52 /* used on the client side */
53 gchar *to_send;
55 /* used on the server side */
56 gchar *cookie;
57 gchar *server_challenge;
60 static gint mechanism_get_priority (void);
61 static const gchar *mechanism_get_name (void);
63 static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
64 static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
65 const gchar *data,
66 gsize data_len,
67 gsize *out_data_len);
68 static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
69 const gchar *data,
70 gsize data_len,
71 gsize *out_data_len);
72 static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
73 static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
74 const gchar *initial_response,
75 gsize initial_response_len);
76 static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
77 const gchar *data,
78 gsize data_len);
79 static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
80 gsize *out_data_len);
81 static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
82 static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
83 static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
84 static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
85 gsize *out_initial_response_len);
86 static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
87 const gchar *data,
88 gsize data_len);
89 static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
90 gsize *out_data_len);
91 static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
93 /* ---------------------------------------------------------------------------------------------------- */
95 G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM)
97 /* ---------------------------------------------------------------------------------------------------- */
99 static void
100 _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
102 GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
104 g_free (mechanism->priv->to_send);
106 g_free (mechanism->priv->cookie);
107 g_free (mechanism->priv->server_challenge);
109 if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL)
110 G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object);
113 static void
114 _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
116 GObjectClass *gobject_class;
117 GDBusAuthMechanismClass *mechanism_class;
119 gobject_class = G_OBJECT_CLASS (klass);
120 gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
122 mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
123 mechanism_class->get_priority = mechanism_get_priority;
124 mechanism_class->get_name = mechanism_get_name;
125 mechanism_class->is_supported = mechanism_is_supported;
126 mechanism_class->encode_data = mechanism_encode_data;
127 mechanism_class->decode_data = mechanism_decode_data;
128 mechanism_class->server_get_state = mechanism_server_get_state;
129 mechanism_class->server_initiate = mechanism_server_initiate;
130 mechanism_class->server_data_receive = mechanism_server_data_receive;
131 mechanism_class->server_data_send = mechanism_server_data_send;
132 mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
133 mechanism_class->server_shutdown = mechanism_server_shutdown;
134 mechanism_class->client_get_state = mechanism_client_get_state;
135 mechanism_class->client_initiate = mechanism_client_initiate;
136 mechanism_class->client_data_receive = mechanism_client_data_receive;
137 mechanism_class->client_data_send = mechanism_client_data_send;
138 mechanism_class->client_shutdown = mechanism_client_shutdown;
141 static void
142 _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
144 mechanism->priv = _g_dbus_auth_mechanism_sha1_get_instance_private (mechanism);
147 /* ---------------------------------------------------------------------------------------------------- */
149 static gint
150 mechanism_get_priority (void)
152 return 0;
155 static const gchar *
156 mechanism_get_name (void)
158 return "DBUS_COOKIE_SHA1";
161 static gboolean
162 mechanism_is_supported (GDBusAuthMechanism *mechanism)
164 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
165 return TRUE;
168 static gchar *
169 mechanism_encode_data (GDBusAuthMechanism *mechanism,
170 const gchar *data,
171 gsize data_len,
172 gsize *out_data_len)
174 return NULL;
178 static gchar *
179 mechanism_decode_data (GDBusAuthMechanism *mechanism,
180 const gchar *data,
181 gsize data_len,
182 gsize *out_data_len)
184 return NULL;
187 /* ---------------------------------------------------------------------------------------------------- */
189 static gint
190 random_ascii (void)
192 gint ret;
193 ret = g_random_int_range (0, 60);
194 if (ret < 25)
195 ret += 'A';
196 else if (ret < 50)
197 ret += 'a' - 25;
198 else
199 ret += '0' - 50;
200 return ret;
203 static gchar *
204 random_ascii_string (guint len)
206 GString *challenge;
207 guint n;
209 challenge = g_string_new (NULL);
210 for (n = 0; n < len; n++)
211 g_string_append_c (challenge, random_ascii ());
212 return g_string_free (challenge, FALSE);
215 static gchar *
216 random_blob (guint len)
218 GString *challenge;
219 guint n;
221 challenge = g_string_new (NULL);
222 for (n = 0; n < len; n++)
223 g_string_append_c (challenge, g_random_int_range (0, 256));
224 return g_string_free (challenge, FALSE);
227 /* ---------------------------------------------------------------------------------------------------- */
229 /* ensure keyring dir exists and permissions are correct */
230 static gchar *
231 ensure_keyring_directory (GError **error)
233 gchar *path;
234 const gchar *e;
236 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
238 e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
239 if (e != NULL)
241 path = g_strdup (e);
243 else
245 path = g_build_filename (g_get_home_dir (),
246 ".dbus-keyrings",
247 NULL);
250 if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
252 if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL)
254 #ifdef G_OS_UNIX
255 struct stat statbuf;
256 if (stat (path, &statbuf) != 0)
258 g_set_error (error,
259 G_IO_ERROR,
260 g_io_error_from_errno (errno),
261 _("Error when getting information for directory “%s”: %s"),
262 path,
263 strerror (errno));
264 g_free (path);
265 path = NULL;
266 goto out;
268 if ((statbuf.st_mode & 0777) != 0700)
270 g_set_error (error,
271 G_IO_ERROR,
272 G_IO_ERROR_FAILED,
273 _("Permissions on directory “%s” are malformed. Expected mode 0700, got 0%o"),
274 path,
275 statbuf.st_mode & 0777);
276 g_free (path);
277 path = NULL;
278 goto out;
280 #else
281 #ifdef __GNUC__
282 #warning Please implement permission checking on this non-UNIX platform
283 #endif
284 #endif
286 goto out;
289 if (g_mkdir (path, 0700) != 0)
291 g_set_error (error,
292 G_IO_ERROR,
293 g_io_error_from_errno (errno),
294 _("Error creating directory “%s”: %s"),
295 path,
296 strerror (errno));
297 g_free (path);
298 path = NULL;
299 goto out;
302 out:
303 return path;
306 /* ---------------------------------------------------------------------------------------------------- */
308 static void
309 append_nibble (GString *s, gint val)
311 g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
314 static gchar *
315 hexencode (const gchar *str,
316 gssize len)
318 guint n;
319 GString *s;
321 if (len == -1)
322 len = strlen (str);
324 s = g_string_new (NULL);
325 for (n = 0; n < len; n++)
327 gint val;
328 gint upper_nibble;
329 gint lower_nibble;
331 val = ((const guchar *) str)[n];
332 upper_nibble = val >> 4;
333 lower_nibble = val & 0x0f;
335 append_nibble (s, upper_nibble);
336 append_nibble (s, lower_nibble);
339 return g_string_free (s, FALSE);
342 /* ---------------------------------------------------------------------------------------------------- */
344 /* looks up an entry in the keyring */
345 static gchar *
346 keyring_lookup_entry (const gchar *cookie_context,
347 gint cookie_id,
348 GError **error)
350 gchar *ret;
351 gchar *keyring_dir;
352 gchar *contents;
353 gchar *path;
354 guint n;
355 gchar **lines;
357 g_return_val_if_fail (cookie_context != NULL, NULL);
358 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
360 ret = NULL;
361 path = NULL;
362 contents = NULL;
363 lines = NULL;
365 keyring_dir = ensure_keyring_directory (error);
366 if (keyring_dir == NULL)
367 goto out;
369 path = g_build_filename (keyring_dir, cookie_context, NULL);
371 if (!g_file_get_contents (path,
372 &contents,
373 NULL,
374 error))
376 g_prefix_error (error,
377 _("Error opening keyring “%s” for reading: "),
378 path);
379 goto out;
381 g_assert (contents != NULL);
383 lines = g_strsplit (contents, "\n", 0);
384 for (n = 0; lines[n] != NULL; n++)
386 const gchar *line = lines[n];
387 gchar **tokens;
388 gchar *endp;
389 gint line_id;
390 guint64 line_when;
392 if (line[0] == '\0')
393 continue;
395 tokens = g_strsplit (line, " ", 0);
396 if (g_strv_length (tokens) != 3)
398 g_set_error (error,
399 G_IO_ERROR,
400 G_IO_ERROR_FAILED,
401 _("Line %d of the keyring at “%s” with content “%s” is malformed"),
402 n + 1,
403 path,
404 line);
405 g_strfreev (tokens);
406 goto out;
409 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
410 if (*endp != '\0')
412 g_set_error (error,
413 G_IO_ERROR,
414 G_IO_ERROR_FAILED,
415 _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
416 n + 1,
417 path,
418 line);
419 g_strfreev (tokens);
420 goto out;
423 line_when = g_ascii_strtoll (tokens[1], &endp, 10);
424 line_when = line_when; /* To avoid -Wunused-but-set-variable */
425 if (*endp != '\0')
427 g_set_error (error,
428 G_IO_ERROR,
429 G_IO_ERROR_FAILED,
430 _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
431 n + 1,
432 path,
433 line);
434 g_strfreev (tokens);
435 goto out;
438 if (line_id == cookie_id)
440 /* YAY, success */
441 ret = tokens[2]; /* steal pointer */
442 tokens[2] = NULL;
443 g_strfreev (tokens);
444 goto out;
447 g_strfreev (tokens);
450 /* BOOH, didn't find the cookie */
451 g_set_error (error,
452 G_IO_ERROR,
453 G_IO_ERROR_FAILED,
454 _("Didn’t find cookie with id %d in the keyring at “%s”"),
455 cookie_id,
456 path);
458 out:
459 g_free (keyring_dir);
460 g_free (path);
461 g_free (contents);
462 g_strfreev (lines);
463 return ret;
466 /* function for logging important events that the system administrator should take notice of */
467 G_GNUC_PRINTF(1, 2)
468 static void
469 _log (const gchar *message,
470 ...)
472 gchar *s;
473 va_list var_args;
475 va_start (var_args, message);
476 s = g_strdup_vprintf (message, var_args);
477 va_end (var_args);
479 /* TODO: might want to send this to syslog instead */
480 g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
481 g_free (s);
484 static gint
485 keyring_acquire_lock (const gchar *path,
486 GError **error)
488 gchar *lock;
489 gint ret;
490 guint num_tries;
491 guint num_create_tries;
493 g_return_val_if_fail (path != NULL, FALSE);
494 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
496 ret = -1;
497 lock = g_strdup_printf ("%s.lock", path);
499 /* This is what the D-Bus spec says
501 * Create a lockfile name by appending ".lock" to the name of the
502 * cookie file. The server should attempt to create this file using
503 * O_CREAT | O_EXCL. If file creation fails, the lock
504 * fails. Servers should retry for a reasonable period of time,
505 * then they may choose to delete an existing lock to keep users
506 * from having to manually delete a stale lock. [1]
508 * [1] : Lockfiles are used instead of real file locking fcntl() because
509 * real locking implementations are still flaky on network filesystems
512 num_create_tries = 0;
513 #ifdef EEXISTS
514 again:
515 #endif
516 num_tries = 0;
517 while (g_file_test (lock, G_FILE_TEST_EXISTS))
519 /* sleep 10ms, then try again */
520 g_usleep (1000*10);
521 num_tries++;
522 if (num_tries == 50)
524 /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
525 * stale (nuke the it from orbit)
527 if (g_unlink (lock) != 0)
529 g_set_error (error,
530 G_IO_ERROR,
531 g_io_error_from_errno (errno),
532 _("Error deleting stale lock file “%s”: %s"),
533 lock,
534 strerror (errno));
535 goto out;
537 _log ("Deleted stale lock file '%s'", lock);
538 break;
542 ret = g_open (lock, O_CREAT |
543 #ifdef O_EXCL
544 O_EXCL,
545 #else
547 #endif
548 0700);
549 if (ret == -1)
551 #ifdef EEXISTS
552 /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
553 if (errno == EEXISTS)
555 num_create_tries++;
556 if (num_create_tries < 5)
557 goto again;
559 #endif
560 num_create_tries = num_create_tries; /* To avoid -Wunused-but-set-variable */
561 g_set_error (error,
562 G_IO_ERROR,
563 g_io_error_from_errno (errno),
564 _("Error creating lock file “%s”: %s"),
565 lock,
566 strerror (errno));
567 goto out;
570 out:
571 g_free (lock);
572 return ret;
575 static gboolean
576 keyring_release_lock (const gchar *path,
577 gint lock_fd,
578 GError **error)
580 gchar *lock;
581 gboolean ret;
583 g_return_val_if_fail (path != NULL, FALSE);
584 g_return_val_if_fail (lock_fd != -1, FALSE);
585 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
587 ret = FALSE;
588 lock = g_strdup_printf ("%s.lock", path);
589 if (close (lock_fd) != 0)
591 g_set_error (error,
592 G_IO_ERROR,
593 g_io_error_from_errno (errno),
594 _("Error closing (unlinked) lock file “%s”: %s"),
595 lock,
596 strerror (errno));
597 goto out;
599 if (g_unlink (lock) != 0)
601 g_set_error (error,
602 G_IO_ERROR,
603 g_io_error_from_errno (errno),
604 _("Error unlinking lock file “%s”: %s"),
605 lock,
606 strerror (errno));
607 goto out;
610 ret = TRUE;
612 out:
613 g_free (lock);
614 return ret;
618 /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
619 static gboolean
620 keyring_generate_entry (const gchar *cookie_context,
621 gint *out_id,
622 gchar **out_cookie,
623 GError **error)
625 gboolean ret;
626 gchar *keyring_dir;
627 gchar *path;
628 gchar *contents;
629 GError *local_error;
630 gchar **lines;
631 gint max_line_id;
632 GString *new_contents;
633 guint64 now;
634 gboolean have_id;
635 gint use_id;
636 gchar *use_cookie;
637 gboolean changed_file;
638 gint lock_fd;
640 g_return_val_if_fail (cookie_context != NULL, FALSE);
641 g_return_val_if_fail (out_id != NULL, FALSE);
642 g_return_val_if_fail (out_cookie != NULL, FALSE);
643 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
645 ret = FALSE;
646 path = NULL;
647 contents = NULL;
648 lines = NULL;
649 new_contents = NULL;
650 have_id = FALSE;
651 use_id = 0;
652 use_cookie = NULL;
653 lock_fd = -1;
655 keyring_dir = ensure_keyring_directory (error);
656 if (keyring_dir == NULL)
657 goto out;
659 path = g_build_filename (keyring_dir, cookie_context, NULL);
661 lock_fd = keyring_acquire_lock (path, error);
662 if (lock_fd == -1)
663 goto out;
665 local_error = NULL;
666 contents = NULL;
667 if (!g_file_get_contents (path,
668 &contents,
669 NULL,
670 &local_error))
672 if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
674 /* file doesn't have to exist */
675 g_error_free (local_error);
677 else
679 g_propagate_prefixed_error (error,
680 local_error,
681 _("Error opening keyring “%s” for writing: "),
682 path);
683 goto out;
687 new_contents = g_string_new (NULL);
688 now = time (NULL);
689 changed_file = FALSE;
691 max_line_id = 0;
692 if (contents != NULL)
694 guint n;
695 lines = g_strsplit (contents, "\n", 0);
696 for (n = 0; lines[n] != NULL; n++)
698 const gchar *line = lines[n];
699 gchar **tokens;
700 gchar *endp;
701 gint line_id;
702 guint64 line_when;
703 gboolean keep_entry;
705 if (line[0] == '\0')
706 continue;
708 tokens = g_strsplit (line, " ", 0);
709 if (g_strv_length (tokens) != 3)
711 g_set_error (error,
712 G_IO_ERROR,
713 G_IO_ERROR_FAILED,
714 _("Line %d of the keyring at “%s” with content “%s” is malformed"),
715 n + 1,
716 path,
717 line);
718 g_strfreev (tokens);
719 goto out;
722 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
723 if (*endp != '\0')
725 g_set_error (error,
726 G_IO_ERROR,
727 G_IO_ERROR_FAILED,
728 _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
729 n + 1,
730 path,
731 line);
732 g_strfreev (tokens);
733 goto out;
736 line_when = g_ascii_strtoll (tokens[1], &endp, 10);
737 if (*endp != '\0')
739 g_set_error (error,
740 G_IO_ERROR,
741 G_IO_ERROR_FAILED,
742 _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
743 n + 1,
744 path,
745 line);
746 g_strfreev (tokens);
747 goto out;
749 line_when = line_when; /* To avoid -Wunused-but-set-variable */
752 /* D-Bus spec says:
754 * Once the lockfile has been created, the server loads the
755 * cookie file. It should then delete any cookies that are
756 * old (the timeout can be fairly short), or more than a
757 * reasonable time in the future (so that cookies never
758 * accidentally become permanent, if the clock was set far
759 * into the future at some point). If no recent keys remain,
760 * the server may generate a new key.
763 keep_entry = TRUE;
764 if (line_when > now)
766 /* Oddball case: entry is more recent than our current wall-clock time..
767 * This is OK, it means that another server on another machine but with
768 * same $HOME wrote the entry.
770 * So discard the entry if it's more than 1 day in the future ("reasonable
771 * time in the future").
773 if (line_when - now > 24*60*60)
775 keep_entry = FALSE;
776 _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
779 else
781 /* Discard entry if it's older than 15 minutes ("can be fairly short") */
782 if (now - line_when > 15*60)
784 keep_entry = FALSE;
788 if (!keep_entry)
790 changed_file = FALSE;
792 else
794 g_string_append_printf (new_contents,
795 "%d %" G_GUINT64_FORMAT " %s\n",
796 line_id,
797 line_when,
798 tokens[2]);
799 max_line_id = MAX (line_id, max_line_id);
800 /* Only reuse entry if not older than 10 minutes.
802 * (We need a bit of grace time compared to 15 minutes above.. otherwise
803 * there's a race where we reuse the 14min59.9 secs old entry and a
804 * split-second later another server purges the now 15 minute old entry.)
806 if (now - line_when < 10 * 60)
808 if (!have_id)
810 use_id = line_id;
811 use_cookie = tokens[2]; /* steal memory */
812 tokens[2] = NULL;
813 have_id = TRUE;
817 g_strfreev (tokens);
819 } /* for each line */
821 ret = TRUE;
823 if (have_id)
825 *out_id = use_id;
826 *out_cookie = use_cookie;
827 use_cookie = NULL;
829 else
831 gchar *raw_cookie;
832 *out_id = max_line_id + 1;
833 raw_cookie = random_blob (32);
834 *out_cookie = hexencode (raw_cookie, 32);
835 g_free (raw_cookie);
837 g_string_append_printf (new_contents,
838 "%d %" G_GUINT64_FORMAT " %s\n",
839 *out_id,
840 (guint64) time (NULL),
841 *out_cookie);
842 changed_file = TRUE;
845 /* and now actually write the cookie file if there are changes (this is atomic) */
846 if (changed_file)
848 if (!g_file_set_contents (path,
849 new_contents->str,
851 error))
853 *out_id = 0;
854 *out_cookie = 0;
855 g_free (*out_cookie);
856 ret = FALSE;
857 goto out;
861 out:
863 if (lock_fd != -1)
865 GError *local_error;
866 local_error = NULL;
867 if (!keyring_release_lock (path, lock_fd, &local_error))
869 if (error != NULL)
871 if (*error == NULL)
873 *error = local_error;
875 else
877 g_prefix_error (error,
878 _("(Additionally, releasing the lock for “%s” also failed: %s) "),
879 path,
880 local_error->message);
883 else
885 g_error_free (local_error);
890 g_free (keyring_dir);
891 g_free (path);
892 g_strfreev (lines);
893 g_free (contents);
894 if (new_contents != NULL)
895 g_string_free (new_contents, TRUE);
896 g_free (use_cookie);
897 return ret;
900 /* ---------------------------------------------------------------------------------------------------- */
902 static gchar *
903 generate_sha1 (const gchar *server_challenge,
904 const gchar *client_challenge,
905 const gchar *cookie)
907 GString *str;
908 gchar *sha1;
910 str = g_string_new (server_challenge);
911 g_string_append_c (str, ':');
912 g_string_append (str, client_challenge);
913 g_string_append_c (str, ':');
914 g_string_append (str, cookie);
915 sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
916 g_string_free (str, TRUE);
918 return sha1;
921 /* ---------------------------------------------------------------------------------------------------- */
923 static GDBusAuthMechanismState
924 mechanism_server_get_state (GDBusAuthMechanism *mechanism)
926 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
928 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
929 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
931 return m->priv->state;
934 static void
935 mechanism_server_initiate (GDBusAuthMechanism *mechanism,
936 const gchar *initial_response,
937 gsize initial_response_len)
939 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
941 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
942 g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
944 m->priv->is_server = TRUE;
945 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
947 if (initial_response != NULL && strlen (initial_response) > 0)
949 #ifdef G_OS_UNIX
950 gint64 uid;
951 gchar *endp;
953 uid = g_ascii_strtoll (initial_response, &endp, 10);
954 if (*endp == '\0')
956 if (uid == getuid ())
958 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
961 #elif defined(G_OS_WIN32)
962 gchar *sid;
963 sid = _g_dbus_win32_get_user_sid ();
964 if (g_strcmp0 (initial_response, sid) == 0)
965 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
966 g_free (sid);
967 #else
968 #error Please implement for your OS
969 #endif
973 static void
974 mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
975 const gchar *data,
976 gsize data_len)
978 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
979 gchar **tokens;
980 const gchar *client_challenge;
981 const gchar *alleged_sha1;
982 gchar *sha1;
984 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
985 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
986 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
988 tokens = NULL;
989 sha1 = NULL;
991 tokens = g_strsplit (data, " ", 0);
992 if (g_strv_length (tokens) != 2)
994 g_warning ("Malformed data '%s'", data);
995 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
996 goto out;
999 client_challenge = tokens[0];
1000 alleged_sha1 = tokens[1];
1002 sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
1004 if (g_strcmp0 (sha1, alleged_sha1) == 0)
1006 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1008 else
1010 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1013 out:
1014 g_strfreev (tokens);
1015 g_free (sha1);
1018 static gchar *
1019 mechanism_server_data_send (GDBusAuthMechanism *mechanism,
1020 gsize *out_data_len)
1022 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1023 gchar *s;
1024 gint cookie_id;
1025 const gchar *cookie_context;
1026 GError *error;
1028 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1029 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1030 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1032 s = NULL;
1034 /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1035 cookie_context = "org_gtk_gdbus_general";
1037 cookie_id = -1;
1038 error = NULL;
1039 if (!keyring_generate_entry (cookie_context,
1040 &cookie_id,
1041 &m->priv->cookie,
1042 &error))
1044 g_warning ("Error adding entry to keyring: %s", error->message);
1045 g_error_free (error);
1046 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1047 goto out;
1050 m->priv->server_challenge = random_ascii_string (16);
1051 s = g_strdup_printf ("%s %d %s",
1052 cookie_context,
1053 cookie_id,
1054 m->priv->server_challenge);
1056 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1058 out:
1059 return s;
1062 static gchar *
1063 mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
1065 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1067 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1068 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1069 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1071 /* can never end up here because we are never in the REJECTED state */
1072 g_assert_not_reached ();
1074 return NULL;
1077 static void
1078 mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
1080 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1082 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1083 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1085 m->priv->is_server = FALSE;
1088 /* ---------------------------------------------------------------------------------------------------- */
1090 static GDBusAuthMechanismState
1091 mechanism_client_get_state (GDBusAuthMechanism *mechanism)
1093 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1095 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1096 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1098 return m->priv->state;
1101 static gchar *
1102 mechanism_client_initiate (GDBusAuthMechanism *mechanism,
1103 gsize *out_initial_response_len)
1105 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1106 gchar *initial_response;
1108 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1109 g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1111 m->priv->is_client = TRUE;
1112 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1114 *out_initial_response_len = -1;
1116 #ifdef G_OS_UNIX
1117 initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1118 #elif defined (G_OS_WIN32)
1119 initial_response = _g_dbus_win32_get_user_sid ();
1120 #else
1121 #error Please implement for your OS
1122 #endif
1123 g_assert (initial_response != NULL);
1125 return initial_response;
1128 static void
1129 mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
1130 const gchar *data,
1131 gsize data_len)
1133 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1134 gchar **tokens;
1135 const gchar *cookie_context;
1136 guint cookie_id;
1137 const gchar *server_challenge;
1138 gchar *client_challenge;
1139 gchar *endp;
1140 gchar *cookie;
1141 GError *error;
1142 gchar *sha1;
1144 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1145 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1146 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1148 tokens = NULL;
1149 cookie = NULL;
1150 client_challenge = NULL;
1152 tokens = g_strsplit (data, " ", 0);
1153 if (g_strv_length (tokens) != 3)
1155 g_warning ("Malformed data '%s'", data);
1156 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1157 goto out;
1160 cookie_context = tokens[0];
1161 cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1162 if (*endp != '\0')
1164 g_warning ("Malformed cookie_id '%s'", tokens[1]);
1165 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1166 goto out;
1168 server_challenge = tokens[2];
1170 error = NULL;
1171 cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1172 if (cookie == NULL)
1174 g_warning ("Problems looking up entry in keyring: %s", error->message);
1175 g_error_free (error);
1176 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1177 goto out;
1180 client_challenge = random_ascii_string (16);
1181 sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1182 m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1183 g_free (sha1);
1184 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1186 out:
1187 g_strfreev (tokens);
1188 g_free (cookie);
1189 g_free (client_challenge);
1192 static gchar *
1193 mechanism_client_data_send (GDBusAuthMechanism *mechanism,
1194 gsize *out_data_len)
1196 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1198 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1199 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1200 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1202 g_assert (m->priv->to_send != NULL);
1204 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1206 return g_strdup (m->priv->to_send);
1209 static void
1210 mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
1212 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1214 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1215 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1217 m->priv->is_client = FALSE;
1220 /* ---------------------------------------------------------------------------------------------------- */