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