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
32 struct _GDBusAuthMechanismExternalPrivate
33 {
34 gboolean is_client;
35 gboolean is_server;
36 GDBusAuthMechanismState state;
37 };
38
39 static gint mechanism_get_priority (void);
40 static const gchar *mechanism_get_name (void);
41
42 static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
43 static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
44 const gchar *data,
45 gsize data_len,
46 gsize *out_data_len);
47 static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
48 const gchar *data,
49 gsize data_len,
50 gsize *out_data_len);
51 static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
52 static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
53 const gchar *initial_response,
54 gsize initial_response_len);
55 static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
56 const gchar *data,
57 gsize data_len);
58 static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
59 gsize *out_data_len);
60 static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
61 static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
62 static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
63 static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
64 gsize *out_initial_response_len);
65 static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
66 const gchar *data,
67 gsize data_len);
68 static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
69 gsize *out_data_len);
70 static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
71
72 /* ---------------------------------------------------------------------------------------------------- */
73
G_DEFINE_TYPE_WITH_PRIVATE(GDBusAuthMechanismExternal,_g_dbus_auth_mechanism_external,G_TYPE_DBUS_AUTH_MECHANISM)74 G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
75
76 /* ---------------------------------------------------------------------------------------------------- */
77
78 static 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
87 static void
_g_dbus_auth_mechanism_external_class_init(GDBusAuthMechanismExternalClass * klass)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
115 static void
_g_dbus_auth_mechanism_external_init(GDBusAuthMechanismExternal * mechanism)116 _g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
117 {
118 mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
119 }
120
121 /* ---------------------------------------------------------------------------------------------------- */
122
123 static gboolean
mechanism_is_supported(GDBusAuthMechanism * mechanism)124 mechanism_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
134 static gint
mechanism_get_priority(void)135 mechanism_get_priority (void)
136 {
137 /* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
138 return 100;
139 }
140
141 static const gchar *
mechanism_get_name(void)142 mechanism_get_name (void)
143 {
144 return "EXTERNAL";
145 }
146
147 static gchar *
mechanism_encode_data(GDBusAuthMechanism * mechanism,const gchar * data,gsize data_len,gsize * out_data_len)148 mechanism_encode_data (GDBusAuthMechanism *mechanism,
149 const gchar *data,
150 gsize data_len,
151 gsize *out_data_len)
152 {
153 return NULL;
154 }
155
156
157 static gchar *
mechanism_decode_data(GDBusAuthMechanism * mechanism,const gchar * data,gsize data_len,gsize * out_data_len)158 mechanism_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
168 static GDBusAuthMechanismState
mechanism_server_get_state(GDBusAuthMechanism * mechanism)169 mechanism_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
179 static gboolean
data_matches_credentials(const gchar * data,gsize data_len,GCredentials * credentials)180 data_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 (data, &endp, 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
217 static void
mechanism_server_initiate(GDBusAuthMechanism * mechanism,const gchar * initial_response,gsize initial_response_len)218 mechanism_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 (initial_response,
232 initial_response_len,
233 _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
248 static void
mechanism_server_data_receive(GDBusAuthMechanism * mechanism,const gchar * data,gsize data_len)249 mechanism_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 _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
271 static gchar *
mechanism_server_data_send(GDBusAuthMechanism * mechanism,gsize * out_data_len)272 mechanism_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
287 static gchar *
mechanism_server_get_reject_reason(GDBusAuthMechanism * mechanism)288 mechanism_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
302 static void
mechanism_server_shutdown(GDBusAuthMechanism * mechanism)303 mechanism_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
315 static GDBusAuthMechanismState
mechanism_client_get_state(GDBusAuthMechanism * mechanism)316 mechanism_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
326 static gchar *
mechanism_client_initiate(GDBusAuthMechanism * mechanism,gsize * out_initial_response_len)327 mechanism_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 ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
348 *out_initial_response_len = strlen (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
361 static void
mechanism_client_data_receive(GDBusAuthMechanism * mechanism,const gchar * data,gsize data_len)362 mechanism_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
376 static gchar *
mechanism_client_data_send(GDBusAuthMechanism * mechanism,gsize * out_data_len)377 mechanism_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
392 static void
mechanism_client_shutdown(GDBusAuthMechanism * mechanism)393 mechanism_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