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