Coverage Report

Created: 2026-10-14 08:19

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