2024-05-08 05:10:23

by Jan Tatje

[permalink] [raw]
Subject: [PATCH] nfsidmap: fallback if sysconf(_SC_GET(PW|GR)_R_SIZE_MAX) doesn't exist

On musl-libc systems _SC_GET(PW|GR)_R_SIZE_MAX does not exist.
If sysconf returns -1 start with a sane value and call
get(pw|gr)(nam|uid)_r repeatedly until it no longer returns ERANGE.

Signed-off-by: Jan Tatje <[email protected]>
---
support/nfsidmap/gums.c | 22 +++++++++++--
support/nfsidmap/libnfsidmap.c | 29 +++++++++++++++--
support/nfsidmap/nss.c | 59 +++++++++++++++++++++++++++-------
support/nfsidmap/regex.c | 54 +++++++++++++++++++++++++++----
support/nfsidmap/static.c | 22 +++++++++++--
5 files changed, 159 insertions(+), 27 deletions(-)

diff --git a/support/nfsidmap/gums.c b/support/nfsidmap/gums.c
index 1d6eb318..caffc679 100644
--- a/support/nfsidmap/gums.c
+++ b/support/nfsidmap/gums.c
@@ -475,13 +475,25 @@ static int translate_to_uid(char *local_uid, uid_t *uid, uid_t *gid)
int ret = -1;
struct passwd *pw = NULL;
struct pwbuf *buf = NULL;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
+
+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;

buf = malloc(sizeof(*buf) + buflen);
if (buf == NULL)
goto out;

- ret = getpwnam_r(local_uid, &buf->pwbuf, buf->buf, buflen, &pw);
+ while ((ret = getpwnam_r(local_uid, &buf->pwbuf, buf->buf, buflen, &pw)) == ERANGE) {
+ buflen = buflen * 2;
+ struct pwbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ buf = nbuf;
+ }
if (pw == NULL) {
IDMAP_LOG(0, ("getpwnam: name %s not found\n", local_uid));
goto out;
@@ -501,9 +513,13 @@ static int translate_to_gid(char *local_gid, uid_t *gid)
struct group *gr = NULL;
struct group grbuf;
char *buf = NULL;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
int ret = -1;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
do {
buf = malloc(buflen);
if (buf == NULL)
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
index f8c36480..14cafc3d 100644
--- a/support/nfsidmap/libnfsidmap.c
+++ b/support/nfsidmap/libnfsidmap.c
@@ -457,14 +457,26 @@ int nfs4_init_name_mapping(char *conffile)

nobody_user = conf_get_str("Mapping", "Nobody-User");
if (nobody_user) {
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
struct passwd *buf;
struct passwd *pw = NULL;
int err;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (buf) {
err = getpwnam_r(nobody_user, buf, ((char *)buf) + sizeof(*buf), buflen, &pw);
+ while ((err = getpwnam_r(nobody_user, buf, ((char *)buf) + sizeof(*buf), buflen, &pw)) == ERANGE) {
+ buflen = buflen * 2;
+ struct passwd* nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ break;
+ }
+ buf = nbuf;
+ }
if (err == 0 && pw != NULL)
nobody_uid = pw->pw_uid;
else
@@ -478,14 +490,25 @@ int nfs4_init_name_mapping(char *conffile)

nobody_group = conf_get_str("Mapping", "Nobody-Group");
if (nobody_group) {
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
struct group *buf;
struct group *gr = NULL;
int err;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (buf) {
- err = getgrnam_r(nobody_group, buf, ((char *)buf) + sizeof(*buf), buflen, &gr);
+ while ((err = getgrnam_r(nobody_group, buf, ((char *)buf) + sizeof(*buf), buflen, &gr)) == ERANGE) {
+ buflen = buflen * 2;
+ struct group *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ break;
+ }
+ buf = nbuf;
+ }
if (err == 0 && gr != NULL)
nobody_gid = gr->gr_gid;
else
diff --git a/support/nfsidmap/nss.c b/support/nfsidmap/nss.c
index 0f43076e..ec4e9fdd 100644
--- a/support/nfsidmap/nss.c
+++ b/support/nfsidmap/nss.c
@@ -91,15 +91,27 @@ static int nss_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
struct passwd *pw = NULL;
struct passwd pwbuf;
char *buf;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024; /*sysconf(_SC_GETPW_R_SIZE_MAX) == 1024 on glibc*/
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
int err = -ENOMEM;

+ if (scbuflen > 0) {
+ buflen = (size_t)scbuflen;
+ }
+
buf = malloc(buflen);
if (!buf)
goto out;
if (domain == NULL)
domain = get_default_domain();
- err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw);
+ while ((err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw)) == -ERANGE) {
+ buflen = buflen * 2;
+ char* nbuf = realloc(buf, buflen);
+ if (nbuf == NULL) {
+ goto out_buf;
+ }
+ buf = nbuf;
+ }
if (pw == NULL)
err = -ENOENT;
if (err)
@@ -119,9 +131,14 @@ static int nss_gid_to_name(gid_t gid, char *domain, char *name, size_t len)
struct group *gr = NULL;
struct group grbuf;
char *buf;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
int err;

+ if (scbuflen > 0) {
+ buflen = (size_t)scbuflen;
+ }
+
if (domain == NULL)
domain = get_default_domain();

@@ -192,12 +209,14 @@ static struct passwd *nss_getpwnam(const char *name, const char *domain,
{
struct passwd *pw;
struct pwbuf *buf;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char *localname;
int err = ENOMEM;

- if (buflen > UINT_MAX)
- goto err;
+ if (scbuflen > 0) {
+ buflen = (size_t)scbuflen;
+ }

buf = malloc(sizeof(*buf) + buflen);
if (buf == NULL)
@@ -215,14 +234,29 @@ static struct passwd *nss_getpwnam(const char *name, const char *domain,
goto err_free_buf;
}

- err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+ while ((err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw)) == ERANGE) {
+ buflen = buflen * 2;
+ struct pwbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ free(localname);
+ goto err_free_buf;
+ }
+ buf = nbuf;
+ }
if (pw == NULL && domain != NULL)
IDMAP_LOG(1,
("nss_getpwnam: name '%s' not found in domain '%s'",
localname, domain));
free(localname);
} else {
- err = getpwnam_r(name, &buf->pwbuf, buf->buf, buflen, &pw);
+ while ((err = getpwnam_r(name, &buf->pwbuf, buf->buf, buflen, &pw)) == ERANGE) {
+ buflen = buflen * 2;
+ struct pwbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ goto err_free_buf;
+ }
+ buf = nbuf;
+ }
if (pw == NULL)
IDMAP_LOG(1,
("nss_getpwnam: name '%s' not found (domain not stripped)", name));
@@ -301,11 +335,16 @@ static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip)
struct group *gr = NULL;
struct group grbuf;
char *buf, *domain;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
int err = -EINVAL;
char *localname = NULL;
char *ref_name = NULL;

+ if (scbuflen > 0) {
+ buflen = (size_t)scbuflen;
+ }
+
domain = get_default_domain();
if (dostrip) {
localname = strip_domain(name, domain);
@@ -327,8 +366,6 @@ static int _nss_name_to_gid(char *name, gid_t *gid, int dostrip)
}

err = -ENOMEM;
- if (buflen > UINT_MAX)
- goto out_name;

do {
buf = malloc(buflen);
diff --git a/support/nfsidmap/regex.c b/support/nfsidmap/regex.c
index 8424179f..fa316660 100644
--- a/support/nfsidmap/regex.c
+++ b/support/nfsidmap/regex.c
@@ -95,7 +95,8 @@ static struct passwd *regex_getpwnam(const char *name, const char *UNUSED(domain
{
struct passwd *pw;
struct pwbuf *buf;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
char *localname;
size_t namelen;
int err;
@@ -103,6 +104,9 @@ static struct passwd *regex_getpwnam(const char *name, const char *UNUSED(domain
int index;
regmatch_t matches[MAX_MATCHES];

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (!buf) {
err = ENOMEM;
@@ -139,7 +143,15 @@ static struct passwd *regex_getpwnam(const char *name, const char *UNUSED(domain
localname[namelen] = '\0';

again:
- err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw);
+ while ((err = getpwnam_r(localname, &buf->pwbuf, buf->buf, buflen, &pw)) == ERANGE) {
+ buflen = buflen * 2;
+ struct pwbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ err = ENOMEM;
+ goto err_free_name;
+ }
+ buf = nbuf;
+ }

if (err == EINTR)
goto again;
@@ -175,7 +187,8 @@ static struct group *regex_getgrnam(const char *name, const char *UNUSED(domain)
{
struct group *gr;
struct grbuf *buf;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
char *localgroup;
char *groupname;
size_t namelen;
@@ -184,6 +197,9 @@ static struct group *regex_getgrnam(const char *name, const char *UNUSED(domain)
int status;
regmatch_t matches[MAX_MATCHES];

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (!buf) {
err = ENOMEM;
@@ -242,7 +258,15 @@ static struct group *regex_getgrnam(const char *name, const char *UNUSED(domain)
IDMAP_LOG(4, ("regexp_getgrnam: will use '%s'", groupname));

again:
- err = getgrnam_r(groupname, &buf->grbuf, buf->buf, buflen, &gr);
+ while ((err = getgrnam_r(groupname, &buf->grbuf, buf->buf, buflen, &gr)) == ERANGE) {
+ buflen = buflen * 2;
+ struct grbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ err = ENOMEM;
+ goto err_free_name;
+ }
+ buf = nbuf;
+ }

if (err == EINTR)
goto again;
@@ -366,15 +390,27 @@ static int regex_uid_to_name(uid_t uid, char *domain, char *name, size_t len)
struct passwd *pw = NULL;
struct passwd pwbuf;
char *buf;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
int err = -ENOMEM;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(buflen);
if (!buf)
goto out;
if (domain == NULL)
domain = get_default_domain();
- err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw);
+ while ((err = -getpwuid_r(uid, &pwbuf, buf, buflen, &pw)) == -ERANGE) {
+ buflen = buflen * 2;
+ char *nbuf = realloc(buf, buflen);
+ if (nbuf == NULL) {
+ err = -ENOMEM;
+ goto out_buf;
+ }
+ buf = nbuf;
+ }
if (pw == NULL)
err = -ENOENT;
if (err)
@@ -392,10 +428,14 @@ static int regex_gid_to_name(gid_t gid, char *UNUSED(domain), char *name, size_t
struct group grbuf;
char *buf;
const char *name_prefix;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
int err;
char * groupname = NULL;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
do {
err = -ENOMEM;
buf = malloc(buflen);
diff --git a/support/nfsidmap/static.c b/support/nfsidmap/static.c
index 8ac4a398..0bb1728d 100644
--- a/support/nfsidmap/static.c
+++ b/support/nfsidmap/static.c
@@ -98,10 +98,14 @@ static struct passwd *static_getpwnam(const char *name,
{
struct passwd *pw;
struct pwbuf *buf;
- size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = 1024;
char *localname;
int err;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (!buf) {
err = ENOMEM;
@@ -149,10 +153,14 @@ static struct group *static_getgrnam(const char *name,
{
struct group *gr;
struct grbuf *buf;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ long scbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = 1024;
char *localgroup;
int err;

+ if (scbuflen > 0)
+ buflen = (size_t)scbuflen;
+
buf = malloc(sizeof(*buf) + buflen);
if (!buf) {
err = ENOMEM;
@@ -166,7 +174,15 @@ static struct group *static_getgrnam(const char *name,
}

again:
- err = getgrnam_r(localgroup, &buf->grbuf, buf->buf, buflen, &gr);
+ while ((err = getgrnam_r(localgroup, &buf->grbuf, buf->buf, buflen, &gr)) == ERANGE) {
+ buflen = buflen * 2;
+ struct grbuf *nbuf = realloc(buf, sizeof(*buf) + buflen);
+ if (nbuf == NULL) {
+ err = ENOMEM;
+ goto err_free_buf;
+ }
+ buf = nbuf;
+ }

if (err == EINTR)
goto again;
--
2.45.0