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