Do not call dbus_g_bus_get in headless mode on legacy systems
[moonshot-ui.git] / libmoonshot / libmoonshot-dbus.c
1 /* libmoonshot - Moonshot client library
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * Author: Sam Thursfield <samthursfield@codethink.co.uk>
33  */
34
35 #include <assert.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <dbus/dbus-glib.h>
40 #include <dbus/dbus.h>
41 #include <dbus/dbus-glib-lowlevel.h>
42 #include <glib/gspawn.h>
43
44
45 #include "libmoonshot.h"
46 #include "libmoonshot-common.h"
47
48 /*30 days in ms*/
49 #define INFINITE_TIMEOUT 10*24*60*60*1000
50
51 #define MOONSHOT_DBUS_NAME "org.janet.Moonshot"
52 #define MOONSHOT_DBUS_PATH "/org/janet/moonshot"
53
54 /* This library is overly complicated currently due to the requirement
55  * that it work on Debian Squeeze - this has GLib 2.24 which requires us
56  * to use dbus-glib instead of GDBus. If/when this requirement is
57  * dropped the DBus version of the library can be greatly simplified.
58  */
59
60 /* Note that ideally this library would not depend on GLib. This would be
61  * possible using libdbus directly and running our own message loop while
62  * waiting for calls.
63  */
64
65 void moonshot_free (void *data)
66 {
67     g_free (data);
68 }
69 static char *moonshot_launch_argv[] = {
70   MOONSHOT_LAUNCH_SCRIPT, NULL
71 };
72
73 static DBusGConnection *dbus_launch_moonshot()
74 {
75     DBusGConnection *connection = NULL;
76     GError *error = NULL;
77     DBusError dbus_error;
78     GPid child_pid;
79     gint fd_stdin = -1, fd_stdout = -1;
80     ssize_t addresslen;
81     dbus_error_init(&dbus_error);
82     char dbus_address[1024];
83   
84     if (g_spawn_async_with_pipes( NULL /*cwd*/,
85                                   moonshot_launch_argv, NULL /*environ*/,
86                                   0 /*flags*/, NULL /*setup*/, NULL,
87                                   &child_pid, &fd_stdin, &fd_stdout,
88                                   NULL /*stderr*/, NULL /*error*/) == 0 ) {
89       return NULL;
90     }
91
92     addresslen = read( fd_stdout, dbus_address, sizeof dbus_address);
93     close(fd_stdout);
94     /* we require at least 2 octets of address because we trim the newline*/
95     if (addresslen <= 1) {
96     fail: dbus_error_free(&dbus_error);
97       if (connection != NULL)
98         dbus_g_connection_unref(connection);
99       close(fd_stdin);
100       g_spawn_close_pid(child_pid);
101       return NULL;
102     }
103     dbus_address[addresslen-1] = '\0';
104     connection = dbus_g_connection_open(dbus_address, &error);
105     if (error) {
106       g_error_free(error);
107       goto fail;
108     }
109     if (!dbus_bus_register(dbus_g_connection_get_connection(connection),
110                            &dbus_error))
111       goto fail;
112         return connection;
113 }
114
115 static int is_setid()
116 {
117 #ifdef HAVE_GETEUID
118   if ((getuid() != geteuid()) || 
119       (getgid() != getegid())) {
120     return 1;
121   }
122 #endif
123   return 0;
124 }
125
126 static DBusGProxy *dbus_connect (MoonshotError **error)
127 {
128     DBusConnection  *dbconnection;
129     DBusError        dbus_error;
130     DBusGConnection *connection;
131     DBusGProxy      *g_proxy;
132     GError          *g_error = NULL;
133     dbus_bool_t      name_has_owner;
134
135     g_return_val_if_fail (*error == NULL, NULL);
136
137     dbus_error_init (&dbus_error);
138
139     /* Check for moonshot server and start the service if possible. We use
140      * libdbus here because dbus-glib doesn't handle autostarting the service.
141      * If/when we move to GDBus this code can become a one-liner.
142      */
143
144     if (is_setid()) {
145         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
146                                      "Cannot use IPC while setid");
147         return NULL;
148     }
149 #ifdef IPC_DBUS_GLIB
150     if (getenv("DISPLAY")==NULL) {
151         connection = dbus_launch_moonshot();
152         if (connection == NULL) {
153             *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
154                                          "Headless dbus launch failed");
155             return NULL;
156         }
157     } else
158 #endif
159     {
160         connection = dbus_g_bus_get (DBUS_BUS_SESSION, &g_error);
161
162         if (g_error_matches(g_error, DBUS_GERROR, DBUS_GERROR_NOT_SUPPORTED)) {
163             /*Generally this means autolaunch failed because probably DISPLAY is unset*/
164             connection = dbus_launch_moonshot();
165             if (connection != NULL) {
166                 g_error_free(g_error);
167                 g_error = NULL;
168             }
169         }
170         if (g_error != NULL) {
171             *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
172                                          "DBus error: %s",
173                                          g_error->message);
174             g_error_free (g_error);
175             return NULL;
176         }
177     }
178
179
180     dbconnection = dbus_g_connection_get_connection(connection);
181     name_has_owner  = dbus_bus_name_has_owner (dbconnection,
182                                                MOONSHOT_DBUS_NAME,
183                                                &dbus_error);
184
185     if (dbus_error_is_set (&dbus_error)) {
186         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
187                                      "DBus error: %s",
188                                      dbus_error.message);
189         dbus_error_free (&dbus_error);
190         return NULL;
191     }
192
193     if (! name_has_owner) {
194         dbus_bus_start_service_by_name (dbconnection,
195                                         MOONSHOT_DBUS_NAME,
196                                         0,
197                                         NULL,
198                                         &dbus_error);
199
200         if (dbus_error_is_set (&dbus_error)) {
201             if (strcmp (dbus_error.name + 27, "ServiceUnknown") == 0) {
202                 /* Missing .service file; the moonshot-ui install is broken */
203                 *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
204                                              "The Moonshot service was not found. "
205                                              "Please make sure that moonshot-ui is "
206                                              "correctly installed.");
207             } else {
208                 *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
209                                              "DBus error: %s",
210                                              dbus_error.message);
211             }
212             dbus_error_free (&dbus_error);
213             return NULL;
214         }
215     }
216
217     /* Now the service should be running */
218     g_error = NULL;
219
220     g_proxy = dbus_g_proxy_new_for_name_owner (connection,
221                                                MOONSHOT_DBUS_NAME,
222                                                MOONSHOT_DBUS_PATH,
223                                                MOONSHOT_DBUS_NAME,
224                                                &g_error);
225
226     if (g_error != NULL) {
227         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
228                                      "DBus error: %s",
229                                      g_error->message);
230         g_error_free (g_error);
231         return NULL;
232     }
233
234     return g_proxy; 
235 }
236
237 static DBusGProxy *get_dbus_proxy (MoonshotError **error)
238 {
239     static DBusGProxy    *dbus_proxy = NULL;
240     static GStaticMutex   init_lock = G_STATIC_MUTEX_INIT;
241
242     g_static_mutex_lock (&init_lock);
243
244     if (dbus_proxy == NULL) {
245         /* Make sure GObject is initialised, in case we are the only user
246          * of GObject in the process
247          */
248         g_type_init ();
249         dbus_proxy = dbus_connect (error);
250     }
251
252     if (dbus_proxy != NULL)
253         g_object_ref (dbus_proxy);
254
255     g_static_mutex_unlock (&init_lock);
256
257     return dbus_proxy;
258 }
259
260 int moonshot_get_identity (const char     *nai,
261                            const char     *password,
262                            const char     *service,
263                            char          **nai_out,
264                            char          **password_out,
265                            char          **server_certificate_hash_out,
266                            char          **ca_certificate_out,
267                            char          **subject_name_constraint_out,
268                            char          **subject_alt_name_constraint_out,
269                            MoonshotError **error)
270 {
271     GError     *g_error = NULL;
272     DBusGProxy *dbus_proxy;
273     int         success;
274
275     dbus_proxy = get_dbus_proxy (error);
276
277     if (*error != NULL)
278         return FALSE;
279
280     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
281
282     dbus_g_proxy_call_with_timeout (dbus_proxy,
283                        "GetIdentity",
284                                     INFINITE_TIMEOUT,
285                                     &g_error,
286                        G_TYPE_STRING, nai,
287                        G_TYPE_STRING, password,
288                        G_TYPE_STRING, service,
289                        G_TYPE_INVALID,
290                        G_TYPE_STRING, nai_out,
291                        G_TYPE_STRING, password_out,
292                        G_TYPE_STRING, server_certificate_hash_out,
293                        G_TYPE_STRING, ca_certificate_out,
294                        G_TYPE_STRING, subject_name_constraint_out,
295                        G_TYPE_STRING, subject_alt_name_constraint_out,
296                        G_TYPE_BOOLEAN, &success,
297                        G_TYPE_INVALID);
298
299     g_object_unref (dbus_proxy);
300
301     if (g_error != NULL) {
302         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
303                                      g_error->message);
304         return FALSE;
305     }
306
307     if (success == FALSE) {
308         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
309                                      "No identity was returned by the Moonshot "
310                                      "user interface.");
311         return FALSE;
312     }
313
314     return TRUE;
315 }
316
317 int moonshot_get_default_identity (char          **nai_out,
318                                    char          **password_out,
319                                    char          **server_certificate_hash_out,
320                                    char          **ca_certificate_out,
321                                    char          **subject_name_constraint_out,
322                                    char          **subject_alt_name_constraint_out,
323                                    MoonshotError **error)
324 {
325     GError     *g_error = NULL;
326     DBusGProxy *dbus_proxy;
327     int         success = FALSE;
328
329     dbus_proxy = get_dbus_proxy (error);
330
331     if (*error != NULL)
332         return FALSE;
333
334     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
335
336     dbus_g_proxy_call_with_timeout (dbus_proxy,
337                        "GetDefaultIdentity",
338                                     INFINITE_TIMEOUT,
339                        &g_error,
340                        G_TYPE_INVALID,
341                        G_TYPE_STRING, nai_out,
342                        G_TYPE_STRING, password_out,
343                        G_TYPE_STRING, server_certificate_hash_out,
344                        G_TYPE_STRING, ca_certificate_out,
345                        G_TYPE_STRING, subject_name_constraint_out,
346                        G_TYPE_STRING, subject_alt_name_constraint_out,
347                        G_TYPE_BOOLEAN, &success,
348                        G_TYPE_INVALID);
349
350     g_object_unref (dbus_proxy);
351
352     if (g_error != NULL) {
353         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
354                                      g_error->message);
355         return FALSE;
356     }
357
358     if (success == FALSE) {
359         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
360                                      "No identity was returned by the Moonshot "
361                                      "user interface.");
362         return FALSE;
363     }
364
365     return TRUE;
366 }
367
368 int moonshot_install_id_card (const char     *display_name,
369                               const char     *user_name,
370                               const char     *password,
371                               const char     *realm,
372                               char           *rules_patterns[],
373                               int             rules_patterns_length,
374                               char           *rules_always_confirm[],
375                               int             rules_always_confirm_length,
376                               char           *services[],
377                               int             services_length,
378                               const char     *ca_cert,
379                               const char     *subject,
380                               const char     *subject_alt,
381                               const char     *server_cert,
382                               int            force_flat_file_store,
383                               MoonshotError **error)
384 {
385     GError      *g_error = NULL;
386     DBusGProxy  *dbus_proxy;
387     int          success = FALSE;
388     int          i;
389     const char **rules_patterns_strv,
390                **rules_always_confirm_strv,
391                **services_strv;
392
393     dbus_proxy = get_dbus_proxy (error);
394
395     if (*error != NULL)
396         return FALSE;
397
398     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
399     g_return_val_if_fail (rules_patterns_length == rules_always_confirm_length, FALSE);
400
401     /* Marshall array and struct parameters for DBus */
402     rules_patterns_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
403     rules_always_confirm_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
404     services_strv = g_malloc ((services_length + 1) * sizeof (const char *));
405
406     for (i = 0; i < rules_patterns_length; i ++) {
407         rules_patterns_strv[i] = rules_patterns[i];
408         rules_always_confirm_strv[i] = rules_always_confirm[i];
409     }
410
411     for (i = 0; i < services_length; i ++)
412         services_strv[i] = services[i];
413
414     rules_patterns_strv[rules_patterns_length] = NULL;
415     rules_always_confirm_strv[rules_patterns_length] = NULL;
416     services_strv[services_length] = NULL;
417
418     dbus_g_proxy_call (dbus_proxy,
419                        "InstallIdCard",
420                        &g_error,
421                        G_TYPE_STRING, display_name,
422                        G_TYPE_STRING, user_name,
423                        G_TYPE_STRING, password,
424                        G_TYPE_STRING, realm,
425                        G_TYPE_STRV, rules_patterns_strv,
426                        G_TYPE_STRV, rules_always_confirm_strv,
427                        G_TYPE_STRV, services_strv,
428                        G_TYPE_STRING, ca_cert,
429                        G_TYPE_STRING, subject,
430                        G_TYPE_STRING, subject_alt,
431                        G_TYPE_STRING, server_cert,
432                        G_TYPE_INT, force_flat_file_store,
433                        G_TYPE_INVALID,
434                        G_TYPE_BOOLEAN, &success,
435                        G_TYPE_INVALID);
436
437     g_object_unref (dbus_proxy);
438
439     if (g_error != NULL) {
440         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
441                                      g_error->message);
442         return FALSE;
443     }
444
445     return success;
446 }