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.
This commit is contained in:
+13
-6
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
|
||||
int get_keymaps(char ***l);
|
||||
bool keymap_is_valid(const char *name);
|
||||
int keymap_exists(const char *name);
|
||||
|
||||
Reference in New Issue
Block a user