diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c index f265a2af93..b27a12c55d 100644 --- a/src/shared/user-record-nss.c +++ b/src/shared/user-record-nss.c @@ -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) diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 16edaa45fa..2c0de383e0 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -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; }