Files
ModemManager/src/mm-auth-provider.c
T
Dan Williams ef5cee3ab5 auth-provider: move auth provider logic into each class
Rather than make all of them rely on MMBaseModem for it. This
lets us disentangle dependencies for easier unit testing.

For interfaces, rather than casting directly to MMBaseModem
use intermediate interfaces (MMIfaceAuth and MMIfaceOpLock)
that tests can fake out.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 20:08:06 -05:00

266 lines
8.6 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details:
*
* Copyright (C) 2010 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <ModemManager.h>
#include "mm-errors-types.h"
#include "mm-log-object.h"
#include "mm-utils.h"
#include "mm-auth-provider.h"
#include "mm-context.h"
#if defined WITH_POLKIT
# include <polkit/polkit.h>
#endif
struct _MMAuthProvider {
GObject parent;
#if defined WITH_POLKIT
PolkitAuthority *authority;
#endif
};
struct _MMAuthProviderClass {
GObjectClass parent;
};
static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
/*****************************************************************************/
gboolean
mm_auth_provider_authorize_finish (MMAuthProvider *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
#if defined WITH_POLKIT
typedef struct {
PolkitSubject *subject;
gchar *authorization;
GDBusMethodInvocation *invocation;
} AuthorizeContext;
static void
authorize_context_free (AuthorizeContext *ctx)
{
g_object_unref (ctx->invocation);
g_object_unref (ctx->subject);
g_free (ctx->authorization);
g_free (ctx);
}
static void
check_authorization_ready (PolkitAuthority *authority,
GAsyncResult *res,
GTask *task)
{
PolkitAuthorizationResult *pk_result;
GError *error = NULL;
AuthorizeContext *ctx;
if (g_task_return_error_if_cancelled (task)) {
g_object_unref (task);
return;
}
ctx = g_task_get_task_data (task);
pk_result = polkit_authority_check_authorization_finish (authority, res, &error);
if (!pk_result) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"PolicyKit authorization failed: '%s'",
error->message);
g_error_free (error);
} else {
if (polkit_authorization_result_get_is_authorized (pk_result))
/* Good! */
g_task_return_boolean (task, TRUE);
else if (polkit_authorization_result_get_is_challenge (pk_result))
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"PolicyKit authorization failed: challenge needed for '%s'",
ctx->authorization);
else
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"PolicyKit authorization failed: not authorized for '%s'",
ctx->authorization);
g_object_unref (pk_result);
}
g_object_unref (task);
}
#endif
void
mm_auth_provider_authorize (MMAuthProvider *self,
GDBusMethodInvocation *invocation,
const gchar *authorization,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, cancellable, callback, user_data);
/* When running in the session bus for tests, default to always allow */
if (mm_context_get_test_session ()) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
#if defined WITH_POLKIT
{
AuthorizeContext *ctx;
/* When creating the object, we actually allowed errors when looking for the
* authority. If that is the case, we'll just forbid any incoming
* authentication request */
if (!self->authority) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"PolicyKit authorization error: 'authority not found'");
g_object_unref (task);
return;
}
ctx = g_new (AuthorizeContext, 1);
ctx->invocation = g_object_ref (invocation);
ctx->authorization = g_strdup (authorization);
ctx->subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (ctx->invocation));
g_task_set_task_data (task, ctx, (GDestroyNotify)authorize_context_free);
polkit_authority_check_authorization (self->authority,
ctx->subject,
authorization,
NULL, /* details */
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
cancellable,
(GAsyncReadyCallback)check_authorization_ready,
task);
}
#else
/* Just create the result and complete it */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
#endif
}
/*****************************************************************************/
static gchar *
log_object_build_id (MMLogObject *_self)
{
return g_strdup ("auth-provider");
}
/*****************************************************************************/
static void
mm_auth_provider_init (MMAuthProvider *self)
{
#if defined WITH_POLKIT
{
GError *error = NULL;
self->authority = polkit_authority_get_sync (NULL, &error);
if (!self->authority) {
/* NOTE: we failed to create the polkit authority, but we still create
* our AuthProvider. Every request will fail, though. */
mm_obj_warn (self, "failed to create PolicyKit authority: '%s'",
error ? error->message : "unknown");
g_clear_error (&error);
}
}
#endif
}
static void
dispose (GObject *object)
{
#if defined WITH_POLKIT
g_clear_object (&(MM_AUTH_PROVIDER (object)->authority));
#endif
G_OBJECT_CLASS (mm_auth_provider_parent_class)->dispose (object);
}
static void
log_object_iface_init (MMLogObjectInterface *iface)
{
iface->build_id = log_object_build_id;
}
static void
mm_auth_provider_class_init (MMAuthProviderClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = dispose;
}
MM_DEFINE_SINGLETON_GETTER (MMAuthProvider, mm_auth_provider_get, MM_TYPE_AUTH_PROVIDER)
/*****************************************************************************/
/* Auth interface */
G_DEFINE_INTERFACE (MMIfaceAuth, mm_iface_auth, G_TYPE_OBJECT)
void
mm_iface_auth_authorize (MMIfaceAuth *self,
GDBusMethodInvocation *invocation,
const gchar *authorization,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (MM_IFACE_AUTH_GET_IFACE (self)->authorize != NULL);
MM_IFACE_AUTH_GET_IFACE (self)->authorize (self,
invocation,
authorization,
callback,
user_data);
}
gboolean
mm_iface_auth_authorize_finish (MMIfaceAuth *self,
GAsyncResult *res,
GError **error)
{
g_assert (MM_IFACE_AUTH_GET_IFACE (self)->authorize_finish != NULL);
return MM_IFACE_AUTH_GET_IFACE (self)->authorize_finish (self, res, error);
}
static void
mm_iface_auth_default_init (MMIfaceAuthInterface *iface)
{
}