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