Blob Blame History Raw
autofs-5.1.8 - dont use initgroups() at spawn

From: Ian Kent <raven@themaw.net>

The initgroups(3) function isn't safe to use between fork() and
exec() in a threaded program.

Using it this way often leads to a hang for even moderate work
loads.

But the getgrouplist()/setgroups() combination can be used safely
in this case and this patch changes autofs to use these (the safety
of using of setgroups() is yet to to be documented).

A large portion of the work on this patch has been contributed
by Roberto Bergantinos <rbergant@redhat.com>.

Reported-by: Roberto Bergantinos <rbergant@redhat.com>
Fixes: 6343a3292020 ("autofs-5.1.3 - fix ordering of seteuid/setegid in do_spawn()")
Signed-off-by: Roberto Bergantinos <rbergant@redhat.com>
Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG      |    1 +
 daemon/spawn.c |   51 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 48 insertions(+), 4 deletions(-)

--- autofs-5.1.4.orig/CHANGELOG
+++ autofs-5.1.4/CHANGELOG
@@ -94,6 +94,7 @@
 - fix sysconf(3) return handling.
 - remove nonstrict parameter from tree_mapent_umount_offsets().
 - fix handling of incorrect return from umount_ent().
+- dont use initgroups() at spawn.
 
 xx/xx/2018 autofs-5.1.5
 - fix flag file permission.
--- autofs-5.1.4.orig/daemon/spawn.c
+++ autofs-5.1.4/daemon/spawn.c
@@ -26,6 +26,7 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/mount.h>
+#include <pwd.h>
 
 #include "automount.h"
 
@@ -335,6 +336,10 @@ static int do_spawn(unsigned logopt, uns
 	struct thread_stdenv_vars *tsv;
 	pid_t euid = 0;
 	gid_t egid = 0;
+	gid_t *groups = NULL;
+	gid_t *saved_groups = NULL;
+	int ngroups = 0;
+	int nsaved_groups = 0;
 
 	if (open_pipe(pipefd))
 		return -1;
@@ -357,6 +362,31 @@ static int do_spawn(unsigned logopt, uns
 	}
 
 	open_mutex_lock();
+
+	if (euid) {
+		struct passwd *pwd;
+
+		pwd = getpwuid(getuid());
+		if (!pwd)
+			fprintf(stderr,
+				"warning: getpwuid: can't get current username\n");
+		else {
+			/* get number of groups for current gid */
+			getgrouplist(pwd->pw_name, getgid(), NULL, &nsaved_groups);
+			saved_groups = malloc(nsaved_groups * sizeof(gid_t));
+
+			/* get current gid groups list */
+			getgrouplist(pwd->pw_name, getgid(), saved_groups, &nsaved_groups);
+		}
+
+		/* get number of groups of mount triggering process */
+		getgrouplist(tsv->user, egid, NULL, &ngroups);
+		groups = malloc(ngroups * sizeof(gid_t));
+
+		/* get groups list of mount triggering process */
+		getgrouplist(tsv->user, egid, groups, &ngroups);
+	}
+
 	f = fork();
 	if (f == 0) {
 		char **pargv = (char **) argv;
@@ -398,10 +428,13 @@ static int do_spawn(unsigned logopt, uns
 				if (!tsv->user)
 					fprintf(stderr,
 						"warning: can't init groups\n");
-				else if (initgroups(tsv->user, egid) == -1)
-					fprintf(stderr,
-						"warning: initgroups: %s\n",
-						strerror(errno));
+				else if (groups) {
+					if (setgroups(ngroups, groups) == -1)
+						fprintf(stderr,
+							"warning: setgroups: %s\n",
+							strerror(errno));
+					free(groups);
+				}
 
 				if (setegid(egid) == -1)
 					fprintf(stderr,
@@ -436,6 +469,11 @@ static int do_spawn(unsigned logopt, uns
 					strerror(errno));
 			if (pgrp >= 0)
 				setpgid(0, pgrp);
+			/* Reset groups for trigger of trailing mount */
+			if (euid && saved_groups) {
+				setgroups(nsaved_groups, saved_groups);
+				free(saved_groups);
+			}
 
 			/*
 			 * The kernel leaves mount type autofs alone because
@@ -474,6 +512,11 @@ done:
 		pthread_sigmask(SIG_SETMASK, &tmpsig, NULL);
 		open_mutex_unlock();
 
+		if (groups)
+			free(groups);
+		if (saved_groups)
+			free(saved_groups);
+
 		close(pipefd[1]);
 
 		if (f < 0) {