libmoonshot: register with dbus
[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 <string.h>
37 #include <unistd.h>
38 #include <dbus/dbus-glib.h>
39 #include <dbus/dbus.h>
40 #include <dbus/dbus-glib-lowlevel.h>
41 #include <glib/gspawn.h>
42
43
44 #include "libmoonshot.h"
45 #include "libmoonshot-common.h"
46
47 /*30 days in ms*/
48 #define INFINITE_TIMEOUT 10*24*60*60*1000
49
50 #define MOONSHOT_DBUS_NAME "org.janet.Moonshot"
51 #define MOONSHOT_DBUS_PATH "/org/janet/moonshot"
52
53 /* This library is overly complicated currently due to the requirement
54  * that it work on Debian Squeeze - this has GLib 2.24 which requires us
55  * to use dbus-glib instead of GDBus. If/when this requirement is
56  * dropped the DBus version of the library can be greatly simplified.
57  */
58
59 /* Note that ideally this library would not depend on GLib. This would be
60  * possible using libdbus directly and running our own message loop while
61  * waiting for calls.
62  */
63
64 void moonshot_free (void *data)
65 {
66     g_free (data);
67 }
68 static char *moonshot_launch_argv[] = {
69   MOONSHOT_LAUNCH_SCRIPT, NULL
70 };
71
72 static DBusConnection *dbus_launch_moonshot()
73 {
74     DBusConnection *connection = NULL;
75     DBusError dbus_error;
76     GPid child_pid;
77     gint fd_stdin = -1, fd_stdout = -1;
78     ssize_t addresslen;
79     char dbus_address[1024];
80   
81     if (g_spawn_async_with_pipes( NULL /*cwd*/,
82                                   moonshot_launch_argv, NULL /*environ*/,
83                                   0 /*flags*/, NULL /*setup*/, NULL,
84                                   &child_pid, &fd_stdin, &fd_stdout,
85                                   NULL /*stderr*/, NULL /*error*/) == 0 ) {
86       return NULL;
87     }
88
89     dbus_error_init(&dbus_error);
90     addresslen = read( fd_stdout, dbus_address, sizeof dbus_address);
91     close(fd_stdout);
92     /* we require at least 2 octets of address because we trim the newline*/
93     if (addresslen <= 1) {
94     fail: dbus_error_free(&dbus_error);
95       if (connection != NULL)
96         dbus_connection_unref(connection);
97       close(fd_stdin);
98       g_spawn_close_pid(child_pid);
99       return NULL;
100     }
101     dbus_address[addresslen-1] = '\0';
102     connection = dbus_connection_open(dbus_address, &dbus_error);
103     if (dbus_error_is_set(&dbus_error)) {
104       goto fail;
105     }
106     if (!dbus_bus_register(connection, &dbus_error))
107       goto fail;
108         return connection;
109 }
110
111
112 static DBusGProxy *dbus_connect (MoonshotError **error)
113 {
114     DBusConnection  *connection;
115     DBusError        dbus_error;
116     DBusGConnection *g_connection;
117     DBusGProxy      *g_proxy;
118     GError          *g_error = NULL;
119     dbus_bool_t      name_has_owner;
120
121     g_return_val_if_fail (*error == NULL, NULL);
122
123     dbus_error_init (&dbus_error);
124
125     /* Check for moonshot server and start the service if possible. We use
126      * libdbus here because dbus-glib doesn't handle autostarting the service.
127      * If/when we move to GDBus this code can become a one-liner.
128      */
129
130     connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
131
132     if (dbus_error_has_name(&dbus_error, DBUS_ERROR_NOT_SUPPORTED)) {
133       /*Generally this means autolaunch failed because probably DISPLAY is unset*/
134       connection = dbus_launch_moonshot();
135       if (connection != NULL) {
136         dbus_error_free(&dbus_error);
137         dbus_error_init(&dbus_error);
138       }
139     }
140
141       if (dbus_error_is_set (&dbus_error)) {
142         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
143                                      "DBus error: %s",
144                                      dbus_error.message);
145         dbus_error_free (&dbus_error);
146         return NULL;
147     }
148
149     name_has_owner  = dbus_bus_name_has_owner (connection,
150                                                MOONSHOT_DBUS_NAME,
151                                                &dbus_error);
152
153     if (dbus_error_is_set (&dbus_error)) {
154         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
155                                      "DBus error: %s",
156                                      dbus_error.message);
157         dbus_error_free (&dbus_error);
158         return NULL;
159     }
160
161     if (! name_has_owner) {
162         dbus_bus_start_service_by_name (connection,
163                                         MOONSHOT_DBUS_NAME,
164                                         0,
165                                         NULL,
166                                         &dbus_error);
167
168         if (dbus_error_is_set (&dbus_error)) {
169             if (strcmp (dbus_error.name + 27, "ServiceUnknown") == 0) {
170                 /* Missing .service file; the moonshot-ui install is broken */
171                 *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
172                                              "The Moonshot service was not found. "
173                                              "Please make sure that moonshot-ui is "
174                                              "correctly installed.");
175             } else {
176                 *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
177                                              "DBus error: %s",
178                                              dbus_error.message);
179             }
180             dbus_error_free (&dbus_error);
181             return NULL;
182         }
183     }
184
185     /* Now the service should be running */
186     g_error = NULL;
187
188     g_connection = dbus_connection_get_g_connection(connection);
189     assert (g_connection != NULL);
190     g_proxy = dbus_g_proxy_new_for_name_owner (g_connection,
191                                                MOONSHOT_DBUS_NAME,
192                                                MOONSHOT_DBUS_PATH,
193                                                MOONSHOT_DBUS_NAME,
194                                                &g_error);
195
196     if (g_error != NULL) {
197         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
198                                      "DBus error: %s",
199                                      g_error->message);
200         g_error_free (g_error);
201         return NULL;
202     }
203
204     return g_proxy; 
205 }
206
207 static DBusGProxy *get_dbus_proxy (MoonshotError **error)
208 {
209     static DBusGProxy    *dbus_proxy = NULL;
210     static GStaticMutex   init_lock = G_STATIC_MUTEX_INIT;
211
212     g_static_mutex_lock (&init_lock);
213
214     if (dbus_proxy == NULL) {
215         /* Make sure GObject is initialised, in case we are the only user
216          * of GObject in the process
217          */
218         g_type_init ();
219         dbus_proxy = dbus_connect (error);
220     }
221
222     if (dbus_proxy != NULL)
223         g_object_ref (dbus_proxy);
224
225     g_static_mutex_unlock (&init_lock);
226
227     return dbus_proxy;
228 }
229
230 int moonshot_get_identity (const char     *nai,
231                            const char     *password,
232                            const char     *service,
233                            char          **nai_out,
234                            char          **password_out,
235                            char          **server_certificate_hash_out,
236                            char          **ca_certificate_out,
237                            char          **subject_name_constraint_out,
238                            char          **subject_alt_name_constraint_out,
239                            MoonshotError **error)
240 {
241     GError     *g_error = NULL;
242     DBusGProxy *dbus_proxy;
243     int         success;
244
245     dbus_proxy = get_dbus_proxy (error);
246
247     if (*error != NULL)
248         return FALSE;
249
250     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
251
252     dbus_g_proxy_call_with_timeout (dbus_proxy,
253                        "GetIdentity",
254                                     INFINITE_TIMEOUT,
255                                     &g_error,
256                        G_TYPE_STRING, nai,
257                        G_TYPE_STRING, password,
258                        G_TYPE_STRING, service,
259                        G_TYPE_INVALID,
260                        G_TYPE_STRING, nai_out,
261                        G_TYPE_STRING, password_out,
262                        G_TYPE_STRING, server_certificate_hash_out,
263                        G_TYPE_STRING, ca_certificate_out,
264                        G_TYPE_STRING, subject_name_constraint_out,
265                        G_TYPE_STRING, subject_alt_name_constraint_out,
266                        G_TYPE_BOOLEAN, &success,
267                        G_TYPE_INVALID);
268
269     g_object_unref (dbus_proxy);
270
271     if (g_error != NULL) {
272         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
273                                      g_error->message);
274         return FALSE;
275     }
276
277     if (success == FALSE) {
278         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
279                                      "No identity was returned by the Moonshot "
280                                      "user interface.");
281         return FALSE;
282     }
283
284     return TRUE;
285 }
286
287 int moonshot_get_default_identity (char          **nai_out,
288                                    char          **password_out,
289                                    char          **server_certificate_hash_out,
290                                    char          **ca_certificate_out,
291                                    char          **subject_name_constraint_out,
292                                    char          **subject_alt_name_constraint_out,
293                                    MoonshotError **error)
294 {
295     GError     *g_error = NULL;
296     DBusGProxy *dbus_proxy;
297     int         success = FALSE;
298
299     dbus_proxy = get_dbus_proxy (error);
300
301     if (*error != NULL)
302         return FALSE;
303
304     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
305
306     dbus_g_proxy_call_with_timeout (dbus_proxy,
307                        "GetDefaultIdentity",
308                                     INFINITE_TIMEOUT,
309                        &g_error,
310                        G_TYPE_INVALID,
311                        G_TYPE_STRING, nai_out,
312                        G_TYPE_STRING, password_out,
313                        G_TYPE_STRING, server_certificate_hash_out,
314                        G_TYPE_STRING, ca_certificate_out,
315                        G_TYPE_STRING, subject_name_constraint_out,
316                        G_TYPE_STRING, subject_alt_name_constraint_out,
317                        G_TYPE_BOOLEAN, &success,
318                        G_TYPE_INVALID);
319
320     g_object_unref (dbus_proxy);
321
322     if (g_error != NULL) {
323         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
324                                      g_error->message);
325         return FALSE;
326     }
327
328     if (success == FALSE) {
329         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
330                                      "No identity was returned by the Moonshot "
331                                      "user interface.");
332         return FALSE;
333     }
334
335     return TRUE;
336 }
337
338 int moonshot_install_id_card (const char     *display_name,
339                               const char     *user_name,
340                               const char     *password,
341                               const char     *realm,
342                               char           *rules_patterns[],
343                               int             rules_patterns_length,
344                               char           *rules_always_confirm[],
345                               int             rules_always_confirm_length,
346                               char           *services[],
347                               int             services_length,
348                               const char     *ca_cert,
349                               const char     *subject,
350                               const char     *subject_alt,
351                               const char     *server_cert,
352                               MoonshotError **error)
353 {
354     GError      *g_error = NULL;
355     DBusGProxy  *dbus_proxy;
356     int          success = FALSE;
357     int          i;
358     const char **rules_patterns_strv,
359                **rules_always_confirm_strv,
360                **services_strv;
361
362     dbus_proxy = get_dbus_proxy (error);
363
364     if (*error != NULL)
365         return FALSE;
366
367     g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
368     g_return_val_if_fail (rules_patterns_length == rules_always_confirm_length, FALSE);
369
370     /* Marshall array and struct parameters for DBus */
371     rules_patterns_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
372     rules_always_confirm_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
373     services_strv = g_malloc ((services_length + 1) * sizeof (const char *));
374
375     for (i = 0; i < rules_patterns_length; i ++) {
376         rules_patterns_strv[i] = rules_patterns[i];
377         rules_always_confirm_strv[i] = rules_always_confirm[i];
378     }
379
380     for (i = 0; i < services_length; i ++)
381         services_strv[i] = services[i];
382
383     rules_patterns_strv[rules_patterns_length] = NULL;
384     rules_always_confirm_strv[rules_patterns_length] = NULL;
385     services_strv[services_length] = NULL;
386
387     dbus_g_proxy_call (dbus_proxy,
388                        "InstallIdCard",
389                        &g_error,
390                        G_TYPE_STRING, display_name,
391                        G_TYPE_STRING, user_name,
392                        G_TYPE_STRING, password,
393                        G_TYPE_STRING, realm,
394                        G_TYPE_STRV, rules_patterns_strv,
395                        G_TYPE_STRV, rules_always_confirm_strv,
396                        G_TYPE_STRV, services_strv,
397                        G_TYPE_STRING, ca_cert,
398                        G_TYPE_STRING, subject,
399                        G_TYPE_STRING, subject_alt,
400                        G_TYPE_STRING, server_cert,
401                        G_TYPE_INVALID,
402                        G_TYPE_BOOLEAN, &success,
403                        G_TYPE_INVALID);
404
405     g_object_unref (dbus_proxy);
406
407     if (g_error != NULL) {
408         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
409                                      g_error->message);
410         return FALSE;
411     }
412
413     return success;
414 }