From d0364db717b63d5480b080d74d7e6c00ac7b1b12 Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Wed, 30 Nov 2016 20:00:32 +0000 Subject: [PATCH] win: map ERROR_ELEVATION_REQUIRED to UV_EACCES uv_spawn() on Windows will fail with ERROR_ELEVATION_REQUIRED (740) if attempting to run an application that requires elevation. Add a Windows test that calls uv_spawn() with a helper .exe that requires elevated permissions. Test should either run successfully if run with Administrator privileges or fail with the mapped error. Refs: https://github.com/nodejs/node/issues/9464 --- src/win/error.c | 1 + test/require-elevation.c | 28 +++++++++++++++ test/test-error.c | 1 + test/test-list.h | 6 ++++ test/test-spawn.c | 76 ++++++++++++++++++++++++++++++++++++++++ uv.gyp | 26 ++++++++++++-- 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 test/require-elevation.c diff --git a/src/win/error.c b/src/win/error.c index c512f35af97..642d1112e11 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -71,6 +71,7 @@ int uv_translate_sys_error(int sys_errno) { switch (sys_errno) { case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; + case ERROR_ELEVATION_REQUIRED: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; diff --git a/test/require-elevation.c b/test/require-elevation.c new file mode 100644 index 00000000000..970b2c4664d --- /dev/null +++ b/test/require-elevation.c @@ -0,0 +1,28 @@ +/* Copyright libuv project and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +int main(int argc, char **argv) { + printf("Hello world\n"); + return 1; +} diff --git a/test/test-error.c b/test/test-error.c index 4c4efa30cdd..ff53eb1d777 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -53,6 +53,7 @@ TEST_IMPL(error_message) { TEST_IMPL(sys_error) { #if defined(_WIN32) ASSERT(uv_translate_sys_error(ERROR_NOACCESS) == UV_EACCES); + ASSERT(uv_translate_sys_error(ERROR_ELEVATION_REQUIRED) == UV_EACCES); ASSERT(uv_translate_sys_error(WSAEADDRINUSE) == UV_EADDRINUSE); ASSERT(uv_translate_sys_error(ERROR_BAD_PIPE) == UV_EPIPE); #else diff --git a/test/test-list.h b/test/test-list.h index be3f9069cc7..dce8d0aa24f 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -230,6 +230,9 @@ TEST_DECLARE (spawn_fails) #ifndef _WIN32 TEST_DECLARE (spawn_fails_check_for_waitpid_cleanup) #endif +#ifdef _WIN32 +TEST_DECLARE (spawn_requires_elevation) +#endif TEST_DECLARE (spawn_exit_code) TEST_DECLARE (spawn_stdout) TEST_DECLARE (spawn_stdin) @@ -656,6 +659,9 @@ TASK_LIST_START TEST_ENTRY (spawn_fails) #ifndef _WIN32 TEST_ENTRY (spawn_fails_check_for_waitpid_cleanup) +#endif +#ifdef _WIN32 + TEST_ENTRY (spawn_requires_elevation) #endif TEST_ENTRY (spawn_exit_code) TEST_ENTRY (spawn_stdout) diff --git a/test/test-spawn.c b/test/test-spawn.c index 53a036969b6..20c1d9f3bad 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -215,6 +215,82 @@ TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) { #endif +#ifdef _WIN32 +BOOL is_elevated() { + DWORD cb_size; + BOOL elevated; + TOKEN_ELEVATION elevation_token; + DWORD elevation_token_size; + HANDLE h_token; + + elevated = FALSE; + h_token = NULL; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h_token)) { + cb_size = sizeof(TOKEN_ELEVATION); + elevation_token_size = sizeof(elevation_token); + if (GetTokenInformation(h_token, TokenElevation, &elevation_token, + elevation_token_size, &cb_size)) { + elevated = elevation_token.TokenIsElevated; + } + } + if (h_token) { + CloseHandle(h_token); + } + return elevated; +} + + +TEST_IMPL(spawn_requires_elevation) { + int r; + uv_fs_t req; + char path[1024]; + + if (is_elevated()) { + RETURN_SKIP("Process is already elevated"); + } + + /* Find helper .exe -- Assumed to be in same location as test runner */ + r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + snprintf(path, sizeof(path), "%s\\..\\require-elevation.exe", exepath); + r = uv_fs_realpath(uv_default_loop(), &req, path, NULL); + ASSERT(r == 0); + + /* + * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() + */ + if (req.result == UV_ENOSYS) { + uv_fs_req_cleanup(&req); + RETURN_SKIP("realpath is not supported on Windows XP"); + } + + ASSERT(req.ptr != NULL); + + init_process_options("", fail_cb); + options.file = options.args[0] = req.ptr; + + printf("Launching %s\n", (char*) req.ptr); + r = uv_spawn(uv_default_loop(), &process, &options); + if (r != 0) { + printf("r == %s - %s\n", uv_err_name(r), uv_strerror(r)); + } + + ASSERT(r == UV_EACCES); + r = uv_is_active((uv_handle_t*) &process); + ASSERT(r == 0); + uv_close((uv_handle_t*) &process, NULL); + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif + + TEST_IMPL(spawn_exit_code) { int r; diff --git a/uv.gyp b/uv.gyp index b969a8cd5da..903e6d7b5b4 100644 --- a/uv.gyp +++ b/uv.gyp @@ -311,7 +311,6 @@ }], ] }, - { 'target_name': 'run-tests', 'type': 'executable', @@ -569,5 +568,28 @@ }, }, }, - ] + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'require-elevation', + 'type': 'executable', + 'sources': [ + 'test/require-elevation.c' + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + 'UACExecutionLevel': 2, # requireAdministrator + 'UACUIAccess': 'false', + }, + 'VCManifestTool': { + 'EmbedManifest': 'true', + } + }, + } + ], + }], + ], }