--- Hi, Changes in v3: - call the feature reboot to firmware everywhere - make the login interface a property and methods to change it and don't couple it with a reboot action - changed/added policykit action defaulting to auth_admin_keep. Please change this if something else is desired.
The policy kit behavior feels weird to me: if I call bus_verify_polkit() with interactive = false, I still get a password prompt for CanRebootToFirmware. Is this a bug or am I doing something wrong here? Do I need to make a separate get policy (with no auth_admin_keep) for this to work? Jan man/systemctl.xml | 10 ++++ shell-completion/bash/systemctl.in | 2 +- shell-completion/zsh/_systemctl.in | 1 + src/login/logind-dbus.c | 96 ++++++++++++++++++++++++++++++ src/login/org.freedesktop.login1.conf | 8 +++ src/login/org.freedesktop.login1.policy.in | 10 ++++ src/shared/efivars.c | 73 +++++++++++++++++++++++ src/shared/efivars.h | 3 + src/systemctl/systemctl.c | 18 ++++++ 9 files changed, 220 insertions(+), 1 deletion(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 50e6bc9..b77f4ab 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -527,6 +527,16 @@ </varlistentry> <varlistentry> + <term><option>--firmware-setup</option></term> + + <listitem> + <para>Indicate to the firmware to boot into setup mode. Note + that this is currently only supported on some EFI systems and + only if it was booted in EFI mode.</para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>--plain</option></term> <listitem> diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 8063316..773a59d 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -92,7 +92,7 @@ _systemctl () { local -A OPTS=( [STANDALONE]='--all -a --reverse --after --before --defaults --fail --ignore-dependencies --failed --force -f --full -l --global --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall - --quiet -q --privileged -P --system --user --version --runtime --recursive -r' + --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup' [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --root' ) diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index 7f2d5ac..3bbfb6f 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -384,5 +384,6 @@ _arguments -s \ {-P,--privileged}'[Acquire privileges before execution]' \ {-n+,--lines=}'[Journal entries to show]:number of entries' \ {-o+,--output=}'[Change journal output mode]:modes:_sd_outputmodes' \ + '--firmware-setup[Tell the firmware to show the setup menu on next boot]' \ '--plain[When used with list-dependencies, print output as a list]' \ '*::systemctl command:_systemctl_command' diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index a3d49ef..cea99fc 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -38,6 +38,7 @@ #include "bus-common-errors.h" #include "udev-util.h" #include "selinux-util.h" +#include "efivars.h" #include "logind.h" int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) { @@ -1850,6 +1851,98 @@ static int method_can_hybrid_sleep(sd_bus *bus, sd_bus_message *message, void *u error); } +static int property_get_reboot_to_firmware( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = efi_get_reboot_to_fw(); + if (r < 0 && r != -EOPNOTSUPP) + return r; + + return sd_bus_message_append(reply, "b", r > 0); +} + +static int method_set_reboot_to_firmware(sd_bus *bus, + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + int b, r; + int interactive; + Manager *m = userdata; + + assert(bus); + assert(message); + assert(m); + + r = sd_bus_message_read(message, "bb", &b, &interactive); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware", + interactive, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = efi_set_reboot_to_fw(b); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_firmware(sd_bus *bus, + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + int r; + bool challenge; + const char *result; + Manager *m = userdata; + + assert(bus); + assert(message); + assert(m); + + if (!is_efi_reboot_to_fw_supported()) + return sd_bus_reply_method_return(message, "s", "na"); + + r = bus_verify_polkit(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware", + false, + UID_INVALID, + &challenge, + error); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + static int method_inhibit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; const char *who, *why, *what, *mode; @@ -1970,6 +2063,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RebootToFirmware", "b", property_get_reboot_to_firmware, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -2023,6 +2117,8 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToFirmware", "bb", NULL, method_set_reboot_to_firmware, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToFirmware", NULL, "s", method_can_reboot_to_firmware, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("SessionNew", "so", 0), SD_BUS_SIGNAL("SessionRemoved", "so", 0), diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index 1318328..589cca6 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -130,6 +130,14 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Manager" + send_member="CanRebootToFirmware"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" + send_member="SetRebootToFirmware"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" send_member="AttachDevice"/> <allow send_destination="org.freedesktop.login1" diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index c45491b..b2efab1 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -290,4 +290,14 @@ </defaults> </action> + <action id="org.freedesktop.login1.set-reboot-to-firmware"> + <_description>Allow indication to the firmware to boot to setup interface</_description> + <_message>Authentication is required to indicate to the firmware to boot to setup interface.</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 20067c6..5879116 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -25,6 +25,7 @@ #include "util.h" #include "utf8.h" +#include "virt.h" #include "efivars.h" #ifdef ENABLE_EFI @@ -37,6 +38,7 @@ #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 #define END_DEVICE_PATH_TYPE 0x7f #define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 struct boot_option { uint32_t attr; @@ -93,6 +95,77 @@ int is_efi_secure_boot_setup_mode(void) { return read_flag("SetupMode"); } +static int get_os_indications_supported(uint64_t *b) { + int r; + size_t s; + _cleanup_free_ void *v = NULL; + + if (!is_efi_boot() || detect_container(NULL) > 0) + return -EOPNOTSUPP; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s); + if (r < 0) + return r; + else if (s != sizeof(uint64_t)) + return -EINVAL; + + *b = *(uint64_t *)v; + if (!(*b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) + return -EOPNOTSUPP; + + return 0; +} + +static int get_os_indications(uint64_t *b) { + int r; + size_t s; + _cleanup_free_ void *v = NULL; + + r = get_os_indications_supported(b); + if (r < 0) + return r; + + r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s); + if (r < 0) + return r; + else if (s != sizeof(uint64_t)) + return -EINVAL; + + *b = *(uint64_t *)v; + return 0; +} + +bool is_efi_reboot_to_fw_supported(void) { + int r; + uint64_t b; + + r = get_os_indications_supported(&b); + return r >= 0; +} + +int efi_get_reboot_to_fw(void) { + int r; + uint64_t b; + + r = get_os_indications(&b); + return r < 0 ? r : !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI); +} + +int efi_set_reboot_to_fw(bool value) { + int r; + uint64_t b; + + r = get_os_indications(&b); + if (r < 0) + return r; + + if (value) + b |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + else + b &= ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + return efi_set_variable(EFI_VENDOR_GLOBAL, "OsIndications", &b, sizeof(uint64_t)); +} + int efi_get_variable( sd_id128_t vendor, const char *name, diff --git a/src/shared/efivars.h b/src/shared/efivars.h index 2492893..e57934f 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -35,6 +35,9 @@ bool is_efi_boot(void); int is_efi_secure_boot(void); int is_efi_secure_boot_setup_mode(void); +bool is_efi_reboot_to_fw_supported(void); +int efi_get_reboot_to_fw(void); +int efi_set_reboot_to_fw(bool value); int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size); int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 3158a38..c82618d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -68,6 +68,7 @@ #include "bus-common-errors.h" #include "mkdir.h" #include "dropin.h" +#include "efivars.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -132,6 +133,7 @@ static char *arg_host = NULL; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; +static bool arg_firmware_setup = false; static bool original_stdout_is_tty; @@ -2928,6 +2930,15 @@ static int start_special(sd_bus *bus, char **args) { return -EPERM; } + if (arg_firmware_setup) { + if (!is_efi_reboot_to_fw_supported()) + return log_error_errno(EOPNOTSUPP, "Boot to EFI setup not supported."); + + r = efi_set_reboot_to_fw(true); + if (r < 0) + return log_error_errno(r, "Cannot indicate to boot to EFI setup: %m"); + } + if (a == ACTION_REBOOT && args[1]) { r = update_reboot_param_file(args[1]); if (r < 0) @@ -5971,6 +5982,7 @@ static void systemctl_help(void) { " -o --output=STRING Change journal output mode (short, short-iso,\n" " short-precise, short-monotonic, verbose,\n" " export, json, json-pretty, json-sse, cat)\n" + " --firmware-setup Tell the firmware to show the setup menu on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" "Unit Commands:\n" " list-units [PATTERN...] List loaded units\n" @@ -6150,6 +6162,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_STATE, ARG_JOB_MODE, ARG_PRESET_MODE, + ARG_FIRMWARE_SETUP, }; static const struct option options[] = { @@ -6192,6 +6205,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "state", required_argument, NULL, ARG_STATE }, { "recursive", no_argument, NULL, 'r' }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, + { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, {} }; @@ -6432,6 +6446,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_plain = true; break; + case ARG_FIRMWARE_SETUP: + arg_firmware_setup = true; + break; + case ARG_STATE: { const char *word, *state; size_t size; -- 2.3.5 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel