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 | |
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 |
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 |
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 | |
123 | static gboolean |
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 |
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 * |
142 | mechanism_get_name (void) |
143 | { |
144 | return "EXTERNAL" ; |
145 | } |
146 | |
147 | static gchar * |
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 * |
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 |
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 |
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 (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 | |
217 | static void |
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 (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 | |
248 | static void |
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 | 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 | |
271 | static gchar * |
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 * |
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 |
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 |
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 * |
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 (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 | |
361 | static void |
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 * |
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 |
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 | |