Initial GDBus code-drop from GDBus-standalone repo
[platform/upstream/glib.git] / gio / gdbusauthmechanismsha1.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2009 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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <glib/gi18n.h>
26
27 #include "gdbusauthmechanismsha1.h"
28 #include "gcredentials.h"
29 #include "gdbuserror.h"
30 #include "gioenumtypes.h"
31 #include "gioerror.h"
32
33 #ifdef G_OS_UNIX
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #endif
38
39 #include <errno.h>
40
41 #include <glib/gstdio.h>
42
43 struct _GDBusAuthMechanismSha1Private
44 {
45   gboolean is_client;
46   gboolean is_server;
47   GDBusAuthMechanismState state;
48
49   /* used on the client side */
50   gchar *to_send;
51
52   /* used on the server side */
53   gchar *cookie;
54   gchar *server_challenge;
55 };
56
57 static gint                     mechanism_get_priority              (void);
58 static const gchar             *mechanism_get_name                  (void);
59
60 static gboolean                 mechanism_is_supported              (GDBusAuthMechanism   *mechanism);
61 static gchar                   *mechanism_encode_data               (GDBusAuthMechanism   *mechanism,
62                                                                      const gchar          *data,
63                                                                      gsize                 data_len,
64                                                                      gsize                *out_data_len);
65 static gchar                   *mechanism_decode_data               (GDBusAuthMechanism   *mechanism,
66                                                                      const gchar          *data,
67                                                                      gsize                 data_len,
68                                                                      gsize                *out_data_len);
69 static GDBusAuthMechanismState  mechanism_server_get_state          (GDBusAuthMechanism   *mechanism);
70 static void                     mechanism_server_initiate           (GDBusAuthMechanism   *mechanism,
71                                                                      const gchar          *initial_response,
72                                                                      gsize                 initial_response_len);
73 static void                     mechanism_server_data_receive       (GDBusAuthMechanism   *mechanism,
74                                                                      const gchar          *data,
75                                                                      gsize                 data_len);
76 static gchar                   *mechanism_server_data_send          (GDBusAuthMechanism   *mechanism,
77                                                                      gsize                *out_data_len);
78 static gchar                   *mechanism_server_get_reject_reason  (GDBusAuthMechanism   *mechanism);
79 static void                     mechanism_server_shutdown           (GDBusAuthMechanism   *mechanism);
80 static GDBusAuthMechanismState  mechanism_client_get_state          (GDBusAuthMechanism   *mechanism);
81 static gchar                   *mechanism_client_initiate           (GDBusAuthMechanism   *mechanism,
82                                                                      gsize                *out_initial_response_len);
83 static void                     mechanism_client_data_receive       (GDBusAuthMechanism   *mechanism,
84                                                                      const gchar          *data,
85                                                                      gsize                 data_len);
86 static gchar                   *mechanism_client_data_send          (GDBusAuthMechanism   *mechanism,
87                                                                      gsize                *out_data_len);
88 static void                     mechanism_client_shutdown           (GDBusAuthMechanism   *mechanism);
89
90 /* ---------------------------------------------------------------------------------------------------- */
91
92 G_DEFINE_TYPE (GDBusAuthMechanismSha1, _g_dbus_auth_mechanism_sha1, G_TYPE_DBUS_AUTH_MECHANISM);
93
94 /* ---------------------------------------------------------------------------------------------------- */
95
96 static void
97 _g_dbus_auth_mechanism_sha1_finalize (GObject *object)
98 {
99   GDBusAuthMechanismSha1 *mechanism = G_DBUS_AUTH_MECHANISM_SHA1 (object);
100
101   g_free (mechanism->priv->to_send);
102
103   g_free (mechanism->priv->cookie);
104   g_free (mechanism->priv->server_challenge);
105
106   if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize != NULL)
107     G_OBJECT_CLASS (_g_dbus_auth_mechanism_sha1_parent_class)->finalize (object);
108 }
109
110 static void
111 _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
112 {
113   GObjectClass *gobject_class;
114   GDBusAuthMechanismClass *mechanism_class;
115
116   g_type_class_add_private (klass, sizeof (GDBusAuthMechanismSha1Private));
117
118   gobject_class = G_OBJECT_CLASS (klass);
119   gobject_class->finalize = _g_dbus_auth_mechanism_sha1_finalize;
120
121   mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
122   mechanism_class->get_priority              = mechanism_get_priority;
123   mechanism_class->get_name                  = mechanism_get_name;
124   mechanism_class->is_supported              = mechanism_is_supported;
125   mechanism_class->encode_data               = mechanism_encode_data;
126   mechanism_class->decode_data               = mechanism_decode_data;
127   mechanism_class->server_get_state          = mechanism_server_get_state;
128   mechanism_class->server_initiate           = mechanism_server_initiate;
129   mechanism_class->server_data_receive       = mechanism_server_data_receive;
130   mechanism_class->server_data_send          = mechanism_server_data_send;
131   mechanism_class->server_get_reject_reason  = mechanism_server_get_reject_reason;
132   mechanism_class->server_shutdown           = mechanism_server_shutdown;
133   mechanism_class->client_get_state          = mechanism_client_get_state;
134   mechanism_class->client_initiate           = mechanism_client_initiate;
135   mechanism_class->client_data_receive       = mechanism_client_data_receive;
136   mechanism_class->client_data_send          = mechanism_client_data_send;
137   mechanism_class->client_shutdown           = mechanism_client_shutdown;
138 }
139
140 static void
141 _g_dbus_auth_mechanism_sha1_init (GDBusAuthMechanismSha1 *mechanism)
142 {
143   mechanism->priv = G_TYPE_INSTANCE_GET_PRIVATE (mechanism,
144                                                  G_TYPE_DBUS_AUTH_MECHANISM_SHA1,
145                                                  GDBusAuthMechanismSha1Private);
146 }
147
148 /* ---------------------------------------------------------------------------------------------------- */
149
150 static gint
151 mechanism_get_priority (void)
152 {
153   return 0;
154 }
155
156 static const gchar *
157 mechanism_get_name (void)
158 {
159   return "DBUS_COOKIE_SHA1";
160 }
161
162 static gboolean
163 mechanism_is_supported (GDBusAuthMechanism *mechanism)
164 {
165   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), FALSE);
166   return TRUE;
167 }
168
169 static gchar *
170 mechanism_encode_data (GDBusAuthMechanism   *mechanism,
171                        const gchar          *data,
172                        gsize                 data_len,
173                        gsize                *out_data_len)
174 {
175   return NULL;
176 }
177
178
179 static gchar *
180 mechanism_decode_data (GDBusAuthMechanism   *mechanism,
181                        const gchar          *data,
182                        gsize                 data_len,
183                        gsize                *out_data_len)
184 {
185   return NULL;
186 }
187
188 /* ---------------------------------------------------------------------------------------------------- */
189
190 static gint
191 random_ascii (void)
192 {
193   gint ret;
194   ret = g_random_int_range (0, 60);
195   if (ret < 25)
196     ret += 'A';
197   else if (ret < 50)
198     ret += 'a' - 25;
199   else
200     ret += '0' - 50;
201   return ret;
202 }
203
204 static gchar *
205 random_ascii_string (guint len)
206 {
207   GString *challenge;
208   guint n;
209
210   challenge = g_string_new (NULL);
211   for (n = 0; n < len; n++)
212     g_string_append_c (challenge, random_ascii ());
213   return g_string_free (challenge, FALSE);
214 }
215
216 static gchar *
217 random_blob (guint len)
218 {
219   GString *challenge;
220   guint n;
221
222   challenge = g_string_new (NULL);
223   for (n = 0; n < len; n++)
224     g_string_append_c (challenge, g_random_int_range (0, 256));
225   return g_string_free (challenge, FALSE);
226 }
227
228 /* ---------------------------------------------------------------------------------------------------- */
229
230 /* ensure keyring dir exists and permissions are correct */
231 static gchar *
232 ensure_keyring_directory (GError **error)
233 {
234   gchar *path;
235   const gchar *e;
236
237   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
238
239   e = g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR");
240   if (e != NULL)
241     {
242       path = g_strdup (e);
243     }
244   else
245     {
246       path = g_build_filename (g_get_home_dir (),
247                                ".dbus-keyrings",
248                                NULL);
249     }
250
251   if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
252     {
253       if (g_getenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION") == NULL)
254         {
255 #ifdef G_OS_UNIX
256           struct stat statbuf;
257           if (stat (path, &statbuf) != 0)
258             {
259               g_set_error (error,
260                            G_IO_ERROR,
261                            g_io_error_from_errno (errno),
262                            _("Error statting directory `%s': %s"),
263                            path,
264                            strerror (errno));
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 #error Please implement permission checking on non-UNIX platforms
283 #endif
284         }
285         goto out;
286     }
287
288   if (g_mkdir (path, 0700) != 0)
289     {
290       g_set_error (error,
291                    G_IO_ERROR,
292                    g_io_error_from_errno (errno),
293                    _("Error creating directory `%s': %s"),
294                    path,
295                    strerror (errno));
296       g_free (path);
297       path = NULL;
298       goto out;
299     }
300
301 out:
302   return path;
303 }
304
305 /* ---------------------------------------------------------------------------------------------------- */
306
307 static void
308 append_nibble (GString *s, gint val)
309 {
310   g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val));
311 }
312
313 static gchar *
314 hexencode (const gchar *str,
315            gssize       len)
316 {
317   guint n;
318   GString *s;
319
320   if (len == -1)
321     len = strlen (str);
322
323   s = g_string_new (NULL);
324   for (n = 0; n < len; n++)
325     {
326       gint val;
327       gint upper_nibble;
328       gint lower_nibble;
329
330       val = ((const guchar *) str)[n];
331       upper_nibble = val >> 4;
332       lower_nibble = val & 0x0f;
333
334       append_nibble (s, upper_nibble);
335       append_nibble (s, lower_nibble);
336     }
337
338   return g_string_free (s, FALSE);
339 }
340
341 /* ---------------------------------------------------------------------------------------------------- */
342
343 /* looks up an entry in the keyring */
344 static gchar *
345 keyring_lookup_entry (const gchar  *cookie_context,
346                       gint          cookie_id,
347                       GError      **error)
348 {
349   gchar *ret;
350   gchar *keyring_dir;
351   gchar *contents;
352   gchar *path;
353   guint n;
354   gchar **lines;
355
356   g_return_val_if_fail (cookie_context != NULL, FALSE);
357   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
358
359   ret = NULL;
360   path = NULL;
361   contents = NULL;
362   lines = NULL;
363
364   keyring_dir = ensure_keyring_directory (error);
365   if (keyring_dir == NULL)
366     goto out;
367
368   path = g_build_filename (keyring_dir, cookie_context, NULL);
369
370   if (!g_file_get_contents (path,
371                             &contents,
372                             NULL,
373                             error))
374     {
375       g_prefix_error (error,
376                       _("Error opening keyring `%s' for reading: "),
377                       path);
378       goto out;
379     }
380   g_assert (contents != NULL);
381
382   lines = g_strsplit (contents, "\n", 0);
383   for (n = 0; lines[n] != NULL; n++)
384     {
385       const gchar *line = lines[n];
386       gchar **tokens;
387       gchar *endp;
388       gint line_id;
389       guint64 line_when;
390
391       if (line[0] == '\0')
392         continue;
393
394       tokens = g_strsplit (line, " ", 0);
395       if (g_strv_length (tokens) != 3)
396         {
397           g_set_error (error,
398                        G_IO_ERROR,
399                        G_IO_ERROR_FAILED,
400                        _("Line %d of the keyring at `%s' with content `%s' is malformed"),
401                        n + 1,
402                        path,
403                        line);
404           g_strfreev (tokens);
405           goto out;
406         }
407
408       line_id = g_ascii_strtoll (tokens[0], &endp, 10);
409       if (*endp != '\0')
410         {
411           g_set_error (error,
412                        G_IO_ERROR,
413                        G_IO_ERROR_FAILED,
414                        _("First token of line %d of the keyring at `%s' with content `%s' is malformed"),
415                        n + 1,
416                        path,
417                        line);
418           g_strfreev (tokens);
419           goto out;
420         }
421
422       line_when = g_ascii_strtoll (tokens[1], &endp, 10);
423       if (*endp != '\0')
424         {
425           g_set_error (error,
426                        G_IO_ERROR,
427                        G_IO_ERROR_FAILED,
428                        _("Second token of line %d of the keyring at `%s' with content `%s' is malformed"),
429                        n + 1,
430                        path,
431                        line);
432           g_strfreev (tokens);
433           goto out;
434         }
435
436       if (line_id == cookie_id)
437         {
438           /* YAY, success */
439           ret = tokens[2]; /* steal pointer */
440           tokens[2] = NULL;
441           g_strfreev (tokens);
442           goto out;
443         }
444
445       g_strfreev (tokens);
446     }
447
448   /* BOOH, didn't find the cookie */
449   g_set_error (error,
450                G_IO_ERROR,
451                G_IO_ERROR_FAILED,
452                _("Didn't find cookie with id %d in the keyring at `%s'"),
453                cookie_id,
454                path);
455
456  out:
457   g_free (keyring_dir);
458   g_free (path);
459   g_free (contents);
460   g_strfreev (lines);
461   return ret;
462 }
463
464 /* function for logging important events that the system administrator should take notice of */
465 static void
466 _log (const gchar *message,
467       ...)
468 {
469   gchar *s;
470   va_list var_args;
471
472   va_start (var_args, message);
473   s = g_strdup_vprintf (message, var_args);
474   va_end (var_args);
475
476   /* TODO: might want to send this to syslog instead */
477   g_printerr ("GDBus-DBUS_COOKIE_SHA1: %s\n", s);
478   g_free (s);
479 }
480
481 static gint
482 keyring_acquire_lock (const gchar  *path,
483                       GError      **error)
484 {
485   gchar *lock;
486   gint ret;
487   guint num_tries;
488   guint num_create_tries;
489
490   g_return_val_if_fail (path != NULL, FALSE);
491   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
492
493   ret = -1;
494   lock = g_strdup_printf ("%s.lock", path);
495
496   /* This is what the D-Bus spec says
497    *
498    *  Create a lockfile name by appending ".lock" to the name of the
499    *  cookie file. The server should attempt to create this file using
500    *  O_CREAT | O_EXCL. If file creation fails, the lock
501    *  fails. Servers should retry for a reasonable period of time,
502    *  then they may choose to delete an existing lock to keep users
503    *  from having to manually delete a stale lock. [1]
504    *
505    *  [1] : Lockfiles are used instead of real file locking fcntl() because
506    *         real locking implementations are still flaky on network filesystems
507    */
508
509   num_create_tries = 0;
510 #ifdef EEXISTS
511  again:
512 #endif
513   num_tries = 0;
514   while (g_file_test (lock, G_FILE_TEST_EXISTS))
515     {
516       /* sleep 10ms, then try again */
517       g_usleep (1000*10);
518       num_tries++;
519       if (num_tries == 50)
520         {
521           /* ok, we slept 50*10ms = 0.5 seconds.. Conclude that the lock-file must be
522            * stale (nuke the it from orbit)
523            */
524           if (g_unlink (lock) != 0)
525             {
526               g_set_error (error,
527                            G_IO_ERROR,
528                            g_io_error_from_errno (errno),
529                            _("Error deleting stale lock-file `%s': %s"),
530                            lock,
531                            strerror (errno));
532               goto out;
533             }
534           _log ("Deleted stale lock-file `%s'", lock);
535           break;
536         }
537     }
538
539   ret = g_open (lock, O_CREAT |
540 #ifdef O_EXCL
541                 O_EXCL,
542 #else
543                 0,
544 #endif
545                 0700);
546   if (ret == -1)
547     {
548 #ifdef EEXISTS
549       /* EEXIST: pathname already exists and O_CREAT and O_EXCL were used. */
550       if (errno == EEXISTS)
551         {
552           num_create_tries++;
553           if (num_create_tries < 5)
554             goto again;
555         }
556 #endif
557       g_set_error (error,
558                    G_IO_ERROR,
559                    g_io_error_from_errno (errno),
560                    _("Error creating lock-file `%s': %s"),
561                    lock,
562                    strerror (errno));
563       goto out;
564     }
565
566  out:
567   g_free (lock);
568   return ret;
569 }
570
571 static gboolean
572 keyring_release_lock (const gchar  *path,
573                       gint          lock_fd,
574                       GError      **error)
575 {
576   gchar *lock;
577   gboolean ret;
578
579   g_return_val_if_fail (path != NULL, FALSE);
580   g_return_val_if_fail (lock_fd != -1, FALSE);
581   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
582
583   ret = FALSE;
584   lock = g_strdup_printf ("%s.lock", path);
585   if (g_unlink (lock) != 0)
586     {
587       g_set_error (error,
588                    G_IO_ERROR,
589                    g_io_error_from_errno (errno),
590                    _("Error unlinking lock-file `%s': %s"),
591                    lock,
592                    strerror (errno));
593       goto out;
594     }
595   if (close (lock_fd) != 0)
596     {
597       g_set_error (error,
598                    G_IO_ERROR,
599                    g_io_error_from_errno (errno),
600                    _("Error closing (unlinked) lock-file `%s': %s"),
601                    lock,
602                    strerror (errno));
603       goto out;
604     }
605
606   ret = TRUE;
607
608  out:
609   g_free (lock);
610   return ret;
611 }
612
613
614 /* adds an entry to the keyring, taking care of locking and deleting stale/future entries */
615 static gboolean
616 keyring_generate_entry (const gchar  *cookie_context,
617                         gint         *out_id,
618                         gchar       **out_cookie,
619                         GError      **error)
620 {
621   gboolean ret;
622   gchar *keyring_dir;
623   gchar *path;
624   gchar *contents;
625   GError *local_error;
626   gchar **lines;
627   gint max_line_id;
628   GString *new_contents;
629   guint64 now;
630   gboolean have_id;
631   gint use_id;
632   gchar *use_cookie;
633   gboolean changed_file;
634   gint lock_fd;
635
636   g_return_val_if_fail (cookie_context != NULL, FALSE);
637   g_return_val_if_fail (out_id != NULL, FALSE);
638   g_return_val_if_fail (out_cookie != NULL, FALSE);
639   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
640
641   ret = FALSE;
642   path = NULL;
643   contents = NULL;
644   lines = NULL;
645   new_contents = NULL;
646   have_id = FALSE;
647   use_cookie = NULL;
648   lock_fd = -1;
649
650   keyring_dir = ensure_keyring_directory (error);
651   if (keyring_dir == NULL)
652     goto out;
653
654   path = g_build_filename (keyring_dir, cookie_context, NULL);
655
656   lock_fd = keyring_acquire_lock (path, error);
657   if (lock_fd == -1)
658     goto out;
659
660   local_error = NULL;
661   contents = NULL;
662   if (!g_file_get_contents (path,
663                             &contents,
664                             NULL,
665                             &local_error))
666     {
667       if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
668         {
669           /* file doesn't have to exist */
670           g_error_free (local_error);
671         }
672       else
673         {
674           g_propagate_prefixed_error (error,
675                                       local_error,
676                                       _("Error opening keyring `%s' for writing: "),
677                                       path);
678           goto out;
679         }
680     }
681
682   new_contents = g_string_new (NULL);
683   now = time (NULL);
684   changed_file = FALSE;
685
686   max_line_id = 0;
687   if (contents != NULL)
688     {
689       guint n;
690       lines = g_strsplit (contents, "\n", 0);
691       for (n = 0; lines[n] != NULL; n++)
692         {
693           const gchar *line = lines[n];
694           gchar **tokens;
695           gchar *endp;
696           gint line_id;
697           guint64 line_when;
698           gboolean keep_entry;
699
700           if (line[0] == '\0')
701             continue;
702
703           tokens = g_strsplit (line, " ", 0);
704           if (g_strv_length (tokens) != 3)
705             {
706               g_set_error (error,
707                            G_IO_ERROR,
708                            G_IO_ERROR_FAILED,
709                            _("Line %d of the keyring at `%s' with content `%s' is malformed"),
710                            n + 1,
711                            path,
712                            line);
713               g_strfreev (tokens);
714               goto out;
715             }
716
717           line_id = g_ascii_strtoll (tokens[0], &endp, 10);
718           if (*endp != '\0')
719             {
720               g_set_error (error,
721                            G_IO_ERROR,
722                            G_IO_ERROR_FAILED,
723                            _("First token of line %d of the keyring at `%s' with content `%s' is malformed"),
724                            n + 1,
725                            path,
726                            line);
727               g_strfreev (tokens);
728               goto out;
729             }
730
731           line_when = g_ascii_strtoll (tokens[1], &endp, 10);
732           if (*endp != '\0')
733             {
734               g_set_error (error,
735                            G_IO_ERROR,
736                            G_IO_ERROR_FAILED,
737                            _("Second token of line %d of the keyring at `%s' with content `%s' is malformed"),
738                            n + 1,
739                            path,
740                            line);
741               g_strfreev (tokens);
742               goto out;
743             }
744
745           /* D-Bus spec says:
746            *
747            *  Once the lockfile has been created, the server loads the
748            *  cookie file. It should then delete any cookies that are
749            *  old (the timeout can be fairly short), or more than a
750            *  reasonable time in the future (so that cookies never
751            *  accidentally become permanent, if the clock was set far
752            *  into the future at some point). If no recent keys remain,
753            *  the server may generate a new key.
754            *
755            */
756           keep_entry = TRUE;
757           if (line_when > now)
758             {
759               /* Oddball case: entry is more recent than our current wall-clock time..
760                * This is OK, it means that another server on another machine but with
761                * same $HOME wrote the entry.
762                *
763                * So discard the entry if it's more than 1 day in the future ("reasonable
764                * time in the future").
765                */
766               if (line_when - now > 24*60*60)
767                 {
768                   keep_entry = FALSE;
769                   _log ("Deleted SHA1 cookie from %" G_GUINT64_FORMAT " seconds in the future", line_when - now);
770                 }
771             }
772           else
773             {
774               /* Discard entry if it's older than 15 minutes ("can be fairly short") */
775               if (now - line_when > 15*60)
776                 {
777                   keep_entry = FALSE;
778                 }
779             }
780
781           if (!keep_entry)
782             {
783               changed_file = FALSE;
784             }
785           else
786             {
787               g_string_append_printf (new_contents,
788                                       "%d %" G_GUINT64_FORMAT " %s\n",
789                                       line_id,
790                                       line_when,
791                                       tokens[2]);
792               max_line_id = MAX (line_id, max_line_id);
793               /* Only reuse entry if not older than 10 minutes.
794                *
795                * (We need a bit of grace time compared to 15 minutes above.. otherwise
796                * there's a race where we reuse the 14min59.9 secs old entry and a
797                * split-second later another server purges the now 15 minute old entry.)
798                */
799               if (now - line_when < 10 * 60)
800                 {
801                   if (!have_id)
802                     {
803                       use_id = line_id;
804                       use_cookie = tokens[2]; /* steal memory */
805                       tokens[2] = NULL;
806                       have_id = TRUE;
807                     }
808                 }
809             }
810           g_strfreev (tokens);
811         }
812     } /* for each line */
813
814   ret = TRUE;
815
816   if (have_id)
817     {
818       *out_id = use_id;
819       *out_cookie = use_cookie;
820       use_cookie = NULL;
821     }
822   else
823     {
824       gchar *raw_cookie;
825       *out_id = max_line_id + 1;
826       raw_cookie = random_blob (32);
827       *out_cookie = hexencode (raw_cookie, 32);
828       g_free (raw_cookie);
829
830       g_string_append_printf (new_contents,
831                               "%d %" G_GUINT64_FORMAT " %s\n",
832                               *out_id,
833                               (guint64) time (NULL),
834                               *out_cookie);
835       changed_file = TRUE;
836     }
837
838   /* and now actually write the cookie file if there are changes (this is atomic) */
839   if (changed_file)
840     {
841       if (!g_file_set_contents (path,
842                                 new_contents->str,
843                                 -1,
844                                 error))
845         {
846           *out_id = 0;
847           *out_cookie = 0;
848           g_free (*out_cookie);
849           ret = FALSE;
850           goto out;
851         }
852     }
853
854  out:
855
856   if (lock_fd != -1)
857     {
858       GError *local_error;
859       local_error = NULL;
860       if (!keyring_release_lock (path, lock_fd, &local_error))
861         {
862           if (error != NULL)
863             {
864               if (*error == NULL)
865                 {
866                   *error = local_error;
867                 }
868               else
869                 {
870                   g_prefix_error (error,
871                                   _("(Additionally, releasing the lock for `%s' also failed: %s) "),
872                                   path,
873                                   local_error->message);
874                 }
875             }
876           else
877             {
878               g_error_free (local_error);
879             }
880         }
881     }
882
883   g_free (keyring_dir);
884   g_free (path);
885   g_strfreev (lines);
886   g_free (contents);
887   if (new_contents != NULL)
888     g_string_free (new_contents, TRUE);
889   g_free (use_cookie);
890   return ret;
891 }
892
893 /* ---------------------------------------------------------------------------------------------------- */
894
895 static gchar *
896 generate_sha1 (const gchar *server_challenge,
897                const gchar *client_challenge,
898                const gchar *cookie)
899 {
900   GString *str;
901   gchar *sha1;
902
903   str = g_string_new (server_challenge);
904   g_string_append_c (str, ':');
905   g_string_append (str, client_challenge);
906   g_string_append_c (str, ':');
907   g_string_append (str, cookie);
908   sha1 = g_compute_checksum_for_string (G_CHECKSUM_SHA1, str->str, -1);
909   g_string_free (str, TRUE);
910
911   return sha1;
912 }
913
914 /* ---------------------------------------------------------------------------------------------------- */
915
916 static GDBusAuthMechanismState
917 mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
918 {
919   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
920
921   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
922   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
923
924   return m->priv->state;
925 }
926
927 static void
928 mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
929                            const gchar          *initial_response,
930                            gsize                 initial_response_len)
931 {
932   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
933
934   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
935   g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
936
937   m->priv->is_server = TRUE;
938   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
939
940   if (initial_response != NULL && strlen (initial_response) > 0)
941     {
942 #ifdef G_OS_UNIX
943       gint64 uid;
944       gchar *endp;
945
946       uid = g_ascii_strtoll (initial_response, &endp, 10);
947       if (*endp == '\0')
948         {
949           if (uid == getuid ())
950             {
951               m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
952             }
953         }
954 #elif defined(G_OS_WIN32)
955       GCredentials *credentials;
956       credentials = g_credentials_new_for_process ();
957       if (g_strcmp0 (g_credentials_get_windows_user (credentials), initial_response) == 0)
958         m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
959       g_object_unref (credentials);
960 #else
961 #error Please implement for your OS
962 #endif
963     }
964 }
965
966 static void
967 mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
968                                const gchar          *data,
969                                gsize                 data_len)
970 {
971   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
972   gchar **tokens;
973   const gchar *client_challenge;
974   const gchar *alleged_sha1;
975   gchar *sha1;
976
977   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
978   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
979   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
980
981   tokens = NULL;
982   sha1 = NULL;
983
984   tokens = g_strsplit (data, " ", 0);
985   if (g_strv_length (tokens) != 2)
986     {
987       g_warning ("Malformed data `%s'", data);
988       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
989       goto out;
990     }
991
992   client_challenge = tokens[0];
993   alleged_sha1 = tokens[1];
994
995   sha1 = generate_sha1 (m->priv->server_challenge, client_challenge, m->priv->cookie);
996
997   if (g_strcmp0 (sha1, alleged_sha1) == 0)
998     {
999       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1000     }
1001   else
1002     {
1003       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1004     }
1005
1006  out:
1007   g_strfreev (tokens);
1008   g_free (sha1);
1009 }
1010
1011 static gchar *
1012 mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
1013                             gsize                *out_data_len)
1014 {
1015   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1016   gchar *s;
1017   gint cookie_id;
1018   const gchar *cookie_context;
1019   GError *error;
1020
1021   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1022   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1023   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1024
1025   s = NULL;
1026
1027   /* TODO: use GDBusAuthObserver here to get the cookie context to use? */
1028   cookie_context = "org_gtk_gdbus_general";
1029
1030   error = NULL;
1031   if (!keyring_generate_entry (cookie_context,
1032                                &cookie_id,
1033                                &m->priv->cookie,
1034                                &error))
1035     {
1036       g_warning ("Error adding entry to keyring: %s", error->message);
1037       g_error_free (error);
1038       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1039       goto out;
1040     }
1041
1042   m->priv->server_challenge = random_ascii_string (16);
1043   s = g_strdup_printf ("%s %d %s",
1044                        cookie_context,
1045                        cookie_id,
1046                        m->priv->server_challenge);
1047
1048   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1049
1050  out:
1051   return s;
1052 }
1053
1054 static gchar *
1055 mechanism_server_get_reject_reason (GDBusAuthMechanism   *mechanism)
1056 {
1057   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1058
1059   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1060   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
1061   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
1062
1063   /* can never end up here because we are never in the REJECTED state */
1064   g_assert_not_reached ();
1065
1066   return NULL;
1067 }
1068
1069 static void
1070 mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
1071 {
1072   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1073
1074   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1075   g_return_if_fail (m->priv->is_server && !m->priv->is_client);
1076
1077   m->priv->is_server = FALSE;
1078 }
1079
1080 /* ---------------------------------------------------------------------------------------------------- */
1081
1082 static GDBusAuthMechanismState
1083 mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
1084 {
1085   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1086
1087   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1088   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
1089
1090   return m->priv->state;
1091 }
1092
1093 static gchar *
1094 mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
1095                            gsize                *out_initial_response_len)
1096 {
1097   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1098   gchar *initial_response;
1099
1100   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1101   g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
1102
1103   m->priv->is_client = TRUE;
1104   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
1105
1106   *out_initial_response_len = -1;
1107
1108 #ifdef G_OS_UNIX
1109   initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
1110 #elif defined (G_OS_WIN32)
1111   {
1112     GCredentials *credentials;
1113     credentials = g_credentials_new_for_process ();
1114     initial_response = g_strdup (g_credentials_get_windows_user (credentials));
1115     g_object_unref (credentials);
1116   }
1117 #else
1118 #endif
1119   g_assert (initial_response != NULL);
1120
1121   return initial_response;
1122 }
1123
1124 static void
1125 mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
1126                                const gchar          *data,
1127                                gsize                 data_len)
1128 {
1129   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1130   gchar **tokens;
1131   const gchar *cookie_context;
1132   guint cookie_id;
1133   const gchar *server_challenge;
1134   gchar *client_challenge;
1135   gchar *endp;
1136   gchar *cookie;
1137   GError *error;
1138   gchar *sha1;
1139
1140   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1141   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1142   g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
1143
1144   tokens = NULL;
1145   cookie = NULL;
1146   client_challenge = NULL;
1147
1148   tokens = g_strsplit (data, " ", 0);
1149   if (g_strv_length (tokens) != 3)
1150     {
1151       g_warning ("Malformed data `%s'", data);
1152       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1153       goto out;
1154     }
1155
1156   cookie_context = tokens[0];
1157   cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
1158   if (*endp != '\0')
1159     {
1160       g_warning ("Malformed cookie_id `%s'", tokens[1]);
1161       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1162       goto out;
1163     }
1164   server_challenge = tokens[2];
1165
1166   error = NULL;
1167   cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
1168   if (cookie == NULL)
1169     {
1170       g_warning ("Problems looking up entry in keyring: %s", error->message);
1171       g_error_free (error);
1172       m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
1173       goto out;
1174     }
1175
1176   client_challenge = random_ascii_string (16);
1177   sha1 = generate_sha1 (server_challenge, client_challenge, cookie);
1178   m->priv->to_send = g_strdup_printf ("%s %s", client_challenge, sha1);
1179   g_free (sha1);
1180   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
1181
1182  out:
1183   g_strfreev (tokens);
1184   g_free (cookie);
1185   g_free (client_challenge);
1186 }
1187
1188 static gchar *
1189 mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
1190                             gsize                *out_data_len)
1191 {
1192   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1193
1194   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
1195   g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
1196   g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
1197
1198   g_assert (m->priv->to_send != NULL);
1199
1200   m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
1201
1202   return g_strdup (m->priv->to_send);
1203 }
1204
1205 static void
1206 mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
1207 {
1208   GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
1209
1210   g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism));
1211   g_return_if_fail (m->priv->is_client && !m->priv->is_server);
1212
1213   m->priv->is_client = FALSE;
1214 }
1215
1216 /* ---------------------------------------------------------------------------------------------------- */