1/* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: David Zeuthen <davidz@redhat.com>
19 */
20
21#include "config.h"
22
23#include <string.h>
24
25#include "gdbusauthmechanismexternal.h"
26#include "gcredentials.h"
27#include "gdbuserror.h"
28#include "gioenumtypes.h"
29
30#include "glibintl.h"
31
32struct _GDBusAuthMechanismExternalPrivate
33{
34 gboolean is_client;
35 gboolean is_server;
36 GDBusAuthMechanismState state;
37};
38
39static gint mechanism_get_priority (void);
40static const gchar *mechanism_get_name (void);
41
42static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
43static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
44 const gchar *data,
45 gsize data_len,
46 gsize *out_data_len);
47static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
48 const gchar *data,
49 gsize data_len,
50 gsize *out_data_len);
51static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
52static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
53 const gchar *initial_response,
54 gsize initial_response_len);
55static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
56 const gchar *data,
57 gsize data_len);
58static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
59 gsize *out_data_len);
60static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
61static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
62static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
63static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
64 gsize *out_initial_response_len);
65static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
66 const gchar *data,
67 gsize data_len);
68static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
69 gsize *out_data_len);
70static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
71
72/* ---------------------------------------------------------------------------------------------------- */
73
74G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
75
76/* ---------------------------------------------------------------------------------------------------- */
77
78static void
79_g_dbus_auth_mechanism_external_finalize (GObject *object)
80{
81 //GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
82
83 if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
84 G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
85}
86
87static void
88_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
89{
90 GObjectClass *gobject_class;
91 GDBusAuthMechanismClass *mechanism_class;
92
93 gobject_class = G_OBJECT_CLASS (klass);
94 gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
95
96 mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
97 mechanism_class->get_name = mechanism_get_name;
98 mechanism_class->get_priority = mechanism_get_priority;
99 mechanism_class->is_supported = mechanism_is_supported;
100 mechanism_class->encode_data = mechanism_encode_data;
101 mechanism_class->decode_data = mechanism_decode_data;
102 mechanism_class->server_get_state = mechanism_server_get_state;
103 mechanism_class->server_initiate = mechanism_server_initiate;
104 mechanism_class->server_data_receive = mechanism_server_data_receive;
105 mechanism_class->server_data_send = mechanism_server_data_send;
106 mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
107 mechanism_class->server_shutdown = mechanism_server_shutdown;
108 mechanism_class->client_get_state = mechanism_client_get_state;
109 mechanism_class->client_initiate = mechanism_client_initiate;
110 mechanism_class->client_data_receive = mechanism_client_data_receive;
111 mechanism_class->client_data_send = mechanism_client_data_send;
112 mechanism_class->client_shutdown = mechanism_client_shutdown;
113}
114
115static void
116_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
117{
118 mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (self: mechanism);
119}
120
121/* ---------------------------------------------------------------------------------------------------- */
122
123static gboolean
124mechanism_is_supported (GDBusAuthMechanism *mechanism)
125{
126 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
127 /* This mechanism is only available if credentials has been exchanged */
128 if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
129 return TRUE;
130 else
131 return FALSE;
132}
133
134static gint
135mechanism_get_priority (void)
136{
137 /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
138 return 100;
139}
140
141static const gchar *
142mechanism_get_name (void)
143{
144 return "EXTERNAL";
145}
146
147static gchar *
148mechanism_encode_data (GDBusAuthMechanism *mechanism,
149 const gchar *data,
150 gsize data_len,
151 gsize *out_data_len)
152{
153 return NULL;
154}
155
156
157static gchar *
158mechanism_decode_data (GDBusAuthMechanism *mechanism,
159 const gchar *data,
160 gsize data_len,
161 gsize *out_data_len)
162{
163 return NULL;
164}
165
166/* ---------------------------------------------------------------------------------------------------- */
167
168static GDBusAuthMechanismState
169mechanism_server_get_state (GDBusAuthMechanism *mechanism)
170{
171 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
172
173 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
174 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
175
176 return m->priv->state;
177}
178
179static gboolean
180data_matches_credentials (const gchar *data,
181 gsize data_len,
182 GCredentials *credentials)
183{
184 gboolean match;
185
186 match = FALSE;
187
188 if (credentials == NULL)
189 goto out;
190
191 if (data == NULL || data_len == 0)
192 goto out;
193
194#if defined(G_OS_UNIX)
195 {
196 gint64 alleged_uid;
197 gchar *endp;
198
199 /* on UNIX, this is the uid as a string in base 10 */
200 alleged_uid = g_ascii_strtoll (nptr: data, endptr: &endp, base: 10);
201 if (*endp == '\0')
202 {
203 if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
204 {
205 match = TRUE;
206 }
207 }
208 }
209#else
210 /* TODO: Dont know how to compare credentials on this OS. Please implement. */
211#endif
212
213 out:
214 return match;
215}
216
217static void
218mechanism_server_initiate (GDBusAuthMechanism *mechanism,
219 const gchar *initial_response,
220 gsize initial_response_len)
221{
222 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
223
224 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
225 g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
226
227 m->priv->is_server = TRUE;
228
229 if (initial_response != NULL)
230 {
231 if (data_matches_credentials (data: initial_response,
232 data_len: initial_response_len,
233 credentials: _g_dbus_auth_mechanism_get_credentials (mechanism)))
234 {
235 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
236 }
237 else
238 {
239 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
240 }
241 }
242 else
243 {
244 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
245 }
246}
247
248static void
249mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
250 const gchar *data,
251 gsize data_len)
252{
253 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
254
255 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
256 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
257 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
258
259 if (data_matches_credentials (data,
260 data_len,
261 credentials: _g_dbus_auth_mechanism_get_credentials (mechanism)))
262 {
263 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
264 }
265 else
266 {
267 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
268 }
269}
270
271static gchar *
272mechanism_server_data_send (GDBusAuthMechanism *mechanism,
273 gsize *out_data_len)
274{
275 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
276
277 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
278 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
279 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
280
281 /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
282 g_assert_not_reached ();
283
284 return NULL;
285}
286
287static gchar *
288mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
289{
290 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
291
292 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
293 g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
294 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
295
296 /* can never end up here because we are never in the REJECTED state */
297 g_assert_not_reached ();
298
299 return NULL;
300}
301
302static void
303mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
304{
305 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
306
307 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
308 g_return_if_fail (m->priv->is_server && !m->priv->is_client);
309
310 m->priv->is_server = FALSE;
311}
312
313/* ---------------------------------------------------------------------------------------------------- */
314
315static GDBusAuthMechanismState
316mechanism_client_get_state (GDBusAuthMechanism *mechanism)
317{
318 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
319
320 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
321 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
322
323 return m->priv->state;
324}
325
326static gchar *
327mechanism_client_initiate (GDBusAuthMechanism *mechanism,
328 gsize *out_initial_response_len)
329{
330 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
331 gchar *initial_response = NULL;
332 GCredentials *credentials;
333
334 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
335 g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
336
337 m->priv->is_client = TRUE;
338 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
339
340 *out_initial_response_len = 0;
341
342 credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
343 g_assert (credentials != NULL);
344
345 /* return the uid */
346#if defined(G_OS_UNIX)
347 initial_response = g_strdup_printf (format: "%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
348 *out_initial_response_len = strlen (s: initial_response);
349#elif defined(G_OS_WIN32)
350#ifdef __GNUC__
351#pragma GCC diagnostic push
352#pragma GCC diagnostic warning "-Wcpp"
353#warning Dont know how to send credentials on this OS. The EXTERNAL D-Bus authentication mechanism will not work.
354#pragma GCC diagnostic pop
355#endif
356 m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
357#endif
358 return initial_response;
359}
360
361static void
362mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
363 const gchar *data,
364 gsize data_len)
365{
366 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
367
368 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
369 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
370 g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
371
372 /* can never end up here because we are never in the WAITING_FOR_DATA state */
373 g_assert_not_reached ();
374}
375
376static gchar *
377mechanism_client_data_send (GDBusAuthMechanism *mechanism,
378 gsize *out_data_len)
379{
380 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
381
382 g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
383 g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
384 g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
385
386 /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
387 g_assert_not_reached ();
388
389 return NULL;
390}
391
392static void
393mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
394{
395 GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
396
397 g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
398 g_return_if_fail (m->priv->is_client && !m->priv->is_server);
399
400 m->priv->is_client = FALSE;
401}
402
403/* ---------------------------------------------------------------------------------------------------- */
404

source code of gtk/subprojects/glib/gio/gdbusauthmechanismexternal.c