The openpty(3) call used by slurmstepd to allocate a pseudo-terminal
is a convenience function in BSD and glibc that internally calls
the equivalent of

    int masterfd = open("/dev/ptmx", flags);
    grantpt (masterfd);
    unlockpt (masterfd);
    int slavefd = open (slave, O_RDRW|O_NOCTTY);

(in psuedocode)

On Linux, with some combinations of glibc/kernel (in this
case glibc-2.14/Linux-3.1), the equivalent of grantpt(3) was failing
in slurmstepd with EPERM, because the allocated pty was getting
root ownership instead of the user running the slurm job.

>From the POSIX description of grantpt:

 "The grantpt() function shall change the mode and ownership of the
  slave pseudo-terminal device... The user ID of the slave shall
  be set to the real UID of the calling process..."

 http://pubs.opengroup.org/onlinepubs/007904875/functions/grantpt.html

This means that for POSIX-compliance, the real user id of slurmstepd
must be the user executing the SLURM job at the time openpty(3) is
called. Unfortunately, the real user id of slurmstepd at this
point is still root, and only the effective uid is set to the user.

This patch is a work-around that uses the (non-portable) setresuid(2)
system call to reset the real and effective uids of the slurmstepd
process to the job user, but keep the saved uid of root. Then after
the openpty(3) call, the previous credentials are restablished
using the same call.
---
 src/slurmd/slurmstepd/io.c |   21 +++++++++++++++++++++
 1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/src/slurmd/slurmstepd/io.c b/src/slurmd/slurmstepd/io.c
index 4c1b241..8942700 100644
--- a/src/slurmd/slurmstepd/io.c
+++ b/src/slurmd/slurmstepd/io.c
@@ -42,6 +42,9 @@
 #endif
 
 #if HAVE_UNISTD_H
+#  if HAVE_SETRESUID
+#    define _GNU_SOURCE /* for setresuid(3) */
+#  endif
 #  include <unistd.h>
 #endif
 
@@ -947,10 +950,28 @@ _init_task_stdio_fds(slurmd_task_info_t *task, 
slurmd_job_t *job)
                if (task->gtid == 0) {
                        int amaster, aslave;
                        debug("  stdin uses a pty object");
+#if HAVE_SETRESUID
+                       /*
+                        *  openpty(3) calls grantpt(3), which sets
+                        *   the owner of the pty device to the *real*
+                        *   uid of the caller. Because of this, we must
+                        *   change our uid temporarily to that of the
+                        *   user (now the effective uid). We have to
+                        *   use setresuid(2) so that we keep a saved uid
+                        *   of root, and can regain previous permissions
+                        *   after the call to openpty.
+                        */
+                       if (setresuid(geteuid(), geteuid(), 0) < 0)
+                               error ("pre openpty: setresuid: %m");
+#endif
                        if (openpty(&amaster, &aslave, NULL, NULL, NULL) < 0) {
                                error("stdin openpty: %m");
                                return SLURM_ERROR;
                        }
+#if HAVE_SETRESUID
+                       if (setresuid(0, getuid(), 0) < 0)
+                               error ("post openpty: setresuid: %m");
+#endif
                        task->stdin_fd = aslave;
                        fd_set_close_on_exec(task->stdin_fd);
                        task->to_stdin = amaster;
-- 
1.7.1

Reply via email to