Add polkit support to varlinkctl and sd-sysext (#39796)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "bus-polkit.h"
|
||||
#include "varlink-io.systemd.sysext.h"
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
@@ -19,24 +20,28 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(force, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Unmerge,
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Refresh,
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(force, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
|
||||
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
List,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT,
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(Class, ImageClass, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(Type, ImageType, 0),
|
||||
SD_VARLINK_DEFINE_OUTPUT(Name, SD_VARLINK_STRING, 0),
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
This file is part of systemd.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
-->
|
||||
|
||||
<policyconfig>
|
||||
|
||||
<vendor>The systemd Project</vendor>
|
||||
<vendor_url>https://systemd.io</vendor_url>
|
||||
|
||||
<action id="io.systemd.sysext.manage">
|
||||
<description gettext-domain="systemd">Allow managing (merging, unmerging, ...) of system extension images.</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to manage a system extension image.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
|
||||
<action id="io.systemd.sysext.read">
|
||||
<description gettext-domain="systemd">Allow reading (listing, ...) of system extension images.</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to list system extension images.</message>
|
||||
<defaults>
|
||||
<allow_any>yes</allow_any>
|
||||
<allow_inactive>yes</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
|
||||
<action id="io.systemd.confext.manage">
|
||||
<description gettext-domain="systemd">Allow managing (merging, unmerging, ...) of configuration extension images.</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to manage a configuration extension image.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
|
||||
<action id="io.systemd.confext.read">
|
||||
<description gettext-domain="systemd">Allow reading (listing, ...) of configuration extension images.</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to list configuration extension images.</message>
|
||||
<defaults>
|
||||
<allow_any>yes</allow_any>
|
||||
<allow_inactive>yes</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>
|
||||
@@ -18,4 +18,7 @@ if conf.get('ENABLE_SYSEXT') == 1
|
||||
install_symlink('systemd-confext',
|
||||
pointing_to : 'systemd-sysext',
|
||||
install_dir : bindir)
|
||||
|
||||
install_data('io.systemd.sysext.policy',
|
||||
install_dir : polkitpolicydir)
|
||||
endif
|
||||
|
||||
+83
-17
@@ -14,6 +14,7 @@
|
||||
#include "blkid-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "build.h"
|
||||
#include "bus-polkit.h"
|
||||
#include "bus-unit-util.h"
|
||||
#include "bus-util.h"
|
||||
#include "capability-util.h"
|
||||
@@ -117,6 +118,8 @@ static const struct {
|
||||
const char *full_identifier;
|
||||
const char *short_identifier;
|
||||
const char *short_identifier_plural;
|
||||
const char *polkit_rw_action_id;
|
||||
const char *polkit_ro_action_id;
|
||||
const char *blurb;
|
||||
const char *dot_directory_name;
|
||||
const char *directory_name;
|
||||
@@ -132,6 +135,8 @@ static const struct {
|
||||
.full_identifier = "systemd-sysext",
|
||||
.short_identifier = "sysext",
|
||||
.short_identifier_plural = "extensions",
|
||||
.polkit_rw_action_id = "io.systemd.sysext.manage",
|
||||
.polkit_ro_action_id = "io.systemd.sysext.read",
|
||||
.blurb = "Merge system extension images into /usr/ and /opt/.",
|
||||
.dot_directory_name = ".systemd-sysext",
|
||||
.level_env = "SYSEXT_LEVEL",
|
||||
@@ -146,6 +151,8 @@ static const struct {
|
||||
.full_identifier = "systemd-confext",
|
||||
.short_identifier = "confext",
|
||||
.short_identifier_plural = "confexts",
|
||||
.polkit_rw_action_id = "io.systemd.confext.manage",
|
||||
.polkit_ro_action_id = "io.systemd.confext.read",
|
||||
.blurb = "Merge configuration extension images into /etc/.",
|
||||
.dot_directory_name = ".systemd-confext",
|
||||
.level_env = "CONFEXT_LEVEL",
|
||||
@@ -623,13 +630,16 @@ static int vl_method_unmerge(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "class", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodUnmergeParameters, class), 0 },
|
||||
{ "noReload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(MethodUnmergeParameters, no_reload), 0 },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{}
|
||||
};
|
||||
MethodUnmergeParameters p = {
|
||||
.no_reload = -1,
|
||||
};
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
ImageClass image_class = arg_image_class;
|
||||
bool no_reload;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@@ -638,13 +648,24 @@ static int vl_method_unmerge(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
|
||||
|
||||
r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unmerge(image_class,
|
||||
hierarchies ?: arg_hierarchies,
|
||||
p.no_reload >= 0 ? p.no_reload : arg_no_reload);
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
/* bus= */ NULL,
|
||||
image_class_info[image_class].polkit_rw_action_id,
|
||||
(const char**) STRV_MAKE(
|
||||
"verb", "unmerge",
|
||||
"noReload", one_zero(no_reload)),
|
||||
polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = unmerge(image_class, hierarchies ?: arg_hierarchies, no_reload);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2261,6 +2282,7 @@ static int parse_merge_parameters(sd_varlink *link, sd_json_variant *parameters,
|
||||
{ "force", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, force), 0 },
|
||||
{ "noReload", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, no_reload), 0 },
|
||||
{ "noexec", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(MethodMergeParameters, noexec), 0 },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2272,6 +2294,7 @@ static int parse_merge_parameters(sd_varlink *link, sd_json_variant *parameters,
|
||||
}
|
||||
|
||||
static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||
MethodMergeParameters p = {
|
||||
.force = -1,
|
||||
@@ -2280,7 +2303,8 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
};
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
ImageClass image_class = arg_image_class;
|
||||
int r;
|
||||
bool force, no_reload;
|
||||
int r, noexec;
|
||||
|
||||
assert(link);
|
||||
|
||||
@@ -2292,6 +2316,23 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
force = p.force >= 0 ? p.force : arg_force;
|
||||
no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
|
||||
noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
/* bus= */ NULL,
|
||||
image_class_info[image_class].polkit_rw_action_id,
|
||||
(const char**) STRV_MAKE(
|
||||
"verb", "merge",
|
||||
"force", one_zero(force),
|
||||
"noReload", one_zero(no_reload),
|
||||
"noexec", one_zero(noexec > 0)),
|
||||
polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = image_discover_and_read_metadata(image_class, &images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -2306,12 +2347,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
if (r > 0)
|
||||
return sd_varlink_errorbo(link, "io.systemd.sysext.AlreadyMerged", SD_JSON_BUILD_PAIR_STRING("hierarchy", which));
|
||||
|
||||
r = merge(image_class,
|
||||
hierarchies ?: arg_hierarchies,
|
||||
p.force >= 0 ? p.force : arg_force,
|
||||
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||
p.noexec >= 0 ? p.noexec : arg_noexec,
|
||||
images);
|
||||
r = merge(image_class, hierarchies ?: arg_hierarchies, force, no_reload, noexec, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2381,9 +2417,11 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
.no_reload = -1,
|
||||
.noexec = -1,
|
||||
};
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
ImageClass image_class = arg_image_class;
|
||||
int r;
|
||||
bool force, no_reload;
|
||||
int r, noexec;
|
||||
|
||||
assert(link);
|
||||
|
||||
@@ -2395,11 +2433,24 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = refresh(image_class,
|
||||
hierarchies ?: arg_hierarchies,
|
||||
p.force >= 0 ? p.force : arg_force,
|
||||
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||
p.noexec >= 0 ? p.noexec : arg_noexec);
|
||||
force = p.force >= 0 ? p.force : arg_force;
|
||||
no_reload = p.no_reload >= 0 ? p.no_reload : arg_no_reload;
|
||||
noexec = p.noexec >= 0 ? p.noexec : arg_noexec;
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
/* bus= */ NULL,
|
||||
image_class_info[image_class].polkit_rw_action_id,
|
||||
(const char**) STRV_MAKE(
|
||||
"verb", "refresh",
|
||||
"force", one_zero(force),
|
||||
"noReload", one_zero(no_reload),
|
||||
"noexec", one_zero(noexec > 0)),
|
||||
polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = refresh(image_class, hierarchies ?: arg_hierarchies, force, no_reload, noexec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2445,9 +2496,11 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "class", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, 0 },
|
||||
VARLINK_DISPATCH_POLKIT_FIELD,
|
||||
{}
|
||||
};
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
Hashmap **polkit_registry = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@@ -2462,6 +2515,15 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_verify_polkit_async(
|
||||
link,
|
||||
/* bus= */ NULL,
|
||||
image_class_info[image_class].polkit_ro_action_id,
|
||||
(const char**) STRV_MAKE("verb", "list"),
|
||||
polkit_registry);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||
r = image_discover(RUNTIME_SCOPE_SYSTEM, image_class, arg_root, &images);
|
||||
if (r < 0)
|
||||
@@ -2727,10 +2789,14 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
if (arg_varlink) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
|
||||
|
||||
/* Invocation as Varlink service */
|
||||
|
||||
r = varlink_server_new(&varlink_server, SD_VARLINK_SERVER_ROOT_ONLY, NULL);
|
||||
r = varlink_server_new(
|
||||
&varlink_server,
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA,
|
||||
&polkit_registry);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
|
||||
+24
-12
@@ -9,6 +9,7 @@
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "build.h"
|
||||
#include "bus-util.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "polkit-agent.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -46,6 +48,7 @@ static char **arg_graceful = NULL;
|
||||
static usec_t arg_timeout = 0;
|
||||
static bool arg_exec = false;
|
||||
static PushFds arg_push_fds = {};
|
||||
static bool arg_ask_password = true;
|
||||
|
||||
static void push_fds_done(PushFds *p) {
|
||||
assert(p);
|
||||
@@ -87,6 +90,7 @@ static int help(void) {
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --no-ask-password Do not prompt for password\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --more Request multiple responses\n"
|
||||
" --collect Collect multiple responses in a JSON array\n"
|
||||
@@ -126,21 +130,23 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_TIMEOUT,
|
||||
ARG_EXEC,
|
||||
ARG_PUSH_FD,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "more", no_argument, NULL, ARG_MORE },
|
||||
{ "oneway", no_argument, NULL, ARG_ONEWAY },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "collect", no_argument, NULL, ARG_COLLECT },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "graceful", required_argument, NULL, ARG_GRACEFUL },
|
||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||
{ "exec", no_argument, NULL, ARG_EXEC },
|
||||
{ "push-fd", required_argument, NULL, ARG_PUSH_FD },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "more", no_argument, NULL, ARG_MORE },
|
||||
{ "oneway", no_argument, NULL, ARG_ONEWAY },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "collect", no_argument, NULL, ARG_COLLECT },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "graceful", required_argument, NULL, ARG_GRACEFUL },
|
||||
{ "timeout", required_argument, NULL, ARG_TIMEOUT },
|
||||
{ "exec", no_argument, NULL, ARG_EXEC },
|
||||
{ "push-fd", required_argument, NULL, ARG_PUSH_FD },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -253,6 +259,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_NO_ASK_PASSWORD:
|
||||
arg_ask_password = false;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -619,6 +629,8 @@ static int verb_call(int argc, char *argv[], void *userdata) {
|
||||
if (arg_exec && (arg_collect || (arg_method_flags & (SD_VARLINK_METHOD_ONEWAY|SD_VARLINK_METHOD_MORE))) != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--exec and --collect/--more/--oneway may not be combined.");
|
||||
|
||||
(void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
|
||||
|
||||
url = argv[1];
|
||||
method = argv[2];
|
||||
parameter = argc > 3 && !streq(argv[3], "-") ? argv[3] : NULL;
|
||||
|
||||
Reference in New Issue
Block a user