diff --git a/meson.build b/meson.build index 3a3e70c2..d7ff5e55 100644 --- a/meson.build +++ b/meson.build @@ -57,15 +57,18 @@ if get_option('enable_gtk') dazzle_dep = dependency('libdazzle-1.0', version: dazzle_req_version) endif -# If we find polkit 0.114, we don't need to backport autoptr cleanups -if get_option('libsysprof') or get_option('with_sysprofd') == 'bundled' - polkit_dep = dependency('polkit-gobject-1', version: '>= 0.114', required: false) - if polkit_dep.found() - config_h.set10('HAVE_POLKIT_AUTOPTR', true) - else - polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version) +polkit_dep = dependency('polkit-gobject-1', version: '>= 0.114', required: false) +if polkit_dep.found() + config_h.set10('HAVE_POLKIT_AUTOPTR', true) +endif +polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false) + +if polkit_dep.found() + config_h.set10('HAVE_POLKIT', true) +else + if get_option('with_sysprofd') == 'bundled' + error('sysprofd requires polkit @0@'.format(polkit_req_version)) endif - polkit_agent_dep = dependency('polkit-agent-1') endif debugdir = get_option('debugdir') diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index e27e5868..56f5186d 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -59,6 +59,7 @@ libsysprof_private_sources = [ 'sysprof-kallsyms.c', 'sysprof-line-reader.c', 'sysprof-map-lookaside.c', + 'sysprof-polkit.c', 'sysprof-symbol-map.c', ipc_service_src, stackstash_sources, diff --git a/src/libsysprof/sysprof-helpers.c b/src/libsysprof/sysprof-helpers.c index 13e3ff1d..3a371af5 100644 --- a/src/libsysprof/sysprof-helpers.c +++ b/src/libsysprof/sysprof-helpers.c @@ -26,7 +26,7 @@ #include #include "sysprof-helpers.h" -#include "sysprof-backport-autocleanups.h" +#include "sysprof-polkit-private.h" #include "helpers.h" #include "ipc-service.h" @@ -526,63 +526,22 @@ sysprof_helpers_perf_event_open (SysprofHelpers *self, #endif /* __linux__ */ static void -sysprof_helpers_check_authorization_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) +sysprof_helpers_authorize_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) { - PolkitAuthority *authority = (PolkitAuthority *)object; - g_autoptr(PolkitAuthorizationResult) res = NULL; - g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; - g_assert (POLKIT_IS_AUTHORITY (authority)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); - if (!(res = polkit_authority_check_authorization_finish (authority, result, &error))) + if (!_sysprof_polkit_authorize_for_bus_finish (result, &error)) g_task_return_error (task, g_steal_pointer (&error)); - else if (!polkit_authorization_result_get_is_authorized (res)) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_PROXY_AUTH_FAILED, - "Failed to authorize user credentials"); else g_task_return_boolean (task, TRUE); } -static void -sysprof_helpers_get_authority_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(PolkitAuthority) authority = user_data; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - PolkitSubject *subject; - GCancellable *cancellable; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - cancellable = g_task_get_cancellable (task); - subject = g_task_get_task_data (task); - - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - g_assert (POLKIT_IS_SUBJECT (subject)); - - if (!(authority = polkit_authority_get_finish (result, &error))) - g_task_return_error (task, g_steal_pointer (&error)); - else - polkit_authority_check_authorization (authority, - subject, - "org.gnome.sysprof3.profile", - NULL, - POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, - cancellable, - sysprof_helpers_check_authorization_cb, - g_steal_pointer (&task)); -} - void sysprof_helpers_authorize_async (SysprofHelpers *self, GCancellable *cancellable, @@ -592,7 +551,6 @@ sysprof_helpers_authorize_async (SysprofHelpers *self, g_autoptr(GTask) task = NULL; g_autoptr(PolkitSubject) subject = NULL; GDBusConnection *bus; - const gchar *unique_name; g_return_if_fail (SYSPROF_IS_HELPERS (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); @@ -600,17 +558,24 @@ sysprof_helpers_authorize_async (SysprofHelpers *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_source_tag (task, sysprof_helpers_authorize_async); - if (fail_if_no_proxy (self, task)) - return; + if (self->proxy == NULL) + { + /* No D-Bus/Polkit? Just bail early and let the hard failure + * happen sooner. + */ + g_task_return_boolean (task, TRUE); + return; + } bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->proxy)); - unique_name = g_dbus_connection_get_unique_name (bus); - subject = polkit_system_bus_name_new (unique_name); - g_task_set_task_data (task, g_steal_pointer (&subject), g_object_unref); - polkit_authority_get_async (cancellable, - sysprof_helpers_get_authority_cb, - g_steal_pointer (&task)); + _sysprof_polkit_authorize_for_bus_async (bus, + "org.gnome.sysprof3.profile", + NULL, + TRUE, + cancellable, + sysprof_helpers_authorize_cb, + g_steal_pointer (&task)); } gboolean diff --git a/src/libsysprof/sysprof-polkit-private.h b/src/libsysprof/sysprof-polkit-private.h new file mode 100644 index 00000000..03b59f3e --- /dev/null +++ b/src/libsysprof/sysprof-polkit-private.h @@ -0,0 +1,39 @@ +/* sysprof-polkit-private.h + * + * Copyright 2019 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 + +G_BEGIN_DECLS + +G_GNUC_INTERNAL +void _sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus, + const gchar *policy, + GHashTable *details, + gboolean allow_user_interaction, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +G_GNUC_INTERNAL +gboolean _sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-polkit.c b/src/libsysprof/sysprof-polkit.c new file mode 100644 index 00000000..a39eba14 --- /dev/null +++ b/src/libsysprof/sysprof-polkit.c @@ -0,0 +1,177 @@ +/* sysprof-polkit.c + * + * Copyright 2019 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 + */ + +#define G_LOG_DOMAIN "sysprof-polkit" + +#include "config.h" + +#ifdef HAVE_POLKIT +# include +#endif + +#include "sysprof-polkit-private.h" +#include "sysprof-backport-autocleanups.h" + +#ifdef HAVE_POLKIT +typedef struct +{ + const gchar *policy; + PolkitSubject *subject; + GHashTable *details; + guint allow_user_interaction : 1; +} Authorize; + +static void +authorize_free (gpointer data) +{ + Authorize *auth = data; + + g_clear_object (&auth->subject); + g_clear_pointer (&auth->details, g_hash_table_unref); + g_slice_free (Authorize, auth); +} + +static void +sysprof_polkit_check_authorization_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + PolkitAuthority *authority = (PolkitAuthority *)object; + g_autoptr(PolkitAuthorizationResult) res = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (POLKIT_IS_AUTHORITY (authority)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!(res = polkit_authority_check_authorization_finish (authority, result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else if (!polkit_authorization_result_get_is_authorized (res)) + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_PROXY_AUTH_FAILED, + "Failed to authorize user credentials"); + else + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_polkit_get_authority_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(PolkitAuthority) authority = NULL; + g_autoptr(PolkitDetails) details = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + GCancellable *cancellable; + Authorize *auth; + guint flags = 0; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + cancellable = g_task_get_cancellable (task); + auth = g_task_get_task_data (task); + + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_assert (auth != NULL); + g_assert (POLKIT_IS_SUBJECT (auth->subject)); + + if (!(authority = polkit_authority_get_finish (result, &error))) + { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + if (auth->allow_user_interaction) + flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; + + if (auth->details != NULL) + { + GHashTableIter iter; + gpointer k, v; + + details = polkit_details_new (); + g_hash_table_iter_init (&iter, auth->details); + while (g_hash_table_iter_next (&iter, &k, &v)) + polkit_details_insert (details, k, v); + } + + polkit_authority_check_authorization (authority, + auth->subject, + auth->policy, + details, + flags, + cancellable, + sysprof_polkit_check_authorization_cb, + g_steal_pointer (&task)); +} +#endif + +void +_sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus, + const gchar *policy, + GHashTable *details, + gboolean allow_user_interaction, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; +#ifdef HAVE_POLKIT + const gchar *bus_name; + Authorize *auth; +#endif + + g_return_if_fail (G_IS_DBUS_CONNECTION (bus)); + g_return_if_fail (policy != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, _sysprof_polkit_authorize_for_bus_async); + +#ifdef HAVE_POLKIT + bus_name = g_dbus_connection_get_unique_name (bus); + + auth = g_slice_new0 (Authorize); + auth->subject = polkit_system_bus_name_new (bus_name); + auth->policy = g_intern_string (policy); + auth->details = details ? g_hash_table_ref (details) : NULL; + auth->allow_user_interaction = !!allow_user_interaction; + g_task_set_task_data (task, auth, authorize_free); + + polkit_authority_get_async (cancellable, + sysprof_polkit_get_authority_cb, + g_steal_pointer (&task)); +#else + g_task_return_boolean (task, TRUE); +#endif +} + +gboolean +_sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} diff --git a/src/tools/meson.build b/src/tools/meson.build index b7f08c1a..670052a7 100644 --- a/src/tools/meson.build +++ b/src/tools/meson.build @@ -3,6 +3,7 @@ tools_deps = [ ] if get_option('libsysprof') and host_machine.system() == 'linux' + polkit_agent_dep = dependency('polkit-agent-1') sysprof_cli = executable('sysprof-cli', 'sysprof-cli.c', dependencies: tools_deps + [libsysprof_dep, polkit_dep, polkit_agent_dep], install_dir: get_option('bindir'),