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