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