Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Allow :class:`subprocess.Popen` to run subprocesses as root after dropping
root privileges but saving the original user/group identity so it can be
re-assumed.
88 changes: 71 additions & 17 deletions Modules/_posixsubprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,76 @@ reset_signal_handlers(const sigset_t *child_sigmask)
#endif /* VFORK_USABLE */


static int set_user_identity(uid_t uid)
{
if (uid == (uid_t)-1)
return 0;
#if defined(HAVE_SETRESUID)
return setresuid(uid, uid, uid);
#elif defined(HAVE_SETREUID)
return setreuid(uid, uid);
#endif
return 0;
}


static int set_group_identity(gid_t gid)
{
if (gid == (gid_t)-1)
return 0;
#if defined(HAVE_SETRESGID)
return setresgid(gid, gid, gid);
#elif defined(HAVE_SETREGID)
return setregid(gid, gid);
#endif
return 0;
}


static int set_groups(Py_ssize_t extra_group_size, const gid_t *extra_groups)
{
#ifdef HAVE_SETGROUPS
if (extra_group_size >= 0) {
assert((extra_group_size == 0) == (extra_groups == NULL));
return setgroups(extra_group_size, extra_groups);
}
#endif /* HAVE_SETGROUPS */
return 0;
}


static int
set_identity_priv(uid_t uid, gid_t gid,
Py_ssize_t extra_group_size, const gid_t *extra_groups)
{
return (set_user_identity(uid)
|| set_group_identity(gid)
|| set_groups(extra_group_size, extra_groups)) ? -1 : 0;
}


static int
set_identity_unpriv(uid_t uid, gid_t gid,
Py_ssize_t extra_group_size, const gid_t *extra_groups)
{
return (set_groups(extra_group_size, extra_groups)
|| set_group_identity(gid)
|| set_user_identity(uid)) ? -1 : 0;
}


static int
set_identity(uid_t uid, gid_t gid,
Py_ssize_t extra_group_size, const gid_t *extra_groups)
{
if (uid == 0) {
return set_identity_priv(uid, gid, extra_group_size, extra_groups);
} else {
return set_identity_unpriv(uid, gid, extra_group_size, extra_groups);
}
}


/*
* This function is code executed in the child process immediately after
* (v)fork to set things up and call exec().
Expand Down Expand Up @@ -773,23 +843,7 @@ child_exec(char *const exec_array[],
}
#endif

#ifdef HAVE_SETGROUPS
if (extra_group_size >= 0) {
assert((extra_group_size == 0) == (extra_groups == NULL));
POSIX_CALL(setgroups(extra_group_size, extra_groups));
}
#endif /* HAVE_SETGROUPS */

#ifdef HAVE_SETREGID
if (gid != (gid_t)-1)
POSIX_CALL(setregid(gid, gid));
#endif /* HAVE_SETREGID */

#ifdef HAVE_SETREUID
if (uid != (uid_t)-1)
POSIX_CALL(setreuid(uid, uid));
#endif /* HAVE_SETREUID */

POSIX_CALL(set_identity(uid, gid, extra_group_size, extra_groups));

err_msg = "";
if (preexec_fn != Py_None && preexec_fn_args_tuple) {
Expand Down
Loading