chiark / gitweb /
localed: validate xkb keymaps
[elogind.git] / src / locale / localed.c
index 552ffdf87a60c479ad2e8ef4c02f199c58da0550..4e56382f4f2365d6bd4acd2cd7c72cec7981808b 100644 (file)
 #include "event-util.h"
 #include "locale-util.h"
 
+#ifdef HAVE_XKBCOMMON
+#include <xkbcommon/xkbcommon.h>
+#endif
+
 enum {
         /* We don't list LC_ALL here on purpose. People should be
          * using LANG instead. */
@@ -224,7 +228,7 @@ static int x11_read_data(Context *c) {
                 if (in_section && first_word(l, "Option")) {
                         _cleanup_strv_free_ char **a = NULL;
 
-                        r = strv_split_quoted(&a, l);
+                        r = strv_split_quoted(&a, l, false);
                         if (r < 0)
                                 return r;
 
@@ -247,7 +251,7 @@ static int x11_read_data(Context *c) {
                 } else if (!in_section && first_word(l, "Section")) {
                         _cleanup_strv_free_ char **a = NULL;
 
-                        r = strv_split_quoted(&a, l);
+                        r = strv_split_quoted(&a, l, false);
                         if (r < 0)
                                 return -ENOMEM;
 
@@ -533,7 +537,7 @@ static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
                 if (l[0] == 0 || l[0] == '#')
                         continue;
 
-                r = strv_split_quoted(&b, l);
+                r = strv_split_quoted(&b, l, false);
                 if (r < 0)
                         return r;
 
@@ -1005,6 +1009,51 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata
         return sd_bus_reply_method_return(m, NULL);
 }
 
+#ifdef HAVE_XKBCOMMON
+static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
+        /* suppress xkb messages for now */
+}
+
+static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
+        const struct xkb_rule_names rmlvo = {
+                .model          = model,
+                .layout         = layout,
+                .variant        = variant,
+                .options        = options,
+        };
+        struct xkb_context *ctx = NULL;
+        struct xkb_keymap *km = NULL;
+        int r;
+
+        /* compile keymap from RMLVO information to check out its validity */
+
+        ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
+        if (!ctx) {
+                r = -ENOMEM;
+                goto exit;
+        }
+
+        xkb_context_set_log_fn(ctx, log_xkb);
+
+        km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
+        if (!km) {
+                r = -EINVAL;
+                goto exit;
+        }
+
+        r = 0;
+
+exit:
+        xkb_keymap_unref(km);
+        xkb_context_unref(ctx);
+        return r;
+}
+#else
+static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
+        return 0;
+}
+#endif
+
 static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = userdata;
         const char *layout, *model, *variant, *options;
@@ -1038,6 +1087,11 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat
                     (options && !string_is_safe(options)))
                         return sd_bus_error_set_errnof(error, -EINVAL, "Received invalid keyboard data");
 
+                r = verify_xkb_rmlvo(model, layout, variant, options);
+                if (r < 0)
+                        log_warning("Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %s",
+                                    strempty(model), strempty(layout), strempty(variant), strempty(options), strerror(-r));
+
                 r = bus_verify_polkit_async(m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", interactive, &c->polkit_registry, error);
                 if (r < 0)
                         return r;