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