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