Branch data Line data Source code
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 : 21746 : G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
83 : :
84 : : /* ---------------------------------------------------------------------------------------------------- */
85 : :
86 : : static void
87 : 1923 : _g_dbus_auth_mechanism_external_finalize (GObject *object)
88 : : {
89 : : //GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
90 : :
91 : 1923 : if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
92 : 1923 : G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
93 : 1923 : }
94 : :
95 : : static void
96 : 97 : _g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
97 : : {
98 : : GObjectClass *gobject_class;
99 : : GDBusAuthMechanismClass *mechanism_class;
100 : :
101 : 97 : gobject_class = G_OBJECT_CLASS (klass);
102 : 97 : gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
103 : :
104 : 97 : mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
105 : 97 : mechanism_class->get_name = mechanism_get_name;
106 : 97 : mechanism_class->get_priority = mechanism_get_priority;
107 : 97 : mechanism_class->is_supported = mechanism_is_supported;
108 : 97 : mechanism_class->encode_data = mechanism_encode_data;
109 : 97 : mechanism_class->decode_data = mechanism_decode_data;
110 : 97 : mechanism_class->server_get_state = mechanism_server_get_state;
111 : 97 : mechanism_class->server_initiate = mechanism_server_initiate;
112 : 97 : mechanism_class->server_data_receive = mechanism_server_data_receive;
113 : 97 : mechanism_class->server_data_send = mechanism_server_data_send;
114 : 97 : mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
115 : 97 : mechanism_class->server_shutdown = mechanism_server_shutdown;
116 : 97 : mechanism_class->client_get_state = mechanism_client_get_state;
117 : 97 : mechanism_class->client_initiate = mechanism_client_initiate;
118 : 97 : mechanism_class->client_data_receive = mechanism_client_data_receive;
119 : 97 : mechanism_class->client_data_send = mechanism_client_data_send;
120 : 97 : mechanism_class->client_shutdown = mechanism_client_shutdown;
121 : 97 : }
122 : :
123 : : static void
124 : 1923 : _g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
125 : : {
126 : 1923 : mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
127 : 1923 : }
128 : :
129 : : /* ---------------------------------------------------------------------------------------------------- */
130 : :
131 : : static gboolean
132 : 1793 : mechanism_is_supported (GDBusAuthMechanism *mechanism)
133 : : {
134 : 1793 : 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 : 1793 : if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
142 : 1587 : return TRUE;
143 : : else
144 : 206 : return FALSE;
145 : : #endif
146 : : }
147 : :
148 : : static gint
149 : 2137 : mechanism_get_priority (void)
150 : : {
151 : : /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
152 : 2137 : return 100;
153 : : }
154 : :
155 : : static const gchar *
156 : 7603 : mechanism_get_name (void)
157 : : {
158 : 7603 : return "EXTERNAL";
159 : : }
160 : :
161 : : static gchar *
162 : 0 : mechanism_encode_data (GDBusAuthMechanism *mechanism,
163 : : const gchar *data,
164 : : gsize data_len,
165 : : gsize *out_data_len)
166 : : {
167 : 0 : return NULL;
168 : : }
169 : :
170 : :
171 : : static gchar *
172 : 0 : mechanism_decode_data (GDBusAuthMechanism *mechanism,
173 : : const gchar *data,
174 : : gsize data_len,
175 : : gsize *out_data_len)
176 : : {
177 : 0 : return NULL;
178 : : }
179 : :
180 : : /* ---------------------------------------------------------------------------------------------------- */
181 : :
182 : : static GDBusAuthMechanismState
183 : 138 : mechanism_server_get_state (GDBusAuthMechanism *mechanism)
184 : : {
185 : 138 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
186 : :
187 : 138 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
188 : 138 : g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
189 : :
190 : 138 : return m->priv->state;
191 : : }
192 : :
193 : : static gboolean
194 : 130 : data_matches_credentials (const gchar *data,
195 : : gsize data_len,
196 : : GCredentials *credentials)
197 : : {
198 : : gboolean match;
199 : :
200 : 130 : match = FALSE;
201 : :
202 : 130 : if (credentials == NULL)
203 : 0 : goto out;
204 : :
205 : : #if defined(G_OS_UNIX)
206 : : {
207 : : gint64 alleged_uid;
208 : : gchar *endp;
209 : :
210 : : /* If we were unable to find out the uid, then nothing
211 : : * can possibly match it. */
212 : 130 : if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
213 : 22 : 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 : 110 : if (data == NULL || data_len == 0)
219 : : {
220 : 2 : match = TRUE;
221 : 2 : goto out;
222 : : }
223 : : /* on UNIX, this is the uid as a string in base 10 */
224 : 108 : alleged_uid = g_ascii_strtoll (data, &endp, 10);
225 : 108 : if (*endp == '\0')
226 : : {
227 : 108 : if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
228 : : {
229 : 106 : match = TRUE;
230 : : }
231 : : }
232 : : }
233 : : #else
234 : : /* TODO: Dont know how to compare credentials on this OS. Please implement. */
235 : : #endif
236 : :
237 : 130 : out:
238 : 130 : return match;
239 : : }
240 : :
241 : : static void
242 : 130 : mechanism_server_initiate (GDBusAuthMechanism *mechanism,
243 : : const gchar *initial_response,
244 : : gsize initial_response_len)
245 : : {
246 : 130 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
247 : :
248 : 130 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
249 : 130 : g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
250 : :
251 : 130 : m->priv->is_server = TRUE;
252 : :
253 : 130 : if (initial_response != NULL)
254 : : {
255 : 126 : if (data_matches_credentials (initial_response,
256 : : initial_response_len,
257 : : _g_dbus_auth_mechanism_get_credentials (mechanism)))
258 : : {
259 : 105 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
260 : : }
261 : : else
262 : : {
263 : 21 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
264 : : }
265 : : }
266 : : else
267 : : {
268 : : /* The initial-response optimization was not used, so we need to
269 : : * send an empty challenge to prompt the client to respond. */
270 : 4 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
271 : : }
272 : : }
273 : :
274 : : static void
275 : 4 : mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
276 : : const gchar *data,
277 : : gsize data_len)
278 : : {
279 : 4 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
280 : :
281 : 4 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
282 : 4 : g_return_if_fail (m->priv->is_server && !m->priv->is_client);
283 : 4 : g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
284 : :
285 : 4 : if (data_matches_credentials (data,
286 : : data_len,
287 : : _g_dbus_auth_mechanism_get_credentials (mechanism)))
288 : : {
289 : 3 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
290 : : }
291 : : else
292 : : {
293 : 1 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
294 : : }
295 : : }
296 : :
297 : : static gchar *
298 : 4 : mechanism_server_data_send (GDBusAuthMechanism *mechanism,
299 : : gsize *out_data_len)
300 : : {
301 : 4 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
302 : :
303 : 4 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
304 : 4 : g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
305 : :
306 : 4 : if (out_data_len)
307 : 4 : *out_data_len = 0;
308 : :
309 : 4 : if (m->priv->empty_data_sent)
310 : : {
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 : : }
316 : :
317 : 4 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
318 : 4 : m->priv->empty_data_sent = TRUE;
319 : :
320 : 4 : return g_strdup ("");
321 : : }
322 : :
323 : : static gchar *
324 : 0 : mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
325 : : {
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 : : g_assert_not_reached ();
334 : :
335 : : return NULL;
336 : : }
337 : :
338 : : static void
339 : 0 : mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
340 : : {
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 : : }
348 : :
349 : : /* ---------------------------------------------------------------------------------------------------- */
350 : :
351 : : static GDBusAuthMechanismState
352 : 3039 : mechanism_client_get_state (GDBusAuthMechanism *mechanism)
353 : : {
354 : 3039 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
355 : :
356 : 3039 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
357 : 3039 : g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
358 : :
359 : 3039 : return m->priv->state;
360 : : }
361 : :
362 : : static gchar *
363 : 1587 : mechanism_client_initiate (GDBusAuthMechanism *mechanism,
364 : : GDBusConnectionFlags conn_flags,
365 : : gsize *out_initial_response_len)
366 : : {
367 : 1587 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
368 : 1587 : gchar *initial_response = NULL;
369 : :
370 : 1587 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
371 : 1587 : g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
372 : :
373 : 1587 : m->priv->is_client = TRUE;
374 : 1587 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
375 : :
376 : 1587 : *out_initial_response_len = 0;
377 : :
378 : 1587 : if (conn_flags & G_DBUS_CONNECTION_FLAGS_CROSS_NAMESPACE)
379 : : {
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 : : }
386 : : else
387 : : {
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 : : #if defined(G_OS_UNIX)
392 : : GCredentials *credentials;
393 : :
394 : 135 : credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
395 : 135 : g_assert (credentials != NULL);
396 : :
397 : 135 : 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 : : }
407 : :
408 : 1587 : if (initial_response)
409 : : {
410 : 135 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
411 : 135 : *out_initial_response_len = strlen (initial_response);
412 : : }
413 : 1587 : return initial_response;
414 : : }
415 : :
416 : : static void
417 : 1452 : mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
418 : : const gchar *data,
419 : : gsize data_len)
420 : : {
421 : 1452 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
422 : :
423 : 1452 : g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
424 : 1452 : g_return_if_fail (m->priv->is_client && !m->priv->is_server);
425 : 1452 : 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 : 1452 : m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
430 : : }
431 : :
432 : : static gchar *
433 : 1452 : mechanism_client_data_send (GDBusAuthMechanism *mechanism,
434 : : gsize *out_data_len)
435 : : {
436 : 1452 : GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
437 : :
438 : 1452 : g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
439 : 1452 : g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
440 : 1452 : 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 : 1452 : *out_data_len = 0;
446 : 1452 : return g_strdup ("");
447 : : }
448 : :
449 : : static void
450 : 0 : mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
451 : : {
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 : : }
459 : :
460 : : /* ---------------------------------------------------------------------------------------------------- */
|