gmem: Only evaluate pointer argument to g_clear_pointer() once
[glib.git] / gio / gdbusauthmechanismsha1.c
blob553bcdc3285e890d4a908fa0a67610bf785b3e11
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.1 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 int errsv = errno;
259 g_set_error (error,
260 G_IO_ERROR,
261 g_io_error_from_errno (errsv),
262 _("Error when getting information for directory “%s”: %s"),
263 path,
264 g_strerror (errsv));
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 (guint) (statbuf.st_mode & 0777));
277 g_free (path);
278 path = NULL;
279 goto out;
281 #else
282 #ifdef __GNUC__
283 #pragma GCC diagnostic push
284 #pragma GCC diagnostic warning "-Wcpp"
285 #warning Please implement permission checking on this non-UNIX platform
286 #pragma GCC diagnostic pop
287 #endif
288 #endif
290 goto out;
293 if (g_mkdir (path, 0700) != 0)
295 int errsv = errno;
296 g_set_error (error,
297 G_IO_ERROR,
298 g_io_error_from_errno (errsv),
299 _("Error creating directory “%s”: %s"),
300 path,
301 g_strerror (errsv));
302 g_free (path);
303 path = NULL;
304 goto out;
307 out:
308 return path;
311 /* ---------------------------------------------------------------------------------------------------- */
313 /* looks up an entry in the keyring */
314 static gchar *
315 keyring_lookup_entry (const gchar *cookie_context,
316 gint cookie_id,
317 GError **error)
319 gchar *ret;
320 gchar *keyring_dir;
321 gchar *contents;
322 gchar *path;
323 guint n;
324 gchar **lines;
326 g_return_val_if_fail (cookie_context != NULL, NULL);
327 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
329 ret = NULL;
330 path = NULL;
331 contents = NULL;
332 lines = NULL;
334 keyring_dir = ensure_keyring_directory (error);
335 if (keyring_dir == NULL)
336 goto out;
338 path = g_build_filename (keyring_dir, cookie_context, NULL);
340 if (!g_file_get_contents (path,
341 &contents,
342 NULL,
343 error))
345 g_prefix_error (error,
346 _("Error opening keyring “%s” for reading: "),
347 path);
348 goto out;
350 g_assert (contents != NULL);
352 lines = g_strsplit (contents, "\n", 0);
353 for (n = 0; lines[n] != NULL; n++)
355 const gchar *line = lines[n];
356 gchar **tokens;
357 gchar *endp;
358 gint line_id;
360 if (line[0] == '\0')
361 continue;
363 tokens = g_strsplit (line, " ", 0);
364 if (g_strv_length (tokens) != 3)
366 g_set_error (error,
367 G_IO_ERROR,
368 G_IO_ERROR_FAILED,
369 _("Line %d of the keyring at “%s” with content “%s” is malformed"),
370 n + 1,
371 path,
372 line);
373 g_strfreev (tokens);
374 goto out;
377 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
378 if (*endp != '\0')
380 g_set_error (error,
381 G_IO_ERROR,
382 G_IO_ERROR_FAILED,
383 _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
384 n + 1,
385 path,
386 line);
387 g_strfreev (tokens);
388 goto out;
391 (void)g_ascii_strtoll (tokens[1], &endp, 10); /* do not care what the timestamp is */
392 if (*endp != '\0')
394 g_set_error (error,
395 G_IO_ERROR,
396 G_IO_ERROR_FAILED,
397 _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
398 n + 1,
399 path,
400 line);
401 g_strfreev (tokens);
402 goto out;
405 if (line_id == cookie_id)
407 /* YAY, success */
408 ret = tokens[2]; /* steal pointer */
409 tokens[2] = NULL;
410 g_strfreev (tokens);
411 goto out;
414 g_strfreev (tokens);
417 /* BOOH, didn't find the cookie */
418 g_set_error (error,
419 G_IO_ERROR,
420 G_IO_ERROR_FAILED,
421 _("Didn’t find cookie with id %d in the keyring at “%s”"),
422 cookie_id,
423 path);
425 out:
426 g_free (keyring_dir);
427 g_free (path);
428 g_free (contents);
429 g_strfreev (lines);
430 return ret;
433 /* function for logging important events that the system administrator should take notice of */
434 G_GNUC_PRINTF(1, 2)
435 static void
436 _log (const gchar *message,
437 ...)
439 gchar *s;
440 va_list var_args;
442 va_start (var_args, message);
443 s = g_strdup_vprintf (message, var_args);
444 va_end (var_args);
446 /* TODO: might want to send this to syslog instead */
447 g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
448 g_free (s);
451 static gint
452 keyring_acquire_lock (const gchar *path,
453 GError **error)
455 gchar *lock;
456 gint ret;
457 guint num_tries;
458 #ifdef EEXISTS
459 guint num_create_tries;
460 #endif
461 int errsv;
463 g_return_val_if_fail (path != NULL, FALSE);
464 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
466 ret = -1;
467 lock = g_strdup_printf ("%s.lock", path);
469 /* This is what the D-Bus spec says
471 * Create a lockfile name by appending ".lock" to the name of the
472 * cookie file. The server should attempt to create this file using
473 * O_CREAT | O_EXCL. If file creation fails, the lock
474 * fails. Servers should retry for a reasonable period of time,
475 * then they may choose to delete an existing lock to keep users
476 * from having to manually delete a stale lock. [1]
478 * [1] : Lockfiles are used instead of real file locking fcntl() because
479 * real locking implementations are still flaky on network filesystems
482 #ifdef EEXISTS
483 num_create_tries = 0;
484 again:
485 #endif
486 num_tries = 0;
487 while (g_file_test (lock, G_FILE_TEST_EXISTS))
489 /* sleep 10ms, then try again */
490 g_usleep (1000*10);
491 num_tries++;
492 if (num_tries == 50)
494 /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
495 * stale (nuke the it from orbit)
497 if (g_unlink (lock) != 0)
499 errsv = errno;
500 g_set_error (error,
501 G_IO_ERROR,
502 g_io_error_from_errno (errsv),
503 _("Error deleting stale lock file “%s”: %s"),
504 lock,
505 g_strerror (errsv));
506 goto out;
508 _log ("Deleted stale lock file '%s'", lock);
509 break;
513 ret = g_open (lock, O_CREAT |
514 #ifdef O_EXCL
515 O_EXCL,
516 #else
518 #endif
519 0700);
520 errsv = errno;
521 if (ret == -1)
523 #ifdef EEXISTS
524 /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
525 if (errsv == EEXISTS)
527 num_create_tries++;
528 if (num_create_tries < 5)
529 goto again;
531 #endif
532 g_set_error (error,
533 G_IO_ERROR,
534 g_io_error_from_errno (errsv),
535 _("Error creating lock file “%s”: %s"),
536 lock,
537 g_strerror (errsv));
538 goto out;
541 out:
542 g_free (lock);
543 return ret;
546 static gboolean
547 keyring_release_lock (const gchar *path,
548 gint lock_fd,
549 GError **error)
551 gchar *lock;
552 gboolean ret;
554 g_return_val_if_fail (path != NULL, FALSE);
555 g_return_val_if_fail (lock_fd != -1, FALSE);
556 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
558 ret = FALSE;
559 lock = g_strdup_printf ("%s.lock", path);
560 if (close (lock_fd) != 0)
562 int errsv = errno;
563 g_set_error (error,
564 G_IO_ERROR,
565 g_io_error_from_errno (errsv),
566 _("Error closing (unlinked) lock file “%s”: %s"),
567 lock,
568 g_strerror (errsv));
569 goto out;
571 if (g_unlink (lock) != 0)
573 int errsv = errno;
574 g_set_error (error,
575 G_IO_ERROR,
576 g_io_error_from_errno (errsv),
577 _("Error unlinking lock file “%s”: %s"),
578 lock,
579 g_strerror (errsv));
580 goto out;
583 ret = TRUE;
585 out:
586 g_free (lock);
587 return ret;
591 /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
592 static gboolean
593 keyring_generate_entry (const gchar *cookie_context,
594 gint *out_id,
595 gchar **out_cookie,
596 GError **error)
598 gboolean ret;
599 gchar *keyring_dir;
600 gchar *path;
601 gchar *contents;
602 GError *local_error;
603 gchar **lines;
604 gint max_line_id;
605 GString *new_contents;
606 gint64 now;
607 gboolean have_id;
608 gint use_id;
609 gchar *use_cookie;
610 gboolean changed_file;
611 gint lock_fd;
613 g_return_val_if_fail (cookie_context != NULL, FALSE);
614 g_return_val_if_fail (out_id != NULL, FALSE);
615 g_return_val_if_fail (out_cookie != NULL, FALSE);
616 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
618 ret = FALSE;
619 path = NULL;
620 contents = NULL;
621 lines = NULL;
622 new_contents = NULL;
623 have_id = FALSE;
624 use_id = 0;
625 use_cookie = NULL;
626 lock_fd = -1;
628 keyring_dir = ensure_keyring_directory (error);
629 if (keyring_dir == NULL)
630 goto out;
632 path = g_build_filename (keyring_dir, cookie_context, NULL);
634 lock_fd = keyring_acquire_lock (path, error);
635 if (lock_fd == -1)
636 goto out;
638 local_error = NULL;
639 contents = NULL;
640 if (!g_file_get_contents (path,
641 &contents,
642 NULL,
643 &local_error))
645 if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
647 /* file doesn't have to exist */
648 g_error_free (local_error);
650 else
652 g_propagate_prefixed_error (error,
653 local_error,
654 _("Error opening keyring “%s” for writing: "),
655 path);
656 goto out;
660 new_contents = g_string_new (NULL);
661 now = g_get_real_time () / G_USEC_PER_SEC;
662 changed_file = FALSE;
664 max_line_id = 0;
665 if (contents != NULL)
667 guint n;
668 lines = g_strsplit (contents, "\n", 0);
669 for (n = 0; lines[n] != NULL; n++)
671 const gchar *line = lines[n];
672 gchar **tokens;
673 gchar *endp;
674 gint line_id;
675 gint64 line_when;
676 gboolean keep_entry;
678 if (line[0] == '\0')
679 continue;
681 tokens = g_strsplit (line, " ", 0);
682 if (g_strv_length (tokens) != 3)
684 g_set_error (error,
685 G_IO_ERROR,
686 G_IO_ERROR_FAILED,
687 _("Line %d of the keyring at “%s” with content “%s” is malformed"),
688 n + 1,
689 path,
690 line);
691 g_strfreev (tokens);
692 goto out;
695 line_id = g_ascii_strtoll (tokens[0], &endp, 10);
696 if (*endp != '\0')
698 g_set_error (error,
699 G_IO_ERROR,
700 G_IO_ERROR_FAILED,
701 _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
702 n + 1,
703 path,
704 line);
705 g_strfreev (tokens);
706 goto out;
709 line_when = g_ascii_strtoll (tokens[1], &endp, 10);
710 if (*endp != '\0')
712 g_set_error (error,
713 G_IO_ERROR,
714 G_IO_ERROR_FAILED,
715 _("Second token of 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;
724 /* D-Bus spec says:
726 * Once the lockfile has been created, the server loads the
727 * cookie file. It should then delete any cookies that are
728 * old (the timeout can be fairly short), or more than a
729 * reasonable time in the future (so that cookies never
730 * accidentally become permanent, if the clock was set far
731 * into the future at some point). If no recent keys remain,
732 * the server may generate a new key.
735 keep_entry = TRUE;
736 if (line_when > now)
738 /* Oddball case: entry is more recent than our current wall-clock time..
739 * This is OK, it means that another server on another machine but with
740 * same $HOME wrote the entry.
742 * So discard the entry if it's more than 1 day in the future ("reasonable
743 * time in the future").
745 if (line_when - now > 24*60*60)
747 keep_entry = FALSE;
748 _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
751 else
753 /* Discard entry if it's older than 15 minutes ("can be fairly short") */
754 if (now - line_when > 15*60)
756 keep_entry = FALSE;
760 if (!keep_entry)
762 changed_file = FALSE;
764 else
766 g_string_append_printf (new_contents,
767 "%d %" G_GUINT64_FORMAT " %s\n",
768 line_id,
769 line_when,
770 tokens[2]);
771 max_line_id = MAX (line_id, max_line_id);
772 /* Only reuse entry if not older than 10 minutes.
774 * (We need a bit of grace time compared to 15 minutes above.. otherwise
775 * there's a race where we reuse the 14min59.9 secs old entry and a
776 * split-second later another server purges the now 15 minute old entry.)
778 if (now - line_when < 10 * 60)
780 if (!have_id)
782 use_id = line_id;
783 use_cookie = tokens[2]; /* steal memory */
784 tokens[2] = NULL;
785 have_id = TRUE;
789 g_strfreev (tokens);
791 } /* for each line */
793 ret = TRUE;
795 if (have_id)
797 *out_id = use_id;
798 *out_cookie = use_cookie;
799 use_cookie = NULL;
801 else
803 gchar *raw_cookie;
804 *out_id = max_line_id + 1;
805 raw_cookie = random_blob (32);
806 *out_cookie = _g_dbus_hexencode (raw_cookie, 32);
807 g_free (raw_cookie);
809 g_string_append_printf (new_contents,
810 "%d %" G_GINT64_FORMAT " %s\n",
811 *out_id,
812 g_get_real_time () / G_USEC_PER_SEC,
813 *out_cookie);
814 changed_file = TRUE;
817 /* and now actually write the cookie file if there are changes (this is atomic) */
818 if (changed_file)
820 if (!g_file_set_contents (path,
821 new_contents->str,
823 error))
825 *out_id = 0;
826 *out_cookie = 0;
827 g_free (*out_cookie);
828 ret = FALSE;
829 goto out;
833 out:
835 if (lock_fd != -1)
837 GError *local_error;
838 local_error = NULL;
839 if (!keyring_release_lock (path, lock_fd, &local_error))
841 if (error != NULL)
843 if (*error == NULL)
845 *error = local_error;
847 else
849 g_prefix_error (error,
850 _("(Additionally, releasing the lock for “%s” also failed: %s) "),
851 path,
852 local_error->message);
855 else
857 g_error_free (local_error);
862 g_free (keyring_dir);
863 g_free (path);
864 g_strfreev (lines);
865 g_free (contents);
866 if (new_contents != NULL)
867 g_string_free (new_contents, TRUE);
868 g_free (use_cookie);
869 return ret;
872 /* ---------------------------------------------------------------------------------------------------- */
874 static gchar *
875 generate_sha1 (const gchar *server_challenge,
876 const gchar *client_challenge,
877 const gchar *cookie)
879 GString *str;
880 gchar *sha1;
882 str = g_string_new (server_challenge);
883 g_string_append_c (str, ':');
884 g_string_append (str, client_challenge);
885 g_string_append_c (str, ':');
886 g_string_append (str, cookie);
887 sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
888 g_string_free (str, TRUE);
890 return sha1;
893 /* ---------------------------------------------------------------------------------------------------- */
895 static GDBusAuthMechanismState
896 mechanism_server_get_state (GDBusAuthMechanism *mechanism)
898 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
900 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
901 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
903 return m->priv->state;
906 static void
907 mechanism_server_initiate (GDBusAuthMechanism *mechanism,
908 const gchar *initial_response,
909 gsize initial_response_len)
911 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
913 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
914 g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
916 m->priv->is_server = TRUE;
917 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
919 if (initial_response != NULL && initial_response_len > 0)
921 #ifdef G_OS_UNIX
922 gint64 uid;
923 gchar *endp;
925 uid = g_ascii_strtoll (initial_response, &endp, 10);
926 if (*endp == '\0')
928 if (uid == getuid ())
930 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
933 #elif defined(G_OS_WIN32)
934 gchar *sid;
935 sid = _g_dbus_win32_get_user_sid ();
936 if (g_strcmp0 (initial_response, sid) == 0)
937 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
938 g_free (sid);
939 #else
940 #error Please implement for your OS
941 #endif
945 static void
946 mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
947 const gchar *data,
948 gsize data_len)
950 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
951 gchar **tokens;
952 const gchar *client_challenge;
953 const gchar *alleged_sha1;
954 gchar *sha1;
956 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
957 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
958 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
960 tokens = NULL;
961 sha1 = NULL;
963 tokens = g_strsplit (data, " ", 0);
964 if (g_strv_length (tokens) != 2)
966 g_warning ("Malformed data '%s'", data);
967 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
968 goto out;
971 client_challenge = tokens[0];
972 alleged_sha1 = tokens[1];
974 sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
976 if (g_strcmp0 (sha1, alleged_sha1) == 0)
978 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
980 else
982 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
985 out:
986 g_strfreev (tokens);
987 g_free (sha1);
990 static gchar *
991 mechanism_server_data_send (GDBusAuthMechanism *mechanism,
992 gsize *out_data_len)
994 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
995 gchar *s;
996 gint cookie_id;
997 const gchar *cookie_context;
998 GError *error;
1000 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1001 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1002 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1004 s = NULL;
1005 *out_data_len = 0;
1007 /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1008 cookie_context = "org_gtk_gdbus_general";
1010 cookie_id = -1;
1011 error = NULL;
1012 if (!keyring_generate_entry (cookie_context,
1013 &cookie_id,
1014 &m->priv->cookie,
1015 &error))
1017 g_warning ("Error adding entry to keyring: %s", error->message);
1018 g_error_free (error);
1019 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1020 goto out;
1023 m->priv->server_challenge = random_ascii_string (16);
1024 s = g_strdup_printf ("%s %d %s",
1025 cookie_context,
1026 cookie_id,
1027 m->priv->server_challenge);
1028 *out_data_len = strlen (s);
1030 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1032 out:
1033 return s;
1036 static gchar *
1037 mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
1039 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1041 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1042 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1043 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1045 /* can never end up here because we are never in the REJECTED state */
1046 g_assert_not_reached ();
1048 return NULL;
1051 static void
1052 mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
1054 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1056 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1057 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1059 m->priv->is_server = FALSE;
1062 /* ---------------------------------------------------------------------------------------------------- */
1064 static GDBusAuthMechanismState
1065 mechanism_client_get_state (GDBusAuthMechanism *mechanism)
1067 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1069 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1070 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1072 return m->priv->state;
1075 static gchar *
1076 mechanism_client_initiate (GDBusAuthMechanism *mechanism,
1077 gsize *out_initial_response_len)
1079 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1080 gchar *initial_response;
1082 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1083 g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1085 m->priv->is_client = TRUE;
1086 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1088 *out_initial_response_len = 0;
1090 #ifdef G_OS_UNIX
1091 initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1092 *out_initial_response_len = strlen (initial_response);
1093 #elif defined (G_OS_WIN32)
1094 initial_response = _g_dbus_win32_get_user_sid ();
1095 *out_initial_response_len = strlen (initial_response);
1096 #else
1097 #error Please implement for your OS
1098 #endif
1099 g_assert (initial_response != NULL);
1101 return initial_response;
1104 static void
1105 mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
1106 const gchar *data,
1107 gsize data_len)
1109 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1110 gchar **tokens;
1111 const gchar *cookie_context;
1112 guint cookie_id;
1113 const gchar *server_challenge;
1114 gchar *client_challenge;
1115 gchar *endp;
1116 gchar *cookie;
1117 GError *error;
1118 gchar *sha1;
1120 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1121 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1122 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1124 tokens = NULL;
1125 cookie = NULL;
1126 client_challenge = NULL;
1128 tokens = g_strsplit (data, " ", 0);
1129 if (g_strv_length (tokens) != 3)
1131 g_warning ("Malformed data '%s'", data);
1132 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1133 goto out;
1136 cookie_context = tokens[0];
1137 cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1138 if (*endp != '\0')
1140 g_warning ("Malformed cookie_id '%s'", tokens[1]);
1141 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1142 goto out;
1144 server_challenge = tokens[2];
1146 error = NULL;
1147 cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1148 if (cookie == NULL)
1150 g_warning ("Problems looking up entry in keyring: %s", error->message);
1151 g_error_free (error);
1152 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1153 goto out;
1156 client_challenge = random_ascii_string (16);
1157 sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1158 m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1159 g_free (sha1);
1160 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1162 out:
1163 g_strfreev (tokens);
1164 g_free (cookie);
1165 g_free (client_challenge);
1168 static gchar *
1169 mechanism_client_data_send (GDBusAuthMechanism *mechanism,
1170 gsize *out_data_len)
1172 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1174 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1175 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1176 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1178 g_assert (m->priv->to_send != NULL);
1180 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1182 *out_data_len = strlen (m->priv->to_send);
1183 return g_strdup (m->priv->to_send);
1186 static void
1187 mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
1189 GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1191 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1192 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1194 m->priv->is_client = FALSE;
1197 /* ---------------------------------------------------------------------------------------------------- */