nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* GDBus - GLib D-Bus Library |
2 | * |
||
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General |
||
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: David Zeuthen <davidz@redhat.com> |
||
19 | */ |
||
20 | |||
21 | #include "config.h" |
||
22 | |||
23 | #include "gdbusauth.h" |
||
24 | |||
25 | #include "gdbusauthmechanismanon.h" |
||
26 | #include "gdbusauthmechanismexternal.h" |
||
27 | #include "gdbusauthmechanismsha1.h" |
||
28 | #include "gdbusauthobserver.h" |
||
29 | |||
30 | #include "gdbuserror.h" |
||
31 | #include "gdbusutils.h" |
||
32 | #include "gioenumtypes.h" |
||
33 | #include "gcredentials.h" |
||
34 | #include "gdbusprivate.h" |
||
35 | #include "giostream.h" |
||
36 | #include "gdatainputstream.h" |
||
37 | #include "gdataoutputstream.h" |
||
38 | |||
39 | #ifdef G_OS_UNIX |
||
40 | #include "gnetworking.h" |
||
41 | #include "gunixconnection.h" |
||
42 | #include "gunixcredentialsmessage.h" |
||
43 | #endif |
||
44 | |||
45 | #include "glibintl.h" |
||
46 | |||
47 | G_GNUC_PRINTF(1, 2) |
||
48 | static void |
||
49 | debug_print (const gchar *message, ...) |
||
50 | { |
||
51 | if (G_UNLIKELY (_g_dbus_debug_authentication ())) |
||
52 | { |
||
53 | gchar *s; |
||
54 | GString *str; |
||
55 | va_list var_args; |
||
56 | guint n; |
||
57 | |||
58 | _g_dbus_debug_print_lock (); |
||
59 | |||
60 | va_start (var_args, message); |
||
61 | s = g_strdup_vprintf (message, var_args); |
||
62 | va_end (var_args); |
||
63 | |||
64 | str = g_string_new (NULL); |
||
65 | for (n = 0; s[n] != '\0'; n++) |
||
66 | { |
||
67 | if (G_UNLIKELY (s[n] == '\r')) |
||
68 | g_string_append (str, "\\r"); |
||
69 | else if (G_UNLIKELY (s[n] == '\n')) |
||
70 | g_string_append (str, "\\n"); |
||
71 | else |
||
72 | g_string_append_c (str, s[n]); |
||
73 | } |
||
74 | g_print ("GDBus-debug:Auth: %s\n", str->str); |
||
75 | g_string_free (str, TRUE); |
||
76 | g_free (s); |
||
77 | |||
78 | _g_dbus_debug_print_unlock (); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | typedef struct |
||
83 | { |
||
84 | const gchar *name; |
||
85 | gint priority; |
||
86 | GType gtype; |
||
87 | } Mechanism; |
||
88 | |||
89 | static void mechanism_free (Mechanism *m); |
||
90 | |||
91 | struct _GDBusAuthPrivate |
||
92 | { |
||
93 | GIOStream *stream; |
||
94 | |||
95 | /* A list of available Mechanism, sorted according to priority */ |
||
96 | GList *available_mechanisms; |
||
97 | }; |
||
98 | |||
99 | enum |
||
100 | { |
||
101 | PROP_0, |
||
102 | PROP_STREAM |
||
103 | }; |
||
104 | |||
105 | G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuth, _g_dbus_auth, G_TYPE_OBJECT) |
||
106 | |||
107 | /* ---------------------------------------------------------------------------------------------------- */ |
||
108 | |||
109 | static void |
||
110 | _g_dbus_auth_finalize (GObject *object) |
||
111 | { |
||
112 | GDBusAuth *auth = G_DBUS_AUTH (object); |
||
113 | |||
114 | if (auth->priv->stream != NULL) |
||
115 | g_object_unref (auth->priv->stream); |
||
116 | g_list_free_full (auth->priv->available_mechanisms, (GDestroyNotify) mechanism_free); |
||
117 | |||
118 | if (G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize != NULL) |
||
119 | G_OBJECT_CLASS (_g_dbus_auth_parent_class)->finalize (object); |
||
120 | } |
||
121 | |||
122 | static void |
||
123 | _g_dbus_auth_get_property (GObject *object, |
||
124 | guint prop_id, |
||
125 | GValue *value, |
||
126 | GParamSpec *pspec) |
||
127 | { |
||
128 | GDBusAuth *auth = G_DBUS_AUTH (object); |
||
129 | |||
130 | switch (prop_id) |
||
131 | { |
||
132 | case PROP_STREAM: |
||
133 | g_value_set_object (value, auth->priv->stream); |
||
134 | break; |
||
135 | |||
136 | default: |
||
137 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
138 | break; |
||
139 | } |
||
140 | } |
||
141 | |||
142 | static void |
||
143 | _g_dbus_auth_set_property (GObject *object, |
||
144 | guint prop_id, |
||
145 | const GValue *value, |
||
146 | GParamSpec *pspec) |
||
147 | { |
||
148 | GDBusAuth *auth = G_DBUS_AUTH (object); |
||
149 | |||
150 | switch (prop_id) |
||
151 | { |
||
152 | case PROP_STREAM: |
||
153 | auth->priv->stream = g_value_dup_object (value); |
||
154 | break; |
||
155 | |||
156 | default: |
||
157 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
||
158 | break; |
||
159 | } |
||
160 | } |
||
161 | |||
162 | static void |
||
163 | _g_dbus_auth_class_init (GDBusAuthClass *klass) |
||
164 | { |
||
165 | GObjectClass *gobject_class; |
||
166 | |||
167 | gobject_class = G_OBJECT_CLASS (klass); |
||
168 | gobject_class->get_property = _g_dbus_auth_get_property; |
||
169 | gobject_class->set_property = _g_dbus_auth_set_property; |
||
170 | gobject_class->finalize = _g_dbus_auth_finalize; |
||
171 | |||
172 | g_object_class_install_property (gobject_class, |
||
173 | PROP_STREAM, |
||
174 | g_param_spec_object ("stream", |
||
175 | P_("IO Stream"), |
||
176 | P_("The underlying GIOStream used for I/O"), |
||
177 | G_TYPE_IO_STREAM, |
||
178 | G_PARAM_READABLE | |
||
179 | G_PARAM_WRITABLE | |
||
180 | G_PARAM_CONSTRUCT_ONLY | |
||
181 | G_PARAM_STATIC_NAME | |
||
182 | G_PARAM_STATIC_BLURB | |
||
183 | G_PARAM_STATIC_NICK)); |
||
184 | } |
||
185 | |||
186 | static void |
||
187 | mechanism_free (Mechanism *m) |
||
188 | { |
||
189 | g_free (m); |
||
190 | } |
||
191 | |||
192 | static void |
||
193 | add_mechanism (GDBusAuth *auth, |
||
194 | GDBusAuthObserver *observer, |
||
195 | GType mechanism_type) |
||
196 | { |
||
197 | const gchar *name; |
||
198 | |||
199 | name = _g_dbus_auth_mechanism_get_name (mechanism_type); |
||
200 | if (observer == NULL || g_dbus_auth_observer_allow_mechanism (observer, name)) |
||
201 | { |
||
202 | Mechanism *m; |
||
203 | m = g_new0 (Mechanism, 1); |
||
204 | m->name = name; |
||
205 | m->priority = _g_dbus_auth_mechanism_get_priority (mechanism_type); |
||
206 | m->gtype = mechanism_type; |
||
207 | auth->priv->available_mechanisms = g_list_prepend (auth->priv->available_mechanisms, m); |
||
208 | } |
||
209 | } |
||
210 | |||
211 | static gint |
||
212 | mech_compare_func (Mechanism *a, Mechanism *b) |
||
213 | { |
||
214 | gint ret; |
||
215 | /* ensure deterministic order */ |
||
216 | ret = b->priority - a->priority; |
||
217 | if (ret == 0) |
||
218 | ret = g_strcmp0 (b->name, a->name); |
||
219 | return ret; |
||
220 | } |
||
221 | |||
222 | static void |
||
223 | _g_dbus_auth_init (GDBusAuth *auth) |
||
224 | { |
||
225 | auth->priv = _g_dbus_auth_get_instance_private (auth); |
||
226 | } |
||
227 | |||
228 | static void |
||
229 | _g_dbus_auth_add_mechs (GDBusAuth *auth, |
||
230 | GDBusAuthObserver *observer) |
||
231 | { |
||
232 | /* TODO: trawl extension points */ |
||
233 | add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_ANON); |
||
234 | add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_SHA1); |
||
235 | add_mechanism (auth, observer, G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL); |
||
236 | |||
237 | auth->priv->available_mechanisms = g_list_sort (auth->priv->available_mechanisms, |
||
238 | (GCompareFunc) mech_compare_func); |
||
239 | } |
||
240 | |||
241 | static GType |
||
242 | find_mech_by_name (GDBusAuth *auth, |
||
243 | const gchar *name) |
||
244 | { |
||
245 | GType ret; |
||
246 | GList *l; |
||
247 | |||
248 | ret = (GType) 0; |
||
249 | |||
250 | for (l = auth->priv->available_mechanisms; l != NULL; l = l->next) |
||
251 | { |
||
252 | Mechanism *m = l->data; |
||
253 | if (g_strcmp0 (name, m->name) == 0) |
||
254 | { |
||
255 | ret = m->gtype; |
||
256 | goto out; |
||
257 | } |
||
258 | } |
||
259 | |||
260 | out: |
||
261 | return ret; |
||
262 | } |
||
263 | |||
264 | GDBusAuth * |
||
265 | _g_dbus_auth_new (GIOStream *stream) |
||
266 | { |
||
267 | return g_object_new (G_TYPE_DBUS_AUTH, |
||
268 | "stream", stream, |
||
269 | NULL); |
||
270 | } |
||
271 | |||
272 | /* ---------------------------------------------------------------------------------------------------- */ |
||
273 | /* like g_data_input_stream_read_line() but sets error if there's no content to read */ |
||
274 | static gchar * |
||
275 | _my_g_data_input_stream_read_line (GDataInputStream *dis, |
||
276 | gsize *out_line_length, |
||
277 | GCancellable *cancellable, |
||
278 | GError **error) |
||
279 | { |
||
280 | gchar *ret; |
||
281 | |||
282 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
||
283 | |||
284 | ret = g_data_input_stream_read_line (dis, |
||
285 | out_line_length, |
||
286 | cancellable, |
||
287 | error); |
||
288 | if (ret == NULL && error != NULL && *error == NULL) |
||
289 | { |
||
290 | g_set_error_literal (error, |
||
291 | G_IO_ERROR, |
||
292 | G_IO_ERROR_FAILED, |
||
293 | _("Unexpected lack of content trying to read a line")); |
||
294 | } |
||
295 | |||
296 | return ret; |
||
297 | } |
||
298 | |||
299 | /* This function is to avoid situations like this |
||
300 | * |
||
301 | * BEGIN\r\nl\0\0\1... |
||
302 | * |
||
303 | * e.g. where we read into the first D-Bus message while waiting for |
||
304 | * the final line from the client (TODO: file bug against gio for |
||
305 | * this) |
||
306 | */ |
||
307 | static gchar * |
||
308 | _my_g_input_stream_read_line_safe (GInputStream *i, |
||
309 | gsize *out_line_length, |
||
310 | GCancellable *cancellable, |
||
311 | GError **error) |
||
312 | { |
||
313 | GString *str; |
||
314 | gchar c; |
||
315 | gssize num_read; |
||
316 | gboolean last_was_cr; |
||
317 | |||
318 | str = g_string_new (NULL); |
||
319 | |||
320 | last_was_cr = FALSE; |
||
321 | while (TRUE) |
||
322 | { |
||
323 | num_read = g_input_stream_read (i, |
||
324 | &c, |
||
325 | 1, |
||
326 | cancellable, |
||
327 | error); |
||
328 | if (num_read == -1) |
||
329 | goto fail; |
||
330 | if (num_read == 0) |
||
331 | { |
||
332 | if (error != NULL && *error == NULL) |
||
333 | { |
||
334 | g_set_error_literal (error, |
||
335 | G_IO_ERROR, |
||
336 | G_IO_ERROR_FAILED, |
||
337 | _("Unexpected lack of content trying to (safely) read a line")); |
||
338 | } |
||
339 | goto fail; |
||
340 | } |
||
341 | |||
342 | g_string_append_c (str, (gint) c); |
||
343 | if (last_was_cr) |
||
344 | { |
||
345 | if (c == 0x0a) |
||
346 | { |
||
347 | g_assert (str->len >= 2); |
||
348 | g_string_set_size (str, str->len - 2); |
||
349 | goto out; |
||
350 | } |
||
351 | } |
||
352 | last_was_cr = (c == 0x0d); |
||
353 | } |
||
354 | |||
355 | out: |
||
356 | if (out_line_length != NULL) |
||
357 | *out_line_length = str->len; |
||
358 | return g_string_free (str, FALSE); |
||
359 | |||
360 | fail: |
||
361 | g_assert (error == NULL || *error != NULL); |
||
362 | g_string_free (str, TRUE); |
||
363 | return NULL; |
||
364 | } |
||
365 | |||
366 | /* ---------------------------------------------------------------------------------------------------- */ |
||
367 | |||
368 | static void |
||
369 | append_nibble (GString *s, gint val) |
||
370 | { |
||
371 | g_string_append_c (s, val >= 10 ? ('a' + val - 10) : ('0' + val)); |
||
372 | } |
||
373 | |||
374 | static gchar * |
||
375 | hexdecode (const gchar *str, |
||
376 | gsize *out_len, |
||
377 | GError **error) |
||
378 | { |
||
379 | gchar *ret; |
||
380 | GString *s; |
||
381 | guint n; |
||
382 | |||
383 | ret = NULL; |
||
384 | s = g_string_new (NULL); |
||
385 | |||
386 | for (n = 0; str[n] != '\0'; n += 2) |
||
387 | { |
||
388 | gint upper_nibble; |
||
389 | gint lower_nibble; |
||
390 | guint value; |
||
391 | |||
392 | upper_nibble = g_ascii_xdigit_value (str[n]); |
||
393 | lower_nibble = g_ascii_xdigit_value (str[n + 1]); |
||
394 | if (upper_nibble == -1 || lower_nibble == -1) |
||
395 | { |
||
396 | g_set_error (error, |
||
397 | G_IO_ERROR, |
||
398 | G_IO_ERROR_FAILED, |
||
399 | "Error hexdecoding string '%s' around position %d", |
||
400 | str, n); |
||
401 | goto out; |
||
402 | } |
||
403 | value = (upper_nibble<<4) | lower_nibble; |
||
404 | g_string_append_c (s, value); |
||
405 | } |
||
406 | |||
407 | ret = g_string_free (s, FALSE); |
||
408 | s = NULL; |
||
409 | |||
410 | out: |
||
411 | if (s != NULL) |
||
412 | g_string_free (s, TRUE); |
||
413 | return ret; |
||
414 | } |
||
415 | |||
416 | /* TODO: take len */ |
||
417 | static gchar * |
||
418 | hexencode (const gchar *str) |
||
419 | { |
||
420 | guint n; |
||
421 | GString *s; |
||
422 | |||
423 | s = g_string_new (NULL); |
||
424 | for (n = 0; str[n] != '\0'; n++) |
||
425 | { |
||
426 | gint val; |
||
427 | gint upper_nibble; |
||
428 | gint lower_nibble; |
||
429 | |||
430 | val = ((const guchar *) str)[n]; |
||
431 | upper_nibble = val >> 4; |
||
432 | lower_nibble = val & 0x0f; |
||
433 | |||
434 | append_nibble (s, upper_nibble); |
||
435 | append_nibble (s, lower_nibble); |
||
436 | } |
||
437 | |||
438 | return g_string_free (s, FALSE); |
||
439 | } |
||
440 | |||
441 | /* ---------------------------------------------------------------------------------------------------- */ |
||
442 | |||
443 | static GDBusAuthMechanism * |
||
444 | client_choose_mech_and_send_initial_response (GDBusAuth *auth, |
||
445 | GCredentials *credentials_that_were_sent, |
||
446 | const gchar* const *supported_auth_mechs, |
||
447 | GPtrArray *attempted_auth_mechs, |
||
448 | GDataOutputStream *dos, |
||
449 | GCancellable *cancellable, |
||
450 | GError **error) |
||
451 | { |
||
452 | GDBusAuthMechanism *mech; |
||
453 | GType auth_mech_to_use_gtype; |
||
454 | guint n; |
||
455 | guint m; |
||
456 | gchar *initial_response; |
||
457 | gsize initial_response_len; |
||
458 | gchar *encoded; |
||
459 | gchar *s; |
||
460 | |||
461 | again: |
||
462 | mech = NULL; |
||
463 | |||
464 | debug_print ("CLIENT: Trying to choose mechanism"); |
||
465 | |||
466 | /* find an authentication mechanism to try, if any */ |
||
467 | auth_mech_to_use_gtype = (GType) 0; |
||
468 | for (n = 0; supported_auth_mechs[n] != NULL; n++) |
||
469 | { |
||
470 | gboolean attempted_already; |
||
471 | attempted_already = FALSE; |
||
472 | for (m = 0; m < attempted_auth_mechs->len; m++) |
||
473 | { |
||
474 | if (g_strcmp0 (supported_auth_mechs[n], attempted_auth_mechs->pdata[m]) == 0) |
||
475 | { |
||
476 | attempted_already = TRUE; |
||
477 | break; |
||
478 | } |
||
479 | } |
||
480 | if (!attempted_already) |
||
481 | { |
||
482 | auth_mech_to_use_gtype = find_mech_by_name (auth, supported_auth_mechs[n]); |
||
483 | if (auth_mech_to_use_gtype != (GType) 0) |
||
484 | break; |
||
485 | } |
||
486 | } |
||
487 | |||
488 | if (auth_mech_to_use_gtype == (GType) 0) |
||
489 | { |
||
490 | guint n; |
||
491 | gchar *available; |
||
492 | GString *tried_str; |
||
493 | |||
494 | debug_print ("CLIENT: Exhausted all available mechanisms"); |
||
495 | |||
496 | available = g_strjoinv (", ", (gchar **) supported_auth_mechs); |
||
497 | |||
498 | tried_str = g_string_new (NULL); |
||
499 | for (n = 0; n < attempted_auth_mechs->len; n++) |
||
500 | { |
||
501 | if (n > 0) |
||
502 | g_string_append (tried_str, ", "); |
||
503 | g_string_append (tried_str, attempted_auth_mechs->pdata[n]); |
||
504 | } |
||
505 | g_set_error (error, |
||
506 | G_IO_ERROR, |
||
507 | G_IO_ERROR_FAILED, |
||
508 | _("Exhausted all available authentication mechanisms (tried: %s) (available: %s)"), |
||
509 | tried_str->str, |
||
510 | available); |
||
511 | g_string_free (tried_str, TRUE); |
||
512 | g_free (available); |
||
513 | goto out; |
||
514 | } |
||
515 | |||
516 | /* OK, decided on a mechanism - let's do this thing */ |
||
517 | mech = g_object_new (auth_mech_to_use_gtype, |
||
518 | "stream", auth->priv->stream, |
||
519 | "credentials", credentials_that_were_sent, |
||
520 | NULL); |
||
521 | debug_print ("CLIENT: Trying mechanism '%s'", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); |
||
522 | g_ptr_array_add (attempted_auth_mechs, (gpointer) _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); |
||
523 | |||
524 | /* the auth mechanism may not be supported |
||
525 | * (for example, EXTERNAL only works if credentials were exchanged) |
||
526 | */ |
||
527 | if (!_g_dbus_auth_mechanism_is_supported (mech)) |
||
528 | { |
||
529 | debug_print ("CLIENT: Mechanism '%s' says it is not supported", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); |
||
530 | g_object_unref (mech); |
||
531 | mech = NULL; |
||
532 | goto again; |
||
533 | } |
||
534 | |||
535 | initial_response_len = -1; |
||
536 | initial_response = _g_dbus_auth_mechanism_client_initiate (mech, |
||
537 | &initial_response_len); |
||
538 | #if 0 |
||
539 | g_printerr ("using auth mechanism with name '%s' of type '%s' with initial response '%s'\n", |
||
540 | _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype), |
||
541 | g_type_name (G_TYPE_FROM_INSTANCE (mech)), |
||
542 | initial_response); |
||
543 | #endif |
||
544 | if (initial_response != NULL) |
||
545 | { |
||
546 | //g_printerr ("initial_response = '%s'\n", initial_response); |
||
547 | encoded = hexencode (initial_response); |
||
548 | s = g_strdup_printf ("AUTH %s %s\r\n", |
||
549 | _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype), |
||
550 | encoded); |
||
551 | g_free (initial_response); |
||
552 | g_free (encoded); |
||
553 | } |
||
554 | else |
||
555 | { |
||
556 | s = g_strdup_printf ("AUTH %s\r\n", _g_dbus_auth_mechanism_get_name (auth_mech_to_use_gtype)); |
||
557 | } |
||
558 | debug_print ("CLIENT: writing '%s'", s); |
||
559 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
560 | { |
||
561 | g_object_unref (mech); |
||
562 | mech = NULL; |
||
563 | g_free (s); |
||
564 | goto out; |
||
565 | } |
||
566 | g_free (s); |
||
567 | |||
568 | out: |
||
569 | return mech; |
||
570 | } |
||
571 | |||
572 | |||
573 | /* ---------------------------------------------------------------------------------------------------- */ |
||
574 | |||
575 | typedef enum |
||
576 | { |
||
577 | CLIENT_STATE_WAITING_FOR_DATA, |
||
578 | CLIENT_STATE_WAITING_FOR_OK, |
||
579 | CLIENT_STATE_WAITING_FOR_REJECT, |
||
580 | CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD |
||
581 | } ClientState; |
||
582 | |||
583 | gchar * |
||
584 | _g_dbus_auth_run_client (GDBusAuth *auth, |
||
585 | GDBusAuthObserver *observer, |
||
586 | GDBusCapabilityFlags offered_capabilities, |
||
587 | GDBusCapabilityFlags *out_negotiated_capabilities, |
||
588 | GCancellable *cancellable, |
||
589 | GError **error) |
||
590 | { |
||
591 | gchar *s; |
||
592 | GDataInputStream *dis; |
||
593 | GDataOutputStream *dos; |
||
594 | GCredentials *credentials; |
||
595 | gchar *ret_guid; |
||
596 | gchar *line; |
||
597 | gsize line_length; |
||
598 | gchar **supported_auth_mechs; |
||
599 | GPtrArray *attempted_auth_mechs; |
||
600 | GDBusAuthMechanism *mech; |
||
601 | ClientState state; |
||
602 | GDBusCapabilityFlags negotiated_capabilities; |
||
603 | |||
604 | debug_print ("CLIENT: initiating"); |
||
605 | |||
606 | _g_dbus_auth_add_mechs (auth, observer); |
||
607 | |||
608 | ret_guid = NULL; |
||
609 | supported_auth_mechs = NULL; |
||
610 | attempted_auth_mechs = g_ptr_array_new (); |
||
611 | mech = NULL; |
||
612 | negotiated_capabilities = 0; |
||
613 | credentials = NULL; |
||
614 | |||
615 | dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream))); |
||
616 | dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream))); |
||
617 | g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE); |
||
618 | g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE); |
||
619 | |||
620 | g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); |
||
621 | |||
622 | #ifdef G_OS_UNIX |
||
623 | if (G_IS_UNIX_CONNECTION (auth->priv->stream)) |
||
624 | { |
||
625 | credentials = g_credentials_new (); |
||
626 | if (!g_unix_connection_send_credentials (G_UNIX_CONNECTION (auth->priv->stream), |
||
627 | cancellable, |
||
628 | error)) |
||
629 | goto out; |
||
630 | } |
||
631 | else |
||
632 | { |
||
633 | if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error)) |
||
634 | goto out; |
||
635 | } |
||
636 | #else |
||
637 | if (!g_data_output_stream_put_byte (dos, '\0', cancellable, error)) |
||
638 | goto out; |
||
639 | #endif |
||
640 | |||
641 | if (credentials != NULL) |
||
642 | { |
||
643 | if (G_UNLIKELY (_g_dbus_debug_authentication ())) |
||
644 | { |
||
645 | s = g_credentials_to_string (credentials); |
||
646 | debug_print ("CLIENT: sent credentials '%s'", s); |
||
647 | g_free (s); |
||
648 | } |
||
649 | } |
||
650 | else |
||
651 | { |
||
652 | debug_print ("CLIENT: didn't send any credentials"); |
||
653 | } |
||
654 | |||
655 | /* TODO: to reduce roundtrips, try to pick an auth mechanism to start with */ |
||
656 | |||
657 | /* Get list of supported authentication mechanisms */ |
||
658 | s = "AUTH\r\n"; |
||
659 | debug_print ("CLIENT: writing '%s'", s); |
||
660 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
661 | goto out; |
||
662 | state = CLIENT_STATE_WAITING_FOR_REJECT; |
||
663 | |||
664 | while (TRUE) |
||
665 | { |
||
666 | switch (state) |
||
667 | { |
||
668 | case CLIENT_STATE_WAITING_FOR_REJECT: |
||
669 | debug_print ("CLIENT: WaitingForReject"); |
||
670 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
671 | if (line == NULL) |
||
672 | goto out; |
||
673 | debug_print ("CLIENT: WaitingForReject, read '%s'", line); |
||
674 | |||
675 | choose_mechanism: |
||
676 | if (!g_str_has_prefix (line, "REJECTED ")) |
||
677 | { |
||
678 | g_set_error (error, |
||
679 | G_IO_ERROR, |
||
680 | G_IO_ERROR_FAILED, |
||
681 | "In WaitingForReject: Expected 'REJECTED am1 am2 ... amN', got '%s'", |
||
682 | line); |
||
683 | g_free (line); |
||
684 | goto out; |
||
685 | } |
||
686 | if (supported_auth_mechs == NULL) |
||
687 | { |
||
688 | supported_auth_mechs = g_strsplit (line + sizeof ("REJECTED ") - 1, " ", 0); |
||
689 | #if 0 |
||
690 | for (n = 0; supported_auth_mechs != NULL && supported_auth_mechs[n] != NULL; n++) |
||
691 | g_printerr ("supported_auth_mechs[%d] = '%s'\n", n, supported_auth_mechs[n]); |
||
692 | #endif |
||
693 | } |
||
694 | g_free (line); |
||
695 | mech = client_choose_mech_and_send_initial_response (auth, |
||
696 | credentials, |
||
697 | (const gchar* const *) supported_auth_mechs, |
||
698 | attempted_auth_mechs, |
||
699 | dos, |
||
700 | cancellable, |
||
701 | error); |
||
702 | if (mech == NULL) |
||
703 | goto out; |
||
704 | if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA) |
||
705 | state = CLIENT_STATE_WAITING_FOR_DATA; |
||
706 | else |
||
707 | state = CLIENT_STATE_WAITING_FOR_OK; |
||
708 | break; |
||
709 | |||
710 | case CLIENT_STATE_WAITING_FOR_OK: |
||
711 | debug_print ("CLIENT: WaitingForOK"); |
||
712 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
713 | if (line == NULL) |
||
714 | goto out; |
||
715 | debug_print ("CLIENT: WaitingForOK, read '%s'", line); |
||
716 | if (g_str_has_prefix (line, "OK ")) |
||
717 | { |
||
718 | if (!g_dbus_is_guid (line + 3)) |
||
719 | { |
||
720 | g_set_error (error, |
||
721 | G_IO_ERROR, |
||
722 | G_IO_ERROR_FAILED, |
||
723 | "Invalid OK response '%s'", |
||
724 | line); |
||
725 | g_free (line); |
||
726 | goto out; |
||
727 | } |
||
728 | ret_guid = g_strdup (line + 3); |
||
729 | g_free (line); |
||
730 | |||
731 | if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) |
||
732 | { |
||
733 | s = "NEGOTIATE_UNIX_FD\r\n"; |
||
734 | debug_print ("CLIENT: writing '%s'", s); |
||
735 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
736 | goto out; |
||
737 | state = CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD; |
||
738 | } |
||
739 | else |
||
740 | { |
||
741 | s = "BEGIN\r\n"; |
||
742 | debug_print ("CLIENT: writing '%s'", s); |
||
743 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
744 | goto out; |
||
745 | /* and we're done! */ |
||
746 | goto out; |
||
747 | } |
||
748 | } |
||
749 | else if (g_str_has_prefix (line, "REJECTED ")) |
||
750 | { |
||
751 | goto choose_mechanism; |
||
752 | } |
||
753 | else |
||
754 | { |
||
755 | /* TODO: handle other valid responses */ |
||
756 | g_set_error (error, |
||
757 | G_IO_ERROR, |
||
758 | G_IO_ERROR_FAILED, |
||
759 | "In WaitingForOk: unexpected response '%s'", |
||
760 | line); |
||
761 | g_free (line); |
||
762 | goto out; |
||
763 | } |
||
764 | break; |
||
765 | |||
766 | case CLIENT_STATE_WAITING_FOR_AGREE_UNIX_FD: |
||
767 | debug_print ("CLIENT: WaitingForAgreeUnixFD"); |
||
768 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
769 | if (line == NULL) |
||
770 | goto out; |
||
771 | debug_print ("CLIENT: WaitingForAgreeUnixFD, read='%s'", line); |
||
772 | if (g_strcmp0 (line, "AGREE_UNIX_FD") == 0) |
||
773 | { |
||
774 | g_free (line); |
||
775 | negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; |
||
776 | s = "BEGIN\r\n"; |
||
777 | debug_print ("CLIENT: writing '%s'", s); |
||
778 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
779 | goto out; |
||
780 | /* and we're done! */ |
||
781 | goto out; |
||
782 | } |
||
783 | else if (g_str_has_prefix (line, "ERROR") && (line[5] == 0 || g_ascii_isspace (line[5]))) |
||
784 | { |
||
785 | //g_strstrip (line + 5); g_debug ("bah, no unix_fd: '%s'", line + 5); |
||
786 | g_free (line); |
||
787 | s = "BEGIN\r\n"; |
||
788 | debug_print ("CLIENT: writing '%s'", s); |
||
789 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
790 | goto out; |
||
791 | /* and we're done! */ |
||
792 | goto out; |
||
793 | } |
||
794 | else |
||
795 | { |
||
796 | /* TODO: handle other valid responses */ |
||
797 | g_set_error (error, |
||
798 | G_IO_ERROR, |
||
799 | G_IO_ERROR_FAILED, |
||
800 | "In WaitingForAgreeUnixFd: unexpected response '%s'", |
||
801 | line); |
||
802 | g_free (line); |
||
803 | goto out; |
||
804 | } |
||
805 | break; |
||
806 | |||
807 | case CLIENT_STATE_WAITING_FOR_DATA: |
||
808 | debug_print ("CLIENT: WaitingForData"); |
||
809 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
810 | if (line == NULL) |
||
811 | goto out; |
||
812 | debug_print ("CLIENT: WaitingForData, read='%s'", line); |
||
813 | if (g_str_has_prefix (line, "DATA ")) |
||
814 | { |
||
815 | gchar *encoded; |
||
816 | gchar *decoded_data; |
||
817 | gsize decoded_data_len = 0; |
||
818 | |||
819 | encoded = g_strdup (line + 5); |
||
820 | g_free (line); |
||
821 | g_strstrip (encoded); |
||
822 | decoded_data = hexdecode (encoded, &decoded_data_len, error); |
||
823 | g_free (encoded); |
||
824 | if (decoded_data == NULL) |
||
825 | { |
||
826 | g_prefix_error (error, "DATA response is malformed: "); |
||
827 | /* invalid encoding, disconnect! */ |
||
828 | goto out; |
||
829 | } |
||
830 | _g_dbus_auth_mechanism_client_data_receive (mech, decoded_data, decoded_data_len); |
||
831 | g_free (decoded_data); |
||
832 | |||
833 | if (_g_dbus_auth_mechanism_client_get_state (mech) == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND) |
||
834 | { |
||
835 | gchar *data; |
||
836 | gsize data_len; |
||
837 | gchar *encoded_data; |
||
838 | data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len); |
||
839 | encoded_data = hexencode (data); |
||
840 | s = g_strdup_printf ("DATA %s\r\n", encoded_data); |
||
841 | g_free (encoded_data); |
||
842 | g_free (data); |
||
843 | debug_print ("CLIENT: writing '%s'", s); |
||
844 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
845 | { |
||
846 | g_free (s); |
||
847 | goto out; |
||
848 | } |
||
849 | g_free (s); |
||
850 | } |
||
851 | state = CLIENT_STATE_WAITING_FOR_OK; |
||
852 | } |
||
853 | else if (g_str_has_prefix (line, "REJECTED ")) |
||
854 | { |
||
855 | /* could be the chosen authentication method just doesn't work. Try |
||
856 | * another one... |
||
857 | */ |
||
858 | goto choose_mechanism; |
||
859 | } |
||
860 | else |
||
861 | { |
||
862 | g_set_error (error, |
||
863 | G_IO_ERROR, |
||
864 | G_IO_ERROR_FAILED, |
||
865 | "In WaitingForData: unexpected response '%s'", |
||
866 | line); |
||
867 | g_free (line); |
||
868 | goto out; |
||
869 | } |
||
870 | break; |
||
871 | |||
872 | default: |
||
873 | g_assert_not_reached (); |
||
874 | break; |
||
875 | } |
||
876 | |||
877 | }; /* main authentication client loop */ |
||
878 | |||
879 | out: |
||
880 | if (mech != NULL) |
||
881 | g_object_unref (mech); |
||
882 | g_ptr_array_unref (attempted_auth_mechs); |
||
883 | g_strfreev (supported_auth_mechs); |
||
884 | g_object_unref (dis); |
||
885 | g_object_unref (dos); |
||
886 | |||
887 | /* ensure return value is NULL if error is set */ |
||
888 | if (error != NULL && *error != NULL) |
||
889 | { |
||
890 | g_free (ret_guid); |
||
891 | ret_guid = NULL; |
||
892 | } |
||
893 | |||
894 | if (ret_guid != NULL) |
||
895 | { |
||
896 | if (out_negotiated_capabilities != NULL) |
||
897 | *out_negotiated_capabilities = negotiated_capabilities; |
||
898 | } |
||
899 | |||
900 | if (credentials != NULL) |
||
901 | g_object_unref (credentials); |
||
902 | |||
903 | debug_print ("CLIENT: Done, authenticated=%d", ret_guid != NULL); |
||
904 | |||
905 | return ret_guid; |
||
906 | } |
||
907 | |||
908 | /* ---------------------------------------------------------------------------------------------------- */ |
||
909 | |||
910 | static gchar * |
||
911 | get_auth_mechanisms (GDBusAuth *auth, |
||
912 | gboolean allow_anonymous, |
||
913 | const gchar *prefix, |
||
914 | const gchar *suffix, |
||
915 | const gchar *separator) |
||
916 | { |
||
917 | GList *l; |
||
918 | GString *str; |
||
919 | gboolean need_sep; |
||
920 | |||
921 | str = g_string_new (prefix); |
||
922 | need_sep = FALSE; |
||
923 | for (l = auth->priv->available_mechanisms; l != NULL; l = l->next) |
||
924 | { |
||
925 | Mechanism *m = l->data; |
||
926 | |||
927 | if (!allow_anonymous && g_strcmp0 (m->name, "ANONYMOUS") == 0) |
||
928 | continue; |
||
929 | |||
930 | if (need_sep) |
||
931 | g_string_append (str, separator); |
||
932 | g_string_append (str, m->name); |
||
933 | need_sep = TRUE; |
||
934 | } |
||
935 | |||
936 | g_string_append (str, suffix); |
||
937 | return g_string_free (str, FALSE); |
||
938 | } |
||
939 | |||
940 | |||
941 | typedef enum |
||
942 | { |
||
943 | SERVER_STATE_WAITING_FOR_AUTH, |
||
944 | SERVER_STATE_WAITING_FOR_DATA, |
||
945 | SERVER_STATE_WAITING_FOR_BEGIN |
||
946 | } ServerState; |
||
947 | |||
948 | gboolean |
||
949 | _g_dbus_auth_run_server (GDBusAuth *auth, |
||
950 | GDBusAuthObserver *observer, |
||
951 | const gchar *guid, |
||
952 | gboolean allow_anonymous, |
||
953 | GDBusCapabilityFlags offered_capabilities, |
||
954 | GDBusCapabilityFlags *out_negotiated_capabilities, |
||
955 | GCredentials **out_received_credentials, |
||
956 | GCancellable *cancellable, |
||
957 | GError **error) |
||
958 | { |
||
959 | gboolean ret; |
||
960 | ServerState state; |
||
961 | GDataInputStream *dis; |
||
962 | GDataOutputStream *dos; |
||
963 | GError *local_error; |
||
964 | guchar byte; |
||
965 | gchar *line; |
||
966 | gsize line_length; |
||
967 | GDBusAuthMechanism *mech; |
||
968 | gchar *s; |
||
969 | GDBusCapabilityFlags negotiated_capabilities; |
||
970 | GCredentials *credentials; |
||
971 | |||
972 | debug_print ("SERVER: initiating"); |
||
973 | |||
974 | _g_dbus_auth_add_mechs (auth, observer); |
||
975 | |||
976 | ret = FALSE; |
||
977 | dis = NULL; |
||
978 | dos = NULL; |
||
979 | mech = NULL; |
||
980 | negotiated_capabilities = 0; |
||
981 | credentials = NULL; |
||
982 | |||
983 | if (!g_dbus_is_guid (guid)) |
||
984 | { |
||
985 | g_set_error (error, |
||
986 | G_IO_ERROR, |
||
987 | G_IO_ERROR_FAILED, |
||
988 | "The given guid '%s' is not valid", |
||
989 | guid); |
||
990 | goto out; |
||
991 | } |
||
992 | |||
993 | dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream))); |
||
994 | dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream))); |
||
995 | g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE); |
||
996 | g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE); |
||
997 | |||
998 | g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF); |
||
999 | |||
1000 | /* first read the NUL-byte (TODO: read credentials if using a unix domain socket) */ |
||
1001 | #ifdef G_OS_UNIX |
||
1002 | if (G_IS_UNIX_CONNECTION (auth->priv->stream)) |
||
1003 | { |
||
1004 | local_error = NULL; |
||
1005 | credentials = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (auth->priv->stream), |
||
1006 | cancellable, |
||
1007 | &local_error); |
||
1008 | if (credentials == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) |
||
1009 | { |
||
1010 | g_propagate_error (error, local_error); |
||
1011 | goto out; |
||
1012 | } |
||
1013 | } |
||
1014 | else |
||
1015 | { |
||
1016 | local_error = NULL; |
||
1017 | byte = g_data_input_stream_read_byte (dis, cancellable, &local_error); |
||
1018 | byte = byte; /* To avoid -Wunused-but-set-variable */ |
||
1019 | if (local_error != NULL) |
||
1020 | { |
||
1021 | g_propagate_error (error, local_error); |
||
1022 | goto out; |
||
1023 | } |
||
1024 | } |
||
1025 | #else |
||
1026 | local_error = NULL; |
||
1027 | byte = g_data_input_stream_read_byte (dis, cancellable, &local_error); |
||
1028 | byte = byte; /* To avoid -Wunused-but-set-variable */ |
||
1029 | if (local_error != NULL) |
||
1030 | { |
||
1031 | g_propagate_error (error, local_error); |
||
1032 | goto out; |
||
1033 | } |
||
1034 | #endif |
||
1035 | if (credentials != NULL) |
||
1036 | { |
||
1037 | if (G_UNLIKELY (_g_dbus_debug_authentication ())) |
||
1038 | { |
||
1039 | s = g_credentials_to_string (credentials); |
||
1040 | debug_print ("SERVER: received credentials '%s'", s); |
||
1041 | g_free (s); |
||
1042 | } |
||
1043 | } |
||
1044 | else |
||
1045 | { |
||
1046 | debug_print ("SERVER: didn't receive any credentials"); |
||
1047 | } |
||
1048 | |||
1049 | state = SERVER_STATE_WAITING_FOR_AUTH; |
||
1050 | while (TRUE) |
||
1051 | { |
||
1052 | switch (state) |
||
1053 | { |
||
1054 | case SERVER_STATE_WAITING_FOR_AUTH: |
||
1055 | debug_print ("SERVER: WaitingForAuth"); |
||
1056 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
1057 | debug_print ("SERVER: WaitingForAuth, read '%s'", line); |
||
1058 | if (line == NULL) |
||
1059 | goto out; |
||
1060 | if (g_strcmp0 (line, "AUTH") == 0) |
||
1061 | { |
||
1062 | s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); |
||
1063 | debug_print ("SERVER: writing '%s'", s); |
||
1064 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1065 | { |
||
1066 | g_free (s); |
||
1067 | goto out; |
||
1068 | } |
||
1069 | g_free (s); |
||
1070 | g_free (line); |
||
1071 | } |
||
1072 | else if (g_str_has_prefix (line, "AUTH ")) |
||
1073 | { |
||
1074 | gchar **tokens; |
||
1075 | const gchar *encoded; |
||
1076 | const gchar *mech_name; |
||
1077 | GType auth_mech_to_use_gtype; |
||
1078 | |||
1079 | tokens = g_strsplit (line, " ", 0); |
||
1080 | g_free (line); |
||
1081 | |||
1082 | switch (g_strv_length (tokens)) |
||
1083 | { |
||
1084 | case 2: |
||
1085 | /* no initial response */ |
||
1086 | mech_name = tokens[1]; |
||
1087 | encoded = NULL; |
||
1088 | break; |
||
1089 | |||
1090 | case 3: |
||
1091 | /* initial response */ |
||
1092 | mech_name = tokens[1]; |
||
1093 | encoded = tokens[2]; |
||
1094 | break; |
||
1095 | |||
1096 | default: |
||
1097 | g_set_error (error, |
||
1098 | G_IO_ERROR, |
||
1099 | G_IO_ERROR_FAILED, |
||
1100 | "Unexpected line '%s' while in WaitingForAuth state", |
||
1101 | line); |
||
1102 | g_strfreev (tokens); |
||
1103 | goto out; |
||
1104 | } |
||
1105 | |||
1106 | /* TODO: record that the client has attempted to use this mechanism */ |
||
1107 | //g_debug ("client is trying '%s'", mech_name); |
||
1108 | |||
1109 | auth_mech_to_use_gtype = find_mech_by_name (auth, mech_name); |
||
1110 | if ((auth_mech_to_use_gtype == (GType) 0) || |
||
1111 | (!allow_anonymous && g_strcmp0 (mech_name, "ANONYMOUS") == 0)) |
||
1112 | { |
||
1113 | /* We don't support this auth mechanism */ |
||
1114 | g_strfreev (tokens); |
||
1115 | s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); |
||
1116 | debug_print ("SERVER: writing '%s'", s); |
||
1117 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1118 | { |
||
1119 | g_free (s); |
||
1120 | goto out; |
||
1121 | } |
||
1122 | g_free (s); |
||
1123 | |||
1124 | /* stay in WAITING FOR AUTH */ |
||
1125 | state = SERVER_STATE_WAITING_FOR_AUTH; |
||
1126 | } |
||
1127 | else |
||
1128 | { |
||
1129 | gchar *initial_response; |
||
1130 | gsize initial_response_len; |
||
1131 | |||
1132 | mech = g_object_new (auth_mech_to_use_gtype, |
||
1133 | "stream", auth->priv->stream, |
||
1134 | "credentials", credentials, |
||
1135 | NULL); |
||
1136 | |||
1137 | initial_response = NULL; |
||
1138 | initial_response_len = 0; |
||
1139 | if (encoded != NULL) |
||
1140 | { |
||
1141 | initial_response = hexdecode (encoded, &initial_response_len, error); |
||
1142 | if (initial_response == NULL) |
||
1143 | { |
||
1144 | g_prefix_error (error, "Initial response is malformed: "); |
||
1145 | /* invalid encoding, disconnect! */ |
||
1146 | g_strfreev (tokens); |
||
1147 | goto out; |
||
1148 | } |
||
1149 | } |
||
1150 | |||
1151 | _g_dbus_auth_mechanism_server_initiate (mech, |
||
1152 | initial_response, |
||
1153 | initial_response_len); |
||
1154 | g_free (initial_response); |
||
1155 | g_strfreev (tokens); |
||
1156 | |||
1157 | change_state: |
||
1158 | switch (_g_dbus_auth_mechanism_server_get_state (mech)) |
||
1159 | { |
||
1160 | case G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED: |
||
1161 | if (observer != NULL && |
||
1162 | !g_dbus_auth_observer_authorize_authenticated_peer (observer, |
||
1163 | auth->priv->stream, |
||
1164 | credentials)) |
||
1165 | { |
||
1166 | /* disconnect */ |
||
1167 | g_set_error_literal (error, |
||
1168 | G_IO_ERROR, |
||
1169 | G_IO_ERROR_FAILED, |
||
1170 | _("Cancelled via GDBusAuthObserver::authorize-authenticated-peer")); |
||
1171 | goto out; |
||
1172 | } |
||
1173 | else |
||
1174 | { |
||
1175 | s = g_strdup_printf ("OK %s\r\n", guid); |
||
1176 | debug_print ("SERVER: writing '%s'", s); |
||
1177 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1178 | { |
||
1179 | g_free (s); |
||
1180 | goto out; |
||
1181 | } |
||
1182 | g_free (s); |
||
1183 | state = SERVER_STATE_WAITING_FOR_BEGIN; |
||
1184 | } |
||
1185 | break; |
||
1186 | |||
1187 | case G_DBUS_AUTH_MECHANISM_STATE_REJECTED: |
||
1188 | s = get_auth_mechanisms (auth, allow_anonymous, "REJECTED ", "\r\n", " "); |
||
1189 | debug_print ("SERVER: writing '%s'", s); |
||
1190 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1191 | { |
||
1192 | g_free (s); |
||
1193 | goto out; |
||
1194 | } |
||
1195 | g_free (s); |
||
1196 | state = SERVER_STATE_WAITING_FOR_AUTH; |
||
1197 | break; |
||
1198 | |||
1199 | case G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA: |
||
1200 | state = SERVER_STATE_WAITING_FOR_DATA; |
||
1201 | break; |
||
1202 | |||
1203 | case G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND: |
||
1204 | { |
||
1205 | gchar *data; |
||
1206 | gsize data_len; |
||
1207 | gchar *encoded_data; |
||
1208 | data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len); |
||
1209 | encoded_data = hexencode (data); |
||
1210 | s = g_strdup_printf ("DATA %s\r\n", encoded_data); |
||
1211 | g_free (encoded_data); |
||
1212 | g_free (data); |
||
1213 | debug_print ("SERVER: writing '%s'", s); |
||
1214 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1215 | { |
||
1216 | g_free (s); |
||
1217 | goto out; |
||
1218 | } |
||
1219 | g_free (s); |
||
1220 | } |
||
1221 | goto change_state; |
||
1222 | break; |
||
1223 | |||
1224 | default: |
||
1225 | /* TODO */ |
||
1226 | g_assert_not_reached (); |
||
1227 | break; |
||
1228 | } |
||
1229 | } |
||
1230 | } |
||
1231 | else |
||
1232 | { |
||
1233 | g_set_error (error, |
||
1234 | G_IO_ERROR, |
||
1235 | G_IO_ERROR_FAILED, |
||
1236 | "Unexpected line '%s' while in WaitingForAuth state", |
||
1237 | line); |
||
1238 | g_free (line); |
||
1239 | goto out; |
||
1240 | } |
||
1241 | break; |
||
1242 | |||
1243 | case SERVER_STATE_WAITING_FOR_DATA: |
||
1244 | debug_print ("SERVER: WaitingForData"); |
||
1245 | line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error); |
||
1246 | debug_print ("SERVER: WaitingForData, read '%s'", line); |
||
1247 | if (line == NULL) |
||
1248 | goto out; |
||
1249 | if (g_str_has_prefix (line, "DATA ")) |
||
1250 | { |
||
1251 | gchar *encoded; |
||
1252 | gchar *decoded_data; |
||
1253 | gsize decoded_data_len = 0; |
||
1254 | |||
1255 | encoded = g_strdup (line + 5); |
||
1256 | g_free (line); |
||
1257 | g_strstrip (encoded); |
||
1258 | decoded_data = hexdecode (encoded, &decoded_data_len, error); |
||
1259 | g_free (encoded); |
||
1260 | if (decoded_data == NULL) |
||
1261 | { |
||
1262 | g_prefix_error (error, "DATA response is malformed: "); |
||
1263 | /* invalid encoding, disconnect! */ |
||
1264 | goto out; |
||
1265 | } |
||
1266 | _g_dbus_auth_mechanism_server_data_receive (mech, decoded_data, decoded_data_len); |
||
1267 | g_free (decoded_data); |
||
1268 | /* oh man, this goto-crap is so ugly.. really need to rewrite the state machine */ |
||
1269 | goto change_state; |
||
1270 | } |
||
1271 | else |
||
1272 | { |
||
1273 | g_set_error (error, |
||
1274 | G_IO_ERROR, |
||
1275 | G_IO_ERROR_FAILED, |
||
1276 | "Unexpected line '%s' while in WaitingForData state", |
||
1277 | line); |
||
1278 | g_free (line); |
||
1279 | } |
||
1280 | goto out; |
||
1281 | |||
1282 | case SERVER_STATE_WAITING_FOR_BEGIN: |
||
1283 | debug_print ("SERVER: WaitingForBegin"); |
||
1284 | /* Use extremely slow (but reliable) line reader - this basically |
||
1285 | * does a recvfrom() system call per character |
||
1286 | * |
||
1287 | * (the problem with using GDataInputStream's read_line is that because of |
||
1288 | * buffering it might start reading into the first D-Bus message that |
||
1289 | * appears after "BEGIN\r\n"....) |
||
1290 | */ |
||
1291 | line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream), |
||
1292 | &line_length, |
||
1293 | cancellable, |
||
1294 | error); |
||
1295 | debug_print ("SERVER: WaitingForBegin, read '%s'", line); |
||
1296 | if (line == NULL) |
||
1297 | goto out; |
||
1298 | if (g_strcmp0 (line, "BEGIN") == 0) |
||
1299 | { |
||
1300 | /* YAY, done! */ |
||
1301 | ret = TRUE; |
||
1302 | g_free (line); |
||
1303 | goto out; |
||
1304 | } |
||
1305 | else if (g_strcmp0 (line, "NEGOTIATE_UNIX_FD") == 0) |
||
1306 | { |
||
1307 | g_free (line); |
||
1308 | if (offered_capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) |
||
1309 | { |
||
1310 | negotiated_capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING; |
||
1311 | s = "AGREE_UNIX_FD\r\n"; |
||
1312 | debug_print ("SERVER: writing '%s'", s); |
||
1313 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1314 | goto out; |
||
1315 | } |
||
1316 | else |
||
1317 | { |
||
1318 | s = "ERROR \"fd passing not offered\"\r\n"; |
||
1319 | debug_print ("SERVER: writing '%s'", s); |
||
1320 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1321 | goto out; |
||
1322 | } |
||
1323 | } |
||
1324 | else |
||
1325 | { |
||
1326 | g_debug ("Unexpected line '%s' while in WaitingForBegin state", line); |
||
1327 | g_free (line); |
||
1328 | s = "ERROR \"Unknown Command\"\r\n"; |
||
1329 | debug_print ("SERVER: writing '%s'", s); |
||
1330 | if (!g_data_output_stream_put_string (dos, s, cancellable, error)) |
||
1331 | goto out; |
||
1332 | } |
||
1333 | break; |
||
1334 | |||
1335 | default: |
||
1336 | g_assert_not_reached (); |
||
1337 | break; |
||
1338 | } |
||
1339 | } |
||
1340 | |||
1341 | |||
1342 | g_set_error_literal (error, |
||
1343 | G_IO_ERROR, |
||
1344 | G_IO_ERROR_FAILED, |
||
1345 | "Not implemented (server)"); |
||
1346 | |||
1347 | out: |
||
1348 | if (mech != NULL) |
||
1349 | g_object_unref (mech); |
||
1350 | if (dis != NULL) |
||
1351 | g_object_unref (dis); |
||
1352 | if (dos != NULL) |
||
1353 | g_object_unref (dos); |
||
1354 | |||
1355 | /* ensure return value is FALSE if error is set */ |
||
1356 | if (error != NULL && *error != NULL) |
||
1357 | { |
||
1358 | ret = FALSE; |
||
1359 | } |
||
1360 | |||
1361 | if (ret) |
||
1362 | { |
||
1363 | if (out_negotiated_capabilities != NULL) |
||
1364 | *out_negotiated_capabilities = negotiated_capabilities; |
||
1365 | if (out_received_credentials != NULL) |
||
1366 | *out_received_credentials = credentials != NULL ? g_object_ref (credentials) : NULL; |
||
1367 | } |
||
1368 | |||
1369 | if (credentials != NULL) |
||
1370 | g_object_unref (credentials); |
||
1371 | |||
1372 | debug_print ("SERVER: Done, authenticated=%d", ret); |
||
1373 | |||
1374 | return ret; |
||
1375 | } |
||
1376 | |||
1377 | /* ---------------------------------------------------------------------------------------------------- */ |