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