unix: Support setting supplementary process groups

uv_spawn currently unsets all supplementary groups if the caller sets
either a gid or uid to run the process under.  This commit adds support
for specifying the supplementary groups to set instead.
This commit is contained in:
Joe Dollard 2016-03-13 22:15:36 -04:00 committed by Jameson Nash
parent 612a8262b8
commit efeaeb242d
6 changed files with 137 additions and 10 deletions

View File

@ -34,6 +34,8 @@ Data types
uv_gid_t gid; uv_gid_t gid;
char* cpumask; char* cpumask;
size_t cpumask_size; size_t cpumask_size;
uv_gid_t* gids;
size_t gids_size;
} uv_process_options_t; } uv_process_options_t;
.. c:type:: void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal) .. c:type:: void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal)
@ -95,6 +97,13 @@ Data types
* extensions like '.exe' or '.cmd'. * extensions like '.exe' or '.cmd'.
*/ */
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME = (1 << 7) UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME = (1 << 7)
/*
* Set the child process' supplementary group ids. The group ids are supplied
* in the 'gids' field in the options struct, and the number of groups is
* specified in the 'gids_sz' field. This does not work on windows;
* setting this flag will cause uv_spawn() to fail.
*/
UV_PROCESS_SETGROUPS = (1 << 8)
}; };
.. c:type:: uv_stdio_container_t .. c:type:: uv_stdio_container_t
@ -221,9 +230,11 @@ Public members
.. c:member:: uv_uid_t uv_process_options_t.uid .. c:member:: uv_uid_t uv_process_options_t.uid
.. c:member:: uv_gid_t uv_process_options_t.gid .. c:member:: uv_gid_t uv_process_options_t.gid
.. c:member:: uv_process_options_t.gids
.. c:member:: uv_process_options_t.gids_size
Libuv can change the child process' user/group id. This happens only when Libuv can change the child process' user/group id and supplementary group
the appropriate bits are set in the flags fields. ids. This happens only when the appropriate bits are set in the flags fields.
.. note:: .. note::
This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error

View File

@ -1131,9 +1131,10 @@ typedef struct uv_process_options_s {
int stdio_count; int stdio_count;
uv_stdio_container_t* stdio; uv_stdio_container_t* stdio;
/* /*
* Libuv can change the child process' user/group id. This happens only when * Libuv can change the child process' user/group id, and supplementarty
* the appropriate bits are set in the flags fields. This is not supported on * group ids. This happens only when the appropriate bits are set in the
* windows; uv_spawn() will fail and set the error to UV_ENOTSUP. * flags fields. This is not supported on windows; uv_spawn() will fail and
* set the error to UV_ENOTSUP.
*/ */
uv_uid_t uid; uv_uid_t uid;
uv_gid_t gid; uv_gid_t gid;
@ -1150,6 +1151,9 @@ typedef struct uv_process_options_s {
*/ */
char* cpumask; char* cpumask;
size_t cpumask_size; size_t cpumask_size;
uv_gid_t* gids;
size_t gids_size;
} uv_process_options_t; } uv_process_options_t;
/* /*
@ -1163,7 +1167,7 @@ enum uv_process_flags {
*/ */
UV_PROCESS_SETUID = (1 << 0), UV_PROCESS_SETUID = (1 << 0),
/* /*
* Set the child process' group id. The user id is supplied in the `gid` * Set the child process' group id. The group id is supplied in the `gid`
* field of the options struct. This does not work on windows; setting this * field of the options struct. This does not work on windows; setting this
* flag will cause uv_spawn() to fail. * flag will cause uv_spawn() to fail.
*/ */
@ -1211,7 +1215,14 @@ enum uv_process_flags {
* This option is only meaningful on Windows systems. On Unix * This option is only meaningful on Windows systems. On Unix
* it is silently ignored. * it is silently ignored.
*/ */
UV_PROCESS_WINDOWS_USE_PARENT_ERROR_MODE = (1 << 8) UV_PROCESS_WINDOWS_USE_PARENT_ERROR_MODE = (1 << 8),
/*
* Set the child process' supplementary group ids. The group ids are supplied
* in the 'gids' field in the options struct, and the number of groups is
* specified in the 'gids_sz' field. This does not work on windows;
* setting this flag will cause uv_spawn() to fail on windows.
*/
UV_PROCESS_SETGROUPS = (1 << 9)
}; };
/* /*

View File

@ -395,8 +395,17 @@ static void uv__process_child_init(const uv_process_options_t* options,
SAVE_ERRNO(setgroups(0, NULL)); SAVE_ERRNO(setgroups(0, NULL));
} }
if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) if (options->flags & UV_PROCESS_SETGROUPS) {
uv__write_errno(error_fd); if (setgroups(options->gids_size, options->gids)) {
uv__write_int(error_fd, -errno);
_exit(127);
}
}
if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) {
uv__write_int(error_fd, -errno);
_exit(127);
}
if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid))
uv__write_errno(error_fd); uv__write_errno(error_fd);
@ -899,6 +908,13 @@ static int uv__spawn_and_init_child(
int exec_errorno; int exec_errorno;
ssize_t r; ssize_t r;
assert(options->file != NULL);
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
UV_PROCESS_SETGID |
UV_PROCESS_SETUID |
UV_PROCESS_SETGROUPS |
UV_PROCESS_WINDOWS_HIDE |
UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
#if defined(__APPLE__) #if defined(__APPLE__)
uv_once(&posix_spawn_init_once, uv__spawn_init_posix_spawn); uv_once(&posix_spawn_init_once, uv__spawn_init_posix_spawn);

View File

@ -899,7 +899,9 @@ int uv_spawn(uv_loop_t* loop,
process->exit_cb = options->exit_cb; process->exit_cb = options->exit_cb;
child_stdio_buffer = NULL; child_stdio_buffer = NULL;
if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { if (options->flags & (UV_PROCESS_SETGID |
UV_PROCESS_SETUID |
UV_PROCESS_SETGROUPS)) {
return UV_ENOTSUP; return UV_ENOTSUP;
} }
@ -918,7 +920,11 @@ int uv_spawn(uv_loop_t* loop,
assert(!(options->flags & ~(UV_PROCESS_DETACHED | assert(!(options->flags & ~(UV_PROCESS_DETACHED |
UV_PROCESS_SETGID | UV_PROCESS_SETGID |
UV_PROCESS_SETUID | UV_PROCESS_SETUID |
<<<<<<< HEAD
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME | UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME |
=======
UV_PROCESS_SETGROUPS |
>>>>>>> 20a263fd (unix: Support setting supplementary process groups)
UV_PROCESS_WINDOWS_HIDE | UV_PROCESS_WINDOWS_HIDE |
UV_PROCESS_WINDOWS_HIDE_CONSOLE | UV_PROCESS_WINDOWS_HIDE_CONSOLE |
UV_PROCESS_WINDOWS_HIDE_GUI | UV_PROCESS_WINDOWS_HIDE_GUI |

View File

@ -341,8 +341,12 @@ TEST_DECLARE (spawn_preserve_env)
TEST_DECLARE (spawn_same_stdout_stderr) TEST_DECLARE (spawn_same_stdout_stderr)
TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setuid_fails)
TEST_DECLARE (spawn_setgid_fails) TEST_DECLARE (spawn_setgid_fails)
<<<<<<< HEAD
TEST_DECLARE (spawn_affinity) TEST_DECLARE (spawn_affinity)
TEST_DECLARE (spawn_affinity_invalid_mask) TEST_DECLARE (spawn_affinity_invalid_mask)
=======
TEST_DECLARE (spawn_setgids_fails)
>>>>>>> 20a263fd (unix: Support setting supplementary process groups)
TEST_DECLARE (spawn_stdout_to_file) TEST_DECLARE (spawn_stdout_to_file)
TEST_DECLARE (spawn_stdout_and_stderr_to_file) TEST_DECLARE (spawn_stdout_and_stderr_to_file)
TEST_DECLARE (spawn_stdout_and_stderr_to_file2) TEST_DECLARE (spawn_stdout_and_stderr_to_file2)
@ -541,6 +545,7 @@ TEST_DECLARE (win32_signum_number)
#else #else
TEST_DECLARE (emfile) TEST_DECLARE (emfile)
TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (spawn_setuid_setgid)
TEST_DECLARE (spawn_setgids)
TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signal)
TEST_DECLARE (we_get_signals) TEST_DECLARE (we_get_signals)
TEST_DECLARE (we_get_signal_one_shot) TEST_DECLARE (we_get_signal_one_shot)
@ -1037,8 +1042,12 @@ TASK_LIST_START
TEST_ENTRY (spawn_same_stdout_stderr) TEST_ENTRY (spawn_same_stdout_stderr)
TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setuid_fails)
TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (spawn_setgid_fails)
<<<<<<< HEAD
TEST_ENTRY (spawn_affinity) TEST_ENTRY (spawn_affinity)
TEST_ENTRY (spawn_affinity_invalid_mask) TEST_ENTRY (spawn_affinity_invalid_mask)
=======
TEST_ENTRY (spawn_setgids_fails)
>>>>>>> 20a263fd (unix: Support setting supplementary process groups)
TEST_ENTRY (spawn_stdout_to_file) TEST_ENTRY (spawn_stdout_to_file)
TEST_ENTRY (spawn_stdout_and_stderr_to_file) TEST_ENTRY (spawn_stdout_and_stderr_to_file)
TEST_ENTRY (spawn_stdout_and_stderr_to_file2) TEST_ENTRY (spawn_stdout_and_stderr_to_file2)
@ -1084,6 +1093,7 @@ TASK_LIST_START
#else #else
TEST_ENTRY (emfile) TEST_ENTRY (emfile)
TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (spawn_setuid_setgid)
TEST_ENTRY (spawn_setgids)
TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signal)
TEST_ENTRY (we_get_signals) TEST_ENTRY (we_get_signals)
TEST_ENTRY (we_get_signal_one_shot) TEST_ENTRY (we_get_signal_one_shot)

View File

@ -1498,6 +1498,46 @@ TEST_IMPL(spawn_setuid_setgid) {
} }
#endif #endif
#ifndef _WIN32
TEST_IMPL(spawn_setgids) {
int r;
struct passwd* pw;
uv_gid_t gids[1];
/* if not root, then this will fail. */
uv_uid_t uid = getuid();
if (uid != 0) {
fprintf(stderr, "spawn_setgids skipped: not root\n");
return 0;
}
init_process_options("spawn_helper1", exit_cb);
pw = getpwnam("nobody");
ASSERT(pw != NULL);
gids[0] = pw->pw_gid;
options.gids = gids;
options.gids_sz = 1;
options.flags = UV_PROCESS_SETGROUPS;
r = uv_spawn(uv_default_loop(), &process, &options);
if (r == UV_EACCES)
RETURN_SKIP("user 'nobody' cannot access the test runner");
ASSERT(r == 0);
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(r == 0);
ASSERT(exit_cb_called == 1);
ASSERT(close_cb_called == 1);
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif
#ifndef _WIN32 #ifndef _WIN32
TEST_IMPL(spawn_setuid_fails) { TEST_IMPL(spawn_setuid_fails) {
@ -1594,6 +1634,39 @@ TEST_IMPL(spawn_setgid_fails) {
MAKE_VALGRIND_HAPPY(uv_default_loop()); MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0; return 0;
} }
TEST_IMPL(spawn_setgids_fails) {
int r;
uv_gid_t gids[1] = {0};
/* if root, become nobody. */
uv_uid_t uid = getuid();
if (uid == 0) {
struct passwd* pw;
pw = getpwnam("nobody");
ASSERT(pw != NULL);
ASSERT(0 == setgid(pw->pw_gid));
ASSERT(0 == setuid(pw->pw_uid));
}
init_process_options("spawn_helper1", fail_cb);
options.flags |= UV_PROCESS_SETGROUPS;
options.gids = gids;
options.gids_sz = 1;
r = uv_spawn(uv_default_loop(), &process, &options);
ASSERT(r == UV_EPERM);
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(r == 0);
ASSERT(close_cb_called == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}
#endif #endif
TEST_IMPL(spawn_affinity) { TEST_IMPL(spawn_affinity) {