diff mbox series

nfsidmap: fallback if sysconf(_SC_GET(PW|GR)_R_SIZE_MAX) doesn't exist

Message ID 20240508050908.293390-1-jan@jnt.io (mailing list archive)
State New, archived
Headers show
Series nfsidmap: fallback if sysconf(_SC_GET(PW|GR)_R_SIZE_MAX) doesn't exist | expand

Commit Message

Jan Tatje May 8, 2024, 5:09 a.m. UTC
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 <jan@jnt.io>
---
 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 mbox series

Patch

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;