]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
user-record: deal with invalid GECOS fields gracefully 16682/head
authorLennart Poettering <lennart@poettering.net>
Thu, 6 Aug 2020 15:00:07 +0000 (17:00 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 7 Aug 2020 15:36:27 +0000 (17:36 +0200)
Let's fix up invalid GECOS fields both when we convert from NSS to JSON
and the other way round.

Kinda sucks we have to do that, but NSS does it when writing data to
/etc/passwd, so let's do the same.

Fixes: #16668
src/shared/user-record-nss.c
src/shared/user-record.c

index f265a2af9333e24457cd634b77cbb77ab0840090..b27a12c55d06bd15e2ad732c009eb066cb03e179 100644 (file)
@@ -5,6 +5,7 @@
 #include "libcrypt-util.h"
 #include "strv.h"
 #include "user-record-nss.h"
+#include "user-util.h"
 
 #define SET_IF(field, condition, value, fallback)  \
         field = (condition) ? (value) : (fallback)
@@ -34,10 +35,25 @@ int nss_passwd_to_user_record(
         if (r < 0)
                 return r;
 
-        r = free_and_strdup(&hr->real_name,
-                            streq_ptr(pwd->pw_gecos, hr->user_name) ? NULL : empty_to_null(pwd->pw_gecos));
-        if (r < 0)
-                return r;
+        /* Some bad NSS modules synthesize GECOS fields with embedded ":" or "\n" characters, which are not
+         * something we can output in /etc/passwd compatible format, since these are record separators
+         * there. We normally refuse that, but we need to maintain compatibility with arbitrary NSS modules,
+         * hence let's do what glibc does: mangle the data to fit the format. */
+        if (isempty(pwd->pw_gecos) || streq_ptr(pwd->pw_gecos, hr->user_name))
+                hr->real_name = mfree(hr->real_name);
+        else if (valid_gecos(pwd->pw_gecos)) {
+                r = free_and_strdup(&hr->real_name, pwd->pw_gecos);
+                if (r < 0)
+                        return r;
+        } else {
+                _cleanup_free_ char *mangled = NULL;
+
+                mangled = mangle_gecos(pwd->pw_gecos);
+                if (!mangled)
+                        return -ENOMEM;
+
+                free_and_replace(hr->real_name, mangled);
+        }
 
         r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir));
         if (r < 0)
index 16edaa45face3bb66a17f196efa47d54ba3873c3..2c0de383e042316623a97cb6fecd9c58e7e2c45b 100644 (file)
@@ -206,7 +206,6 @@ int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlag
 static int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
         char **s = userdata;
         const char *n;
-        int r;
 
         if (json_variant_is_null(variant)) {
                 *s = mfree(*s);
@@ -217,12 +216,20 @@ static int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispa
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
         n = json_variant_string(variant);
-        if (!valid_gecos(n))
-                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible real name.", strna(name));
+        if (valid_gecos(n)) {
+                if (free_and_strdup(s, n) < 0)
+                        return json_log_oom(variant, flags);
+        } else {
+                _cleanup_free_ char *m = NULL;
 
-        r = free_and_strdup(s, n);
-        if (r < 0)
-                return json_log(variant, flags, r, "Failed to allocate string: %m");
+                json_log(variant, flags|JSON_DEBUG, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible string, mangling.", strna(name));
+
+                m = mangle_gecos(n);
+                if (!m)
+                        return json_log_oom(variant, flags);
+
+                free_and_replace(*s, m);
+        }
 
         return 0;
 }