From 84f6bcb4f0b4a97e84e26361bccab00316fc2c23 Mon Sep 17 00:00:00 2001 From: Kalev Lember Date: Fri, 9 Jun 2017 12:52:01 +0200 Subject: [PATCH] Add polkit support This allows non-root users access to the rpm-ostree daemon, which is a pre-requirement for gnome-software rpm-ostree support. Closes: #745 Closes: #825 Approved by: cgwalters --- Makefile-daemon.am | 4 + configure.ac | 1 + packaging/rpm-ostree.spec.in | 4 +- src/daemon/org.projectatomic.rpmostree1.conf | 7 + .../org.projectatomic.rpmostree1.policy | 120 +++++++++++++ src/daemon/rpmostreed-os.c | 159 +++++++++++++++++- 6 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 src/daemon/org.projectatomic.rpmostree1.policy diff --git a/Makefile-daemon.am b/Makefile-daemon.am index 6a5f24b0..9e86f44b 100644 --- a/Makefile-daemon.am +++ b/Makefile-daemon.am @@ -105,8 +105,12 @@ servicedir = $(dbusservicedir) %.service: %.service.in Makefile $(SED_SUBST) $@.in > $@.tmp && mv $@.tmp $@ +polkit_policy_DATA = $(srcdir)/src/daemon/org.projectatomic.rpmostree1.policy +polkit_policydir = $(datadir)/polkit-1/actions + EXTRA_DIST += \ $(dbusservice_DATA) \ + $(polkit_policy_DATA) \ $(service_in_files) \ $(systemdunit_in_files) \ $(NULL) diff --git a/configure.ac b/configure.ac index a7c91d20..eb42a202 100644 --- a/configure.ac +++ b/configure.ac @@ -88,6 +88,7 @@ PKG_CHECK_MODULES(PKGDEP_GIO_UNIX, [gio-unix-2.0]) PKG_CHECK_MODULES(PKGDEP_RPMOSTREE, [gio-unix-2.0 >= 2.40.0 json-glib-1.0 ostree-1 >= 2017.4 libsystemd + polkit-gobject-1 rpm librepo libarchive]) dnl bundled libdnf diff --git a/packaging/rpm-ostree.spec.in b/packaging/rpm-ostree.spec.in index 32dead5f..fd78b1ed 100644 --- a/packaging/rpm-ostree.spec.in +++ b/packaging/rpm-ostree.spec.in @@ -21,6 +21,7 @@ BuildRequires: pkgconfig(json-glib-1.0) BuildRequires: pkgconfig(rpm) BuildRequires: pkgconfig(libarchive) BuildRequires: pkgconfig(libsystemd) +BuildRequires: pkgconfig(polkit-gobject-1) BuildRequires: libcap-devel BuildRequires: libattr-devel @@ -115,7 +116,8 @@ python autofiles.py > files \ '%{_sysconfdir}/dbus-1/system.d/*' \ '%{_prefix}/lib/systemd/system/*' \ '%{_libexecdir}/rpm-ostree*' \ - '%{_datadir}/dbus-1/system-services' + '%{_datadir}/dbus-1/system-services' \ + '%{_datadir}/polkit-1/actions/org.projectatomic.rpmostree1.policy' python autofiles.py > files.devel \ '%{_libdir}/lib*.so' \ '%{_includedir}/*' \ diff --git a/src/daemon/org.projectatomic.rpmostree1.conf b/src/daemon/org.projectatomic.rpmostree1.conf index a1ae0be8..f4a1e8df 100644 --- a/src/daemon/org.projectatomic.rpmostree1.conf +++ b/src/daemon/org.projectatomic.rpmostree1.conf @@ -10,12 +10,16 @@ + + + @@ -27,6 +31,9 @@ send_interface="org.freedesktop.DBus.Properties" send_member="GetAll"/> + + diff --git a/src/daemon/org.projectatomic.rpmostree1.policy b/src/daemon/org.projectatomic.rpmostree1.policy new file mode 100644 index 00000000..e5053e1b --- /dev/null +++ b/src/daemon/org.projectatomic.rpmostree1.policy @@ -0,0 +1,120 @@ + + + + + Project Atomic + https://www.projectatomic.io/ + package-x-generic + + + Install and remove packages + Authentication is required to install and remove software + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Install local packages + Authentication is required to install software + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Override packages + Authentication is required to override base OS software + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Update base OS + Authentication is required to update software + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Update base OS + Authentication is required to update software + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Switch to a different base OS + Authentication is required to switch to a different base OS + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Rollback OS updates + Authentication is required to roll back software updates + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Change boot configuration + Authentication is required to change boot configuration + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Clear cache + Authentication is required to clear cache / pending data + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + + + Refresh repository metadata + Authentication is required to check available updates + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + + diff --git a/src/daemon/rpmostreed-os.c b/src/daemon/rpmostreed-os.c index 5a38084a..7db16bb0 100644 --- a/src/daemon/rpmostreed-os.c +++ b/src/daemon/rpmostreed-os.c @@ -19,7 +19,9 @@ #include "config.h" #include "ostree.h" +#include #include +#include #include "rpmostreed-sysroot.h" #include "rpmostreed-daemon.h" @@ -40,6 +42,7 @@ typedef struct _RpmostreedOSClass RpmostreedOSClass; struct _RpmostreedOS { RPMOSTreeOSSkeleton parent_instance; + PolkitAuthority *authority; RpmostreedTransactionMonitor *transaction_monitor; guint signal_id; }; @@ -53,6 +56,10 @@ static void rpmostreed_os_iface_init (RPMOSTreeOSIface *iface); static gboolean rpmostreed_os_load_internals (RpmostreedOS *self, GError **error); +static inline void *vardict_lookup_ptr (GVariantDict *dict, const char *key, const char *fmt); + +static gboolean vardict_lookup_bool (GVariantDict *dict, const char *key, gboolean dfault); + G_DEFINE_TYPE_WITH_CODE (RpmostreedOS, rpmostreed_os, RPMOSTREE_TYPE_OS_SKELETON, @@ -77,6 +84,146 @@ sysroot_changed (RpmostreedSysroot *sysroot, g_warning ("%s", local_error->message); } +static gboolean +os_authorize_method (GDBusInterfaceSkeleton *interface, + GDBusMethodInvocation *invocation) +{ + RpmostreedOS *self = RPMOSTREED_OS (interface); + const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation); + const gchar *sender = g_dbus_method_invocation_get_sender (invocation); + GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation); + g_autoptr(GPtrArray) actions = g_ptr_array_new (); + gboolean authorized = FALSE; + + if (g_strcmp0 (method_name, "GetDeploymentsRpmDiff") == 0 || + g_strcmp0 (method_name, "GetCachedDeployRpmDiff") == 0 || + g_strcmp0 (method_name, "DownloadDeployRpmDiff") == 0 || + g_strcmp0 (method_name, "GetCachedUpdateRpmDiff") == 0 || + g_strcmp0 (method_name, "DownloadUpdateRpmDiff") == 0 || + g_strcmp0 (method_name, "GetCachedRebaseRpmDiff") == 0 || + g_strcmp0 (method_name, "DownloadRebaseRpmDiff") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.repo-refresh"); + } + else if (g_strcmp0 (method_name, "Deploy") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.deploy"); + } + else if (g_strcmp0 (method_name, "Upgrade") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.upgrade"); + } + else if (g_strcmp0 (method_name, "Rebase") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.rebase"); + } + else if (g_strcmp0 (method_name, "SetInitramfsState") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.bootconfig"); + } + else if (g_strcmp0 (method_name, "Cleanup") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.cleanup"); + } + else if (g_strcmp0 (method_name, "Rollback") == 0 || + g_strcmp0 (method_name, "ClearRollbackTarget") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.rollback"); + } + else if (g_strcmp0 (method_name, "PkgChange") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.install-uninstall-packages"); + } + else if (g_strcmp0 (method_name, "UpdateDeployment") == 0) + { + g_autoptr(GVariant) modifiers = g_variant_get_child_value (parameters, 0); + g_autoptr(GVariant) options = g_variant_get_child_value (parameters, 1); + g_auto(GVariantDict) modifiers_dict; + g_auto(GVariantDict) options_dict; + g_variant_dict_init (&modifiers_dict, modifiers); + g_variant_dict_init (&options_dict, options); + const char *refspec = + vardict_lookup_ptr (&modifiers_dict, "set-refspec", "&s"); + const char *revision = + vardict_lookup_ptr (&modifiers_dict, "set-revision", "&s"); + g_autofree char **install_pkgs = + vardict_lookup_ptr (&modifiers_dict, "install-packages", "^a&s"); + g_autofree char **uninstall_pkgs = + vardict_lookup_ptr (&modifiers_dict, "uninstall-packages", "^a&s"); + g_autofree const char *const *override_replace_pkgs = + vardict_lookup_ptr (&modifiers_dict, "override-replace-packages", "^a&s"); + g_autofree const char *const *override_remove_pkgs = + vardict_lookup_ptr (&modifiers_dict, "override-remove-packages", "^a&s"); + g_autofree const char *const *override_reset_pkgs = + vardict_lookup_ptr (&modifiers_dict, "override-reset-packages", "^a&s"); + g_autoptr(GVariant) install_local_pkgs = + g_variant_dict_lookup_value (&modifiers_dict, "install-local-packages", + G_VARIANT_TYPE("ah")); + g_autoptr(GVariant) override_replace_local_pkgs = + g_variant_dict_lookup_value (&modifiers_dict, "override-replace-local-packages", + G_VARIANT_TYPE("ah")); + gboolean no_pull_base = + vardict_lookup_bool (&options_dict, "no-pull-base", FALSE); + gboolean no_overrides = + vardict_lookup_bool (&options_dict, "no-overrides", FALSE); + + if (refspec != NULL) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.rebase"); + else if (revision != NULL) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.deploy"); + else if (!no_pull_base) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.upgrade"); + + if (install_pkgs != NULL || uninstall_pkgs != NULL) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.install-uninstall-packages"); + + if (install_local_pkgs != NULL && g_variant_n_children (install_local_pkgs) > 0) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.install-local-packages"); + + if (override_replace_pkgs != NULL || override_remove_pkgs != NULL || override_reset_pkgs != NULL || + (override_replace_local_pkgs != NULL && g_variant_n_children (override_replace_local_pkgs) > 0) || + no_overrides) + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.override"); + } + else + { + authorized = FALSE; + } + + for (guint i = 0; i < actions->len; i++) + { + const gchar *action = g_ptr_array_index (actions, i); + glnx_unref_object PolkitSubject *subject = polkit_system_bus_name_new (sender); + glnx_unref_object PolkitAuthorizationResult *result = NULL; + g_autoptr(GError) error = NULL; + + result = polkit_authority_check_authorization_sync (self->authority, subject, + action, NULL, + POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, + NULL, &error); + if (result == NULL) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, + "Authorization error: %s", error->message); + return FALSE; + } + + authorized = polkit_authorization_result_get_is_authorized (result); + if (!authorized) + break; + } + + if (!authorized) + { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "rpmostreed OS operation %s not allowed for user", method_name); + } + + return authorized; +} + static void os_dispose (GObject *object) { @@ -90,6 +237,7 @@ os_dispose (GObject *object) object_path, object); } + g_clear_object (&self->authority); g_clear_object (&self->transaction_monitor); if (self->signal_id > 0) @@ -104,9 +252,13 @@ static void os_constructed (GObject *object) { RpmostreedOS *self = RPMOSTREED_OS (object); + g_autoptr(GError) error = NULL; - /* TODO Integrate with PolicyKit via the "g-authorize-method" signal. */ - + self->authority = polkit_authority_get_sync (NULL, &error); + if (self->authority == NULL) + { + errx (1, "Can't get polkit authority: %s", error->message); + } self->signal_id = g_signal_connect (rpmostreed_sysroot_get (), "updated", G_CALLBACK (sysroot_changed), self); @@ -117,11 +269,14 @@ static void rpmostreed_os_class_init (RpmostreedOSClass *klass) { GObjectClass *gobject_class; + GDBusInterfaceSkeletonClass *gdbus_interface_skeleton_class; gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = os_dispose; gobject_class->constructed = os_constructed; + gdbus_interface_skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); + gdbus_interface_skeleton_class->g_authorize_method = os_authorize_method; } static void