cleanup
[platform/upstream/glib.git] / gio / gdbusauthmechanismsha1.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
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.
9  *
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.
14  *
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/>.
17  *
18  * Author: David Zeuthen <davidz@redhat.com>
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <sys/types.h>
27
28 #include <glib/gstdio.h>
29
30 #ifdef G_OS_UNIX
31 #include <unistd.h>
32 #endif
33 #ifdef G_OS_WIN32
34 #include <io.h>
35 #endif
36
37 #include "gdbusauthmechanismsha1.h"
38 #include "gcredentials.h"
39 #include "gdbuserror.h"
40 #include "gioenumtypes.h"
41 #include "gioerror.h"
42 #include "gdbusprivate.h"
43
44 #include "glibintl.h"
45
46 struct _GDBusAuthMechanismSha1Private
47 {
48   gboolean is_client;
49   gboolean is_server;
50   GDBusAuthMechanismState state;
51
52   /* used on the client side */
53   gchar *to_send;
54
55   /* used on the server side */
56   gchar *cookie;
57   gchar *server_challenge;
58 };
59
60 static gint                     mechanism_get_priority              (void);
61 static const gchar             *mechanism_get_name                  (void);
62
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);
92
93 /* ---------------------------------------------------------------------------------------------------- */
94
95 G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM)
96
97 /* ---------------------------------------------------------------------------------------------------- */
98
99 static void
100 _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
101 {
102   GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
103
104   g_free (mechanism->priv->to_send);
105
106   g_free (mechanism->priv->cookie);
107   g_free (mechanism->priv->server_challenge);
108
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);
111 }
112
113 static void
114 _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
115 {
116   GObjectClass *gobject_class;
117   GDBusAuthMechanismClass *mechanism_class;
118
119   gobject_class = G_OBJECT_CLASS (klass);
120   gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
121
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;
139 }
140
141 static void
142 _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
143 {
144   mechanism->priv = _g_dbus_auth_mechanism_sha1_get_instance_private (mechanism);
145 }
146
147 /* ---------------------------------------------------------------------------------------------------- */
148
149 static gint
150 mechanism_get_priority (void)
151 {
152   return 0;
153 }
154
155 static const gchar *
156 mechanism_get_name (void)
157 {
158   return "DBUS_COOKIE_SHA1";
159 }
160
161 static gboolean
162 mechanism_is_supported (GDBusAuthMechanism *mechanism)
163 {
164   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
165   return TRUE;
166 }
167
168 static gchar *
169 mechanism_encode_data (GDBusAuthMechanism   *mechanism,
170                        const gchar          *data,
171                        gsize                 data_len,
172                        gsize                *out_data_len)
173 {
174   return NULL;
175 }
176
177
178 static gchar *
179 mechanism_decode_data (GDBusAuthMechanism   *mechanism,
180                        const gchar          *data,
181                        gsize                 data_len,
182                        gsize                *out_data_len)
183 {
184   return NULL;
185 }
186
187 /* ---------------------------------------------------------------------------------------------------- */
188
189 static gint
190 random_ascii (void)
191 {
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;
201 }
202
203 static gchar *
204 random_ascii_string (guint len)
205 {
206   GString *challenge;
207   guint n;
208
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);
213 }
214
215 static gchar *
216 random_blob (guint len)
217 {
218   GString *challenge;
219   guint n;
220
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);
225 }
226
227 /* ---------------------------------------------------------------------------------------------------- */
228
229 /* ensure keyring dir exists and permissions are correct */
230 static gchar *
231 ensure_keyring_directory (GError **error)
232 {
233   gchar *path;
234   const gchar *e;
235
236   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
237
238   e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
239   if (e != NULL)
240     {
241       path = g_strdup (e);
242     }
243   else
244     {
245       path = g_build_filename (g_get_home_dir (),
246                                ".dbus-keyrings",
247                                NULL);
248     }
249
250   if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
251     {
252       if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL)
253         {
254 #ifdef G_OS_UNIX
255           struct stat statbuf;
256           if (stat (path, &statbuf) != 0)
257             {
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;
267             }
268           if ((statbuf.st_mode  & 0777) != 0700)
269             {
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;
279             }
280 #else
281 #ifdef __GNUC__
282 #warning Please implement permission checking on this non-UNIX platform
283 #endif
284 #endif
285         }
286         goto out;
287     }
288
289   if (g_mkdir (path, 0700) != 0)
290     {
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;
300     }
301
302 out:
303   return path;
304 }
305
306 /* ---------------------------------------------------------------------------------------------------- */
307
308 static void
309 append_nibble (GString *s, gint val)
310 {
311   g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
312 }
313
314 static gchar *
315 hexencode (const gchar *str,
316            gssize       len)
317 {
318   guint n;
319   GString *s;
320
321   if (len == -1)
322     len = strlen (str);
323
324   s = g_string_new (NULL);
325   for (n = 0; n < len; n++)
326     {
327       gint val;
328       gint upper_nibble;
329       gint lower_nibble;
330
331       val = ((const guchar *) str)[n];
332       upper_nibble = val >> 4;
333       lower_nibble = val & 0x0f;
334
335       append_nibble (s, upper_nibble);
336       append_nibble (s, lower_nibble);
337     }
338
339   return g_string_free (s, FALSE);
340 }
341
342 /* ---------------------------------------------------------------------------------------------------- */
343
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)
349 {
350   gchar *ret;
351   gchar *keyring_dir;
352   gchar *contents;
353   gchar *path;
354   guint n;
355   gchar **lines;
356
357   g_return_val_if_fail (cookie_context != NULL, NULL);
358   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
359
360   ret = NULL;
361   path = NULL;
362   contents = NULL;
363   lines = NULL;
364
365   keyring_dir = ensure_keyring_directory (error);
366   if (keyring_dir == NULL)
367     goto out;
368
369   path = g_build_filename (keyring_dir, cookie_context, NULL);
370
371   if (!g_file_get_contents (path,
372                             &contents,
373                             NULL,
374                             error))
375     {
376       g_prefix_error (error,
377                       _("Error opening keyring '%s' for reading: "),
378                       path);
379       goto out;
380     }
381   g_assert (contents != NULL);
382
383   lines = g_strsplit (contents, "\n", 0);
384   for (n = 0; lines[n] != NULL; n++)
385     {
386       const gchar *line = lines[n];
387       gchar **tokens;
388       gchar *endp;
389       gint line_id;
390       guint64 line_when;
391
392       if (line[0] == '\0')
393         continue;
394
395       tokens = g_strsplit (line, " ", 0);
396       if (g_strv_length (tokens) != 3)
397         {
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;
407         }
408
409       line_id = g_ascii_strtoll (tokens[0], &endp, 10);
410       if (*endp != '\0')
411         {
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;
421         }
422
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')
426         {
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;
436         }
437
438       if (line_id == cookie_id)
439         {
440           /* YAY, success */
441           ret = tokens[2]; /* steal pointer */
442           tokens[2] = NULL;
443           g_strfreev (tokens);
444           goto out;
445         }
446
447       g_strfreev (tokens);
448     }
449
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);
457
458  out:
459   g_free (keyring_dir);
460   g_free (path);
461   g_free (contents);
462   g_strfreev (lines);
463   return ret;
464 }
465
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       ...)
471 {
472   gchar *s;
473   va_list var_args;
474
475   va_start (var_args, message);
476   s = g_strdup_vprintf (message, var_args);
477   va_end (var_args);
478
479   /* TODO: might want to send this to syslog instead */
480   g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
481   g_free (s);
482 }
483
484 static gint
485 keyring_acquire_lock (const gchar  *path,
486                       GError      **error)
487 {
488   gchar *lock;
489   gint ret;
490   guint num_tries;
491   guint num_create_tries;
492
493   g_return_val_if_fail (path != NULL, FALSE);
494   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
495
496   ret = -1;
497   lock = g_strdup_printf ("%s.lock", path);
498
499   /* This is what the D-Bus spec says
500    *
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]
507    *
508    *  [1] : Lockfiles are used instead of real file locking fcntl() because
509    *         real locking implementations are still flaky on network filesystems
510    */
511
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))
518     {
519       /* sleep 10ms, then try again */
520       g_usleep (1000*10);
521       num_tries++;
522       if (num_tries == 50)
523         {
524           /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
525            * stale (nuke the it from orbit)
526            */
527           if (g_unlink (lock) != 0)
528             {
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;
536             }
537           _log ("Deleted stale lock file '%s'", lock);
538           break;
539         }
540     }
541
542   ret = g_open (lock, O_CREAT |
543 #ifdef O_EXCL
544                 O_EXCL,
545 #else
546                 0,
547 #endif
548                 0700);
549   if (ret == -1)
550     {
551 #ifdef EEXISTS
552       /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
553       if (errno == EEXISTS)
554         {
555           num_create_tries++;
556           if (num_create_tries < 5)
557             goto again;
558         }
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;
568     }
569
570  out:
571   g_free (lock);
572   return ret;
573 }
574
575 static gboolean
576 keyring_release_lock (const gchar  *path,
577                       gint          lock_fd,
578                       GError      **error)
579 {
580   gchar *lock;
581   gboolean ret;
582
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);
586
587   ret = FALSE;
588   lock = g_strdup_printf ("%s.lock", path);
589   if (close (lock_fd) != 0)
590     {
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;
598     }
599   if (g_unlink (lock) != 0)
600     {
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;
608     }
609
610   ret = TRUE;
611
612  out:
613   g_free (lock);
614   return ret;
615 }
616
617
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)
624 {
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;
639
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);
644
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;
654
655   keyring_dir = ensure_keyring_directory (error);
656   if (keyring_dir == NULL)
657     goto out;
658
659   path = g_build_filename (keyring_dir, cookie_context, NULL);
660
661   lock_fd = keyring_acquire_lock (path, error);
662   if (lock_fd == -1)
663     goto out;
664
665   local_error = NULL;
666   contents = NULL;
667   if (!g_file_get_contents (path,
668                             &contents,
669                             NULL,
670                             &local_error))
671     {
672       if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
673         {
674           /* file doesn't have to exist */
675           g_error_free (local_error);
676         }
677       else
678         {
679           g_propagate_prefixed_error (error,
680                                       local_error,
681                                       _("Error opening keyring '%s' for writing: "),
682                                       path);
683           goto out;
684         }
685     }
686
687   new_contents = g_string_new (NULL);
688   now = time (NULL);
689   changed_file = FALSE;
690
691   max_line_id = 0;
692   if (contents != NULL)
693     {
694       guint n;
695       lines = g_strsplit (contents, "\n", 0);
696       for (n = 0; lines[n] != NULL; n++)
697         {
698           const gchar *line = lines[n];
699           gchar **tokens;
700           gchar *endp;
701           gint line_id;
702           guint64 line_when;
703           gboolean keep_entry;
704
705           if (line[0] == '\0')
706             continue;
707
708           tokens = g_strsplit (line, " ", 0);
709           if (g_strv_length (tokens) != 3)
710             {
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;
720             }
721
722           line_id = g_ascii_strtoll (tokens[0], &endp, 10);
723           if (*endp != '\0')
724             {
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;
734             }
735
736           line_when = g_ascii_strtoll (tokens[1], &endp, 10);
737           if (*endp != '\0')
738             {
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;
748             }
749           line_when = line_when; /* To avoid -Wunused-but-set-variable */
750
751
752           /* D-Bus spec says:
753            *
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.
761            *
762            */
763           keep_entry = TRUE;
764           if (line_when > now)
765             {
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.
769                *
770                * So discard the entry if it's more than 1 day in the future ("reasonable
771                * time in the future").
772                */
773               if (line_when - now > 24*60*60)
774                 {
775                   keep_entry = FALSE;
776                   _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
777                 }
778             }
779           else
780             {
781               /* Discard entry if it's older than 15 minutes ("can be fairly short") */
782               if (now - line_when > 15*60)
783                 {
784                   keep_entry = FALSE;
785                 }
786             }
787
788           if (!keep_entry)
789             {
790               changed_file = FALSE;
791             }
792           else
793             {
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.
801                *
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.)
805                */
806               if (now - line_when < 10 * 60)
807                 {
808                   if (!have_id)
809                     {
810                       use_id = line_id;
811                       use_cookie = tokens[2]; /* steal memory */
812                       tokens[2] = NULL;
813                       have_id = TRUE;
814                     }
815                 }
816             }
817           g_strfreev (tokens);
818         }
819     } /* for each line */
820
821   ret = TRUE;
822
823   if (have_id)
824     {
825       *out_id = use_id;
826       *out_cookie = use_cookie;
827       use_cookie = NULL;
828     }
829   else
830     {
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);
836
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;
843     }
844
845   /* and now actually write the cookie file if there are changes (this is atomic) */
846   if (changed_file)
847     {
848       if (!g_file_set_contents (path,
849                                 new_contents->str,
850                                 -1,
851                                 error))
852         {
853           *out_id = 0;
854           *out_cookie = 0;
855           g_free (*out_cookie);
856           ret = FALSE;
857           goto out;
858         }
859     }
860
861  out:
862
863   if (lock_fd != -1)
864     {
865       GError *local_error;
866       local_error = NULL;
867       if (!keyring_release_lock (path, lock_fd, &local_error))
868         {
869           if (error != NULL)
870             {
871               if (*error == NULL)
872                 {
873                   *error = local_error;
874                 }
875               else
876                 {
877                   g_prefix_error (error,
878                                   _("(Additionally, releasing the lock for '%s' also failed: %s) "),
879                                   path,
880                                   local_error->message);
881                 }
882             }
883           else
884             {
885               g_error_free (local_error);
886             }
887         }
888     }
889
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;
898 }
899
900 /* ---------------------------------------------------------------------------------------------------- */
901
902 static gchar *
903 generate_sha1 (const gchar *server_challenge,
904                const gchar *client_challenge,
905                const gchar *cookie)
906 {
907   GString *str;
908   gchar *sha1;
909
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);
917
918   return sha1;
919 }
920
921 /* ---------------------------------------------------------------------------------------------------- */
922
923 static GDBusAuthMechanismState
924 mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
925 {
926   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
927
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);
930
931   return m->priv->state;
932 }
933
934 static void
935 mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
936                            const gchar          *initial_response,
937                            gsize                 initial_response_len)
938 {
939   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
940
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);
943
944   m->priv->is_server = TRUE;
945   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
946
947   if (initial_response != NULL && strlen (initial_response) > 0)
948     {
949 #ifdef G_OS_UNIX
950       gint64 uid;
951       gchar *endp;
952
953       uid = g_ascii_strtoll (initial_response, &endp, 10);
954       if (*endp == '\0')
955         {
956           if (uid == getuid ())
957             {
958               m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
959             }
960         }
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
970     }
971 }
972
973 static void
974 mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
975                                const gchar          *data,
976                                gsize                 data_len)
977 {
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;
983
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);
987
988   tokens = NULL;
989   sha1 = NULL;
990
991   tokens = g_strsplit (data, " ", 0);
992   if (g_strv_length (tokens) != 2)
993     {
994       g_warning ("Malformed data '%s'", data);
995       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
996       goto out;
997     }
998
999   client_challenge = tokens[0];
1000   alleged_sha1 = tokens[1];
1001
1002   sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
1003
1004   if (g_strcmp0 (sha1, alleged_sha1) == 0)
1005     {
1006       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1007     }
1008   else
1009     {
1010       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1011     }
1012
1013  out:
1014   g_strfreev (tokens);
1015   g_free (sha1);
1016 }
1017
1018 static gchar *
1019 mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
1020                             gsize                *out_data_len)
1021 {
1022   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1023   gchar *s;
1024   gint cookie_id;
1025   const gchar *cookie_context;
1026   GError *error;
1027
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);
1031
1032   s = NULL;
1033
1034   /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1035   cookie_context = "org_gtk_gdbus_general";
1036
1037   cookie_id = -1;
1038   error = NULL;
1039   if (!keyring_generate_entry (cookie_context,
1040                                &cookie_id,
1041                                &m->priv->cookie,
1042                                &error))
1043     {
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;
1048     }
1049
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);
1055
1056   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1057
1058  out:
1059   return s;
1060 }
1061
1062 static gchar *
1063 mechanism_server_get_reject_reason (GDBusAuthMechanism   *mechanism)
1064 {
1065   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1066
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);
1070
1071   /* can never end up here because we are never in the REJECTED state */
1072   g_assert_not_reached ();
1073
1074   return NULL;
1075 }
1076
1077 static void
1078 mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
1079 {
1080   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1081
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);
1084
1085   m->priv->is_server = FALSE;
1086 }
1087
1088 /* ---------------------------------------------------------------------------------------------------- */
1089
1090 static GDBusAuthMechanismState
1091 mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
1092 {
1093   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1094
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);
1097
1098   return m->priv->state;
1099 }
1100
1101 static gchar *
1102 mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
1103                            gsize                *out_initial_response_len)
1104 {
1105   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1106   gchar *initial_response;
1107
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);
1110
1111   m->priv->is_client = TRUE;
1112   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1113
1114   *out_initial_response_len = -1;
1115
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);
1124
1125   return initial_response;
1126 }
1127
1128 static void
1129 mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
1130                                const gchar          *data,
1131                                gsize                 data_len)
1132 {
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;
1143
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);
1147
1148   tokens = NULL;
1149   cookie = NULL;
1150   client_challenge = NULL;
1151
1152   tokens = g_strsplit (data, " ", 0);
1153   if (g_strv_length (tokens) != 3)
1154     {
1155       g_warning ("Malformed data '%s'", data);
1156       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1157       goto out;
1158     }
1159
1160   cookie_context = tokens[0];
1161   cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1162   if (*endp != '\0')
1163     {
1164       g_warning ("Malformed cookie_id '%s'", tokens[1]);
1165       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1166       goto out;
1167     }
1168   server_challenge = tokens[2];
1169
1170   error = NULL;
1171   cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1172   if (cookie == NULL)
1173     {
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;
1178     }
1179
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;
1185
1186  out:
1187   g_strfreev (tokens);
1188   g_free (cookie);
1189   g_free (client_challenge);
1190 }
1191
1192 static gchar *
1193 mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
1194                             gsize                *out_data_len)
1195 {
1196   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1197
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);
1201
1202   g_assert (m->priv->to_send != NULL);
1203
1204   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1205
1206   return g_strdup (m->priv->to_send);
1207 }
1208
1209 static void
1210 mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
1211 {
1212   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1213
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);
1216
1217   m->priv->is_client = FALSE;
1218 }
1219
1220 /* ---------------------------------------------------------------------------------------------------- */