Package: slurm-llnl
Version: 1.3.6-1
Severity: grave
Tags: security patch pending


The following issue has been reported on the slurm-llnl mailinglist.
Updated packages are in preparation.

----------  Forwarded Message  ----------

A security flaw has been discovered in all releases of SLURM
versions 1.2 and 1.3. This flaw can be exploited by legitimate
users of a computer to increase their privileges based upon
the supplemental groups available to the SLURM daemons.


Description

A vulnerability exists in the current SLURM sbcast implementation.
The result of this flaw is that sbcast may not properly establish
user supplementary groups before opening files for writing, instead
inheriting the supplementary group list from the slurmd daemon,
which may contain system groups with elevated privileges.

Similar logic exists in support of the strigger command. If the
SlurmUser is configured to be root, unprivileged users may execute
a program inheriting the supplementary group list from the slurmctld
daemon, which may contain system groups with elevated privileges.

You can check the current list of supplementary groups that would be
inherited from these daemons by running the following command:

    grep ^Groups /proc/`pidof slurmd`/status
    grep ^Groups /proc/`pidof slurmctld`/status


Impact

A valid SLURM user may be able to write files in directories with
group write access for one of the inherited groups and/or may be able
to overwrite files with similar group write access. Depending upon
system configuration, this may allow a user to gain elevated privileges.


Solution

We are providing four options to fix this problem.

1. Apply the initgroups.patch2 to an existing SLURM version 1.3
    or 1.2 distribution.

2. Install the nogroups.c wrapper to start the SLURM daemons without
    any supplemental groups. This can be used with most configurations
    and no change in the installed SLURM code.

3. Install SLURM version 1.3.14, which is the same as version
    1.3.13 (a very stable release made on 13 January 2009) plus
    initgroups.patch2.

4. Install SLURM version 1.3.15, which includes initgroups.patch2
    plus support for BlueGene/P systems, an assortment of minor
    bug fixes and some minor enhancements.

After performing one of these changes, the SLURM daemons must be
restarted for the change to take effect.

SLURM version 1.4.0-pre12 was also released today for those working
with a beta version of the next major release.
-------------------------------------------------------
Index: src/common/uid.c
===================================================================
--- src/common/uid.c	(revision 17072)
+++ src/common/uid.c	(revision 17170)
@@ -111,6 +111,24 @@
 }
 
 gid_t
+gid_from_uid (uid_t uid)
+{
+	struct passwd pwd, *result;
+	char buffer[PW_BUF_SIZE];
+	gid_t gid;
+	int rc;
+
+	rc = getpwuid_r(uid, &pwd, buffer, PW_BUF_SIZE, &result);
+	if (result == NULL) {
+		gid = (gid_t) -1;
+	} else {
+		gid = result->pw_gid;
+	}
+
+	return gid;
+}
+
+gid_t
 gid_from_string (char *name)
 {
 	struct group grp, *result;
Index: src/common/uid.h
===================================================================
--- src/common/uid.h	(revision 17072)
+++ src/common/uid.h	(revision 17170)
@@ -60,6 +60,12 @@
 uid_t uid_from_string (char *name);
 
 /*
+ * Return the primary group id for a given user id, or 
+ * (gid_t) -1 on failure.
+ */
+gid_t gid_from_uid (uid_t uid);
+
+/*
  * Same as uid_from_name(), but for group name/id.
  */
 gid_t gid_from_string (char *name);
Index: src/slurmd/slurmd/slurmd.c
===================================================================
--- src/slurmd/slurmd/slurmd.c	(revision 17072)
+++ src/slurmd/slurmd/slurmd.c	(revision 17170)
@@ -160,6 +160,18 @@
 		(void) close(i);
 
 	/*
+	 * Drop supplementary groups.
+	 */
+	if (geteuid() == 0) {
+		if (setgroups(0, NULL) != 0) {
+			fatal("Failed to drop supplementary groups, "
+			      "setgroups: %m");
+		}
+	} else {
+		info("Not running as root. Can't drop supplementary groups");
+	}
+
+	/*
 	 * Create and set default values for the slurmd global
 	 * config variable "conf"
 	 */
Index: src/slurmd/slurmd/req.c
===================================================================
--- src/slurmd/slurmd/req.c	(revision 17072)
+++ src/slurmd/slurmd/req.c	(revision 17170)
@@ -108,6 +108,7 @@
 static void _job_limits_free(void *x);
 static int  _job_limits_match(void *x, void *key);
 static bool _job_still_running(uint32_t job_id);
+static int  _init_groups(uid_t my_uid, gid_t my_gid);
 static int  _kill_all_active_steps(uint32_t jobid, int sig, bool batch);
 static int  _terminate_all_steps(uint32_t jobid, bool batch);
 static void _rpc_launch_tasks(slurm_msg_t *);
@@ -1836,12 +1837,34 @@
 }
 
 static int
+_init_groups(uid_t my_uid, gid_t my_gid)
+{
+	char *user_name = uid_to_string(my_uid);
+	int rc;
+
+	if (user_name == NULL) {
+		error("sbcast: Could not find uid %ld", (long)my_uid);
+		return -1;
+	}
+
+	rc = initgroups(user_name, my_gid);
+	xfree(user_name);
+	if (rc) {
+ 		error("sbcast: Error in initgroups(%s, %ld): %m",
+		      user_name, (long)my_gid);
+		return -1;
+	}
+	return 0;
+
+}
+
+static int
 _rpc_file_bcast(slurm_msg_t *msg)
 {
 	file_bcast_msg_t *req = msg->data;
 	int fd, flags, offset, inx, rc;
 	uid_t req_uid = g_slurm_auth_get_uid(msg->auth_cred, NULL);
-	uid_t req_gid = g_slurm_auth_get_gid(msg->auth_cred, NULL);
+	gid_t req_gid = g_slurm_auth_get_gid(msg->auth_cred, NULL);
 	pid_t child;
 
 #if 0
@@ -1867,6 +1890,10 @@
 
 	/* The child actually performs the I/O and exits with 
 	 * a return code, do not return! */
+	if (_init_groups(req_uid, req_gid) < 0) {
+		error("sbcast: initgroups(%u): %m", req_uid);
+		exit(errno);
+	}
 	if (setgid(req_gid) < 0) {
 		error("sbcast: uid:%u setgid(%u): %s", req_uid, req_gid, 
 			strerror(errno));
Index: src/slurmctld/controller.c
===================================================================
--- src/slurmctld/controller.c	(revision 17072)
+++ src/slurmctld/controller.c	(revision 17170)
@@ -202,6 +202,7 @@
 		WRITE_LOCK, WRITE_LOCK, WRITE_LOCK, WRITE_LOCK };
 	assoc_init_args_t assoc_init_arg;
 	pthread_t assoc_cache_thread;
+	gid_t slurm_user_gid;
 
 	/*
 	 * Establish initial configuration
@@ -224,19 +225,41 @@
 	 */
 	_init_pidfile();
 
-	/* Initialize supplementary group ID list for SlurmUser */
-	if ((getuid() == 0)
-	&&  (slurmctld_conf.slurm_user_id != getuid())
-	&&  initgroups(slurmctld_conf.slurm_user_name,
-			gid_from_string(slurmctld_conf.slurm_user_name))) {
-		error("initgroups: %m");
+	/* Determine SlurmUser gid */
+	slurm_user_gid = gid_from_uid(slurmctld_conf.slurm_user_id);
+	if (slurm_user_gid == (gid_t) -1) {
+		fatal("Failed to determine gid of SlurmUser(%d)", 
+		      slurm_user_gid);
 	}
 
-	if ((slurmctld_conf.slurm_user_id != getuid())
-	&&  (setuid(slurmctld_conf.slurm_user_id))) {
+	/* Initialize supplementary groups ID list for SlurmUser */
+	if (getuid() == 0) {
+		/* root does not need supplementary groups */
+		if ((slurmctld_conf.slurm_user_id == 0) &&
+		    (setgroups(0, NULL) != 0)) {
+			fatal("Failed to drop supplementary groups, "
+			      "setgroups: %m");
+		} else if ((slurmctld_conf.slurm_user_id != getuid()) &&
+			   initgroups(slurmctld_conf.slurm_user_name, 
+				      slurm_user_gid)) {
+			fatal("Failed to set supplementary groups, "
+			      "initgroups: %m");
+		}
+	}
+
+	/* Set GID to GID of SlurmUser */
+	if ((slurm_user_gid != getegid()) &&
+	    (setgid(slurm_user_gid))) {
+		fatal("Failed to set GID to %d", slurm_user_gid);
+	}
+
+	/* Set UID to UID of SlurmUser */
+	if ((slurmctld_conf.slurm_user_id != getuid()) &&
+	    (setuid(slurmctld_conf.slurm_user_id))) {
 		fatal("Can not set uid to SlurmUser(%d): %m", 
-			slurmctld_conf.slurm_user_id);
+		      slurmctld_conf.slurm_user_id);
 	}
+
 	if (stat(slurmctld_conf.mail_prog, &stat_buf) != 0)
 		error("Configured MailProg is invalid");
 
Index: src/slurmctld/trigger_mgr.c
===================================================================
--- src/slurmctld/trigger_mgr.c	(revision 17072)
+++ src/slurmctld/trigger_mgr.c	(revision 17170)
@@ -985,9 +985,18 @@
 		setpgrp();
 #endif
 		setsid();
-		setuid(uid);
-		setgid(gid);
-		initgroups(user_name, -1);
+		if (initgroups(user_name, gid) == -1) {
+			error("trigger: initgroups: %m");
+			exit(1);
+		}
+		if (setgid(gid) == -1) {
+			error("trigger: setgid: %m");
+			exit(1);
+		}
+		if (setuid(uid) == -1) {
+			error("trigger: setuid: %m");
+			exit(1);
+		}
 		execl(program, arg0, arg1, NULL);
 		exit(1);
 	} else

/*
 *   Drop all supplementary groups and run a command.
 * 
 *   e.g.:
 *
 *    gcc -o nogroups -Wall nogroups.c
 *
 *   # id
 *   uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),
 *    3(sys),4(adm),6(disk),10(wheel),341(pkcs11)
 *
 *   # ./nogroups id
 *   uid=0(root) gid=0(root)
 *
 */
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main (int ac, char **av)
{

        /*
         *  Require at least one argument (command to run).
         */
        if (ac < 2) {
                fprintf (stderr, "Usage: %s command...\n", av[0]);
                exit (1);
        }

        /*
         *  Drop all supplementary groups 
         *    (requires root privs or CAP_SETGID)
         */
        if (setgroups (0, NULL) < 0) {
                fprintf (stderr, "setgroups: %s\n", strerror (errno));
                exit (1);
        }

        /*
         *  Exec command(s)
         */
        if (execvp (av[1], av+1) < 0)
                fprintf (stderr, "execv: %s\n", strerror (errno));

        /*
         *  Error if we got here
         */
        exit (1);
}

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to