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:
parent
612a8262b8
commit
efeaeb242d
@ -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
|
||||||
|
|||||||
21
include/uv.h
21
include/uv.h
@ -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)
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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 |
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user