]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
localed: refuse to set a keymap which is not installed
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 4 Mar 2021 10:27:05 +0000 (11:27 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 4 Mar 2021 10:44:20 +0000 (11:44 +0100)
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.

src/locale/localed.c
src/shared/kbd-util.c
src/shared/kbd-util.h

index 9c97313edcad6457fd3f44a5655a6eb8726e2fda..f44c0bab2a9e46e016ad1efc3fed1a8a963ae038 100644 (file)
@@ -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,
index f5669f7c03dd889c97eaeb394f2a860161f57210..430ae7c8815fceb5012fa5cc3cf95d76cbb73c29 100644 (file)
@@ -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;
+}
index 6714aeb9e047ce630ce8a9c0f38c4f9eeb0853ae..a2fc2e6a3ee2c8cfdb702da51233381b771ad619 100644 (file)
@@ -18,3 +18,4 @@
 
 int get_keymaps(char ***l);
 bool keymap_is_valid(const char *name);
+int keymap_exists(const char *name);