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/gdbusauthmechanismexternal.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 <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_or_client_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
0
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
83
0
84
0
/* ---------------------------------------------------------------------------------------------------- */
85
0
86
0
static void
87
0
_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_or_client_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_get_reject_reason  = mechanism_server_or_client_get_reject_reason;
121
0
  mechanism_class->client_shutdown           = mechanism_client_shutdown;
122
0
}
123
124
static void
125
_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
126
0
{
127
0
  mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
128
0
}
129
130
/* ---------------------------------------------------------------------------------------------------- */
131
132
static gboolean
133
mechanism_is_supported (GDBusAuthMechanism *mechanism)
134
0
{
135
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
136
137
#if defined(G_OS_WIN32)
138
  /* all that is required is current process SID */
139
  return TRUE;
140
#else
141
  /* This mechanism is only available if credentials has been exchanged */
142
0
  if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
143
0
    return TRUE;
144
0
  else
145
0
    return FALSE;
146
0
#endif
147
0
}
148
149
static gint
150
mechanism_get_priority (void)
151
0
{
152
  /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
153
0
  return 100;
154
0
}
155
156
static const gchar *
157
mechanism_get_name (void)
158
0
{
159
0
  return "EXTERNAL";
160
0
}
161
162
static gchar *
163
mechanism_encode_data (GDBusAuthMechanism   *mechanism,
164
                       const gchar          *data,
165
                       gsize                 data_len,
166
                       gsize                *out_data_len)
167
0
{
168
0
  return NULL;
169
0
}
170
171
172
static gchar *
173
mechanism_decode_data (GDBusAuthMechanism   *mechanism,
174
                       const gchar          *data,
175
                       gsize                 data_len,
176
                       gsize                *out_data_len)
177
0
{
178
0
  return NULL;
179
0
}
180
181
/* ---------------------------------------------------------------------------------------------------- */
182
183
static GDBusAuthMechanismState
184
mechanism_server_get_state (GDBusAuthMechanism   *mechanism)
185
0
{
186
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
187
188
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
189
0
  g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
190
191
0
  return m->priv->state;
192
0
}
193
194
static gboolean
195
data_matches_credentials (const gchar  *data,
196
                          gsize         data_len,
197
                          GCredentials *credentials)
198
0
{
199
0
  gboolean match;
200
201
0
  match = FALSE;
202
203
0
  if (credentials == NULL)
204
0
    goto out;
205
206
0
#if defined(G_OS_UNIX)
207
0
  {
208
0
    gint64 alleged_uid;
209
0
    gchar *endp;
210
211
    /* If we were unable to find out the uid, then nothing
212
     * can possibly match it.  */
213
0
    if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
214
0
      goto out;
215
216
    /* An empty authorization identity means we want to be
217
     * whatever identity the out-of-band credentials say we have
218
     * (RFC 4422 appendix A.1). This effectively matches any uid. */
219
0
    if (data == NULL || data_len == 0)
220
0
      {
221
0
        match = TRUE;
222
0
        goto out;
223
0
      }
224
    /* on UNIX, this is the uid as a string in base 10 */
225
0
    alleged_uid = g_ascii_strtoll (data, &endp, 10);
226
0
    if (*endp == '\0')
227
0
      {
228
0
        if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
229
0
          {
230
0
            match = TRUE;
231
0
          }
232
0
      }
233
0
  }
234
#else
235
  /* TODO: Dont know how to compare credentials on this OS. Please implement. */
236
#endif
237
238
0
 out:
239
0
  return match;
240
0
}
241
242
static void
243
mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
244
                           const gchar          *initial_response,
245
                           gsize                 initial_response_len)
246
0
{
247
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
248
249
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
250
0
  g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
251
252
0
  m->priv->is_server = TRUE;
253
254
0
  if (initial_response != NULL)
255
0
    {
256
0
      if (data_matches_credentials (initial_response,
257
0
                                    initial_response_len,
258
0
                                    _g_dbus_auth_mechanism_get_credentials (mechanism)))
259
0
        {
260
0
          m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
261
0
        }
262
0
      else
263
0
        {
264
0
          m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
265
0
        }
266
0
    }
267
0
  else
268
0
    {
269
      /* The initial-response optimization was not used, so we need to
270
       * send an empty challenge to prompt the client to respond. */
271
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
272
0
    }
273
0
}
274
275
static void
276
mechanism_server_data_receive (GDBusAuthMechanism   *mechanism,
277
                               const gchar          *data,
278
                               gsize                 data_len)
279
0
{
280
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
281
282
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
283
0
  g_return_if_fail (m->priv->is_server && !m->priv->is_client);
284
0
  g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
285
286
0
  if (data_matches_credentials (data,
287
0
                                data_len,
288
0
                                _g_dbus_auth_mechanism_get_credentials (mechanism)))
289
0
    {
290
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
291
0
    }
292
0
  else
293
0
    {
294
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
295
0
    }
296
0
}
297
298
static gchar *
299
mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
300
                            gsize                *out_data_len)
301
0
{
302
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
303
304
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
305
0
  g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
306
307
0
  if (out_data_len)
308
0
    *out_data_len = 0;
309
310
0
  if (m->priv->empty_data_sent)
311
0
    {
312
      /* We have already sent an empty data response.
313
         Reject the connection.  */
314
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
315
0
      return NULL;
316
0
    }
317
318
0
  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
319
0
  m->priv->empty_data_sent = TRUE;
320
321
0
  return g_strdup ("");
322
0
}
323
324
static gchar *
325
mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism   *mechanism)
326
0
{
327
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
328
329
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), 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
    }
386
0
  else
387
0
    {
388
      /* Send the Unix uid or Windows SID as an initial response.
389
       * This is the only thing that is interoperable with GDBus < 2.73.3
390
       * servers (RHEL ≤ 9, Debian ≤ 11, Ubuntu ≤ 22.04, SUSE ≤ 15.5). */
391
0
#if defined(G_OS_UNIX)
392
0
      GCredentials *credentials;
393
394
0
      credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
395
0
      g_assert (credentials != NULL);
396
397
0
      initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
398
#elif defined(G_OS_WIN32)
399
      initial_response = _g_win32_current_process_sid_string (NULL);
400
#else
401
      /* GDBus < 2.73.3 servers can't have worked on this platform anyway,
402
       * so it isn't a regression to behave as though
403
       * G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE had been set. */
404
      g_debug ("Unknown platform, cannot use initial response in EXTERNAL");
405
#endif
406
0
    }
407
408
0
  if (initial_response)
409
0
    {
410
0
      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
411
0
      *out_initial_response_len = strlen (initial_response);
412
0
    }
413
0
  return initial_response;
414
0
}
415
416
static void
417
mechanism_client_data_receive (GDBusAuthMechanism   *mechanism,
418
                               const gchar          *data,
419
                               gsize                 data_len)
420
0
{
421
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
422
423
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
424
0
  g_return_if_fail (m->priv->is_client && !m->priv->is_server);
425
0
  g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
426
427
  /* The server sent us a challenge, which should normally
428
   * be empty.  We respond with our authorization identity.  */
429
0
  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
430
0
}
431
432
static gchar *
433
mechanism_client_data_send (GDBusAuthMechanism   *mechanism,
434
                            gsize                *out_data_len)
435
0
{
436
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
437
438
0
  g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
439
0
  g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
440
0
  g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
441
442
  /* We respond to the server's challenge by sending our
443
   * authorization identity, which is the empty string, meaning
444
   * whoever the out-of-band credentials say we are.  */
445
0
  *out_data_len = 0;
446
0
  return g_strdup ("");
447
0
}
448
449
static void
450
mechanism_client_shutdown (GDBusAuthMechanism   *mechanism)
451
0
{
452
0
  GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
453
454
0
  g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
455
0
  g_return_if_fail (m->priv->is_client && !m->priv->is_server);
456
457
0
  m->priv->is_client = FALSE;
458
0
}
459
460
/* ---------------------------------------------------------------------------------------------------- */