* Author: Sam Thursfield <samthursfield@codethink.co.uk>
*/
+#include <assert.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);
* If/when we move to GDBus this code can become a one-liner.
*/
- connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
+ if (is_setid()) {
+ *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+ "Cannot use IPC while setid");
+ return NULL;
+ }
- 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);
+ 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,
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;
}
/* 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);
- }
- 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);
- }
- else {
- g_simple_async_result_set_op_res_gpointer (token,
- identity_data,
- moonshot_identity_data_free);
+ 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);
}
- g_simple_async_result_complete (token);
- g_object_unref (token);
+ if (dbus_proxy != NULL)
+ g_object_ref (dbus_proxy);
+
+ g_static_mutex_unlock (&init_lock);
+
+ return dbus_proxy;
}
-/**
- * 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_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)
{
- DBusGProxyCall *call_id;
- GSimpleAsyncResult *result;
- GError *error = NULL;
+ GError *g_error = NULL;
+ DBusGProxy *dbus_proxy;
+ int success;
- 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);
- g_return_if_fail (DBUS_IS_G_PROXY (moonshot_dbus_proxy));
+ if (*error != NULL)
+ return FALSE;
- result = g_simple_async_result_new (NULL,
- callback,
- user_data,
- moonshot_get_identity);
+ 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;
+ }
- 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);
+ if (success == FALSE) {
+ *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
+ "No identity was returned by the Moonshot "
+ "user interface.");
+ return FALSE;
+ }
+
+ 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_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)
{
- MoonshotIdentityData *identity;
+ GError *g_error = NULL;
+ DBusGProxy *dbus_proxy;
+ int success = FALSE;
- 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);
+
+ 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;
+ }
- *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;
+ if (success == FALSE) {
+ *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
+ "No identity was returned by the Moonshot "
+ "user interface.");
+ return FALSE;
+ }
return TRUE;
}
+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)
+{
+ GError *g_error = NULL;
+ DBusGProxy *dbus_proxy;
+ int success = FALSE;
+ int i;
+ const char **rules_patterns_strv,
+ **rules_always_confirm_strv,
+ **services_strv;
+
+ dbus_proxy = get_dbus_proxy (error);
- /**
- * 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
- */
+ if (*error != NULL)
+ return FALSE;
+
+ 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);
+
+ /* 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 *));
+
+ 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);
+
+ if (g_error != NULL) {
+ *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
+ g_error->message);
+ return FALSE;
+ }
+
+ return success;
+}