From: Zbigniew Jędrzejewski-Szmek Date: Thu, 4 Mar 2021 10:27:05 +0000 (+0100) Subject: localed: refuse to set a keymap which is not installed X-Git-Tag: v248-rc3~50^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9ef70c06c663a05f7646d690e00e4b4a73af8c61;p=thirdparty%2Fsystemd.git localed: refuse to set a keymap which is not installed In https://bugzilla.redhat.com/show_bug.cgi?id=1933873 a keymap was set without the package that provides it being installed (it2 is in kbd-legacy, which is not installed by default). Setting a non-installed keymap is problematic, because it results in nasty failures afterward (*). So let's to the same as e.g. for locale data, and refuse a setting if the definition doesn't exists in the filesystem. The implementation using nftw() is not the most efficient, but I think it's OK in this case. This is definitely not in any kind of hot path, and I prefer not to duplicate the filename manipulation logic in a second function. (*) If the keymap is not found, vconsole-setup.service will fail. dracut-cmdline-ask.service has Requires=vconsole-setup.service, so it will also fail, and this breaks boot. dracut-cmdline-ask.service having a hard dependency is appropriate though: we sadly don't display what the keymap is, and with a wrong keymap, any attempts to enter a password are likely to fail. --- diff --git a/src/locale/localed.c b/src/locale/localed.c index 9c97313edca..f44c0bab2a9 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -19,6 +19,7 @@ #include "bus-polkit.h" #include "def.h" #include "dlfcn-util.h" +#include "kbd-util.h" #include "keymap-util.h" #include "locale-util.h" #include "macro.h" @@ -474,7 +475,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; - const char *keymap, *keymap_toggle; + const char *name, *keymap, *keymap_toggle; int convert, interactive, r; assert(m); @@ -490,17 +491,23 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro r = vconsole_read_data(c, m); if (r < 0) { log_error_errno(r, "Failed to read virtual console keymap data: %m"); - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read virtual console keymap data"); + return sd_bus_error_set_errnof(error, r, "Failed to read virtual console keymap data: %m"); + } + + FOREACH_STRING(name, keymap ?: keymap_toggle, keymap ? keymap_toggle : NULL) { + r = keymap_exists(name); /* This also verifies that the keymap name is kosher. */ + if (r < 0) { + log_error_errno(r, "Failed to check keymap %s: %m", name); + return sd_bus_error_set_errnof(error, r, "Failed to check keymap %s: %m", name); + } + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Keymap %s is not installed.", name); } if (streq_ptr(keymap, c->vc_keymap) && streq_ptr(keymap_toggle, c->vc_keymap_toggle)) return sd_bus_reply_method_return(m, NULL); - if ((keymap && (!filename_is_valid(keymap) || !string_is_safe(keymap))) || - (keymap_toggle && (!filename_is_valid(keymap_toggle) || !string_is_safe(keymap_toggle)))) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keymap data"); - r = bus_verify_polkit_async( m, CAP_SYS_ADMIN, diff --git a/src/shared/kbd-util.c b/src/shared/kbd-util.c index f5669f7c03d..430ae7c8815 100644 --- a/src/shared/kbd-util.c +++ b/src/shared/kbd-util.c @@ -12,6 +12,7 @@ #include "strv.h" #include "utf8.h" +static thread_local const char *keymap_name = NULL; static thread_local Set *keymaps = NULL; static int nftw_cb( @@ -24,6 +25,9 @@ static int nftw_cb( char *e; int r; + /* If keymap_name is non-null, return true if keymap keymap_name is found. + * Otherwise, add all keymaps to keymaps. */ + if (tflag != FTW_F) return 0; @@ -43,6 +47,9 @@ static int nftw_cb( if (e) *e = 0; + if (keymap_name) + return streq(p, keymap_name); + if (!keymap_is_valid(p)) return 0; @@ -108,3 +115,25 @@ bool keymap_is_valid(const char *name) { return true; } + +int keymap_exists(const char *name) { + int r = 0; + + if (!keymap_is_valid(name)) + return -EINVAL; + + keymap_name = name; + + const char *dir; + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = nftw(dir, nftw_cb, 20, FTW_PHYS); + if (r > 0) + break; + if (r < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to read keymap list from %s, ignoring: %m", dir); + } + + keymap_name = NULL; + + return r > 0; +} diff --git a/src/shared/kbd-util.h b/src/shared/kbd-util.h index 6714aeb9e04..a2fc2e6a3ee 100644 --- a/src/shared/kbd-util.h +++ b/src/shared/kbd-util.h @@ -18,3 +18,4 @@ int get_keymaps(char ***l); bool keymap_is_valid(const char *name); +int keymap_exists(const char *name);