diff mbox

[v2,2/2] genhomedircon: avoid use of non-standard `getpwent_r`

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

Commit Message

Patrick Steinhardt June 22, 2017, 9:45 a.m. UTC
The `getpwent_r` function is a non-standard but reentrant version of the
POSIX-defined `getpwent` function. While it should provide the benefit
of being safe to use in multi-threaded environments, it disallows us
from compiling with libc implementations which stick to the POSIX
standard more closely.

As libsemanage may be used in a multi-threaded environment, being
reentrant may in fact be quite important to us. As such, simply
switching out `getpwent_r` against its non-reentrant function can prove
quite dangerous. But interestingly enough, the glibc implementation of
`getpwent_r` does not even guarantee being reentrant. Quoting from
getpwent_r(7):

    NOTES

    The function getpwent_r() is not really reentrant since it shares
    the reading position in the stream with all other threads.

As such, it is non-reentrant in the same sense as its simple `getpwent`
brother and can simply be switched out without losing any guarantees
here.

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

Comments

Stephen Smalley June 22, 2017, 8:46 p.m. UTC | #1
On Thu, 2017-06-22 at 11:45 +0200, Patrick Steinhardt wrote:
> The `getpwent_r` function is a non-standard but reentrant version of
> the
> POSIX-defined `getpwent` function. While it should provide the
> benefit
> of being safe to use in multi-threaded environments, it disallows us
> from compiling with libc implementations which stick to the POSIX
> standard more closely.
> 
> As libsemanage may be used in a multi-threaded environment, being
> reentrant may in fact be quite important to us. As such, simply
> switching out `getpwent_r` against its non-reentrant function can
> prove
> quite dangerous. But interestingly enough, the glibc implementation
> of
> `getpwent_r` does not even guarantee being reentrant. Quoting from
> getpwent_r(7):
> 
>     NOTES
> 
>     The function getpwent_r() is not really reentrant since it shares
>     the reading position in the stream with all other threads.
> 
> As such, it is non-reentrant in the same sense as its simple
> `getpwent`
> brother and can simply be switched out without losing any guarantees
> here.

Thanks, both patches applied.

> 
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  libsemanage/src/genhomedircon.c | 34 +++++++----------------------
> -----
>  1 file changed, 7 insertions(+), 27 deletions(-)
> 
> diff --git a/libsemanage/src/genhomedircon.c
> b/libsemanage/src/genhomedircon.c
> index e8c95ee4..b9a74b73 100644
> --- a/libsemanage/src/genhomedircon.c
> +++ b/libsemanage/src/genhomedircon.c
> @@ -290,14 +290,11 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  	semanage_list_t *homedir_list = NULL;
>  	semanage_list_t *shells = NULL;
>  	fc_match_handle_t hand;
> -	char *rbuf = NULL;
>  	char *path = NULL;
> -	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) {
> @@ -362,14 +359,9 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  	free(path);
>  	path = NULL;
>  
> -	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
> -	if (rbuflen <= 0)
> -		goto fail;
> -	rbuf = malloc(rbuflen);
> -	if (rbuf == NULL)
> -		goto fail;
> +	errno = 0;
>  	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))
> @@ -411,9 +403,10 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  		}
>  		free(path);
>  		path = NULL;
> +		errno = 0;
>  	}
>  
> -	if (retval && retval != ENOENT) {
> +	if (errno) {
>  		WARN(s->h_semanage, "Error while fetching users.  "
>  		     "Returning list so far.");
>  	}
> @@ -422,14 +415,12 @@ static semanage_list_t
> *get_home_dirs(genhomedircon_settings_t * s)
>  		goto fail;
>  
>  	endpwent();
> -	free(rbuf);
>  	semanage_list_destroy(&shells);
>  
>  	return homedir_list;
>  
>        fail:
>  	endpwent();
> -	free(rbuf);
>  	free(path);
>  	semanage_list_destroy(&homedir_list);
>  	semanage_list_destroy(&shells);
> @@ -1064,10 +1055,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 +1092,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 +1112,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..b9a74b73 100644
--- a/libsemanage/src/genhomedircon.c
+++ b/libsemanage/src/genhomedircon.c
@@ -290,14 +290,11 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 	semanage_list_t *homedir_list = NULL;
 	semanage_list_t *shells = NULL;
 	fc_match_handle_t hand;
-	char *rbuf = NULL;
 	char *path = NULL;
-	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) {
@@ -362,14 +359,9 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 	free(path);
 	path = NULL;
 
-	rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
-	if (rbuflen <= 0)
-		goto fail;
-	rbuf = malloc(rbuflen);
-	if (rbuf == NULL)
-		goto fail;
+	errno = 0;
 	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))
@@ -411,9 +403,10 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 		}
 		free(path);
 		path = NULL;
+		errno = 0;
 	}
 
-	if (retval && retval != ENOENT) {
+	if (errno) {
 		WARN(s->h_semanage, "Error while fetching users.  "
 		     "Returning list so far.");
 	}
@@ -422,14 +415,12 @@  static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
 		goto fail;
 
 	endpwent();
-	free(rbuf);
 	semanage_list_destroy(&shells);
 
 	return homedir_list;
 
       fail:
 	endpwent();
-	free(rbuf);
 	free(path);
 	semanage_list_destroy(&homedir_list);
 	semanage_list_destroy(&shells);
@@ -1064,10 +1055,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 +1092,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 +1112,6 @@  static int get_group_users(genhomedircon_settings_t * s,
 	retval = STATUS_SUCCESS;
 cleanup:
 	endpwent();
-	free(pwbuf);
 	free(grbuf);
 
 	return retval;