diff mbox

[3/3] genhomedircon: avoid use of non-standard `getpwent_r`

Message ID e9a72b068ca86d433ee52a877dad98bc410c39a0.1497967444.git.ps@pks.im (mailing list archive)
State Not Applicable
Headers show

Commit Message

Patrick Steinhardt June 20, 2017, 2:07 p.m. UTC
The `getpwent_r` function is a non-standard but re-entrant version of
the sdardardized `getpwent` function. Next to the benefit of being
re-entrant, though, it avoids compilation with libc implementations that
do not provide this glibc-specific extension, for example musl libc.

As the code is usually not run in a threaded environment, it is save to
use the non-reentrant version, though. As such, convert code to use
`getpwent` instead to fix building with other libc implementations.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 libsemanage/src/genhomedircon.c | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

Comments

Stephen Smalley June 20, 2017, 3:42 p.m. UTC | #1
On Tue, 2017-06-20 at 16:07 +0200, Patrick Steinhardt wrote:
> The `getpwent_r` function is a non-standard but re-entrant version of
> the sdardardized `getpwent` function. Next to the benefit of being
> re-entrant, though, it avoids compilation with libc implementations
> that
> do not provide this glibc-specific extension, for example musl libc.
> 
> As the code is usually not run in a threaded environment, it is save
> to
> use the non-reentrant version, though. As such, convert code to use
> `getpwent` instead to fix building with other libc implementations.

Not sure we are guaranteed that, since libsemanage has external users
too, e.g. sssd among others.  OTOH, getpwent_r man page says that it is
not really reentrant either due to shared reading position in the
stream, so maybe this doesn't matter.

> 
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  libsemanage/src/genhomedircon.c | 22 +++++-----------------
>  1 file changed, 5 insertions(+), 17 deletions(-)
> 
> diff --git a/libsemanage/src/genhomedircon.c
> b/libsemanage/src/genhomedircon.c
> index e8c95ee4..f58c17ce 100644
> --- a/libsemanage/src/genhomedircon.c
> +++ b/libsemanage/src/genhomedircon.c
> @@ -295,9 +295,8 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  	long rbuflen;
>  	uid_t temp, minuid = 500, maxuid = 60000;
>  	int minuid_set = 0;
> -	struct passwd pwstorage, *pwbuf;
> +	struct passwd *pwbuf;
>  	struct stat buf;
> -	int retval;
>  
>  	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
>  	if (path && *path) {
> @@ -369,7 +368,7 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  	if (rbuf == NULL)
>  		goto fail;
>  	setpwent();
> -	while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen,
> &pwbuf)) == 0) {
> +	while ((pwbuf = getpwent()) != NULL) {

Shouldn't you have removed rbuflen and rbuf too?  They are no longer
used (except to allocate and free, but never used otherwise) after this
change.

>  		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid >
> maxuid)
>  			continue;
>  		if (!semanage_list_find(shells, pwbuf->pw_shell))
> @@ -413,7 +412,7 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  		path = NULL;
>  	}
>  
> -	if (retval && retval != ENOENT) {
> +	if (errno && errno != ENOENT) {

Does getpwent() set errno to ENOENT? Not mentioned in its man page,
unlike for getpwent_r().

>  		WARN(s->h_semanage, "Error while fetching users.  "
>  		     "Returning list so far.");
>  	}
> @@ -1064,10 +1063,7 @@ static int
> get_group_users(genhomedircon_settings_t * s,
>  	long grbuflen;
>  	char *grbuf = NULL;
>  	struct group grstorage, *group = NULL;
> -
> -	long prbuflen;
> -	char *pwbuf = NULL;
> -	struct passwd pwstorage, *pw = NULL;
> +	struct passwd *pw = NULL;
>  
>  	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
>  	if (grbuflen <= 0)
> @@ -1104,15 +1100,8 @@ static int
> get_group_users(genhomedircon_settings_t * s,
>  		}
>  	}
>  
> -	prbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
> -	if (prbuflen <= 0)
> -		goto cleanup;
> -	pwbuf = malloc(prbuflen);
> -	if (pwbuf == NULL)
> -		goto cleanup;
> -
>  	setpwent();
> -	while ((retval = getpwent_r(&pwstorage, pwbuf, prbuflen,
> &pw)) == 0) {
> +	while ((pw = getpwent()) != NULL) {
>  		// skip users who also have this group as their
>  		// primary group
>  		if (lfind(pw->pw_name, group->gr_mem, &nmembers,

Interestingly, this goes on to call add_user(), which calls
getpwnam_r().  So if that one was ever switched back to just
getpwnam(), we'd have a potential problem there.

> @@ -1131,7 +1120,6 @@ static int
> get_group_users(genhomedircon_settings_t * s,
>  	retval = STATUS_SUCCESS;
>  cleanup:
>  	endpwent();
> -	free(pwbuf);
>  	free(grbuf);
>  
>  	return retval;
Patrick Steinhardt June 22, 2017, 9 a.m. UTC | #2
On Tue, Jun 20, 2017 at 11:42:59AM -0400, Stephen Smalley wrote:
> On Tue, 2017-06-20 at 16:07 +0200, Patrick Steinhardt wrote:
> > The `getpwent_r` function is a non-standard but re-entrant version of
> > the sdardardized `getpwent` function. Next to the benefit of being
> > re-entrant, though, it avoids compilation with libc implementations
> > that
> > do not provide this glibc-specific extension, for example musl libc.
> > 
> > As the code is usually not run in a threaded environment, it is save
> > to
> > use the non-reentrant version, though. As such, convert code to use
> > `getpwent` instead to fix building with other libc implementations.
> 
> Not sure we are guaranteed that, since libsemanage has external users
> too, e.g. sssd among others.  OTOH, getpwent_r man page says that it is
> not really reentrant either due to shared reading position in the
> stream, so maybe this doesn't matter.

Hum, okay. man-pages differ here in their wording across systems,
but I guess the part you're referring to is

```
The functions getpwent_r() and fgetpwent_r() are the reentrant
versions of getpwent(3) and fgetpwent(3). The former reads the
next passwd entry from the stream initialized by setpwent(3). The
latter reads the next passwd entry from the stream fp.
```

While it indeed seems to imply that the stream by `setpwent` is
used, they also pretend to be reentrant here. Puzzled.

> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> >  libsemanage/src/genhomedircon.c | 22 +++++-----------------
> >  1 file changed, 5 insertions(+), 17 deletions(-)
> > 
> > diff --git a/libsemanage/src/genhomedircon.c
> > b/libsemanage/src/genhomedircon.c
> > index e8c95ee4..f58c17ce 100644
> > --- a/libsemanage/src/genhomedircon.c
> > +++ b/libsemanage/src/genhomedircon.c
> > @@ -295,9 +295,8 @@ static semanage_list_t
> > *get_home_dirs(genhomedircon_settings_t * s)
> >  	long rbuflen;
> >  	uid_t temp, minuid = 500, maxuid = 60000;
> >  	int minuid_set = 0;
> > -	struct passwd pwstorage, *pwbuf;
> > +	struct passwd *pwbuf;
> >  	struct stat buf;
> > -	int retval;
> >  
> >  	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
> >  	if (path && *path) {
> > @@ -369,7 +368,7 @@ static semanage_list_t
> > *get_home_dirs(genhomedircon_settings_t * s)
> >  	if (rbuf == NULL)
> >  		goto fail;
> >  	setpwent();
> > -	while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen,
> > &pwbuf)) == 0) {
> > +	while ((pwbuf = getpwent()) != NULL) {
> 
> Shouldn't you have removed rbuflen and rbuf too?  They are no longer
> used (except to allocate and free, but never used otherwise) after this
> change.

That was an oversight, yeah. Fixed in v2.

> >  		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid >
> > maxuid)
> >  			continue;
> >  		if (!semanage_list_find(shells, pwbuf->pw_shell))
> > @@ -413,7 +412,7 @@ static semanage_list_t
> > *get_home_dirs(genhomedircon_settings_t * s)
> >  		path = NULL;
> >  	}
> >  
> > -	if (retval && retval != ENOENT) {
> > +	if (errno && errno != ENOENT) {
> 
> Does getpwent() set errno to ENOENT? Not mentioned in its man page,
> unlike for getpwent_r().

You're right, it does not. Will remove this check.

> >  		WARN(s->h_semanage, "Error while fetching users.  "
> >  		     "Returning list so far.");
> >  	}
> > @@ -1064,10 +1063,7 @@ static int
> > get_group_users(genhomedircon_settings_t * s,
> >  	long grbuflen;
> >  	char *grbuf = NULL;
> >  	struct group grstorage, *group = NULL;
> > -
> > -	long prbuflen;
> > -	char *pwbuf = NULL;
> > -	struct passwd pwstorage, *pw = NULL;
> > +	struct passwd *pw = NULL;
> >  
> >  	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
> >  	if (grbuflen <= 0)
> > @@ -1104,15 +1100,8 @@ static int
> > get_group_users(genhomedircon_settings_t * s,
> >  		}
> >  	}
> >  
> > -	prbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
> > -	if (prbuflen <= 0)
> > -		goto cleanup;
> > -	pwbuf = malloc(prbuflen);
> > -	if (pwbuf == NULL)
> > -		goto cleanup;
> > -
> >  	setpwent();
> > -	while ((retval = getpwent_r(&pwstorage, pwbuf, prbuflen,
> > &pw)) == 0) {
> > +	while ((pw = getpwent()) != NULL) {
> >  		// skip users who also have this group as their
> >  		// primary group
> >  		if (lfind(pw->pw_name, group->gr_mem, &nmembers,
> 
> Interestingly, this goes on to call add_user(), which calls
> getpwnam_r().  So if that one was ever switched back to just
> getpwnam(), we'd have a potential problem there.

Agreed. But in fact, `getpwnam_r` is part of POSIX, so there is
no need to do so here.

> > @@ -1131,7 +1120,6 @@ static int
> > get_group_users(genhomedircon_settings_t * s,
> >  	retval = STATUS_SUCCESS;
> >  cleanup:
> >  	endpwent();
> > -	free(pwbuf);
> >  	free(grbuf);
> >  
> >  	return retval;
diff mbox

Patch

diff --git a/libsemanage/src/genhomedircon.c b/libsemanage/src/genhomedircon.c
index e8c95ee4..f58c17ce 100644
--- a/libsemanage/src/genhomedircon.c
+++ b/libsemanage/src/genhomedircon.c
@@ -295,9 +295,8 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 	long rbuflen;
 	uid_t temp, minuid = 500, maxuid = 60000;
 	int minuid_set = 0;
-	struct passwd pwstorage, *pwbuf;
+	struct passwd *pwbuf;
 	struct stat buf;
-	int retval;
 
 	path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
 	if (path && *path) {
@@ -369,7 +368,7 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 	if (rbuf == NULL)
 		goto fail;
 	setpwent();
-	while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen, &pwbuf)) == 0) {
+	while ((pwbuf = getpwent()) != NULL) {
 		if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid)
 			continue;
 		if (!semanage_list_find(shells, pwbuf->pw_shell))
@@ -413,7 +412,7 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 		path = NULL;
 	}
 
-	if (retval && retval != ENOENT) {
+	if (errno && errno != ENOENT) {
 		WARN(s->h_semanage, "Error while fetching users.  "
 		     "Returning list so far.");
 	}
@@ -1064,10 +1063,7 @@  static int get_group_users(genhomedircon_settings_t * s,
 	long grbuflen;
 	char *grbuf = NULL;
 	struct group grstorage, *group = NULL;
-
-	long prbuflen;
-	char *pwbuf = NULL;
-	struct passwd pwstorage, *pw = NULL;
+	struct passwd *pw = NULL;
 
 	grbuflen = sysconf(_SC_GETGR_R_SIZE_MAX);
 	if (grbuflen <= 0)
@@ -1104,15 +1100,8 @@  static int get_group_users(genhomedircon_settings_t * s,
 		}
 	}
 
-	prbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
-	if (prbuflen <= 0)
-		goto cleanup;
-	pwbuf = malloc(prbuflen);
-	if (pwbuf == NULL)
-		goto cleanup;
-
 	setpwent();
-	while ((retval = getpwent_r(&pwstorage, pwbuf, prbuflen, &pw)) == 0) {
+	while ((pw = getpwent()) != NULL) {
 		// skip users who also have this group as their
 		// primary group
 		if (lfind(pw->pw_name, group->gr_mem, &nmembers,
@@ -1131,7 +1120,6 @@  static int get_group_users(genhomedircon_settings_t * s,
 	retval = STATUS_SUCCESS;
 cleanup:
 	endpwent();
-	free(pwbuf);
 	free(grbuf);
 
 	return retval;