87d178
From 5813180a75aa1ef90f6d3459fc5beb099b815cfb Mon Sep 17 00:00:00 2001
87d178
From: Lennart Poettering <lennart@poettering.net>
87d178
Date: Thu, 30 Apr 2020 18:32:44 +0200
87d178
Subject: [PATCH] tree-wide: port various bits over to locale_is_installed()
87d178
87d178
(cherry picked from commit a00a78b84e2ab352b3144bfae8bc578d172303be)
87d178
87d178
Resolves: #1755287
87d178
---
87d178
 src/firstboot/firstboot.c | 30 ++++++++------
87d178
 src/locale/localed.c      | 87 ++++++++++++++++++++++++---------------
87d178
 2 files changed, 71 insertions(+), 46 deletions(-)
87d178
87d178
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
87d178
index a98e53b3a3..7e177a50fa 100644
87d178
--- a/src/firstboot/firstboot.c
87d178
+++ b/src/firstboot/firstboot.c
87d178
@@ -192,6 +192,14 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *
87d178
         }
87d178
 }
87d178
 
87d178
+static bool locale_is_ok(const char *name) {
87d178
+
87d178
+        if (arg_root)
87d178
+                return locale_is_valid(name);
87d178
+
87d178
+        return locale_is_installed(name) > 0;
87d178
+}
87d178
+
87d178
 static int prompt_locale(void) {
87d178
         _cleanup_strv_free_ char **locales = NULL;
87d178
         int r;
87d178
@@ -215,14 +223,14 @@ static int prompt_locale(void) {
87d178
 
87d178
         putchar('\n');
87d178
 
87d178
-        r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
87d178
+        r = prompt_loop("Please enter system locale name or number", locales, locale_is_ok, &arg_locale);
87d178
         if (r < 0)
87d178
                 return r;
87d178
 
87d178
         if (isempty(arg_locale))
87d178
                 return 0;
87d178
 
87d178
-        r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
87d178
+        r = prompt_loop("Please enter system message locale name or number", locales, locale_is_ok, &arg_locale_messages);
87d178
         if (r < 0)
87d178
                 return r;
87d178
 
87d178
@@ -780,11 +788,6 @@ static int parse_argv(int argc, char *argv[]) {
87d178
                         break;
87d178
 
87d178
                 case ARG_LOCALE:
87d178
-                        if (!locale_is_valid(optarg)) {
87d178
-                                log_error("Locale %s is not valid.", optarg);
87d178
-                                return -EINVAL;
87d178
-                        }
87d178
-
87d178
                         r = free_and_strdup(&arg_locale, optarg);
87d178
                         if (r < 0)
87d178
                                 return log_oom();
87d178
@@ -792,11 +795,6 @@ static int parse_argv(int argc, char *argv[]) {
87d178
                         break;
87d178
 
87d178
                 case ARG_LOCALE_MESSAGES:
87d178
-                        if (!locale_is_valid(optarg)) {
87d178
-                                log_error("Locale %s is not valid.", optarg);
87d178
-                                return -EINVAL;
87d178
-                        }
87d178
-
87d178
                         r = free_and_strdup(&arg_locale_messages, optarg);
87d178
                         if (r < 0)
87d178
                                 return log_oom();
87d178
@@ -922,6 +920,14 @@ static int parse_argv(int argc, char *argv[]) {
87d178
                         assert_not_reached("Unhandled option");
87d178
                 }
87d178
 
87d178
+        /* We check if the specified locale strings are valid down here, so that we can take --root= into
87d178
+         * account when looking for the locale files. */
87d178
+
87d178
+        if (arg_locale && !locale_is_ok(arg_locale))
87d178
+                return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale);
87d178
+        if (arg_locale_messages && !locale_is_ok(arg_locale_messages))
87d178
+                return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale_messages);
87d178
+
87d178
         return 1;
87d178
 }
87d178
 
87d178
diff --git a/src/locale/localed.c b/src/locale/localed.c
87d178
index 253973fd49..d6ed40babe 100644
87d178
--- a/src/locale/localed.c
87d178
+++ b/src/locale/localed.c
87d178
@@ -259,18 +259,57 @@ static void locale_free(char ***l) {
87d178
                 (*l)[p] = mfree((*l)[p]);
87d178
 }
87d178
 
87d178
+static int process_locale_list_item(
87d178
+                const char *assignment,
87d178
+                char *new_locale[static _VARIABLE_LC_MAX],
87d178
+                sd_bus_error *error) {
87d178
+
87d178
+        assert(assignment);
87d178
+        assert(new_locale);
87d178
+
87d178
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
87d178
+                const char *name, *e;
87d178
+
87d178
+                assert_se(name = locale_variable_to_string(p));
87d178
+
87d178
+                e = startswith(assignment, name);
87d178
+                if (!e)
87d178
+                        continue;
87d178
+
87d178
+                if (*e != '=')
87d178
+                        continue;
87d178
+
87d178
+                e++;
87d178
+
87d178
+                if (!locale_is_valid(e))
87d178
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e);
87d178
+                if (locale_is_installed(e) <= 0)
87d178
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e);
87d178
+                if (new_locale[p])
87d178
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name);
87d178
+
87d178
+                new_locale[p] = strdup(e);
87d178
+                if (!new_locale[p])
87d178
+                        return -ENOMEM;
87d178
+
87d178
+                return 0;
87d178
+        }
87d178
+
87d178
+        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
87d178
+}
87d178
+
87d178
 static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
87d178
         Context *c = userdata;
87d178
         _cleanup_strv_free_ char **settings = NULL, **l = NULL;
87d178
         char *new_locale[_VARIABLE_LC_MAX] = {}, **i;
87d178
         _cleanup_(locale_free) _unused_ char **dummy = new_locale;
87d178
         bool modified = false;
87d178
-        int interactive, p, r;
87d178
+        int interactive, r;
87d178
 
87d178
         assert(m);
87d178
         assert(c);
87d178
 
87d178
-        r = bus_message_read_strv_extend(m, &l);
87d178
+        r = sd_bus_message_read_strv(m, &l);
87d178
         if (r < 0)
87d178
                 return r;
87d178
 
87d178
@@ -279,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
87d178
                 return r;
87d178
 
87d178
         /* If single locale without variable name is provided, then we assume it is LANG=. */
87d178
-        if (strv_length(l) == 1 && !strchr(*l, '=')) {
87d178
-                if (!locale_is_valid(*l))
87d178
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
87d178
+        if (strv_length(l) == 1 && !strchr(l[0], '=')) {
87d178
+                if (!locale_is_valid(l[0]))
87d178
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]);
87d178
+                if (locale_is_installed(l[0]) <= 0)
87d178
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
87d178
 
87d178
-                new_locale[VARIABLE_LANG] = strdup(*l);
87d178
+                new_locale[VARIABLE_LANG] = strdup(l[0]);
87d178
                 if (!new_locale[VARIABLE_LANG])
87d178
                         return -ENOMEM;
87d178
 
87d178
@@ -292,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
87d178
 
87d178
         /* Check whether a variable is valid */
87d178
         STRV_FOREACH(i, l) {
87d178
-                bool valid = false;
87d178
-
87d178
-                for (p = 0; p < _VARIABLE_LC_MAX; p++) {
87d178
-                        size_t k;
87d178
-                        const char *name;
87d178
-
87d178
-                        name = locale_variable_to_string(p);
87d178
-                        assert(name);
87d178
-
87d178
-                        k = strlen(name);
87d178
-                        if (startswith(*i, name) &&
87d178
-                            (*i)[k] == '=' &&
87d178
-                            locale_is_valid((*i) + k + 1)) {
87d178
-                                valid = true;
87d178
-
87d178
-                                new_locale[p] = strdup((*i) + k + 1);
87d178
-                                if (!new_locale[p])
87d178
-                                        return -ENOMEM;
87d178
-
87d178
-                                break;
87d178
-                        }
87d178
-                }
87d178
-
87d178
-                if (!valid)
87d178
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
87d178
+                r = process_locale_list_item(*i, new_locale, error);
87d178
+                if (r < 0)
87d178
+                        return r;
87d178
         }
87d178
 
87d178
         /* If LANG was specified, but not LANGUAGE, check if we should
87d178
@@ -339,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
87d178
         }
87d178
 
87d178
         /* Merge with the current settings */
87d178
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
                 if (!isempty(c->locale[p]) && isempty(new_locale[p])) {
87d178
                         new_locale[p] = strdup(c->locale[p]);
87d178
                         if (!new_locale[p])
87d178
@@ -348,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
87d178
 
87d178
         locale_simplify(new_locale);
87d178
 
87d178
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
                 if (!streq_ptr(c->locale[p], new_locale[p])) {
87d178
                         modified = true;
87d178
                         break;
87d178
@@ -373,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
87d178
         if (r == 0)
87d178
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
87d178
 
87d178
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
87d178
                 free_and_replace(c->locale[p], new_locale[p]);
87d178
 
87d178
         r = locale_write_data(c, &settings);