Commit 4e07805a authored by Christian Kellner's avatar Christian Kellner
Browse files

device: refactor authentication

There is now a dedicated BoltAuth object that, a) stores the
information for the authentication process (level, key), keeps
track who initiated the authentication (origin), as well
as the result.
Device::Authenticate dbus method now only uses existing
authentication information, if any.
Manager::Enroll must be used to authenticate new devices.
parent cf7c7ee6
/*
* Copyright © 2017 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Christian J. Kellner <christian@kellner.me>
*/
#include "config.h"
#include "bolt-auth.h"
#include "bolt-device.h"
#include "bolt-io.h"
#include "bolt-store.h"
#include <gio/gio.h>
static void async_result_iface_init (GAsyncResultIface *iface);
struct _BoltAuth
{
GObject object;
GObject *origin;
BoltSecurity level;
BoltKey *key;
/* the device */
BoltDevice *dev;
/* result */
GError *error;
};
enum {
PROP_0,
PROP_ORIGIN,
PROP_LEVEL,
PROP_KEY,
PROP_DEVICE,
PROP_ERROR,
PROP_LAST
};
static GParamSpec *props[PROP_LAST] = { NULL, };
G_DEFINE_TYPE_WITH_CODE (BoltAuth, bolt_auth, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, async_result_iface_init));
static void
bolt_auth_finalize (GObject *object)
{
BoltAuth *auth = BOLT_AUTH (object);
g_clear_object (&auth->origin);
g_clear_object (&auth->key);
g_clear_object (&auth->dev);
g_clear_error (&auth->error);
G_OBJECT_CLASS (bolt_auth_parent_class)->finalize (object);
}
static void
bolt_auth_init (BoltAuth *auth)
{
}
static void
bolt_auth_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BoltAuth *auth = BOLT_AUTH (object);
switch (prop_id)
{
case PROP_ORIGIN:
g_value_set_object (value, auth->origin);
break;
case PROP_LEVEL:
g_value_set_enum (value, auth->level);
break;
case PROP_KEY:
g_value_set_object (value, auth->key);
break;
case PROP_DEVICE:
g_value_set_object (value, auth->dev);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_auth_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BoltAuth *auth = BOLT_AUTH (object);
switch (prop_id)
{
case PROP_ORIGIN:
auth->origin = g_value_dup_object (value);
break;
case PROP_LEVEL:
auth->level = g_value_get_enum (value);
break;
case PROP_KEY:
auth->key = g_value_dup_object (value);
break;
case PROP_DEVICE:
g_return_if_fail (auth->dev == NULL);
auth->dev = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bolt_auth_class_init (BoltAuthClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = bolt_auth_finalize;
gobject_class->get_property = bolt_auth_get_property;
gobject_class->set_property = bolt_auth_set_property;
props[PROP_ORIGIN] =
g_param_spec_object ("origin",
NULL, NULL,
G_TYPE_OBJECT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_LEVEL] =
g_param_spec_enum ("level",
NULL, NULL,
BOLT_TYPE_SECURITY,
BOLT_SECURITY_NONE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_KEY] =
g_param_spec_object ("key",
NULL, NULL,
BOLT_TYPE_KEY,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NICK);
props[PROP_DEVICE] =
g_param_spec_object ("device",
NULL, NULL,
BOLT_TYPE_DEVICE,
G_PARAM_READWRITE |
G_PARAM_STATIC_NICK);
props[PROP_ERROR] =
g_param_spec_boxed ("error", NULL, NULL,
G_TYPE_ERROR,
G_PARAM_READWRITE |
G_PARAM_STATIC_NICK);
g_object_class_install_properties (gobject_class,
PROP_LAST,
props);
}
/* async result methods */
static gpointer
async_result_get_user_data (GAsyncResult *res)
{
return NULL;
}
static GObject *
async_result_get_source_object (GAsyncResult *res)
{
return G_OBJECT (BOLT_AUTH (res)->dev);
}
static gboolean
async_result_is_tagged (GAsyncResult *res,
gpointer source_tag)
{
return FALSE;
}
static void
async_result_iface_init (GAsyncResultIface *iface)
{
iface->get_source_object = async_result_get_source_object;
iface->get_user_data = async_result_get_user_data;
iface->is_tagged = async_result_is_tagged;
}
/* public methods */
BoltAuth *
bolt_auth_new (gpointer origin,
BoltSecurity level,
BoltKey *key)
{
BoltAuth *auth;
auth = g_object_new (BOLT_TYPE_AUTH,
"origin", origin,
"level", level,
"key", key,
NULL);
return auth;
}
void
bolt_auth_return_new_error (BoltAuth *auth,
GQuark domain,
gint code,
const char *format,
...)
{
va_list args;
va_start (args, format);
auth->error = g_error_new_valist (domain, code, format, args);
va_end (args);
}
void
bolt_auth_return_error (BoltAuth *auth,
GError **error)
{
g_return_if_fail (error != NULL && *error != NULL);
g_return_if_fail (auth->error == NULL);
auth->error = g_steal_pointer (error);
}
gboolean
bolt_auth_check (BoltAuth *auth,
GError **error)
{
if (auth->error)
{
g_autoptr(GError) err = g_error_copy (auth->error);
g_propagate_error (error, g_steal_pointer (&err));
return FALSE;
}
return TRUE;
}
BoltSecurity
bolt_auth_get_level (BoltAuth *auth)
{
return auth->level;
}
BoltKey *
bolt_auth_get_key (BoltAuth *auth)
{
return auth->key;
}
gpointer
bolt_auth_get_origin (BoltAuth *auth)
{
return auth->origin;
}
/*
* Copyright © 2017 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Christian J. Kellner <christian@kellner.me>
*/
#pragma once
#include "bolt-enums.h"
#include "bolt-key.h"
#include <gio/gio.h>
G_BEGIN_DECLS
#define BOLT_TYPE_AUTH bolt_auth_get_type ()
G_DECLARE_FINAL_TYPE (BoltAuth, bolt_auth, BOLT, AUTH, GObject);
BoltAuth * bolt_auth_new (gpointer origin,
BoltSecurity level,
BoltKey *key);
void bolt_auth_return_new_error (BoltAuth *auth,
GQuark domain,
gint code,
const char *format,
...) G_GNUC_PRINTF (4, 5);
;
void bolt_auth_return_error (BoltAuth *auth,
GError **error);
gboolean bolt_auth_check (BoltAuth *auth,
GError **error);
BoltSecurity bolt_auth_get_level (BoltAuth *auth);
BoltKey * bolt_auth_get_key (BoltAuth *auth);
gpointer bolt_auth_get_origin (BoltAuth *auth);
G_END_DECLS
......@@ -475,13 +475,11 @@ security_for_udev (struct udev_device *udev)
typedef struct
{
BoltSecurity level;
BoltKey *key;
BoltPolicy policy;
BoltAuth *auth;
/* the outer callback */
AuthCallback callback;
gpointer user_data;
GAsyncReadyCallback callback;
gpointer user_data;
} AuthData;
......@@ -491,17 +489,23 @@ auth_data_free (gpointer data)
AuthData *auth = data;
g_debug ("freeing auth data");
g_clear_object (&auth->auth);
g_slice_free (AuthData, auth);
}
static gboolean
authorize_device_internal (BoltDevice *dev,
AuthData *auth,
BoltAuth *auth,
GError **error)
{
g_autoptr(DIR) devdir = NULL;
BoltKey *key;
BoltSecurity level;
gboolean ok;
key = bolt_auth_get_key (auth);
level = bolt_auth_get_level (auth);
devdir = bolt_opendir (dev->syspath, error);
if (devdir == NULL)
return FALSE;
......@@ -510,7 +514,7 @@ authorize_device_internal (BoltDevice *dev,
if (!ok)
return FALSE;
if (auth->key)
if (key)
{
int keyfd;
......@@ -519,7 +523,7 @@ authorize_device_internal (BoltDevice *dev,
if (keyfd < 0)
return FALSE;
ok = bolt_key_write_to (auth->key, keyfd, &auth->level, error);
ok = bolt_key_write_to (key, keyfd, &level, error);
close (keyfd);
if (!ok)
return FALSE;
......@@ -528,9 +532,8 @@ authorize_device_internal (BoltDevice *dev,
g_debug ("[%s] writing authorization", dev->uid);
ok = bolt_write_char_at (dirfd (devdir),
"authorized",
auth->level,
level,
error);
return ok;
}
......@@ -542,22 +545,18 @@ authorize_in_thread (GTask *task,
{
g_autoptr(GError) error = NULL;
BoltDevice *dev = source;
AuthData *auth = context;
AuthData *auth_data = context;
BoltAuth *auth = auth_data->auth;
gboolean ok;
ok = authorize_device_internal (dev, auth, &error);
if (!ok)
{
g_task_return_new_error (task,
BOLT_ERROR, BOLT_ERROR_FAILED,
"failed to authorize device: %s",
error->message);
}
g_task_return_new_error (task, BOLT_ERROR, BOLT_ERROR_FAILED,
"failed to authorize device: %s",
error->message);
else
{
g_task_return_boolean (task, TRUE);
}
g_task_return_boolean (task, TRUE);
}
static void
......@@ -568,136 +567,120 @@ authorize_thread_done (GObject *object,
g_autoptr(GError) error = NULL;
BoltDevice *dev = BOLT_DEVICE (object);
GTask *task = G_TASK (res);
AuthData *auth;
BoltStatus status;
AuthData *auth_data;
BoltAuth *auth;
gboolean ok;
auth = g_task_get_task_data (task);
auth_data = g_task_get_task_data (task);
auth = auth_data->auth;
ok = g_task_propagate_boolean (task, &error);
if (ok)
{
if (auth->level == BOLT_SECURITY_SECURE)
status = BOLT_STATUS_AUTHORIZED_SECURE;
else if (auth->key)
status = BOLT_STATUS_AUTHORIZED_NEWKEY;
else
status = BOLT_STATUS_AUTHORIZED;
if (!dev->store)
{
BoltStore *store;
store = bolt_manager_get_store (dev->mgr);
ok = bolt_store_put_device (store,
dev,
BOLT_POLICY_AUTO,
auth->key,
&error);
}
}
else
if (!ok)
{
status = BOLT_STATUS_AUTH_ERROR;
g_object_set (dev, "status", status, NULL);
bolt_auth_return_error (auth, &error);
}
g_object_set (dev, "status", status, NULL);
if (auth->callback)
auth->callback (dev, ok, &error, auth->user_data);
if (auth_data->callback)
auth_data->callback (G_OBJECT (dev),
G_ASYNC_RESULT (auth),
auth_data->user_data);
}
gboolean
bolt_device_authorize (BoltDevice *dev,
AuthCallback callback,
gpointer user_data,
GError **error)
void
bolt_device_authorize (BoltDevice *dev,
BoltAuth *auth,
GAsyncReadyCallback callback,
gpointer user_data)
{
AuthData *auth_data;
BoltSecurity level;
BoltKey *key;
GTask *task;
g_object_set (auth, "device", dev, NULL);
if (dev->status != BOLT_STATUS_CONNECTED &&
dev->status != BOLT_STATUS_AUTH_ERROR)
{
g_set_error (error, BOLT_ERROR, BOLT_ERROR_FAILED,
"wrong device state: %u", dev->status);
return FALSE;
}
bolt_auth_return_new_error (auth, BOLT_ERROR, BOLT_ERROR_FAILED,
"wrong device state: %u", dev->status);
level = dev->security;
key = NULL;
if (callback)
callback (G_OBJECT (dev), G_ASYNC_RESULT (auth), user_data);
if (level == BOLT_SECURITY_SECURE)
{
if (dev->store == NULL)
key = bolt_key_new ();
else if (dev->key)
key = bolt_store_get_key (dev->store, dev->uid, error);
else
level = BOLT_SECURITY_USER;
return;
}
if (level == BOLT_SECURITY_SECURE && key == NULL)
return FALSE;
task = g_task_new (dev, NULL, authorize_thread_done, NULL);
auth_data = g_slice_new (AuthData);
auth_data->level = level;
auth_data->key = key;
auth_data->callback = callback;
auth_data->user_data = user_data;
auth_data->auth = g_object_ref (auth);
g_task_set_task_data (task, auth_data, auth_data_free);
g_object_set (dev, "status", BOLT_STATUS_AUTHORIZING, NULL);
g_task_run_in_thread (task, authorize_in_thread);
g_object_unref (task);
return TRUE;
}
/* dbus methods */
static void
handle_authorize_done (BoltDevice *dev,
gboolean ok,
GError **error,
gpointer user_data)
handle_authorize_done (GObject *device,
GAsyncResult *res,
gpointer user_data)
{
GDBusMethodInvocation *invocation = user_data;
GDBusMethodInvocation *inv;
GError *error = NULL;
BoltAuth *auth;
BoltDevice *dev;
gboolean ok;
dev = BOLT_DEVICE (device);
inv = user_data;
auth = BOLT_AUTH (res);
ok = bolt_auth_check (auth, &error);
if (ok)
{
bolt_dbus_device_complete_authorize (BOLT_DBUS_DEVICE (dev),
invocation);
}
bolt_dbus_device_complete_authorize (BOLT_DBUS_DEVICE (dev), inv);
else
{
g_dbus_method_invocation_take_error (invocation, *error);
error = NULL;
}
g_dbus_method_invocation_take_error (inv, error);
}
static gboolean
handle_authorize (BoltDBusDevice *object,
GDBusMethodInvocation *invocation,
GDBusMethodInvocation *inv,
gpointer user_data)
{
BoltDevice *dev = BOLT_DEVICE (object);
GError *error = NULL;
gboolean ok;
BoltAuth *auth;
BoltSecurity level;
BoltKey *key;
ok = bolt_device_authorize (dev,
handle_authorize_done,
invocation,
&error);
level = dev->security;