| /* -*- 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) |
| { |
| } |
| |