Imported Upstream version 2.55.0
[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.1 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               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;
268             }
269           if ((statbuf.st_mode  & 0777) != 0700)
270             {
271               g_set_error (error,
272                            G_IO_ERROR,
273                            G_IO_ERROR_FAILED,
274                            _("Permissions on directory “%s” are malformed. Expected mode 0700, got 0%o"),
275                            path,
276                            statbuf.st_mode & 0777);
277               g_free (path);
278               path = NULL;
279               goto out;
280             }
281 #else
282 #ifdef __GNUC__
283 #warning Please implement permission checking on this non-UNIX platform
284 #endif
285 #endif
286         }
287         goto out;
288     }
289
290   if (g_mkdir (path, 0700) != 0)
291     {
292       int errsv = errno;
293       g_set_error (error,
294                    G_IO_ERROR,
295                    g_io_error_from_errno (errsv),
296                    _("Error creating directory “%s”: %s"),
297                    path,
298                    g_strerror (errsv));
299       g_free (path);
300       path = NULL;
301       goto out;
302     }
303
304 out:
305   return path;
306 }
307
308 /* ---------------------------------------------------------------------------------------------------- */
309
310 static void
311 append_nibble (GString *s, gint val)
312 {
313   g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
314 }
315
316 static gchar *
317 hexencode (const gchar *str,
318            gssize       len)
319 {
320   guint n;
321   GString *s;
322
323   if (len == -1)
324     len = strlen (str);
325
326   s = g_string_new (NULL);
327   for (n = 0; n < len; n++)
328     {
329       gint val;
330       gint upper_nibble;
331       gint lower_nibble;
332
333       val = ((const guchar *) str)[n];
334       upper_nibble = val >> 4;
335       lower_nibble = val & 0x0f;
336
337       append_nibble (s, upper_nibble);
338       append_nibble (s, lower_nibble);
339     }
340
341   return g_string_free (s, FALSE);
342 }
343
344 /* ---------------------------------------------------------------------------------------------------- */
345
346 /* looks up an entry in the keyring */
347 static gchar *
348 keyring_lookup_entry (const gchar  *cookie_context,
349                       gint          cookie_id,
350                       GError      **error)
351 {
352   gchar *ret;
353   gchar *keyring_dir;
354   gchar *contents;
355   gchar *path;
356   guint n;
357   gchar **lines;
358
359   g_return_val_if_fail (cookie_context != NULL, NULL);
360   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
361
362   ret = NULL;
363   path = NULL;
364   contents = NULL;
365   lines = NULL;
366
367   keyring_dir = ensure_keyring_directory (error);
368   if (keyring_dir == NULL)
369     goto out;
370
371   path = g_build_filename (keyring_dir, cookie_context, NULL);
372
373   if (!g_file_get_contents (path,
374                             &contents,
375                             NULL,
376                             error))
377     {
378       g_prefix_error (error,
379                       _("Error opening keyring “%s” for reading: "),
380                       path);
381       goto out;
382     }
383   g_assert (contents != NULL);
384
385   lines = g_strsplit (contents, "\n", 0);
386   for (n = 0; lines[n] != NULL; n++)
387     {
388       const gchar *line = lines[n];
389       gchar **tokens;
390       gchar *endp;
391       gint line_id;
392
393       if (line[0] == '\0')
394         continue;
395
396       tokens = g_strsplit (line, " ", 0);
397       if (g_strv_length (tokens) != 3)
398         {
399           g_set_error (error,
400                        G_IO_ERROR,
401                        G_IO_ERROR_FAILED,
402                        _("Line %d of the keyring at “%s” with content “%s” is malformed"),
403                        n + 1,
404                        path,
405                        line);
406           g_strfreev (tokens);
407           goto out;
408         }
409
410       line_id = g_ascii_strtoll (tokens[0], &endp, 10);
411       if (*endp != '\0')
412         {
413           g_set_error (error,
414                        G_IO_ERROR,
415                        G_IO_ERROR_FAILED,
416                        _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
417                        n + 1,
418                        path,
419                        line);
420           g_strfreev (tokens);
421           goto out;
422         }
423
424       (void)g_ascii_strtoll (tokens[1], &endp, 10); /* do not care what the timestamp is */
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 #ifdef EEXISTS
492   guint num_create_tries;
493 #endif
494   int errsv;
495
496   g_return_val_if_fail (path != NULL, FALSE);
497   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
498
499   ret = -1;
500   lock = g_strdup_printf ("%s.lock", path);
501
502   /* This is what the D-Bus spec says
503    *
504    *  Create a lockfile name by appending ".lock" to the name of the
505    *  cookie file. The server should attempt to create this file using
506    *  O_CREAT | O_EXCL. If file creation fails, the lock
507    *  fails. Servers should retry for a reasonable period of time,
508    *  then they may choose to delete an existing lock to keep users
509    *  from having to manually delete a stale lock. [1]
510    *
511    *  [1] : Lockfiles are used instead of real file locking fcntl() because
512    *         real locking implementations are still flaky on network filesystems
513    */
514
515 #ifdef EEXISTS
516   num_create_tries = 0;
517  again:
518 #endif
519   num_tries = 0;
520   while (g_file_test (lock, G_FILE_TEST_EXISTS))
521     {
522       /* sleep 10ms, then try again */
523       g_usleep (1000*10);
524       num_tries++;
525       if (num_tries == 50)
526         {
527           /* ok, we slept 50*10ms = 0.5 seconds. Conclude that the lock file must be
528            * stale (nuke the it from orbit)
529            */
530           if (g_unlink (lock) != 0)
531             {
532               errsv = errno;
533               g_set_error (error,
534                            G_IO_ERROR,
535                            g_io_error_from_errno (errsv),
536                            _("Error deleting stale lock file “%s”: %s"),
537                            lock,
538                            g_strerror (errsv));
539               goto out;
540             }
541           _log ("Deleted stale lock file '%s'", lock);
542           break;
543         }
544     }
545
546   ret = g_open (lock, O_CREAT |
547 #ifdef O_EXCL
548                 O_EXCL,
549 #else
550                 0,
551 #endif
552                 0700);
553   errsv = errno;
554   if (ret == -1)
555     {
556 #ifdef EEXISTS
557       /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
558       if (errsv == EEXISTS)
559         {
560           num_create_tries++;
561           if (num_create_tries < 5)
562             goto again;
563         }
564 #endif
565       g_set_error (error,
566                    G_IO_ERROR,
567                    g_io_error_from_errno (errsv),
568                    _("Error creating lock file “%s”: %s"),
569                    lock,
570                    g_strerror (errsv));
571       goto out;
572     }
573
574  out:
575   g_free (lock);
576   return ret;
577 }
578
579 static gboolean
580 keyring_release_lock (const gchar  *path,
581                       gint          lock_fd,
582                       GError      **error)
583 {
584   gchar *lock;
585   gboolean ret;
586
587   g_return_val_if_fail (path != NULL, FALSE);
588   g_return_val_if_fail (lock_fd != -1, FALSE);
589   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
590
591   ret = FALSE;
592   lock = g_strdup_printf ("%s.lock", path);
593   if (close (lock_fd) != 0)
594     {
595       int errsv = errno;
596       g_set_error (error,
597                    G_IO_ERROR,
598                    g_io_error_from_errno (errsv),
599                    _("Error closing (unlinked) lock file “%s”: %s"),
600                    lock,
601                    g_strerror (errsv));
602       goto out;
603     }
604   if (g_unlink (lock) != 0)
605     {
606       int errsv = errno;
607       g_set_error (error,
608                    G_IO_ERROR,
609                    g_io_error_from_errno (errsv),
610                    _("Error unlinking lock file “%s”: %s"),
611                    lock,
612                    g_strerror (errsv));
613       goto out;
614     }
615
616   ret = TRUE;
617
618  out:
619   g_free (lock);
620   return ret;
621 }
622
623
624 /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
625 static gboolean
626 keyring_generate_entry (const gchar  *cookie_context,
627                         gint         *out_id,
628                         gchar       **out_cookie,
629                         GError      **error)
630 {
631   gboolean ret;
632   gchar *keyring_dir;
633   gchar *path;
634   gchar *contents;
635   GError *local_error;
636   gchar **lines;
637   gint max_line_id;
638   GString *new_contents;
639   guint64 now;
640   gboolean have_id;
641   gint use_id;
642   gchar *use_cookie;
643   gboolean changed_file;
644   gint lock_fd;
645
646   g_return_val_if_fail (cookie_context != NULL, FALSE);
647   g_return_val_if_fail (out_id != NULL, FALSE);
648   g_return_val_if_fail (out_cookie != NULL, FALSE);
649   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
650
651   ret = FALSE;
652   path = NULL;
653   contents = NULL;
654   lines = NULL;
655   new_contents = NULL;
656   have_id = FALSE;
657   use_id = 0;
658   use_cookie = NULL;
659   lock_fd = -1;
660
661   keyring_dir = ensure_keyring_directory (error);
662   if (keyring_dir == NULL)
663     goto out;
664
665   path = g_build_filename (keyring_dir, cookie_context, NULL);
666
667   lock_fd = keyring_acquire_lock (path, error);
668   if (lock_fd == -1)
669     goto out;
670
671   local_error = NULL;
672   contents = NULL;
673   if (!g_file_get_contents (path,
674                             &contents,
675                             NULL,
676                             &local_error))
677     {
678       if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
679         {
680           /* file doesn't have to exist */
681           g_error_free (local_error);
682         }
683       else
684         {
685           g_propagate_prefixed_error (error,
686                                       local_error,
687                                       _("Error opening keyring “%s” for writing: "),
688                                       path);
689           goto out;
690         }
691     }
692
693   new_contents = g_string_new (NULL);
694   now = time (NULL);
695   changed_file = FALSE;
696
697   max_line_id = 0;
698   if (contents != NULL)
699     {
700       guint n;
701       lines = g_strsplit (contents, "\n", 0);
702       for (n = 0; lines[n] != NULL; n++)
703         {
704           const gchar *line = lines[n];
705           gchar **tokens;
706           gchar *endp;
707           gint line_id;
708           guint64 line_when;
709           gboolean keep_entry;
710
711           if (line[0] == '\0')
712             continue;
713
714           tokens = g_strsplit (line, " ", 0);
715           if (g_strv_length (tokens) != 3)
716             {
717               g_set_error (error,
718                            G_IO_ERROR,
719                            G_IO_ERROR_FAILED,
720                            _("Line %d of the keyring at “%s” with content “%s” is malformed"),
721                            n + 1,
722                            path,
723                            line);
724               g_strfreev (tokens);
725               goto out;
726             }
727
728           line_id = g_ascii_strtoll (tokens[0], &endp, 10);
729           if (*endp != '\0')
730             {
731               g_set_error (error,
732                            G_IO_ERROR,
733                            G_IO_ERROR_FAILED,
734                            _("First token of line %d of the keyring at “%s” with content “%s” is malformed"),
735                            n + 1,
736                            path,
737                            line);
738               g_strfreev (tokens);
739               goto out;
740             }
741
742           line_when = g_ascii_strtoll (tokens[1], &endp, 10);
743           if (*endp != '\0')
744             {
745               g_set_error (error,
746                            G_IO_ERROR,
747                            G_IO_ERROR_FAILED,
748                            _("Second token of line %d of the keyring at “%s” with content “%s” is malformed"),
749                            n + 1,
750                            path,
751                            line);
752               g_strfreev (tokens);
753               goto out;
754             }
755
756
757           /* D-Bus spec says:
758            *
759            *  Once the lockfile has been created, the server loads the
760            *  cookie file. It should then delete any cookies that are
761            *  old (the timeout can be fairly short), or more than a
762            *  reasonable time in the future (so that cookies never
763            *  accidentally become permanent, if the clock was set far
764            *  into the future at some point). If no recent keys remain,
765            *  the server may generate a new key.
766            *
767            */
768           keep_entry = TRUE;
769           if (line_when > now)
770             {
771               /* Oddball case: entry is more recent than our current wall-clock time..
772                * This is OK, it means that another server on another machine but with
773                * same $HOME wrote the entry.
774                *
775                * So discard the entry if it's more than 1 day in the future ("reasonable
776                * time in the future").
777                */
778               if (line_when - now > 24*60*60)
779                 {
780                   keep_entry = FALSE;
781                   _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
782                 }
783             }
784           else
785             {
786               /* Discard entry if it's older than 15 minutes ("can be fairly short") */
787               if (now - line_when > 15*60)
788                 {
789                   keep_entry = FALSE;
790                 }
791             }
792
793           if (!keep_entry)
794             {
795               changed_file = FALSE;
796             }
797           else
798             {
799               g_string_append_printf (new_contents,
800                                       "%d %" G_GUINT64_FORMAT " %s\n",
801                                       line_id,
802                                       line_when,
803                                       tokens[2]);
804               max_line_id = MAX (line_id, max_line_id);
805               /* Only reuse entry if not older than 10 minutes.
806                *
807                * (We need a bit of grace time compared to 15 minutes above.. otherwise
808                * there's a race where we reuse the 14min59.9 secs old entry and a
809                * split-second later another server purges the now 15 minute old entry.)
810                */
811               if (now - line_when < 10 * 60)
812                 {
813                   if (!have_id)
814                     {
815                       use_id = line_id;
816                       use_cookie = tokens[2]; /* steal memory */
817                       tokens[2] = NULL;
818                       have_id = TRUE;
819                     }
820                 }
821             }
822           g_strfreev (tokens);
823         }
824     } /* for each line */
825
826   ret = TRUE;
827
828   if (have_id)
829     {
830       *out_id = use_id;
831       *out_cookie = use_cookie;
832       use_cookie = NULL;
833     }
834   else
835     {
836       gchar *raw_cookie;
837       *out_id = max_line_id + 1;
838       raw_cookie = random_blob (32);
839       *out_cookie = hexencode (raw_cookie, 32);
840       g_free (raw_cookie);
841
842       g_string_append_printf (new_contents,
843                               "%d %" G_GUINT64_FORMAT " %s\n",
844                               *out_id,
845                               (guint64) time (NULL),
846                               *out_cookie);
847       changed_file = TRUE;
848     }
849
850   /* and now actually write the cookie file if there are changes (this is atomic) */
851   if (changed_file)
852     {
853       if (!g_file_set_contents (path,
854                                 new_contents->str,
855                                 -1,
856                                 error))
857         {
858           *out_id = 0;
859           *out_cookie = 0;
860           g_free (*out_cookie);
861           ret = FALSE;
862           goto out;
863         }
864     }
865
866  out:
867
868   if (lock_fd != -1)
869     {
870       GError *local_error;
871       local_error = NULL;
872       if (!keyring_release_lock (path, lock_fd, &local_error))
873         {
874           if (error != NULL)
875             {
876               if (*error == NULL)
877                 {
878                   *error = local_error;
879                 }
880               else
881                 {
882                   g_prefix_error (error,
883                                   _("(Additionally, releasing the lock for “%s” also failed: %s) "),
884                                   path,
885                                   local_error->message);
886                 }
887             }
888           else
889             {
890               g_error_free (local_error);
891             }
892         }
893     }
894
895   g_free (keyring_dir);
896   g_free (path);
897   g_strfreev (lines);
898   g_free (contents);
899   if (new_contents != NULL)
900     g_string_free (new_contents, TRUE);
901   g_free (use_cookie);
902   return ret;
903 }
904
905 /* ---------------------------------------------------------------------------------------------------- */
906
907 static gchar *
908 generate_sha1 (const gchar *server_challenge,
909                const gchar *client_challenge,
910                const gchar *cookie)
911 {
912   GString *str;
913   gchar *sha1;
914
915   str = g_string_new (server_challenge);
916   g_string_append_c (str, ':');
917   g_string_append (str, client_challenge);
918   g_string_append_c (str, ':');
919   g_string_append (str, cookie);
920   sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
921   g_string_free (str, TRUE);
922
923   return sha1;
924 }
925
926 /* ---------------------------------------------------------------------------------------------------- */
927
928 static GDBusAuthMechanismState
929 mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
930 {
931   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
932
933   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
934   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
935
936   return m->priv->state;
937 }
938
939 static void
940 mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
941                            const gchar          *initial_response,
942                            gsize                 initial_response_len)
943 {
944   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
945
946   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
947   g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
948
949   m->priv->is_server = TRUE;
950   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
951
952   if (initial_response != NULL && strlen (initial_response) > 0)
953     {
954 #ifdef G_OS_UNIX
955       gint64 uid;
956       gchar *endp;
957
958       uid = g_ascii_strtoll (initial_response, &endp, 10);
959       if (*endp == '\0')
960         {
961           if (uid == getuid ())
962             {
963               m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
964             }
965         }
966 #elif defined(G_OS_WIN32)
967       gchar *sid;
968       sid = _g_dbus_win32_get_user_sid ();
969       if (g_strcmp0 (initial_response, sid) == 0)
970         m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
971       g_free (sid);
972 #else
973 #error Please implement for your OS
974 #endif
975     }
976 }
977
978 static void
979 mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
980                                const gchar          *data,
981                                gsize                 data_len)
982 {
983   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
984   gchar **tokens;
985   const gchar *client_challenge;
986   const gchar *alleged_sha1;
987   gchar *sha1;
988
989   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
990   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
991   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
992
993   tokens = NULL;
994   sha1 = NULL;
995
996   tokens = g_strsplit (data, " ", 0);
997   if (g_strv_length (tokens) != 2)
998     {
999       g_warning ("Malformed data '%s'", data);
1000       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1001       goto out;
1002     }
1003
1004   client_challenge = tokens[0];
1005   alleged_sha1 = tokens[1];
1006
1007   sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
1008
1009   if (g_strcmp0 (sha1, alleged_sha1) == 0)
1010     {
1011       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1012     }
1013   else
1014     {
1015       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1016     }
1017
1018  out:
1019   g_strfreev (tokens);
1020   g_free (sha1);
1021 }
1022
1023 static gchar *
1024 mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
1025                             gsize                *out_data_len)
1026 {
1027   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1028   gchar *s;
1029   gint cookie_id;
1030   const gchar *cookie_context;
1031   GError *error;
1032
1033   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1034   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1035   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1036
1037   s = NULL;
1038
1039   /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1040   cookie_context = "org_gtk_gdbus_general";
1041
1042   cookie_id = -1;
1043   error = NULL;
1044   if (!keyring_generate_entry (cookie_context,
1045                                &cookie_id,
1046                                &m->priv->cookie,
1047                                &error))
1048     {
1049       g_warning ("Error adding entry to keyring: %s", error->message);
1050       g_error_free (error);
1051       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1052       goto out;
1053     }
1054
1055   m->priv->server_challenge = random_ascii_string (16);
1056   s = g_strdup_printf ("%s %d %s",
1057                        cookie_context,
1058                        cookie_id,
1059                        m->priv->server_challenge);
1060
1061   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1062
1063  out:
1064   return s;
1065 }
1066
1067 static gchar *
1068 mechanism_server_get_reject_reason (GDBusAuthMechanism   *mechanism)
1069 {
1070   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1071
1072   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1073   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1074   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1075
1076   /* can never end up here because we are never in the REJECTED state */
1077   g_assert_not_reached ();
1078
1079   return NULL;
1080 }
1081
1082 static void
1083 mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
1084 {
1085   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1086
1087   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1088   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1089
1090   m->priv->is_server = FALSE;
1091 }
1092
1093 /* ---------------------------------------------------------------------------------------------------- */
1094
1095 static GDBusAuthMechanismState
1096 mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
1097 {
1098   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1099
1100   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1101   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1102
1103   return m->priv->state;
1104 }
1105
1106 static gchar *
1107 mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
1108                            gsize                *out_initial_response_len)
1109 {
1110   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1111   gchar *initial_response;
1112
1113   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1114   g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1115
1116   m->priv->is_client = TRUE;
1117   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1118
1119   *out_initial_response_len = -1;
1120
1121 #ifdef G_OS_UNIX
1122   initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1123 #elif defined (G_OS_WIN32)
1124 initial_response = _g_dbus_win32_get_user_sid ();
1125 #else
1126 #error Please implement for your OS
1127 #endif
1128   g_assert (initial_response != NULL);
1129
1130   return initial_response;
1131 }
1132
1133 static void
1134 mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
1135                                const gchar          *data,
1136                                gsize                 data_len)
1137 {
1138   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1139   gchar **tokens;
1140   const gchar *cookie_context;
1141   guint cookie_id;
1142   const gchar *server_challenge;
1143   gchar *client_challenge;
1144   gchar *endp;
1145   gchar *cookie;
1146   GError *error;
1147   gchar *sha1;
1148
1149   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1150   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1151   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1152
1153   tokens = NULL;
1154   cookie = NULL;
1155   client_challenge = NULL;
1156
1157   tokens = g_strsplit (data, " ", 0);
1158   if (g_strv_length (tokens) != 3)
1159     {
1160       g_warning ("Malformed data '%s'", data);
1161       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1162       goto out;
1163     }
1164
1165   cookie_context = tokens[0];
1166   cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1167   if (*endp != '\0')
1168     {
1169       g_warning ("Malformed cookie_id '%s'", tokens[1]);
1170       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1171       goto out;
1172     }
1173   server_challenge = tokens[2];
1174
1175   error = NULL;
1176   cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1177   if (cookie == NULL)
1178     {
1179       g_warning ("Problems looking up entry in keyring: %s", error->message);
1180       g_error_free (error);
1181       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1182       goto out;
1183     }
1184
1185   client_challenge = random_ascii_string (16);
1186   sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1187   m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1188   g_free (sha1);
1189   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1190
1191  out:
1192   g_strfreev (tokens);
1193   g_free (cookie);
1194   g_free (client_challenge);
1195 }
1196
1197 static gchar *
1198 mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
1199                             gsize                *out_data_len)
1200 {
1201   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1202
1203   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1204   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1205   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1206
1207   g_assert (m->priv->to_send != NULL);
1208
1209   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1210
1211   return g_strdup (m->priv->to_send);
1212 }
1213
1214 static void
1215 mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
1216 {
1217   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1218
1219   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1220   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1221
1222   m->priv->is_client = FALSE;
1223 }
1224
1225 /* ---------------------------------------------------------------------------------------------------- */