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