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