Merge authors:
  Stéphane Graber (stgraber)
Related merge proposals:
  https://code.launchpad.net/~stgraber/upstart/upstart-initgroups/+merge/136794
  proposed by: Stéphane Graber (stgraber)
  review: Approve - James Hunt (jamesodhunt)
------------------------------------------------------------
revno: 1396 [merge]
committer: James Hunt <[email protected]>
branch nick: upstart
timestamp: Wed 2012-12-05 08:56:58 +0000
message:
  * Merge of lp:~stgraber/upstart/upstart-initgroups.
modified:
  init/job_process.c
  init/job_process.h
  init/tests/test_job_process.c


--
lp:upstart
https://code.launchpad.net/~upstart-devel/upstart/trunk

Your team Upstart Reviewers is subscribed to branch lp:upstart.
To unsubscribe from this branch go to 
https://code.launchpad.net/~upstart-devel/upstart/trunk/+edit-subscription
=== modified file 'init/job_process.c'
--- init/job_process.c	2012-11-21 06:48:08 +0000
+++ init/job_process.c	2012-12-03 20:27:09 +0000
@@ -413,6 +413,8 @@
 	JobClass       *class;
 	uid_t           job_setuid = -1;
 	gid_t           job_setgid = -1;
+	struct passwd   *pwd = NULL;
+	struct group    *grp = NULL;
 
 
 	nih_assert (job != NULL);
@@ -705,6 +707,11 @@
 			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
 		}
 
+		if (geteuid () == 0 && initgroups (pw->pw_name, pw->pw_gid) < 0) {
+			nih_error_raise_system ();
+			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
+		}
+
 		if (pw->pw_gid && setgid (pw->pw_gid) < 0) {
 			nih_error_raise_system ();
 			job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
@@ -844,7 +851,6 @@
 	 * session jobs and jobs with a chroot stanza.
 	 */
 	if (class->setuid) {
-		struct passwd *pwd;
 		/* Without resetting errno, it's impossible to
 		 * distinguish between a non-existent user and and
 		 * error during lookup */
@@ -867,7 +873,6 @@
 	}
 
 	if (class->setgid) {
-		struct group *grp;
 		errno = 0;
 		grp = getgrnam (class->setgid);
 		if (! grp) {
@@ -891,6 +896,35 @@
 		job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
 	}
 
+	/* Make sure we always have the needed pwd and grp structs.
+	 * Then pass those to initgroups() to setup the user's group list.
+	 * Only do that if we're root as initgroups() won't work when non-root. */
+	if (geteuid () == 0) {
+		if (! pwd) {
+			pwd = getpwuid (geteuid ());
+			if (! pwd) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWUID, 0);
+			}
+		}
+
+		if (! grp) {
+			grp = getgrgid (getegid ());
+			if (! grp) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRGID, 0);
+			}
+		}
+
+		if (pwd && grp) {
+			if (initgroups (pwd->pw_name, grp->gr_gid) < 0) {
+				nih_error_raise_system ();
+				job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
+			}
+		}
+	}
+
+	/* Start dropping privileges */
 	if (job_setgid != (gid_t) -1 && setgid (job_setgid) < 0) {
 		nih_error_raise_system ();
 		job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
@@ -1142,6 +1176,11 @@
 				  err, _("unable to getpwuid: %s"),
 				  strerror (err->errnum)));
 		break;
+	case JOB_PROCESS_ERROR_GETGRGID:
+		err->error.message = NIH_MUST (nih_sprintf (
+				  err, _("unable to getgrgid: %s"),
+				  strerror (err->errnum)));
+		break;
 	case JOB_PROCESS_ERROR_BAD_SETUID:
 		err->error.message = NIH_MUST (nih_sprintf (
 				  err, _("unable to find setuid user")));
@@ -1200,6 +1239,11 @@
 				  err, _("unable to allocate memory: %s"),
 				  strerror (err->errnum)));
 		break;
+	case JOB_PROCESS_ERROR_INITGROUPS:
+		err->error.message = NIH_MUST (nih_sprintf (
+				  err, _("unable to initgroups: %s"),
+				  strerror (err->errnum)));
+		break;
 	default:
 		nih_assert_not_reached ();
 	}

=== modified file 'init/job_process.h'
--- init/job_process.h	2012-11-07 15:17:58 +0000
+++ init/job_process.h	2012-11-26 19:30:41 +0000
@@ -94,7 +94,9 @@
 	JOB_PROCESS_ERROR_PTSNAME,
 	JOB_PROCESS_ERROR_OPENPT_SLAVE,
 	JOB_PROCESS_ERROR_SIGNAL,
-	JOB_PROCESS_ERROR_ALLOC
+	JOB_PROCESS_ERROR_ALLOC,
+	JOB_PROCESS_ERROR_INITGROUPS,
+	JOB_PROCESS_ERROR_GETGRGID
 } JobProcessErrorType;
 
 /**

=== modified file 'init/tests/test_job_process.c'
--- init/tests/test_job_process.c	2012-12-04 17:12:46 +0000
+++ init/tests/test_job_process.c	2012-12-05 08:56:58 +0000
@@ -4007,6 +4007,74 @@
 	}
 
 	/************************************************************/
+
+	/* Check that initgroups gets called.
+	 * The test will run a job as nobody/nogroup (setuid/setgid) target.
+	 *
+	 * When run from an unprivileged user, the check will ensure that upstart
+	 * fails to start the job (returning -1) as initgroups() is a privileged
+	 * call (similar to setuid and setgid).
+	 *
+	 * When run from a privileged user (root), the check will ensure that
+	 * upstart succeeds in dropping privileges, which includes calling
+	 * initgroup, setuid and setgid.
+	 *
+	 * If the test is started by user nobody/nogroup, then it'll succeed as
+	 * there's no privilege changes to be done in such case (same uid/gid).
+	 */
+	TEST_FEATURE ("with setuid");
+	TEST_HASH_EMPTY (job_classes);
+
+	TEST_NE_P (output, NULL);
+	TEST_ALLOC_FAIL {
+		TEST_ALLOC_SAFE {
+			class = job_class_new (NULL, "test", NULL);
+			class->process[PROCESS_MAIN] = process_new (class);
+			class->process[PROCESS_MAIN]->command = nih_sprintf (
+				class->process[PROCESS_MAIN],
+				"touch %s", filename);
+
+			pwd = getpwnam ("nobody");
+			TEST_NE (pwd, NULL);
+			class->setuid = nih_strdup (class, pwd->pw_name);
+
+			grp = getgrnam ("nogroup");
+			TEST_NE (grp, NULL);
+			class->setgid = nih_strdup (class, grp->gr_name);
+
+			job = job_new (class, "");
+			job->goal = JOB_START;
+			job->state = JOB_SPAWNED;
+
+			output = tmpfile ();
+		}
+
+		TEST_DIVERT_STDERR (output) {
+			ret = job_process_run (job, PROCESS_MAIN);
+			if (geteuid() == 0 || getuid() == pwd->pw_uid) {
+				TEST_EQ (ret, 0);
+			}
+			else {
+				TEST_EQ (ret, -1);
+			}
+		}
+
+		if (geteuid() == 0 || getuid() == pwd->pw_uid) {
+			TEST_NE (job->pid[PROCESS_MAIN], 0);
+
+			waitpid (job->pid[PROCESS_MAIN], NULL, 0);
+			TEST_EQ (stat (filename, &statbuf), 0);
+		}
+		else {
+			TEST_EQ (stat (filename, &statbuf), -1);
+		}
+
+		unlink (filename);
+		nih_free (class);
+
+	}
+
+	/************************************************************/
 	TEST_FEATURE ("with multiple processes and log");
 	TEST_HASH_EMPTY (job_classes);
 

-- 
upstart-devel mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/upstart-devel

Reply via email to