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