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