From 5c0b7380121e7d2f3556839d48626c903869b378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 30 Aug 2020 20:34:12 +0200 Subject: [PATCH] user-record-nss: check if strings from pwd/spwd/grp/sgrp are valid utf-8 strv_extend_strv_utf8_only() uses a temporary buffer to make the implementation conscise. Otherwise we'd have to rewrite all of strv_extend_strv() which didn't seem worth the trouble for this one use outside of a hot path. If the data is not serializable, we just pretend it doesn't exists. This fixes #16683 and https://bugs.gentoo.org/735072 in a second way. --- src/shared/user-record-nss.c | 48 +++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c index 0da77dfc39..d06c8abdbe 100644 --- a/src/shared/user-record-nss.c +++ b/src/shared/user-record-nss.c @@ -6,10 +6,35 @@ #include "strv.h" #include "user-record-nss.h" #include "user-util.h" +#include "utf8.h" #define SET_IF(field, condition, value, fallback) \ field = (condition) ? (value) : (fallback) +static inline const char* utf8_only(const char *s) { + return s && utf8_is_valid(s) ? s : NULL; +} + +static inline int strv_extend_strv_utf8_only(char ***dst, char **src, bool filter_duplicates) { + _cleanup_free_ char **t = NULL; + size_t l, j = 0; + + /* First, do a shallow copy of s, filtering for only valid utf-8 strings */ + l = strv_length(src); + t = new(char*, l + 1); + if (!t) + return -ENOMEM; + + for (size_t i = 0; i < l; i++) + if (utf8_is_valid(src[i])) + t[j++] = src[i]; + if (j == 0) + return 0; + + t[j] = NULL; + return strv_extend_strv(dst, t, filter_duplicates); +} + int nss_passwd_to_user_record( const struct passwd *pwd, const struct spwd *spwd, @@ -55,18 +80,19 @@ int nss_passwd_to_user_record( free_and_replace(hr->real_name, mangled); } - r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir)); + r = free_and_strdup(&hr->home_directory, utf8_only(empty_to_null(pwd->pw_dir))); if (r < 0) return r; - r = free_and_strdup(&hr->shell, empty_to_null(pwd->pw_shell)); + r = free_and_strdup(&hr->shell, utf8_only(empty_to_null(pwd->pw_shell))); if (r < 0) return r; hr->uid = pwd->pw_uid; hr->gid = pwd->pw_gid; - if (spwd && looks_like_hashed_password(spwd->sp_pwdp)) { + if (spwd && + looks_like_hashed_password(utf8_only(spwd->sp_pwdp))) { /* Ignore locked, disabled, and mojibake passwords */ strv_free_erase(hr->hashed_password); hr->hashed_password = strv_new(spwd->sp_pwdp); if (!hr->hashed_password) @@ -316,26 +342,26 @@ int nss_group_to_group_record( if (!g->group_name) return -ENOMEM; - g->members = strv_copy(grp->gr_mem); - if (!g->members) - return -ENOMEM; + r = strv_extend_strv_utf8_only(&g->members, grp->gr_mem, false); + if (r < 0) + return r; g->gid = grp->gr_gid; if (sgrp) { - if (looks_like_hashed_password(sgrp->sg_passwd)) { + if (looks_like_hashed_password(utf8_only(sgrp->sg_passwd))) { g->hashed_password = strv_new(sgrp->sg_passwd); if (!g->hashed_password) return -ENOMEM; } - r = strv_extend_strv(&g->members, sgrp->sg_mem, 1); + r = strv_extend_strv_utf8_only(&g->members, sgrp->sg_mem, true); if (r < 0) return r; - g->administrators = strv_copy(sgrp->sg_adm); - if (!g->administrators) - return -ENOMEM; + r = strv_extend_strv_utf8_only(&g->administrators, sgrp->sg_adm, false); + if (r < 0) + return r; } r = json_build(&g->json, JSON_BUILD_OBJECT(