Coverage Report

Created: 2018-09-25 13:52

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