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