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