Bump the version number in configure.ac to 1.0.5
[moonshot-ui.git] / libmoonshot / libmoonshot-dbus.c
index fcd19b4..52d1b0e 100644 (file)
  * Author: Sam Thursfield <samthursfield@codethink.co.uk>
  */
 
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib/gspawn.h>
+
 
 #include "libmoonshot.h"
+#include "libmoonshot-common.h"
+
+/*30 days in ms*/
+#define INFINITE_TIMEOUT 10*24*60*60*1000
 
 #define MOONSHOT_DBUS_NAME "org.janet.Moonshot"
 #define MOONSHOT_DBUS_PATH "/org/janet/moonshot"
  * dropped the DBus version of the library can be greatly simplified.
  */
 
-typedef struct {
-    char *nai;
-    char *password;
-    char *server_certificate_hash;
-    char *ca_certificate;
-    char *subject_name_constraint;
-    char *subject_alt_name_constraint;
-} MoonshotIdentityData;
+/* Note that ideally this library would not depend on GLib. This would be
+ * possible using libdbus directly and running our own message loop while
+ * waiting for calls.
+ */
 
-static MoonshotIdentityData *moonshot_identity_data_new ()
+void moonshot_free (void *data)
 {
-    return g_slice_new (MoonshotIdentityData);
+    g_free (data);
 }
+static char *moonshot_launch_argv[] = {
+  MOONSHOT_LAUNCH_SCRIPT, NULL
+};
 
-static void moonshot_identity_data_free (void *data)
+static DBusGConnection *dbus_launch_moonshot()
 {
-    g_slice_free (MoonshotIdentityData, data);
-}
+    DBusGConnection *connection = NULL;
+    GError *error = NULL;
+    DBusError dbus_error;
+    GPid child_pid;
+    gint fd_stdin = -1, fd_stdout = -1;
+    ssize_t addresslen;
+    dbus_error_init(&dbus_error);
+    char dbus_address[1024];
+  
+    if (g_spawn_async_with_pipes( NULL /*cwd*/,
+                                 moonshot_launch_argv, NULL /*environ*/,
+                                 0 /*flags*/, NULL /*setup*/, NULL,
+                                 &child_pid, &fd_stdin, &fd_stdout,
+                                 NULL /*stderr*/, NULL /*error*/) == 0 ) {
+      return NULL;
+    }
 
-static DBusGProxy *moonshot_dbus_proxy = NULL;
+    addresslen = read( fd_stdout, dbus_address, sizeof dbus_address);
+    close(fd_stdout);
+    /* we require at least 2 octets of address because we trim the newline*/
+    if (addresslen <= 1) {
+    fail: dbus_error_free(&dbus_error);
+      if (connection != NULL)
+       dbus_g_connection_unref(connection);
+      close(fd_stdin);
+      g_spawn_close_pid(child_pid);
+      return NULL;
+    }
+    dbus_address[addresslen-1] = '\0';
+    connection = dbus_g_connection_open(dbus_address, &error);
+    if (error) {
+      g_error_free(error);
+      goto fail;
+    }
+    if (!dbus_bus_register(dbus_g_connection_get_connection(connection),
+                          &dbus_error))
+      goto fail;
+       return connection;
+}
 
-GQuark moonshot_error_quark (void)
+static int is_setid()
 {
-    return g_quark_from_static_string ("moonshot-error-quark");
+#ifdef HAVE_GETEUID
+  if ((getuid() != geteuid()) || 
+      (getgid() != getegid())) {
+    return 1;
+  }
+#endif
+  return 0;
 }
 
-static DBusGProxy *dbus_connect (GError **g_error)
+static DBusGProxy *dbus_connect (MoonshotError **error)
 {
-    DBusConnection  *connection;
+    DBusConnection  *dbconnection;
     DBusError        dbus_error;
-    DBusGConnection *g_connection;
+    DBusGConnection *connection;
     DBusGProxy      *g_proxy;
+    GError          *g_error = NULL;
     dbus_bool_t      name_has_owner;
 
-    g_return_val_if_fail (*g_error == NULL, NULL);
+    g_return_val_if_fail (*error == NULL, NULL);
 
     dbus_error_init (&dbus_error);
 
@@ -89,33 +142,57 @@ static DBusGProxy *dbus_connect (GError **g_error)
      * If/when we move to GDBus this code can become a one-liner.
      */
 
-    connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
-
-    if (dbus_error_is_set (&dbus_error)) {
-        *g_error = g_error_new (MOONSHOT_ERROR,
-                                MOONSHOT_ERROR_DBUS_ERROR,
-                                "DBus error: %s",
-                                dbus_error.message);
-        dbus_error_free (&dbus_error);
+    if (is_setid()) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                    "Cannot use IPC while setid");
         return NULL;
     }
+#ifdef IPC_DBUS_GLIB
+    if (getenv("DISPLAY")==NULL) {
+        connection = dbus_launch_moonshot();
+        if (connection == NULL) {
+            *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                         "Headless dbus launch failed");
+            return NULL;
+        }
+    } else
+#endif
+    {
+        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &g_error);
+
+        if (g_error_matches(g_error, DBUS_GERROR, DBUS_GERROR_NOT_SUPPORTED)) {
+            /*Generally this means autolaunch failed because probably DISPLAY is unset*/
+            connection = dbus_launch_moonshot();
+            if (connection != NULL) {
+                g_error_free(g_error);
+                g_error = NULL;
+            }
+        }
+        if (g_error != NULL) {
+            *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                         "DBus error: %s",
+                                         g_error->message);
+            g_error_free (g_error);
+            return NULL;
+        }
+    }
 
-    name_has_owner  = dbus_bus_name_has_owner (connection,
+
+    dbconnection = dbus_g_connection_get_connection(connection);
+    name_has_owner  = dbus_bus_name_has_owner (dbconnection,
                                                MOONSHOT_DBUS_NAME,
                                                &dbus_error);
 
     if (dbus_error_is_set (&dbus_error)) {
-        *g_error = g_error_new (MOONSHOT_ERROR,
-                                MOONSHOT_ERROR_DBUS_ERROR,
-                                "DBus error: %s",
-                                dbus_error.message);
-
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     "DBus error: %s",
+                                     dbus_error.message);
         dbus_error_free (&dbus_error);
         return NULL;
     }
 
     if (! name_has_owner) {
-        dbus_bus_start_service_by_name (connection,
+        dbus_bus_start_service_by_name (dbconnection,
                                         MOONSHOT_DBUS_NAME,
                                         0,
                                         NULL,
@@ -124,16 +201,14 @@ static DBusGProxy *dbus_connect (GError **g_error)
         if (dbus_error_is_set (&dbus_error)) {
             if (strcmp (dbus_error.name + 27, "ServiceUnknown") == 0) {
                 /* Missing .service file; the moonshot-ui install is broken */
-                *g_error = g_error_new (MOONSHOT_ERROR,
-                                        MOONSHOT_ERROR_SERVICE_NOT_FOUND,
-                                        "The Moonshot service was not found. "
-                                        "Please make sure that moonshot-ui is "
-                                        "correctly installed.");
+                *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
+                                             "The Moonshot service was not found. "
+                                             "Please make sure that moonshot-ui is "
+                                             "correctly installed.");
             } else {
-                *g_error = g_error_new (MOONSHOT_ERROR,
-                                        MOONSHOT_ERROR_DBUS_ERROR,
-                                        "DBus error: %s",
-                                        dbus_error.message);
+                *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                             "DBus error: %s",
+                                             dbus_error.message);
             }
             dbus_error_free (&dbus_error);
             return NULL;
@@ -141,188 +216,285 @@ static DBusGProxy *dbus_connect (GError **g_error)
     }
 
     /* Now the service should be running */
+    g_error = NULL;
 
-    g_connection = dbus_g_bus_get (DBUS_BUS_SESSION, g_error);
-
-    if (*g_error != NULL)
-        return NULL;
-
-    g_proxy = dbus_g_proxy_new_for_name_owner (g_connection,
+    g_proxy = dbus_g_proxy_new_for_name_owner (connection,
                                                MOONSHOT_DBUS_NAME,
                                                MOONSHOT_DBUS_PATH,
                                                MOONSHOT_DBUS_NAME,
-                                               g_error);
+                                               &g_error);
+
+    if (g_error != NULL) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     "DBus error: %s",
+                                     g_error->message);
+        g_error_free (g_error);
+        return NULL;
+    }
 
     return g_proxy; 
 }
 
-static void dbus_call_complete_cb (DBusGProxy     *proxy,
-                                   DBusGProxyCall *call_id,
-                                   void           *user_data)
+static DBusGProxy *get_dbus_proxy (MoonshotError **error)
 {
-    GError *error = NULL;
-    GSimpleAsyncResult   *token;
-    MoonshotIdentityData *identity_data;
-    gboolean              success;
-
-    token = G_SIMPLE_ASYNC_RESULT (user_data);
-    identity_data = moonshot_identity_data_new ();
-
-    dbus_g_proxy_end_call (moonshot_dbus_proxy,
-                           call_id,
-                           &error,
-                           G_TYPE_STRING, &identity_data->nai,
-                           G_TYPE_STRING, &identity_data->password,
-                           G_TYPE_STRING, &identity_data->server_certificate_hash,
-                           G_TYPE_STRING, &identity_data->ca_certificate,
-                           G_TYPE_STRING, &identity_data->subject_name_constraint,
-                           G_TYPE_STRING, &identity_data->subject_alt_name_constraint,
-                           G_TYPE_BOOLEAN, &success,
-                           G_TYPE_INVALID);
-
-    if (error != NULL) {
-        g_simple_async_result_set_from_error (token, error);
+    static DBusGProxy    *dbus_proxy = NULL;
+    static GStaticMutex   init_lock = G_STATIC_MUTEX_INIT;
+
+    g_static_mutex_lock (&init_lock);
+
+    if (dbus_proxy == NULL) {
+        /* Make sure GObject is initialised, in case we are the only user
+         * of GObject in the process
+         */
+        g_type_init ();
+        dbus_proxy = dbus_connect (error);
     }
-    else
-    if (success == FALSE) {
-        error = g_error_new (MOONSHOT_ERROR,
-                             MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
-                             "No matching identity was available");
-        g_simple_async_result_set_from_error (token, error);
-        g_error_free (error);
+
+    if (dbus_proxy != NULL)
+        g_object_ref (dbus_proxy);
+
+    g_static_mutex_unlock (&init_lock);
+
+    return dbus_proxy;
+}
+
+int moonshot_get_identity (const char     *nai,
+                           const char     *password,
+                           const char     *service,
+                           char          **nai_out,
+                           char          **password_out,
+                           char          **server_certificate_hash_out,
+                           char          **ca_certificate_out,
+                           char          **subject_name_constraint_out,
+                           char          **subject_alt_name_constraint_out,
+                           MoonshotError **error)
+{
+    GError     *g_error = NULL;
+    DBusGProxy *dbus_proxy;
+    int         success;
+
+    dbus_proxy = get_dbus_proxy (error);
+
+    if (*error != NULL)
+        return FALSE;
+
+    g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
+
+    dbus_g_proxy_call_with_timeout (dbus_proxy,
+                       "GetIdentity",
+                                   INFINITE_TIMEOUT,
+                                   &g_error,
+                       G_TYPE_STRING, nai,
+                       G_TYPE_STRING, password,
+                       G_TYPE_STRING, service,
+                       G_TYPE_INVALID,
+                       G_TYPE_STRING, nai_out,
+                       G_TYPE_STRING, password_out,
+                       G_TYPE_STRING, server_certificate_hash_out,
+                       G_TYPE_STRING, ca_certificate_out,
+                       G_TYPE_STRING, subject_name_constraint_out,
+                       G_TYPE_STRING, subject_alt_name_constraint_out,
+                       G_TYPE_BOOLEAN, &success,
+                       G_TYPE_INVALID);
+
+    g_object_unref (dbus_proxy);
+
+    if (g_error != NULL) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     g_error->message);
+        return FALSE;
     }
-    else {        
-        g_simple_async_result_set_op_res_gpointer (token,
-                                                   identity_data,
-                                                   moonshot_identity_data_free);
+
+    if (success == FALSE) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
+                                     "No identity was returned by the Moonshot "
+                                     "user interface.");
+        return FALSE;
     }
 
-    g_simple_async_result_complete (token);
-    g_object_unref (token);
+    return TRUE;
 }
 
-/**
- * moonshot_get_identity:
- * @cancellable: A #GCancellable, or %NULL.
- * @callback: A #GAsyncReadyCallback, which will be called when the
- *            operation completes, fails or is cancelled.
- * @user_data: Data to pass to @callback
- * @nai: Name and issuer constraint for the required identity, or %NULL.
- * @password: Password for the identity, or %NULL.
- * @service: Service constraint for the required identity, or %NULL.
- *
- * This function initiates a call to the Moonshot server to request an ID card.
- * The server will be activated if it is not already running. The user interface
- * will be displayed if there is more than one matching identity and the user 
- * will be asked to select one.
- *
- * When an identity has been selected, or the operation fails or is cancelled,
- * @callback will be run.
- *
- * Note that the actual IPC call may not be made until control returns to the
- * GLib main loop.
- */
-void moonshot_get_identity (GCancellable        *cancellable,
-                            GAsyncReadyCallback  callback,
-                            gpointer             user_data,
-                            const char          *nai,
-                            const char          *password,
-                            const char          *service)
+int moonshot_get_default_identity (char          **nai_out,
+                                   char          **password_out,
+                                   char          **server_certificate_hash_out,
+                                   char          **ca_certificate_out,
+                                   char          **subject_name_constraint_out,
+                                   char          **subject_alt_name_constraint_out,
+                                   MoonshotError **error)
 {
-    DBusGProxyCall     *call_id;
-    GSimpleAsyncResult *result; 
-    GError *error = NULL;
+    GError     *g_error = NULL;
+    DBusGProxy *dbus_proxy;
+    int         success = FALSE;
 
-    if (moonshot_dbus_proxy == NULL)
-        moonshot_dbus_proxy = dbus_connect (&error);
-
-    if (moonshot_dbus_proxy == NULL) {
-        result = g_simple_async_result_new (NULL,
-                                            callback,
-                                            user_data,
-                                            moonshot_get_identity);
-        g_simple_async_result_set_from_error (result, error);
-        g_simple_async_result_complete_in_idle (result);
-        g_error_free (error);
-        return;
-    }
+    dbus_proxy = get_dbus_proxy (error);
+
+    if (*error != NULL)
+        return FALSE;
 
-    g_return_if_fail (DBUS_IS_G_PROXY (moonshot_dbus_proxy));
+    g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
+
+    dbus_g_proxy_call_with_timeout (dbus_proxy,
+                       "GetDefaultIdentity",
+                                   INFINITE_TIMEOUT,
+                       &g_error,
+                       G_TYPE_INVALID,
+                       G_TYPE_STRING, nai_out,
+                       G_TYPE_STRING, password_out,
+                       G_TYPE_STRING, server_certificate_hash_out,
+                       G_TYPE_STRING, ca_certificate_out,
+                       G_TYPE_STRING, subject_name_constraint_out,
+                       G_TYPE_STRING, subject_alt_name_constraint_out,
+                       G_TYPE_BOOLEAN, &success,
+                       G_TYPE_INVALID);
+
+    g_object_unref (dbus_proxy);
+
+    if (g_error != NULL) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     g_error->message);
+        return FALSE;
+    }
 
-    result = g_simple_async_result_new (NULL,
-                                        callback,
-                                        user_data,
-                                        moonshot_get_identity);
+    if (success == FALSE) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
+                                     "No identity was returned by the Moonshot "
+                                     "user interface.");
+        return FALSE;
+    }
 
-    call_id = dbus_g_proxy_begin_call (moonshot_dbus_proxy,
-                                       "GetIdentity",
-                                       dbus_call_complete_cb,
-                                       result, NULL,
-                                       G_TYPE_STRING, nai,
-                                       G_TYPE_STRING, password,
-                                       G_TYPE_STRING, service);
+    return TRUE;
 }
 
-/**
- * moonshot_get_identity_finish:
- * @result: The #GAsyncResult which was passed to your callback.
- * @nai: A pointer to a string which receives the name and issuer of the
- *       selected identity.
- * @password: A pointer to a string which receives the password.
- * @server_certificate_hash: Receives a hash of the identity server's
- *                           certificate, or %NULL.
- * @ca_certificate: The CA certificate, if @server_certificate_hash was %NULL.
- * @subject_name_constraint: Set if @ca_certificate is set, otherwise %NULL.
- * @subject_alt_name_constraint: Set if @ca_certificate is set, otherwise %NULL.
- * @error: Return location for an error, or %NULL.
- *
- * Gets the details of the identity card that was selected, if any.
- *
- * There are two types of trust anchor that may be returned. If
- * @server_certificate_hash is non-empty, the remaining parameters will be
- * empty. Otherwise, the @ca_certificate parameter and the subject name
- * constraints will be returned.
- *
- * Return value: %TRUE if an identity was successfully selected, %FALSE on
- *               failure.
- */
-gboolean moonshot_get_identity_finish (GAsyncResult  *result,
-                                       char         **nai,
-                                       char         **password,
-                                       char         **server_certificate_hash,
-                                       char         **ca_certificate,
-                                       char         **subject_name_constraint,
-                                       char         **subject_alt_name_constraint,
-                                       GError       **error)
+int moonshot_install_id_card (const char     *display_name,
+                              const char     *user_name,
+                              const char     *password,
+                              const char     *realm,
+                              char           *rules_patterns[],
+                              int             rules_patterns_length,
+                              char           *rules_always_confirm[],
+                              int             rules_always_confirm_length,
+                              char           *services[],
+                              int             services_length,
+                              const char     *ca_cert,
+                              const char     *subject,
+                              const char     *subject_alt,
+                              const char     *server_cert,
+                              int            force_flat_file_store,
+                              MoonshotError **error)
 {
-    MoonshotIdentityData *identity;
+    GError      *g_error = NULL;
+    DBusGProxy  *dbus_proxy;
+    int          success = FALSE;
+    int          i;
+    const char **rules_patterns_strv,
+               **rules_always_confirm_strv,
+               **services_strv;
 
-    g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                          NULL,
-                                                          moonshot_get_identity),
-                          FALSE);
+    dbus_proxy = get_dbus_proxy (error);
 
-    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    if (*error != NULL)
         return FALSE;
 
-    identity = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+    g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
+    g_return_val_if_fail (rules_patterns_length == rules_always_confirm_length, FALSE);
 
-    *nai = identity->nai;
-    *password = identity->password;
-    *server_certificate_hash = identity->server_certificate_hash;
-    *ca_certificate = identity->ca_certificate;
-    *subject_name_constraint = identity->subject_name_constraint;
-    *subject_alt_name_constraint = identity->subject_alt_name_constraint;
+    /* Marshall array and struct parameters for DBus */
+    rules_patterns_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
+    rules_always_confirm_strv = g_malloc ((rules_patterns_length + 1) * sizeof (const char *));
+    services_strv = g_malloc ((services_length + 1) * sizeof (const char *));
 
-    return TRUE;
+    for (i = 0; i < rules_patterns_length; i ++) {
+        rules_patterns_strv[i] = rules_patterns[i];
+        rules_always_confirm_strv[i] = rules_always_confirm[i];
+    }
+
+    for (i = 0; i < services_length; i ++)
+        services_strv[i] = services[i];
+
+    rules_patterns_strv[rules_patterns_length] = NULL;
+    rules_always_confirm_strv[rules_patterns_length] = NULL;
+    services_strv[services_length] = NULL;
+
+    dbus_g_proxy_call (dbus_proxy,
+                       "InstallIdCard",
+                       &g_error,
+                       G_TYPE_STRING, display_name,
+                       G_TYPE_STRING, user_name,
+                       G_TYPE_STRING, password,
+                       G_TYPE_STRING, realm,
+                       G_TYPE_STRV, rules_patterns_strv,
+                       G_TYPE_STRV, rules_always_confirm_strv,
+                       G_TYPE_STRV, services_strv,
+                       G_TYPE_STRING, ca_cert,
+                       G_TYPE_STRING, subject,
+                       G_TYPE_STRING, subject_alt,
+                       G_TYPE_STRING, server_cert,
+                       G_TYPE_INT, force_flat_file_store,
+                       G_TYPE_INVALID,
+                       G_TYPE_BOOLEAN, &success,
+                       G_TYPE_INVALID);
+
+    g_object_unref (dbus_proxy);
+    g_free(rules_patterns_strv);
+    g_free(rules_always_confirm_strv);
+    g_free(services_strv);
+
+    if (g_error != NULL) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     g_error->message);
+        return FALSE;
+    }
+
+    return success;
 }
 
+int moonshot_confirm_ca_certificate (const char           *identity_name,
+                                     const char           *realm,
+                                     const unsigned char  *ca_hash,
+                                     int                   hash_len,
+                                     MoonshotError       **error)
+{
+    GError     *g_error = NULL;
+    int         success = 99;
+    int         confirmed = 99;
+    char        hash_str[65];
+    DBusGProxy *dbus_proxy = get_dbus_proxy (error);
+    int         out = 0;
+    int         i;
+
+    if (*error != NULL) {
+        return FALSE;
+    }
+
+    g_return_val_if_fail (DBUS_IS_G_PROXY (dbus_proxy), FALSE);
 
-    /**
-     * Returns the default identity - most recently used.
-     *
-     * @param nai_out NAI stored in the ID card
-     * @param password_out Password stored in the ID card
-     *
-     * @return true on success, false if no identities are stored
-     */
+    /* Convert hash byte array to string */
+    out = 0;
+    for (i = 0; i < hash_len; i++) {
+        sprintf(&(hash_str[out]), "%02X", ca_hash[i]);
+        out += 2;
+    }
+
+    dbus_g_proxy_call_with_timeout (dbus_proxy,
+                                    "ConfirmCaCertificate",
+                                    INFINITE_TIMEOUT,
+                                    &g_error,
+                                    G_TYPE_STRING, identity_name,
+                                    G_TYPE_STRING, realm,
+                                    G_TYPE_STRING, hash_str,
+                                    G_TYPE_INVALID,
+                                    G_TYPE_INT,   &confirmed,
+                                    G_TYPE_BOOLEAN, &success,
+                                    G_TYPE_INVALID);
+
+    g_object_unref (dbus_proxy);
+
+    if (g_error != NULL) {
+        *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+                                     g_error->message);
+        return FALSE;
+    }
+
+    return (int) confirmed;
+}