Coverage Report

Created: 2025-06-13 06:55

/src/glib/gio/gdbusauthmechanismexternal.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
 * 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 <string.h>
26
27
#include "gdbusauthmechanismexternal.h"
28
#include "gcredentials.h"
29
#include "gdbuserror.h"
30
#include "gioenumtypes.h"
31
32
#include "glibintl.h"
33
34
#ifdef G_OS_WIN32
35
#include "gwin32sid.h"
36
#endif
37
38
struct _GDBusAuthMechanismExternalPrivate
39
{
40
  gboolean is_client;
41
  gboolean is_server;
42
  GDBusAuthMechanismState state;
43
  gboolean empty_data_sent;
44
};
45
46
static gint                     mechanism_get_priority              (void);
47
static const gchar             *mechanism_get_name                  (void);
48
49
static gboolean                 mechanism_is_supported              (GDBusAuthMechanism   *mechanism);
50
static gchar                   *mechanism_encode_data               (GDBusAuthMechanism   *mechanism,
51
                                                                     const gchar          *data,
52
                                                                     gsize                 data_len,
53
                                                                     gsize                *out_data_len);
54
static gchar                   *mechanism_decode_data               (GDBusAuthMechanism   *mechanism,
55
                                                                     const gchar          *data,
56
                                                                     gsize                 data_len,
57
                                                                     gsize                *out_data_len);
58
static GDBusAuthMechanismState  mechanism_server_get_state          (GDBusAuthMechanism   *mechanism);
59
static void                     mechanism_server_initiate           (GDBusAuthMechanism   *mechanism,
60
                                                                     const gchar          *initial_response,
61
                                                                     gsize                 initial_response_len);
62
static void                     mechanism_server_data_receive       (GDBusAuthMechanism   *mechanism,
63
                                                                     const gchar          *data,
64
                                                                     gsize                 data_len);
65
static gchar                   *mechanism_server_data_send          (GDBusAuthMechanism   *mechanism,
66
                                                                     gsize                *out_data_len);
67
static gchar                   *mechanism_server_get_reject_reason  (GDBusAuthMechanism   *mechanism);
68
static void                     mechanism_server_shutdown           (GDBusAuthMechanism   *mechanism);
69
static GDBusAuthMechanismState  mechanism_client_get_state          (GDBusAuthMechanism   *mechanism);
70
static gchar                   *mechanism_client_initiate           (GDBusAuthMechanism   *mechanism,
71
                                                                     GDBusConnectionFlags  conn_flags,
72
                                                                     gsize                *out_initial_response_len);
73
static void                     mechanism_client_data_receive       (GDBusAuthMechanism   *mechanism,
74
                                                                     const gchar          *data,
75
                                                                     gsize                 data_len);
76
static gchar                   *mechanism_client_data_send          (GDBusAuthMechanism   *mechanism,
77
                                                                     gsize                *out_data_len);
78
static void                     mechanism_client_shutdown           (GDBusAuthMechanism   *mechanism);
79
80
/* ---------------------------------------------------------------------------------------------------- */
81
82
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
83
84
/* ---------------------------------------------------------------------------------------------------- */
85
86
static void
87
_g_dbus_auth_mechanism_external_finalize (GObject *object)
88
0
{
89
  //GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
90
91
0
  if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
92
0
    G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
93
0
}
94
95
static void
96
_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
97
0
{
98
0
  GObjectClass *gobject_class;
99
0
  GDBusAuthMechanismClass *mechanism_class;
100
101
0
  gobject_class = G_OBJECT_CLASS (klass);
102
0
  gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
103
104
0
  mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
105
0
  mechanism_class->get_name                  = mechanism_get_name;
106
0
  mechanism_class->get_priority              = mechanism_get_priority;
107
0
  mechanism_class->is_supported              = mechanism_is_supported;
108
0
  mechanism_class->encode_data               = mechanism_encode_data;
109
0
  mechanism_class->decode_data               = mechanism_decode_data;
110
0
  mechanism_class->server_get_state          = mechanism_server_get_state;
111
0
  mechanism_class->server_initiate           = mechanism_server_initiate;
112
0
  mechanism_class->server_data_receive       = mechanism_server_data_receive;
113
0
  mechanism_class->server_data_send          = mechanism_server_data_send;
114
0
  mechanism_class->server_get_reject_reason  = mechanism_server_get_reject_reason;
115
0
  mechanism_class->server_shutdown           = mechanism_server_shutdown;
116
0
  mechanism_class->client_get_state          = mechanism_client_get_state;
117
0
  mechanism_class->client_initiate           = mechanism_client_initiate;
118
0
  mechanism_class->client_data_receive       = mechanism_client_data_receive;
119
0
  mechanism_class->client_data_send          = mechanism_client_data_send;
120
0
  mechanism_class->client_shutdown           = mechanism_client_shutdown;
121
0
}
122
123
static void
124
_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
125
0
{
126
0
  mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
127
0
}
128
129
/* ---------------------------------------------------------------------------------------------------- */
130
131
static gboolean
132
mechanism_is_supported (GDBusAuthMechanism *mechanism)
133
0
{
134
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
135
136
#if defined(G_OS_WIN32)
137
  /* all that is required is current process SID */
138
  return TRUE;
139
#else
140
  /* This mechanism is only available if credentials has been exchanged */
141
0
  if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
142
0
    return TRUE;
143
0
  else
144
0
    return FALSE;
145
0
#endif
146
0
}
147
148
static gint
149
mechanism_get_priority (void)
150
0
{
151
  /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
152
0
  return 100;
153
0
}
154
155
static const gchar *
156
mechanism_get_name (void)
157
0
{
158
0
  return "EXTERNAL";
159
0
}
160
161
static gchar *
162
mechanism_encode_data (GDBusAuthMechanism   *mechanism,
163
                       const gchar          *data,
164
                       gsize                 data_len,
165
                       gsize                *out_data_len)
166
0
{
167
0
  return NULL;
168
0
}
169
170
171
static gchar *
172
mechanism_decode_data (GDBusAuthMechanism   *mechanism,
173
                       const gchar          *data,
174
                       gsize                 data_len,
175
                       gsize                *out_data_len)
176
0
{
177
0
  return NULL;
178
0
}
179
180
/* ---------------------------------------------------------------------------------------------------- */
181
182
static GDBusAuthMechanismState
183
mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
184
0
{
185
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
186
187
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
188
0
  g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
189
190
0
  return m->priv->state;
191
0
}
192
193
static gboolean
194
data_matches_credentials (const gchar  *data,
195
                          gsize         data_len,
196
                          GCredentials *credentials)
197
0
{
198
0
  gboolean match;
199
200
0
  match = FALSE;
201
202
0
  if (credentials == NULL)
203
0
    goto out;
204
205
0
#if defined(G_OS_UNIX)
206
0
  {
207
0
    gint64 alleged_uid;
208
0
    gchar *endp;
209
210
    /* If we were unable to find out the uid, then nothing
211
     * can possibly match it.  */
212
0
    if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
213
0
      goto out;
214
215
    /* An empty authorization identity means we want to be
216
     * whatever identity the out-of-band credentials say we have
217
     * (RFC 4422 appendix A.1). This effectively matches any uid. */
218
0
    if (data == NULL || data_len == 0)
219
0
      {
220
0
        match = TRUE;
221
0
        goto out;
222
0
      }
223
    /* on UNIX, this is the uid as a string in base 10 */
224
0
    alleged_uid = g_ascii_strtoll (data, &endp, 10);
225
0
    if (*endp == '\0')
226
0
      {
227
0
        if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
228
0
          {
229
0
            match = TRUE;
230
0
          }
231
0
      }
232
0
  }
233
#else
234
  /* TODO: Dont know how to compare credentials on this OS. Please implement. */
235
#endif
236
237
0
 out:
238
0
  return match;
239
0
}
240
241
static void
242
mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
243
                           const gchar          *initial_response,
244
                           gsize                 initial_response_len)
245
0
{
246
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
247
248
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
249
0
  g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
250
251
0
  m->priv->is_server = TRUE;
252
253
0
  if (initial_response != NULL)
254
0
    {
255
0
      if (data_matches_credentials (initial_response,
256
0
                                    initial_response_len,
257
0
                                    _g_dbus_auth_mechanism_get_credentials (mechanism)))
258
0
        {
259
0
          m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
260
0
        }
261
0
      else
262
0
        {
263
0
          m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
264
0
        }
265
0
    }
266
0
  else
267
0
    {
268
      /* The initial-response optimization was not used, so we need to
269
       * send an empty challenge to prompt the client to respond. */
270
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
271
0
    }
272
0
}
273
274
static void
275
mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
276
                               const gchar          *data,
277
                               gsize                 data_len)
278
0
{
279
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
280
281
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
282
0
  g_return_if_fail (m->priv->is_server && !m->priv->is_client);
283
0
  g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
284
285
0
  if (data_matches_credentials (data,
286
0
                                data_len,
287
0
                                _g_dbus_auth_mechanism_get_credentials (mechanism)))
288
0
    {
289
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
290
0
    }
291
0
  else
292
0
    {
293
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
294
0
    }
295
0
}
296
297
static gchar *
298
mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
299
                            gsize                *out_data_len)
300
0
{
301
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
302
303
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
304
0
  g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
305
306
0
  if (out_data_len)
307
0
    *out_data_len = 0;
308
309
0
  if (m->priv->empty_data_sent)
310
0
    {
311
      /* We have already sent an empty data response.
312
         Reject the connection.  */
313
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
314
0
      return NULL;
315
0
    }
316
317
0
  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
318
0
  m->priv->empty_data_sent = TRUE;
319
320
0
  return g_strdup ("");
321
0
}
322
323
static gchar *
324
mechanism_server_get_reject_reason (GDBusAuthMechanism   *mechanism)
325
0
{
326
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
327
328
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
329
0
  g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
330
0
  g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
331
332
  /* can never end up here because we are never in the REJECTED state */
333
0
  g_assert_not_reached ();
334
335
0
  return NULL;
336
0
}
337
338
static void
339
mechanism_server_shutdown (GDBusAuthMechanism   *mechanism)
340
0
{
341
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
342
343
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
344
0
  g_return_if_fail (m->priv->is_server && !m->priv->is_client);
345
346
0
  m->priv->is_server = FALSE;
347
0
}
348
349
/* ---------------------------------------------------------------------------------------------------- */
350
351
static GDBusAuthMechanismState
352
mechanism_client_get_state (GDBusAuthMechanism   *mechanism)
353
0
{
354
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
355
356
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
357
0
  g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
358
359
0
  return m->priv->state;
360
0
}
361
362
static gchar *
363
mechanism_client_initiate (GDBusAuthMechanism   *mechanism,
364
                           GDBusConnectionFlags  conn_flags,
365
                           gsize                *out_initial_response_len)
366
0
{
367
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
368
0
  gchar *initial_response = NULL;
369
370
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
371
0
  g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
372
373
0
  m->priv->is_client = TRUE;
374
0
  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
375
376
0
  *out_initial_response_len = 0;
377
378
0
  if (conn_flags & G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE)
379
0
    {
380
      /* If backwards-compatibility with GDBus servers < 2.73.3 is not a
381
       * concern, we do not send an initial response, because there is
382
       * no way to express an empty authorization identity this way.
383
       * Instead, we'll reply to the server's first (empty) challenge
384
       * with an empty authorization identity in our first response.  */
385
0
      g_debug ("Using cross-namespace EXTERNAL authentication (this will deadlock if server is GDBus < 2.73.3)");
386
0
    }
387
0
  else
388
0
    {
389
      /* Send the Unix uid or Windows SID as an initial response.
390
       * This is the only thing that is interoperable with GDBus 2.73.3
391
       * servers. */
392
0
#if defined(G_OS_UNIX)
393
0
      GCredentials *credentials;
394
395
0
      credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
396
0
      g_assert (credentials != NULL);
397
398
0
      initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
399
#elif defined(G_OS_WIN32)
400
      initial_response = _g_win32_current_process_sid_string (NULL);
401
#else
402
      /* GDBus < 2.73.3 servers can't have worked on this platform anyway,
403
       * so it isn't a regression to behave as though
404
       * G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE had been set. */
405
      g_debug ("Unknown platform, cannot use initial response in EXTERNAL");
406
#endif
407
0
    }
408
409
0
  if (initial_response)
410
0
    {
411
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
412
0
      *out_initial_response_len = strlen (initial_response);
413
0
    }
414
0
  return initial_response;
415
0
}
416
417
static void
418
mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
419
                               const gchar          *data,
420
                               gsize                 data_len)
421
0
{
422
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
423
424
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
425
0
  g_return_if_fail (m->priv->is_client && !m->priv->is_server);
426
0
  g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
427
428
  /* The server sent us a challenge, which should normally
429
   * be empty.  We respond with our authorization identity.  */
430
0
  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
431
0
}
432
433
static gchar *
434
mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
435
                            gsize                *out_data_len)
436
0
{
437
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
438
439
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
440
0
  g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
441
0
  g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
442
443
  /* We respond to the server's challenge by sending our
444
   * authorization identity, which is the empty string, meaning
445
   * whoever the out-of-band credentials say we are.  */
446
0
  *out_data_len = 0;
447
0
  return g_strdup ("");
448
0
}
449
450
static void
451
mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
452
0
{
453
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
454
455
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
456
0
  g_return_if_fail (m->priv->is_client && !m->priv->is_server);
457
458
0
  m->priv->is_client = FALSE;
459
0
}
460
461
/* ---------------------------------------------------------------------------------------------------- */