From: Tobias Stoeckmann Date: Sat, 27 Dec 2025 14:27:05 +0000 (+0100) Subject: nss-systemd: set sg_adm/sg_mem for all groups X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57682793dac269994d6e69f7a5a937f5ad459cc8;p=thirdparty%2Fsystemd.git nss-systemd: set sg_adm/sg_mem for all groups Fill sg_adm and sg_mem in nss_pack_group_record_shadow to stay compatible with other NSS getsgnam implementations which set these members to NULL terminated string arrays. Tools like shadow's sg would trigger a NULL pointer dereference with groups only found through nss-systemd otherwise. --- diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 70f0ecfbd5f..1d5e311ce86 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -411,6 +411,40 @@ enum nss_status userdb_getgrgid( return NSS_STATUS_SUCCESS; } +/* Counts string pointers (including terminating NULL element) of given + * string vector strv and stores amount of pointers in n and total + * length of all contained strings including NUL bytes in len. */ +static void nss_count_strv(char * const *strv, size_t *n, size_t *len) { + STRV_FOREACH(str, strv) { + (*len) += sizeof(char*); /* space for array entry */ + (*len) += strlen(*str) + 1; + (*n)++; + } + (*len) += sizeof(char*); /* trailing NULL in array entry */ + (*n)++; +} + +/* Performs deep copy of given string vector src and stores content + * of contained strings into buf with references to these strings + * in dst. At dst location, a new NULL-terminated string vector is + * created. The dst and buf locations are updated to point just behind + * the last pointer or char respectively. Returns total amount of + * pointers in newly created string vector in dst, including the + * terminating NULL element. */ +static size_t nss_deep_copy_strv(char * const *src, char ***dst, char **buf) { + char *p = *buf; + size_t i = 0; + + STRV_FOREACH(str, src) { + (*dst)[i++] = p; + p = stpcpy(p, *str) + 1; + } + (*dst)[i++] = NULL; + *dst += i; + *buf = p; + return i; +} + int nss_pack_group_record_shadow( GroupRecord *hr, struct sgrp *sgrp, @@ -418,7 +452,8 @@ int nss_pack_group_record_shadow( size_t buflen) { const char *hashed; - size_t required; + char **array = NULL, *p; + size_t i = 0, n = 0, required; assert(hr); assert(sgrp); @@ -429,15 +464,26 @@ int nss_pack_group_record_shadow( assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]); required += strlen(hashed) + 1; + nss_count_strv(hr->administrators, &n, &required); + nss_count_strv(hr->members, &n, &required); + if (buflen < required) return -ERANGE; - *sgrp = (struct sgrp) { - .sg_namp = buffer, - }; - assert(buffer); + p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */ + array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */ + + sgrp->sg_mem = array; + i += nss_deep_copy_strv(hr->members, &array, &p); + + sgrp->sg_adm = array; + i += nss_deep_copy_strv(hr->administrators, &array, &p); + + assert_se(i == n); + + sgrp->sg_namp = p; sgrp->sg_passwd = stpcpy(sgrp->sg_namp, hr->group_name) + 1; strcpy(sgrp->sg_passwd, hashed);