diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index c3dd0310..66c7bc04 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -6,6 +6,7 @@ libsysprof_profile_public_sources = [ libsysprof_profile_private_sources = [ 'sysprof-controlfd-instrument.c', + 'sysprof-polkit.c', ] libsysprof_profile_public_headers = [ @@ -19,6 +20,7 @@ libsysprof_profile_public_headers = [ libsysprof_profile_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('libdex-1', version: dex_req_version), + dependency('polkit-gobject-1', version: polkit_req_version), libsysprof_capture_dep, ] diff --git a/src/libsysprof-profile/sysprof-polkit-private.h b/src/libsysprof-profile/sysprof-polkit-private.h new file mode 100644 index 00000000..d5af505d --- /dev/null +++ b/src/libsysprof-profile/sysprof-polkit-private.h @@ -0,0 +1,34 @@ +/* sysprof-polkit-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +DexFuture *_sysprof_polkit_authorize (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction); + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-polkit.c b/src/libsysprof-profile/sysprof-polkit.c new file mode 100644 index 00000000..c84cb31d --- /dev/null +++ b/src/libsysprof-profile/sysprof-polkit.c @@ -0,0 +1,181 @@ +/* sysprof-polkit.c + * + * Copyright 2023 Christian Hergert + * + * 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 3 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-polkit-private.h" + +static void +sysprof_polkit_authority_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(PolkitAuthority) authority = NULL; + g_autoptr(DexPromise) promise = user_data; + g_autoptr(GError) error = NULL; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (DEX_IS_PROMISE (promise)); + + if (!(authority = polkit_authority_get_finish (result, &error))) + dex_promise_reject (promise, g_steal_pointer (&error)); + else + dex_promise_resolve_object (promise, g_steal_pointer (&authority)); +} + +static DexFuture * +_sysprof_polkit_authority (void) +{ + DexPromise *promise = dex_promise_new (); + + polkit_authority_get_async (dex_promise_get_cancellable (DEX_PROMISE (promise)), + sysprof_polkit_authority_cb, + dex_ref (promise)); + + return DEX_FUTURE (promise); +} + +static void +sysprof_polkit_check_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + PolkitAuthority *authority = (PolkitAuthority *)object; + g_autoptr(PolkitAuthorizationResult) res = NULL; + g_autoptr(DexPromise) promise = user_data; + g_autoptr(GError) error = NULL; + + g_assert (POLKIT_IS_AUTHORITY (authority)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (DEX_IS_PROMISE (promise)); + + if (!(res = polkit_authority_check_authorization_finish (authority, result, &error))) + dex_promise_reject (promise, g_steal_pointer (&error)); + else if (!polkit_authorization_result_get_is_authorized (res)) + dex_promise_reject (promise, + g_error_new (G_DBUS_ERROR, + G_DBUS_ERROR_AUTH_FAILED, + "Failed to authorize user credentials")); + else + dex_promise_resolve_boolean (promise, TRUE); +} + +static DexFuture * +_sysprof_polkit_check (PolkitAuthority *authority, + PolkitSubject *subject, + const char *policy, + PolkitDetails *details, + PolkitCheckAuthorizationFlags flags) +{ + DexPromise *promise = dex_promise_new (); + + polkit_authority_check_authorization (authority, + subject, + policy, + details, + flags, + dex_promise_get_cancellable (DEX_PROMISE (promise)), + sysprof_polkit_check_cb, + dex_ref (promise)); + + return DEX_FUTURE (promise); +} + +typedef struct _Authorize +{ + GDBusConnection *connection; + char *policy; + PolkitDetails *details; + guint allow_user_interaction : 1; +} Authorize; + +static void +authorize_free (gpointer data) +{ + Authorize *auth = data; + + g_clear_pointer (&auth->policy, g_free); + g_clear_object (&auth->connection); + g_clear_object (&auth->details); + g_free (auth); +} + +static Authorize * +authorize_new (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction) +{ + Authorize *auth; + + auth = g_new0 (Authorize, 1); + g_set_object (&auth->connection, connection); + g_set_object (&auth->details, details); + auth->policy = g_strdup (policy); + auth->allow_user_interaction = !!allow_user_interaction; + + return auth; +} + +static DexFuture * +authorize_fiber (gpointer data) +{ + g_autoptr(PolkitAuthority) authority = NULL; + g_autoptr(PolkitSubject) subject = NULL; + g_autoptr(GError) error = NULL; + const char *bus_name; + Authorize *auth = data; + guint flags = 0; + + g_assert (auth != NULL); + g_assert (G_IS_DBUS_CONNECTION (auth->connection)); + g_assert (!auth->details || POLKIT_IS_DETAILS (auth->details)); + g_assert (auth->policy != NULL); + + bus_name = g_dbus_connection_get_unique_name (auth->connection); + subject = polkit_system_bus_name_new (bus_name); + + if (!(authority = dex_await_object (_sysprof_polkit_authority (), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + + if (auth->allow_user_interaction) + flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; + + if (!dex_await_boolean (_sysprof_polkit_check (authority, subject, auth->policy, auth->details, flags), &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + return dex_future_new_for_boolean (TRUE); +} + +DexFuture * +_sysprof_polkit_authorize (GDBusConnection *connection, + const char *policy, + PolkitDetails *details, + gboolean allow_user_interaction) +{ + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (policy != NULL, NULL); + g_return_val_if_fail (!details || POLKIT_IS_DETAILS (details), NULL); + + return dex_scheduler_spawn (NULL, 0, + authorize_fiber, + authorize_new (connection, policy, details, allow_user_interaction), + authorize_free); +}