process: move POSIX credential accessors into node_credentials.cc
Expose the POSIX credential accessors through `internalBinding('credentials')` instead of setting them on the process or bootstrapper object from C++ directly. Also moves `SafeGetEnv` from `internalBinding('util')` to `internalBinding('credentials')` since it's closely related to the credentials. In the JS land, instead of wrapping the bindings then writing to the process object directly in main_thread_only.js, return the wrapped functions back to bootstrap/node.js where they get written to the process object conditionally for clarity. Refs: https://github.com/nodejs/node/issues/24961 PR-URL: https://github.com/nodejs/node/pull/25066 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
74a1dfb56e
commit
321e296371
@ -23,8 +23,7 @@ const {
|
|||||||
_setupPromises, _chdir, _cpuUsage,
|
_setupPromises, _chdir, _cpuUsage,
|
||||||
_hrtime, _hrtimeBigInt,
|
_hrtime, _hrtimeBigInt,
|
||||||
_memoryUsage, _rawDebug,
|
_memoryUsage, _rawDebug,
|
||||||
_umask, _initgroups, _setegid, _seteuid,
|
_umask,
|
||||||
_setgid, _setuid, _setgroups,
|
|
||||||
_shouldAbortOnUncaughtToggle
|
_shouldAbortOnUncaughtToggle
|
||||||
} = bootstrappers;
|
} = bootstrappers;
|
||||||
const { internalBinding, NativeModule } = loaderExports;
|
const { internalBinding, NativeModule } = loaderExports;
|
||||||
@ -72,13 +71,28 @@ function startup() {
|
|||||||
NativeModule.require('internal/process/warning').setup();
|
NativeModule.require('internal/process/warning').setup();
|
||||||
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
|
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
|
||||||
_setupPromises);
|
_setupPromises);
|
||||||
|
const credentials = internalBinding('credentials');
|
||||||
|
if (credentials.implementsPosixCredentials) {
|
||||||
|
process.getuid = credentials.getuid;
|
||||||
|
process.geteuid = credentials.geteuid;
|
||||||
|
process.getgid = credentials.getgid;
|
||||||
|
process.getegid = credentials.getegid;
|
||||||
|
process.getgroups = credentials.getgroups;
|
||||||
|
|
||||||
|
if (isMainThread) {
|
||||||
|
const wrapped = mainThreadSetup.wrapPosixCredentialSetters(credentials);
|
||||||
|
process.initgroups = wrapped.initgroups;
|
||||||
|
process.setgroups = wrapped.setgroups;
|
||||||
|
process.setegid = wrapped.setegid;
|
||||||
|
process.seteuid = wrapped.seteuid;
|
||||||
|
process.setgid = wrapped.setgid;
|
||||||
|
process.setuid = wrapped.setuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
mainThreadSetup.setupStdio();
|
mainThreadSetup.setupStdio();
|
||||||
mainThreadSetup.setupProcessMethods(
|
mainThreadSetup.setupProcessMethods(_chdir, _umask);
|
||||||
_chdir, _umask, _initgroups, _setegid, _seteuid,
|
|
||||||
_setgid, _setuid, _setgroups
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
workerThreadSetup.setupStdio();
|
workerThreadSetup.setupStdio();
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ const {
|
|||||||
internalModuleReadJSON,
|
internalModuleReadJSON,
|
||||||
internalModuleStat
|
internalModuleStat
|
||||||
} = internalBinding('fs');
|
} = internalBinding('fs');
|
||||||
const { safeGetenv } = internalBinding('util');
|
const { safeGetenv } = internalBinding('credentials');
|
||||||
const {
|
const {
|
||||||
makeRequireFunction,
|
makeRequireFunction,
|
||||||
requireDepth,
|
requireDepth,
|
||||||
|
@ -29,13 +29,7 @@ function setupStdio() {
|
|||||||
|
|
||||||
// Non-POSIX platforms like Windows don't have certain methods.
|
// Non-POSIX platforms like Windows don't have certain methods.
|
||||||
// Workers also lack these methods since they change process-global state.
|
// Workers also lack these methods since they change process-global state.
|
||||||
function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
function setupProcessMethods(_chdir, _umask) {
|
||||||
_seteuid, _setgid, _setuid, _setgroups) {
|
|
||||||
if (_setgid !== undefined) {
|
|
||||||
setupPosixMethods(_initgroups, _setegid, _seteuid,
|
|
||||||
_setgid, _setuid, _setgroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.chdir = function chdir(directory) {
|
process.chdir = function chdir(directory) {
|
||||||
validateString(directory, 'directory');
|
validateString(directory, 'directory');
|
||||||
return _chdir(directory);
|
return _chdir(directory);
|
||||||
@ -51,10 +45,17 @@ function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
function wrapPosixCredentialSetters(credentials) {
|
||||||
_setgid, _setuid, _setgroups) {
|
const {
|
||||||
|
initgroups: _initgroups,
|
||||||
|
setgroups: _setgroups,
|
||||||
|
setegid: _setegid,
|
||||||
|
seteuid: _seteuid,
|
||||||
|
setgid: _setgid,
|
||||||
|
setuid: _setuid
|
||||||
|
} = credentials;
|
||||||
|
|
||||||
process.initgroups = function initgroups(user, extraGroup) {
|
function initgroups(user, extraGroup) {
|
||||||
validateId(user, 'user');
|
validateId(user, 'user');
|
||||||
validateId(extraGroup, 'extraGroup');
|
validateId(extraGroup, 'extraGroup');
|
||||||
// Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
|
// Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
|
||||||
@ -64,25 +65,9 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
|||||||
} else if (result === 2) {
|
} else if (result === 2) {
|
||||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
|
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
process.setegid = function setegid(id) {
|
function setgroups(groups) {
|
||||||
return execId(id, 'Group', _setegid);
|
|
||||||
};
|
|
||||||
|
|
||||||
process.seteuid = function seteuid(id) {
|
|
||||||
return execId(id, 'User', _seteuid);
|
|
||||||
};
|
|
||||||
|
|
||||||
process.setgid = function setgid(id) {
|
|
||||||
return execId(id, 'Group', _setgid);
|
|
||||||
};
|
|
||||||
|
|
||||||
process.setuid = function setuid(id) {
|
|
||||||
return execId(id, 'User', _setuid);
|
|
||||||
};
|
|
||||||
|
|
||||||
process.setgroups = function setgroups(groups) {
|
|
||||||
if (!Array.isArray(groups)) {
|
if (!Array.isArray(groups)) {
|
||||||
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
|
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
|
||||||
}
|
}
|
||||||
@ -95,15 +80,17 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
|||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
|
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function execId(id, type, method) {
|
function wrapIdSetter(type, method) {
|
||||||
|
return function(id) {
|
||||||
validateId(id, 'id');
|
validateId(id, 'id');
|
||||||
// Result is 0 on success, 1 if credential is unknown.
|
// Result is 0 on success, 1 if credential is unknown.
|
||||||
const result = method(id);
|
const result = method(id);
|
||||||
if (result === 1) {
|
if (result === 1) {
|
||||||
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateId(id, name) {
|
function validateId(id, name) {
|
||||||
@ -113,6 +100,15 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
|||||||
throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
|
throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
initgroups,
|
||||||
|
setgroups,
|
||||||
|
setegid: wrapIdSetter('Group', _setegid),
|
||||||
|
seteuid: wrapIdSetter('User', _seteuid),
|
||||||
|
setgid: wrapIdSetter('Group', _setgid),
|
||||||
|
setuid: wrapIdSetter('User', _setuid)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Worker threads don't receive signals.
|
// Worker threads don't receive signals.
|
||||||
@ -181,5 +177,6 @@ module.exports = {
|
|||||||
setupStdio,
|
setupStdio,
|
||||||
setupProcessMethods,
|
setupProcessMethods,
|
||||||
setupSignalHandlers,
|
setupSignalHandlers,
|
||||||
setupChildProcessIpcChannel
|
setupChildProcessIpcChannel,
|
||||||
|
wrapPosixCredentialSetters
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { safeGetenv } = internalBinding('util');
|
const { safeGetenv } = internalBinding('credentials');
|
||||||
const constants = internalBinding('constants').os;
|
const constants = internalBinding('constants').os;
|
||||||
const { deprecate } = require('internal/util');
|
const { deprecate } = require('internal/util');
|
||||||
const isWindows = process.platform === 'win32';
|
const isWindows = process.platform === 'win32';
|
||||||
|
1
node.gyp
1
node.gyp
@ -350,6 +350,7 @@
|
|||||||
'src/node_config.cc',
|
'src/node_config.cc',
|
||||||
'src/node_constants.cc',
|
'src/node_constants.cc',
|
||||||
'src/node_contextify.cc',
|
'src/node_contextify.cc',
|
||||||
|
'src/node_credentials.cc',
|
||||||
'src/node_domain.cc',
|
'src/node_domain.cc',
|
||||||
'src/node_encoding.cc',
|
'src/node_encoding.cc',
|
||||||
'src/node_env_var.cc',
|
'src/node_env_var.cc',
|
||||||
|
@ -147,17 +147,6 @@ void SetupBootstrapObject(Environment* env,
|
|||||||
BOOTSTRAP_METHOD(_rawDebug, RawDebug);
|
BOOTSTRAP_METHOD(_rawDebug, RawDebug);
|
||||||
BOOTSTRAP_METHOD(_umask, Umask);
|
BOOTSTRAP_METHOD(_umask, Umask);
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
if (env->is_main_thread()) {
|
|
||||||
BOOTSTRAP_METHOD(_initgroups, InitGroups);
|
|
||||||
BOOTSTRAP_METHOD(_setegid, SetEGid);
|
|
||||||
BOOTSTRAP_METHOD(_seteuid, SetEUid);
|
|
||||||
BOOTSTRAP_METHOD(_setgid, SetGid);
|
|
||||||
BOOTSTRAP_METHOD(_setuid, SetUid);
|
|
||||||
BOOTSTRAP_METHOD(_setgroups, SetGroups);
|
|
||||||
}
|
|
||||||
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
|
|
||||||
Local<String> should_abort_on_uncaught_toggle =
|
Local<String> should_abort_on_uncaught_toggle =
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle");
|
FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle");
|
||||||
CHECK(bootstrapper->Set(env->context(),
|
CHECK(bootstrapper->Set(env->context(),
|
||||||
|
@ -225,7 +225,7 @@ Environment::Environment(IsolateData* isolate_data,
|
|||||||
should_abort_on_uncaught_toggle_[0] = 1;
|
should_abort_on_uncaught_toggle_[0] = 1;
|
||||||
|
|
||||||
std::string debug_cats;
|
std::string debug_cats;
|
||||||
SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
|
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
|
||||||
set_debug_categories(debug_cats, true);
|
set_debug_categories(debug_cats, true);
|
||||||
|
|
||||||
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
|
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
|
||||||
|
57
src/node.cc
57
src/node.cc
@ -100,12 +100,7 @@ typedef int mode_t;
|
|||||||
#else
|
#else
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/resource.h> // getrlimit, setrlimit
|
#include <sys/resource.h> // getrlimit, setrlimit
|
||||||
#include <unistd.h> // setuid, getuid
|
#include <unistd.h> // STDIN_FILENO, STDERR_FILENO
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
#include <pwd.h> // getpwnam()
|
|
||||||
#include <grp.h> // getgrnam()
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
@ -153,8 +148,6 @@ unsigned int reverted = 0;
|
|||||||
|
|
||||||
bool v8_initialized = false;
|
bool v8_initialized = false;
|
||||||
|
|
||||||
bool linux_at_secure = false;
|
|
||||||
|
|
||||||
// process-relative uptime base, initialized at start-up
|
// process-relative uptime base, initialized at start-up
|
||||||
double prog_start_time;
|
double prog_start_time;
|
||||||
|
|
||||||
@ -501,27 +494,6 @@ const char* signo_string(int signo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up environment variable unless running as setuid root.
|
|
||||||
bool SafeGetenv(const char* key, std::string* text) {
|
|
||||||
#if !defined(__CloudABI__) && !defined(_WIN32)
|
|
||||||
if (linux_at_secure || getuid() != geteuid() || getgid() != getegid())
|
|
||||||
goto fail;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::ScopedLock lock(environ_mutex);
|
|
||||||
if (const char* value = getenv(key)) {
|
|
||||||
*text = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fail:
|
|
||||||
text->clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void* ArrayBufferAllocator::Allocate(size_t size) {
|
void* ArrayBufferAllocator::Allocate(size_t size) {
|
||||||
if (zero_fill_field_ || per_process_opts->zero_fill_all_buffers)
|
if (zero_fill_field_ || per_process_opts->zero_fill_all_buffers)
|
||||||
return UncheckedCalloc(size);
|
return UncheckedCalloc(size);
|
||||||
@ -1157,14 +1129,6 @@ void SetupProcessObject(Environment* env,
|
|||||||
env->SetMethod(process, "dlopen", binding::DLOpen);
|
env->SetMethod(process, "dlopen", binding::DLOpen);
|
||||||
env->SetMethod(process, "reallyExit", Exit);
|
env->SetMethod(process, "reallyExit", Exit);
|
||||||
env->SetMethodNoSideEffect(process, "uptime", Uptime);
|
env->SetMethodNoSideEffect(process, "uptime", Uptime);
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
env->SetMethodNoSideEffect(process, "getuid", GetUid);
|
|
||||||
env->SetMethodNoSideEffect(process, "geteuid", GetEUid);
|
|
||||||
env->SetMethodNoSideEffect(process, "getgid", GetGid);
|
|
||||||
env->SetMethodNoSideEffect(process, "getegid", GetEGid);
|
|
||||||
env->SetMethodNoSideEffect(process, "getgroups", GetGroups);
|
|
||||||
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1625,37 +1589,40 @@ void Init(std::vector<std::string>* argv,
|
|||||||
{
|
{
|
||||||
std::string text;
|
std::string text;
|
||||||
default_env_options->pending_deprecation =
|
default_env_options->pending_deprecation =
|
||||||
SafeGetenv("NODE_PENDING_DEPRECATION", &text) && text[0] == '1';
|
credentials::SafeGetenv("NODE_PENDING_DEPRECATION", &text) &&
|
||||||
|
text[0] == '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow for environment set preserving symlinks.
|
// Allow for environment set preserving symlinks.
|
||||||
{
|
{
|
||||||
std::string text;
|
std::string text;
|
||||||
default_env_options->preserve_symlinks =
|
default_env_options->preserve_symlinks =
|
||||||
SafeGetenv("NODE_PRESERVE_SYMLINKS", &text) && text[0] == '1';
|
credentials::SafeGetenv("NODE_PRESERVE_SYMLINKS", &text) &&
|
||||||
|
text[0] == '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string text;
|
std::string text;
|
||||||
default_env_options->preserve_symlinks_main =
|
default_env_options->preserve_symlinks_main =
|
||||||
SafeGetenv("NODE_PRESERVE_SYMLINKS_MAIN", &text) && text[0] == '1';
|
credentials::SafeGetenv("NODE_PRESERVE_SYMLINKS_MAIN", &text) &&
|
||||||
|
text[0] == '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (default_env_options->redirect_warnings.empty()) {
|
if (default_env_options->redirect_warnings.empty()) {
|
||||||
SafeGetenv("NODE_REDIRECT_WARNINGS",
|
credentials::SafeGetenv("NODE_REDIRECT_WARNINGS",
|
||||||
&default_env_options->redirect_warnings);
|
&default_env_options->redirect_warnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
std::string* openssl_config = &per_process_opts->openssl_config;
|
std::string* openssl_config = &per_process_opts->openssl_config;
|
||||||
if (openssl_config->empty()) {
|
if (openssl_config->empty()) {
|
||||||
SafeGetenv("OPENSSL_CONF", openssl_config);
|
credentials::SafeGetenv("OPENSSL_CONF", openssl_config);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
|
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
|
||||||
std::string node_options;
|
std::string node_options;
|
||||||
if (SafeGetenv("NODE_OPTIONS", &node_options)) {
|
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
|
||||||
std::vector<std::string> env_argv;
|
std::vector<std::string> env_argv;
|
||||||
// [0] is expected to be the program name, fill it in from the real argv.
|
// [0] is expected to be the program name, fill it in from the real argv.
|
||||||
env_argv.push_back(argv->at(0));
|
env_argv.push_back(argv->at(0));
|
||||||
@ -1687,7 +1654,7 @@ void Init(std::vector<std::string>* argv,
|
|||||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||||
// If the parameter isn't given, use the env variable.
|
// If the parameter isn't given, use the env variable.
|
||||||
if (per_process_opts->icu_data_dir.empty())
|
if (per_process_opts->icu_data_dir.empty())
|
||||||
SafeGetenv("NODE_ICU_DATA", &per_process_opts->icu_data_dir);
|
credentials::SafeGetenv("NODE_ICU_DATA", &per_process_opts->icu_data_dir);
|
||||||
// Initialize ICU.
|
// Initialize ICU.
|
||||||
// If icu_data_dir is empty here, it will load the 'minimal' data.
|
// If icu_data_dir is empty here, it will load the 'minimal' data.
|
||||||
if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
|
if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
|
||||||
@ -2095,7 +2062,7 @@ int Start(int argc, char** argv) {
|
|||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
{
|
{
|
||||||
std::string extra_ca_certs;
|
std::string extra_ca_certs;
|
||||||
if (SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
|
if (credentials::SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
|
||||||
crypto::UseExtraCaCerts(extra_ca_certs);
|
crypto::UseExtraCaCerts(extra_ca_certs);
|
||||||
}
|
}
|
||||||
#ifdef NODE_FIPS_MODE
|
#ifdef NODE_FIPS_MODE
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
V(cares_wrap) \
|
V(cares_wrap) \
|
||||||
V(config) \
|
V(config) \
|
||||||
V(contextify) \
|
V(contextify) \
|
||||||
|
V(credentials) \
|
||||||
V(domain) \
|
V(domain) \
|
||||||
V(fs) \
|
V(fs) \
|
||||||
V(fs_event_wrap) \
|
V(fs_event_wrap) \
|
||||||
|
395
src/node_credentials.cc
Normal file
395
src/node_credentials.cc
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
#include "node_internals.h"
|
||||||
|
|
||||||
|
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
#include <grp.h> // getgrnam()
|
||||||
|
#include <pwd.h> // getpwnam()
|
||||||
|
#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER)
|
||||||
|
#include <unistd.h> // setuid, getuid
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
|
||||||
|
using v8::Array;
|
||||||
|
using v8::Context;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::Integer;
|
||||||
|
using v8::Isolate;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::String;
|
||||||
|
using v8::Uint32;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
namespace per_process {
|
||||||
|
bool linux_at_secure = false;
|
||||||
|
} // namespace per_process
|
||||||
|
|
||||||
|
namespace credentials {
|
||||||
|
|
||||||
|
// Look up environment variable unless running as setuid root.
|
||||||
|
bool SafeGetenv(const char* key, std::string* text) {
|
||||||
|
#if !defined(__CloudABI__) && !defined(_WIN32)
|
||||||
|
if (per_process::linux_at_secure || getuid() != geteuid() ||
|
||||||
|
getgid() != getegid())
|
||||||
|
goto fail;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
Mutex::ScopedLock lock(environ_mutex);
|
||||||
|
if (const char* value = getenv(key)) {
|
||||||
|
*text = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
text->clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
CHECK(args[0]->IsString());
|
||||||
|
Isolate* isolate = args.GetIsolate();
|
||||||
|
Utf8Value strenvtag(isolate, args[0]);
|
||||||
|
std::string text;
|
||||||
|
if (!SafeGetenv(*strenvtag, &text)) return;
|
||||||
|
Local<Value> result =
|
||||||
|
ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
|
||||||
|
args.GetReturnValue().Set(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
|
||||||
|
static const uid_t uid_not_found = static_cast<uid_t>(-1);
|
||||||
|
static const gid_t gid_not_found = static_cast<gid_t>(-1);
|
||||||
|
|
||||||
|
static uid_t uid_by_name(const char* name) {
|
||||||
|
struct passwd pwd;
|
||||||
|
struct passwd* pp;
|
||||||
|
char buf[8192];
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pp = nullptr;
|
||||||
|
|
||||||
|
if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
||||||
|
return pp->pw_uid;
|
||||||
|
|
||||||
|
return uid_not_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* name_by_uid(uid_t uid) {
|
||||||
|
struct passwd pwd;
|
||||||
|
struct passwd* pp;
|
||||||
|
char buf[8192];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pp = nullptr;
|
||||||
|
|
||||||
|
if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
||||||
|
pp != nullptr) {
|
||||||
|
return strdup(pp->pw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0) errno = ENOENT;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gid_t gid_by_name(const char* name) {
|
||||||
|
struct group pwd;
|
||||||
|
struct group* pp;
|
||||||
|
char buf[8192];
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pp = nullptr;
|
||||||
|
|
||||||
|
if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
||||||
|
return pp->gr_gid;
|
||||||
|
|
||||||
|
return gid_not_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 // For future use.
|
||||||
|
static const char* name_by_gid(gid_t gid) {
|
||||||
|
struct group pwd;
|
||||||
|
struct group* pp;
|
||||||
|
char buf[8192];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
pp = nullptr;
|
||||||
|
|
||||||
|
if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
||||||
|
pp != nullptr) {
|
||||||
|
return strdup(pp->gr_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
errno = ENOENT;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
|
||||||
|
if (value->IsUint32()) {
|
||||||
|
return static_cast<uid_t>(value.As<Uint32>()->Value());
|
||||||
|
} else {
|
||||||
|
Utf8Value name(isolate, value);
|
||||||
|
return uid_by_name(*name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
|
||||||
|
if (value->IsUint32()) {
|
||||||
|
return static_cast<gid_t>(value.As<Uint32>()->Value());
|
||||||
|
} else {
|
||||||
|
Utf8Value name(isolate, value);
|
||||||
|
return gid_by_name(*name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetUid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// uid_t is an uint32_t on all supported platforms.
|
||||||
|
args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetGid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// gid_t is an uint32_t on all supported platforms.
|
||||||
|
args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetEUid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// uid_t is an uint32_t on all supported platforms.
|
||||||
|
args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetEGid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
// gid_t is an uint32_t on all supported platforms.
|
||||||
|
args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetGid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK(env->is_main_thread());
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||||
|
|
||||||
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
||||||
|
|
||||||
|
if (gid == gid_not_found) {
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
args.GetReturnValue().Set(1);
|
||||||
|
} else if (setgid(gid)) {
|
||||||
|
env->ThrowErrnoException(errno, "setgid");
|
||||||
|
} else {
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK(env->is_main_thread());
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||||
|
|
||||||
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
||||||
|
|
||||||
|
if (gid == gid_not_found) {
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
args.GetReturnValue().Set(1);
|
||||||
|
} else if (setegid(gid)) {
|
||||||
|
env->ThrowErrnoException(errno, "setegid");
|
||||||
|
} else {
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetUid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK(env->is_main_thread());
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||||
|
|
||||||
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
||||||
|
|
||||||
|
if (uid == uid_not_found) {
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
args.GetReturnValue().Set(1);
|
||||||
|
} else if (setuid(uid)) {
|
||||||
|
env->ThrowErrnoException(errno, "setuid");
|
||||||
|
} else {
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
CHECK(env->is_main_thread());
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||||
|
|
||||||
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
||||||
|
|
||||||
|
if (uid == uid_not_found) {
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
args.GetReturnValue().Set(1);
|
||||||
|
} else if (seteuid(uid)) {
|
||||||
|
env->ThrowErrnoException(errno, "seteuid");
|
||||||
|
} else {
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
int ngroups = getgroups(0, nullptr);
|
||||||
|
|
||||||
|
if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups");
|
||||||
|
|
||||||
|
gid_t* groups = new gid_t[ngroups];
|
||||||
|
|
||||||
|
ngroups = getgroups(ngroups, groups);
|
||||||
|
|
||||||
|
if (ngroups == -1) {
|
||||||
|
delete[] groups;
|
||||||
|
return env->ThrowErrnoException(errno, "getgroups");
|
||||||
|
}
|
||||||
|
|
||||||
|
Local<Array> groups_list = Array::New(env->isolate(), ngroups);
|
||||||
|
bool seen_egid = false;
|
||||||
|
gid_t egid = getegid();
|
||||||
|
|
||||||
|
for (int i = 0; i < ngroups; i++) {
|
||||||
|
groups_list->Set(env->context(), i, Integer::New(env->isolate(), groups[i]))
|
||||||
|
.FromJust();
|
||||||
|
if (groups[i] == egid) seen_egid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] groups;
|
||||||
|
|
||||||
|
if (seen_egid == false)
|
||||||
|
groups_list
|
||||||
|
->Set(env->context(), ngroups, Integer::New(env->isolate(), egid))
|
||||||
|
.FromJust();
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(groups_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 1);
|
||||||
|
CHECK(args[0]->IsArray());
|
||||||
|
|
||||||
|
Local<Array> groups_list = args[0].As<Array>();
|
||||||
|
size_t size = groups_list->Length();
|
||||||
|
gid_t* groups = new gid_t[size];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
gid_t gid = gid_by_name(
|
||||||
|
env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked());
|
||||||
|
|
||||||
|
if (gid == gid_not_found) {
|
||||||
|
delete[] groups;
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[i] = gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = setgroups(size, groups);
|
||||||
|
delete[] groups;
|
||||||
|
|
||||||
|
if (rc == -1) return env->ThrowErrnoException(errno, "setgroups");
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
CHECK_EQ(args.Length(), 2);
|
||||||
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
||||||
|
CHECK(args[1]->IsUint32() || args[1]->IsString());
|
||||||
|
|
||||||
|
Utf8Value arg0(env->isolate(), args[0]);
|
||||||
|
gid_t extra_group;
|
||||||
|
bool must_free;
|
||||||
|
char* user;
|
||||||
|
|
||||||
|
if (args[0]->IsUint32()) {
|
||||||
|
user = name_by_uid(args[0].As<Uint32>()->Value());
|
||||||
|
must_free = true;
|
||||||
|
} else {
|
||||||
|
user = *arg0;
|
||||||
|
must_free = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == nullptr) {
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
return args.GetReturnValue().Set(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_group = gid_by_name(env->isolate(), args[1]);
|
||||||
|
|
||||||
|
if (extra_group == gid_not_found) {
|
||||||
|
if (must_free) free(user);
|
||||||
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
||||||
|
return args.GetReturnValue().Set(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = initgroups(user, extra_group);
|
||||||
|
|
||||||
|
if (must_free) free(user);
|
||||||
|
|
||||||
|
if (rc) return env->ThrowErrnoException(errno, "initgroups");
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
|
||||||
|
static void Initialize(Local<Object> target,
|
||||||
|
Local<Value> unused,
|
||||||
|
Local<Context> context,
|
||||||
|
void* priv) {
|
||||||
|
Environment* env = Environment::GetCurrent(context);
|
||||||
|
Isolate* isolate = env->isolate();
|
||||||
|
|
||||||
|
env->SetMethod(target, "safeGetenv", SafeGetenv);
|
||||||
|
|
||||||
|
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials");
|
||||||
|
env->SetMethodNoSideEffect(target, "getuid", GetUid);
|
||||||
|
env->SetMethodNoSideEffect(target, "geteuid", GetEUid);
|
||||||
|
env->SetMethodNoSideEffect(target, "getgid", GetGid);
|
||||||
|
env->SetMethodNoSideEffect(target, "getegid", GetEGid);
|
||||||
|
env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
|
||||||
|
|
||||||
|
if (env->is_main_thread()) {
|
||||||
|
env->SetMethod(target, "initgroups", InitGroups);
|
||||||
|
env->SetMethod(target, "setegid", SetEGid);
|
||||||
|
env->SetMethod(target, "seteuid", SetEUid);
|
||||||
|
env->SetMethod(target, "setgid", SetGid);
|
||||||
|
env->SetMethod(target, "setuid", SetUid);
|
||||||
|
env->SetMethod(target, "setgroups", SetGroups);
|
||||||
|
}
|
||||||
|
#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace credentials
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
NODE_MODULE_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)
|
@ -126,7 +126,6 @@ void RegisterSignalHandler(int signal,
|
|||||||
bool reset_handler = false);
|
bool reset_handler = false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool SafeGetenv(const char* key, std::string* text);
|
|
||||||
v8::Local<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
|
v8::Local<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
|
||||||
v8::Isolate* isolate,
|
v8::Isolate* isolate,
|
||||||
v8::Local<v8::Value> data);
|
v8::Local<v8::Value> data);
|
||||||
@ -734,19 +733,13 @@ void ProcessTitleSetter(v8::Local<v8::Name> property,
|
|||||||
const v8::PropertyCallbackInfo<void>& info);
|
const v8::PropertyCallbackInfo<void>& info);
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
||||||
void SetGid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
#define NODE_IMPLEMENTS_POSIX_CREDENTIALS 1
|
||||||
void SetEGid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void SetUid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void SetEUid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void SetGroups(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void InitGroups(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void GetUid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void GetGid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void GetEUid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void GetEGid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
void GetGroups(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
||||||
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
||||||
|
|
||||||
|
namespace credentials {
|
||||||
|
bool SafeGetenv(const char* key, std::string* text);
|
||||||
|
} // namespace credentials
|
||||||
|
|
||||||
void DefineZlibConstants(v8::Local<v8::Object> target);
|
void DefineZlibConstants(v8::Local<v8::Object> target);
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
@ -88,7 +88,9 @@ extern char** environ;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
namespace per_process {
|
||||||
extern bool linux_at_secure;
|
extern bool linux_at_secure;
|
||||||
|
} // namespace per_process
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
@ -112,7 +114,7 @@ int main(int argc, char* argv[]) {
|
|||||||
Elf_auxv_t* auxv = reinterpret_cast<Elf_auxv_t*>(envp);
|
Elf_auxv_t* auxv = reinterpret_cast<Elf_auxv_t*>(envp);
|
||||||
for (; auxv->a_type != AT_NULL; auxv++) {
|
for (; auxv->a_type != AT_NULL; auxv++) {
|
||||||
if (auxv->a_type == AT_SECURE) {
|
if (auxv->a_type == AT_SECURE) {
|
||||||
node::linux_at_secure = auxv->a_un.a_val;
|
node::per_process::linux_at_secure = auxv->a_un.a_val;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,6 @@ typedef int mode_t;
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/resource.h> // getrlimit, setrlimit
|
#include <sys/resource.h> // getrlimit, setrlimit
|
||||||
#include <termios.h> // tcgetattr, tcsetattr
|
#include <termios.h> // tcgetattr, tcsetattr
|
||||||
#include <unistd.h> // setuid, getuid
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
#include <pwd.h> // getpwnam()
|
|
||||||
#include <grp.h> // getgrnam()
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
@ -42,7 +36,6 @@ using v8::Float64Array;
|
|||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::FunctionCallbackInfo;
|
using v8::FunctionCallbackInfo;
|
||||||
using v8::HeapStatistics;
|
using v8::HeapStatistics;
|
||||||
using v8::Integer;
|
|
||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::Name;
|
using v8::Name;
|
||||||
@ -254,331 +247,6 @@ void Uptime(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(uptime / 1000);
|
args.GetReturnValue().Set(uptime / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
|
|
||||||
static const uid_t uid_not_found = static_cast<uid_t>(-1);
|
|
||||||
static const gid_t gid_not_found = static_cast<gid_t>(-1);
|
|
||||||
|
|
||||||
|
|
||||||
static uid_t uid_by_name(const char* name) {
|
|
||||||
struct passwd pwd;
|
|
||||||
struct passwd* pp;
|
|
||||||
char buf[8192];
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
pp = nullptr;
|
|
||||||
|
|
||||||
if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
|
||||||
return pp->pw_uid;
|
|
||||||
|
|
||||||
return uid_not_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char* name_by_uid(uid_t uid) {
|
|
||||||
struct passwd pwd;
|
|
||||||
struct passwd* pp;
|
|
||||||
char buf[8192];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
pp = nullptr;
|
|
||||||
|
|
||||||
if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
||||||
pp != nullptr) {
|
|
||||||
return strdup(pp->pw_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == 0)
|
|
||||||
errno = ENOENT;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static gid_t gid_by_name(const char* name) {
|
|
||||||
struct group pwd;
|
|
||||||
struct group* pp;
|
|
||||||
char buf[8192];
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
pp = nullptr;
|
|
||||||
|
|
||||||
if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
|
||||||
return pp->gr_gid;
|
|
||||||
|
|
||||||
return gid_not_found;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 0 // For future use.
|
|
||||||
static const char* name_by_gid(gid_t gid) {
|
|
||||||
struct group pwd;
|
|
||||||
struct group* pp;
|
|
||||||
char buf[8192];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
pp = nullptr;
|
|
||||||
|
|
||||||
if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
||||||
pp != nullptr) {
|
|
||||||
return strdup(pp->gr_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc == 0)
|
|
||||||
errno = ENOENT;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
|
|
||||||
if (value->IsUint32()) {
|
|
||||||
return static_cast<uid_t>(value.As<Uint32>()->Value());
|
|
||||||
} else {
|
|
||||||
Utf8Value name(isolate, value);
|
|
||||||
return uid_by_name(*name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
|
|
||||||
if (value->IsUint32()) {
|
|
||||||
return static_cast<gid_t>(value.As<Uint32>()->Value());
|
|
||||||
} else {
|
|
||||||
Utf8Value name(isolate, value);
|
|
||||||
return gid_by_name(*name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetUid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
// uid_t is an uint32_t on all supported platforms.
|
|
||||||
args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GetGid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
// gid_t is an uint32_t on all supported platforms.
|
|
||||||
args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
// uid_t is an uint32_t on all supported platforms.
|
|
||||||
args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
// gid_t is an uint32_t on all supported platforms.
|
|
||||||
args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetGid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
CHECK(env->is_main_thread());
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 1);
|
|
||||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
||||||
|
|
||||||
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
||||||
|
|
||||||
if (gid == gid_not_found) {
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
args.GetReturnValue().Set(1);
|
|
||||||
} else if (setgid(gid)) {
|
|
||||||
env->ThrowErrnoException(errno, "setgid");
|
|
||||||
} else {
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
CHECK(env->is_main_thread());
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 1);
|
|
||||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
||||||
|
|
||||||
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
||||||
|
|
||||||
if (gid == gid_not_found) {
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
args.GetReturnValue().Set(1);
|
|
||||||
} else if (setegid(gid)) {
|
|
||||||
env->ThrowErrnoException(errno, "setegid");
|
|
||||||
} else {
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetUid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
CHECK(env->is_main_thread());
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 1);
|
|
||||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
||||||
|
|
||||||
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
||||||
|
|
||||||
if (uid == uid_not_found) {
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
args.GetReturnValue().Set(1);
|
|
||||||
} else if (setuid(uid)) {
|
|
||||||
env->ThrowErrnoException(errno, "setuid");
|
|
||||||
} else {
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
CHECK(env->is_main_thread());
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 1);
|
|
||||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
||||||
|
|
||||||
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
||||||
|
|
||||||
if (uid == uid_not_found) {
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
args.GetReturnValue().Set(1);
|
|
||||||
} else if (seteuid(uid)) {
|
|
||||||
env->ThrowErrnoException(errno, "seteuid");
|
|
||||||
} else {
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void GetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
int ngroups = getgroups(0, nullptr);
|
|
||||||
|
|
||||||
if (ngroups == -1)
|
|
||||||
return env->ThrowErrnoException(errno, "getgroups");
|
|
||||||
|
|
||||||
gid_t* groups = new gid_t[ngroups];
|
|
||||||
|
|
||||||
ngroups = getgroups(ngroups, groups);
|
|
||||||
|
|
||||||
if (ngroups == -1) {
|
|
||||||
delete[] groups;
|
|
||||||
return env->ThrowErrnoException(errno, "getgroups");
|
|
||||||
}
|
|
||||||
|
|
||||||
Local<Array> groups_list = Array::New(env->isolate(), ngroups);
|
|
||||||
bool seen_egid = false;
|
|
||||||
gid_t egid = getegid();
|
|
||||||
|
|
||||||
for (int i = 0; i < ngroups; i++) {
|
|
||||||
groups_list->Set(env->context(),
|
|
||||||
i, Integer::New(env->isolate(), groups[i])).FromJust();
|
|
||||||
if (groups[i] == egid)
|
|
||||||
seen_egid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] groups;
|
|
||||||
|
|
||||||
if (seen_egid == false)
|
|
||||||
groups_list->Set(env->context(),
|
|
||||||
ngroups,
|
|
||||||
Integer::New(env->isolate(), egid)).FromJust();
|
|
||||||
|
|
||||||
args.GetReturnValue().Set(groups_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 1);
|
|
||||||
CHECK(args[0]->IsArray());
|
|
||||||
|
|
||||||
Local<Array> groups_list = args[0].As<Array>();
|
|
||||||
size_t size = groups_list->Length();
|
|
||||||
gid_t* groups = new gid_t[size];
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
gid_t gid = gid_by_name(env->isolate(),
|
|
||||||
groups_list->Get(env->context(),
|
|
||||||
i).ToLocalChecked());
|
|
||||||
|
|
||||||
if (gid == gid_not_found) {
|
|
||||||
delete[] groups;
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
groups[i] = gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rc = setgroups(size, groups);
|
|
||||||
delete[] groups;
|
|
||||||
|
|
||||||
if (rc == -1)
|
|
||||||
return env->ThrowErrnoException(errno, "setgroups");
|
|
||||||
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void InitGroups(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
Environment* env = Environment::GetCurrent(args);
|
|
||||||
|
|
||||||
CHECK_EQ(args.Length(), 2);
|
|
||||||
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
||||||
CHECK(args[1]->IsUint32() || args[1]->IsString());
|
|
||||||
|
|
||||||
Utf8Value arg0(env->isolate(), args[0]);
|
|
||||||
gid_t extra_group;
|
|
||||||
bool must_free;
|
|
||||||
char* user;
|
|
||||||
|
|
||||||
if (args[0]->IsUint32()) {
|
|
||||||
user = name_by_uid(args[0].As<Uint32>()->Value());
|
|
||||||
must_free = true;
|
|
||||||
} else {
|
|
||||||
user = *arg0;
|
|
||||||
must_free = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user == nullptr) {
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
return args.GetReturnValue().Set(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
extra_group = gid_by_name(env->isolate(), args[1]);
|
|
||||||
|
|
||||||
if (extra_group == gid_not_found) {
|
|
||||||
if (must_free)
|
|
||||||
free(user);
|
|
||||||
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
||||||
return args.GetReturnValue().Set(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rc = initgroups(user, extra_group);
|
|
||||||
|
|
||||||
if (must_free)
|
|
||||||
free(user);
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
return env->ThrowErrnoException(errno, "initgroups");
|
|
||||||
|
|
||||||
args.GetReturnValue().Set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
||||||
|
|
||||||
void ProcessTitleGetter(Local<Name> property,
|
void ProcessTitleGetter(Local<Name> property,
|
||||||
const PropertyCallbackInfo<Value>& info) {
|
const PropertyCallbackInfo<Value>& info) {
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
|
@ -15,7 +15,6 @@ using v8::Integer;
|
|||||||
using v8::Isolate;
|
using v8::Isolate;
|
||||||
using v8::KeyCollectionMode;
|
using v8::KeyCollectionMode;
|
||||||
using v8::Local;
|
using v8::Local;
|
||||||
using v8::NewStringType;
|
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
using v8::ONLY_CONFIGURABLE;
|
using v8::ONLY_CONFIGURABLE;
|
||||||
using v8::ONLY_ENUMERABLE;
|
using v8::ONLY_ENUMERABLE;
|
||||||
@ -172,17 +171,6 @@ void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) {
|
|||||||
args.GetReturnValue().Set(ret);
|
args.GetReturnValue().Set(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
|
|
||||||
CHECK(args[0]->IsString());
|
|
||||||
Utf8Value strenvtag(args.GetIsolate(), args[0]);
|
|
||||||
std::string text;
|
|
||||||
if (!node::SafeGetenv(*strenvtag, &text)) return;
|
|
||||||
args.GetReturnValue()
|
|
||||||
.Set(String::NewFromUtf8(
|
|
||||||
args.GetIsolate(), text.c_str(),
|
|
||||||
NewStringType::kNormal).ToLocalChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
|
void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
Isolate* isolate = env->isolate();
|
Isolate* isolate = env->isolate();
|
||||||
@ -232,8 +220,6 @@ void Initialize(Local<Object> target,
|
|||||||
env->SetMethodNoSideEffect(target, "watchdogHasPendingSigint",
|
env->SetMethodNoSideEffect(target, "watchdogHasPendingSigint",
|
||||||
WatchdogHasPendingSigint);
|
WatchdogHasPendingSigint);
|
||||||
|
|
||||||
env->SetMethod(target, "safeGetenv", SafeGetenv);
|
|
||||||
|
|
||||||
env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
|
env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
|
||||||
|
|
||||||
Local<Object> constants = Object::New(env->isolate());
|
Local<Object> constants = Object::New(env->isolate());
|
||||||
|
@ -9,7 +9,7 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const isMainThread = common.isMainThread;
|
const isMainThread = common.isMainThread;
|
||||||
const kMaxModuleCount = isMainThread ? 59 : 82;
|
const kMaxModuleCount = isMainThread ? 60 : 82;
|
||||||
|
|
||||||
assert(list.length <= kMaxModuleCount,
|
assert(list.length <= kMaxModuleCount,
|
||||||
`Total length: ${list.length}\n` + list.join('\n')
|
`Total length: ${list.length}\n` + list.join('\n')
|
||||||
|
19
test/parallel/test-safe-get-env.js
Normal file
19
test/parallel/test-safe-get-env.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
// Flags: --expose_internals
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { internalBinding } = require('internal/test/binding');
|
||||||
|
const { safeGetenv } = internalBinding('credentials');
|
||||||
|
|
||||||
|
// FIXME(joyeecheung): this test is not entirely useful. To properly
|
||||||
|
// test this we could create a mismatch between the effective/real
|
||||||
|
// group/user id of a Node.js process and see if the environment variables
|
||||||
|
// are no longer available - but that might be tricky to set up reliably.
|
||||||
|
|
||||||
|
for (const oneEnv in process.env) {
|
||||||
|
assert.strictEqual(
|
||||||
|
safeGetenv(oneEnv),
|
||||||
|
process.env[oneEnv]
|
||||||
|
);
|
||||||
|
}
|
@ -9,17 +9,9 @@ const { internalBinding } = require('internal/test/binding');
|
|||||||
const {
|
const {
|
||||||
getHiddenValue,
|
getHiddenValue,
|
||||||
setHiddenValue,
|
setHiddenValue,
|
||||||
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
|
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex
|
||||||
safeGetenv
|
|
||||||
} = internalBinding('util');
|
} = internalBinding('util');
|
||||||
|
|
||||||
for (const oneEnv in process.env) {
|
|
||||||
assert.strictEqual(
|
|
||||||
safeGetenv(oneEnv),
|
|
||||||
process.env[oneEnv]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
getHiddenValue({}, kArrowMessagePrivateSymbolIndex),
|
getHiddenValue({}, kArrowMessagePrivateSymbolIndex),
|
||||||
undefined);
|
undefined);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user