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