libselinux: support huge passwd/group entries

getpwnam_r(3) and getgrnam_r(3) might return ERANGE in case the supplied
buffer was too short for the passwd/group entry.  Retry with a bigger
buffer.

Also use a fallback buffer size in case the libc returns -1 for
sysconf(3) of _SC_GETPW_R_SIZE_MAX or _SC_GETGR_R_SIZE_MAX, like musl.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
This commit is contained in:
Christian Göttsche 2023-12-19 17:09:30 +01:00 committed by James Carter
parent 846550d78d
commit ebf4168577
1 changed files with 25 additions and 8 deletions

View File

@ -6,6 +6,8 @@
#include <stdio_ext.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <selinux/selinux.h>
#include <selinux/context.h>
@ -99,15 +101,30 @@ static gid_t get_default_gid(const char *name) {
struct passwd pwstorage, *pwent = NULL;
gid_t gid = -1;
/* Allocate space for the getpwnam_r buffer */
char *rbuf = NULL;
long rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (rbuflen <= 0) return -1;
char *rbuf = malloc(rbuflen);
if (rbuf == NULL) return -1;
if (rbuflen <= 0)
rbuflen = 1024;
int retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
if (retval == 0 && pwent) {
gid = pwent->pw_gid;
for (;;) {
int rc;
rbuf = malloc(rbuflen);
if (rbuf == NULL)
break;
rc = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
if (rc == ERANGE && rbuflen < LONG_MAX / 2) {
free(rbuf);
rbuflen *= 2;
continue;
}
if (rc == 0 && pwent)
gid = pwent->pw_gid;
break;
}
free(rbuf);
return gid;
}
@ -120,7 +137,7 @@ static int check_group(const char *group, const char *name, const gid_t gid) {
long rbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
if (rbuflen <= 0)
return 0;
rbuflen = 1024;
char *rbuf;
while(1) {
@ -129,7 +146,7 @@ static int check_group(const char *group, const char *name, const gid_t gid) {
return 0;
int retval = getgrnam_r(group, &gbuf, rbuf,
rbuflen, &grent);
if ( retval == ERANGE )
if (retval == ERANGE && rbuflen < LONG_MAX / 2)
{
free(rbuf);
rbuflen = rbuflen * 2;