user-record: deal with invalid GECOS fields gracefully

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
This commit is contained in:
Lennart Poettering 2020-08-06 17:00:07 +02:00
parent b10fd796f5
commit 5cd12abaa0
2 changed files with 33 additions and 10 deletions

View 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)

View 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;
}