From e68b446988c27a0d096b976c982d0afce3281bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Esser?= Date: Fri, 20 Dec 2024 10:11:27 +0100 Subject: [PATCH] libnss_tcb: Disallow potentially-malicious user names in getspnam(3). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Esser --- ChangeLog | 6 ++++++ libs/nss.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 4a32be9..eb28b54 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-12-20 Björn Esser + + libnss_tcb: Disallow potentially-malicious user names in getspnam(3). + * libs/nss.c (_nss_tcb_getspnam_r): Check for potentially-malicious + user names, and bail out in case. + 2024-12-18 Björn Esser libnss_tcb: Initialize or rewind dirstream from inside setspent(3). diff --git a/libs/nss.c b/libs/nss.c index 549957a..e2a709f 100644 --- a/libs/nss.c +++ b/libs/nss.c @@ -36,6 +36,30 @@ int _nss_tcb_endspent(void) return 1; } +/* IEEE Std 1003.1-2001 allows only the following characters to appear + in group- and usernames: letters, digits, underscores, periods, + -signs (@), and dashes. The name may not start with a dash or @. + The "$" sign is allowed at the end of usernames to allow typical + Samba machine accounts. */ +static int +is_valid_username (const char *un) +{ + if (!un || !*un || un[0] == '-' || un[0] == '@' || + /* curdir || parentdir */ + !strcmp(un, ".") || !strcmp(un, "..")) + return 0; + + do { + char c = *un++; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '-' || c == '.' || + c == '@' || c == '_' || (!*un && c == '$'))) + return 0; + } while (*un); + + return 1; +} + static FILE *tcb_safe_open(const char *file, const char *name) { gid_t grplist[TCB_NGROUPS]; @@ -73,12 +97,18 @@ int _nss_tcb_getspnam_r(const char *name, struct spwd *__result_buf, char *file; int retval, saved_errno; + /* Disallow potentially-malicious user names */ + if (!is_valid_username(name)) { + errno = ENOENT; + return NSS_STATUS_NOTFOUND; + } + if (asprintf(&file, TCB_FMT, name) < 0) return NSS_STATUS_TRYAGAIN; f = tcb_safe_open(file, name); free(file); if (!f) - return NSS_STATUS_UNAVAIL; + return errno == ENOENT ? NSS_STATUS_NOTFOUND : NSS_STATUS_UNAVAIL; retval = fgetspent_r(f, __result_buf, __buffer, __buflen, __result); saved_errno = errno;