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,
|
||||
_hrtime, _hrtimeBigInt,
|
||||
_memoryUsage, _rawDebug,
|
||||
_umask, _initgroups, _setegid, _seteuid,
|
||||
_setgid, _setuid, _setgroups,
|
||||
_umask,
|
||||
_shouldAbortOnUncaughtToggle
|
||||
} = bootstrappers;
|
||||
const { internalBinding, NativeModule } = loaderExports;
|
||||
@ -72,13 +71,28 @@ function startup() {
|
||||
NativeModule.require('internal/process/warning').setup();
|
||||
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
|
||||
_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) {
|
||||
mainThreadSetup.setupStdio();
|
||||
mainThreadSetup.setupProcessMethods(
|
||||
_chdir, _umask, _initgroups, _setegid, _seteuid,
|
||||
_setgid, _setuid, _setgroups
|
||||
);
|
||||
mainThreadSetup.setupProcessMethods(_chdir, _umask);
|
||||
} else {
|
||||
workerThreadSetup.setupStdio();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ const {
|
||||
internalModuleReadJSON,
|
||||
internalModuleStat
|
||||
} = internalBinding('fs');
|
||||
const { safeGetenv } = internalBinding('util');
|
||||
const { safeGetenv } = internalBinding('credentials');
|
||||
const {
|
||||
makeRequireFunction,
|
||||
requireDepth,
|
||||
|
@ -29,13 +29,7 @@ function setupStdio() {
|
||||
|
||||
// Non-POSIX platforms like Windows don't have certain methods.
|
||||
// Workers also lack these methods since they change process-global state.
|
||||
function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
||||
_seteuid, _setgid, _setuid, _setgroups) {
|
||||
if (_setgid !== undefined) {
|
||||
setupPosixMethods(_initgroups, _setegid, _seteuid,
|
||||
_setgid, _setuid, _setgroups);
|
||||
}
|
||||
|
||||
function setupProcessMethods(_chdir, _umask) {
|
||||
process.chdir = function chdir(directory) {
|
||||
validateString(directory, 'directory');
|
||||
return _chdir(directory);
|
||||
@ -51,10 +45,17 @@ function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
||||
};
|
||||
}
|
||||
|
||||
function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
||||
_setgid, _setuid, _setgroups) {
|
||||
function wrapPosixCredentialSetters(credentials) {
|
||||
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(extraGroup, 'extraGroup');
|
||||
// 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) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
process.setegid = function setegid(id) {
|
||||
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) {
|
||||
function setgroups(groups) {
|
||||
if (!Array.isArray(groups)) {
|
||||
throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
|
||||
}
|
||||
@ -95,15 +80,17 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
||||
if (result > 0) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function execId(id, type, method) {
|
||||
validateId(id, 'id');
|
||||
// Result is 0 on success, 1 if credential is unknown.
|
||||
const result = method(id);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
||||
}
|
||||
function wrapIdSetter(type, method) {
|
||||
return function(id) {
|
||||
validateId(id, 'id');
|
||||
// Result is 0 on success, 1 if credential is unknown.
|
||||
const result = method(id);
|
||||
if (result === 1) {
|
||||
throw new ERR_UNKNOWN_CREDENTIAL(type, id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function validateId(id, name) {
|
||||
@ -113,6 +100,15 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
||||
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.
|
||||
@ -181,5 +177,6 @@ module.exports = {
|
||||
setupStdio,
|
||||
setupProcessMethods,
|
||||
setupSignalHandlers,
|
||||
setupChildProcessIpcChannel
|
||||
setupChildProcessIpcChannel,
|
||||
wrapPosixCredentialSetters
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const { safeGetenv } = internalBinding('util');
|
||||
const { safeGetenv } = internalBinding('credentials');
|
||||
const constants = internalBinding('constants').os;
|
||||
const { deprecate } = require('internal/util');
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
1
node.gyp
1
node.gyp
@ -350,6 +350,7 @@
|
||||
'src/node_config.cc',
|
||||
'src/node_constants.cc',
|
||||
'src/node_contextify.cc',
|
||||
'src/node_credentials.cc',
|
||||
'src/node_domain.cc',
|
||||
'src/node_encoding.cc',
|
||||
'src/node_env_var.cc',
|
||||
|
@ -147,17 +147,6 @@ void SetupBootstrapObject(Environment* env,
|
||||
BOOTSTRAP_METHOD(_rawDebug, RawDebug);
|
||||
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 =
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle");
|
||||
CHECK(bootstrapper->Set(env->context(),
|
||||
|
@ -225,7 +225,7 @@ Environment::Environment(IsolateData* isolate_data,
|
||||
should_abort_on_uncaught_toggle_[0] = 1;
|
||||
|
||||
std::string debug_cats;
|
||||
SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
|
||||
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
|
||||
set_debug_categories(debug_cats, true);
|
||||
|
||||
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
|
||||
|
59
src/node.cc
59
src/node.cc
@ -100,12 +100,7 @@ typedef int mode_t;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/resource.h> // getrlimit, setrlimit
|
||||
#include <unistd.h> // setuid, getuid
|
||||
#endif
|
||||
|
||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
||||
#include <pwd.h> // getpwnam()
|
||||
#include <grp.h> // getgrnam()
|
||||
#include <unistd.h> // STDIN_FILENO, STDERR_FILENO
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
@ -153,8 +148,6 @@ unsigned int reverted = 0;
|
||||
|
||||
bool v8_initialized = false;
|
||||
|
||||
bool linux_at_secure = false;
|
||||
|
||||
// process-relative uptime base, initialized at start-up
|
||||
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) {
|
||||
if (zero_fill_field_ || per_process_opts->zero_fill_all_buffers)
|
||||
return UncheckedCalloc(size);
|
||||
@ -1157,14 +1129,6 @@ void SetupProcessObject(Environment* env,
|
||||
env->SetMethod(process, "dlopen", binding::DLOpen);
|
||||
env->SetMethod(process, "reallyExit", Exit);
|
||||
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;
|
||||
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.
|
||||
{
|
||||
std::string text;
|
||||
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;
|
||||
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()) {
|
||||
SafeGetenv("NODE_REDIRECT_WARNINGS",
|
||||
&default_env_options->redirect_warnings);
|
||||
credentials::SafeGetenv("NODE_REDIRECT_WARNINGS",
|
||||
&default_env_options->redirect_warnings);
|
||||
}
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
std::string* openssl_config = &per_process_opts->openssl_config;
|
||||
if (openssl_config->empty()) {
|
||||
SafeGetenv("OPENSSL_CONF", openssl_config);
|
||||
credentials::SafeGetenv("OPENSSL_CONF", openssl_config);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(NODE_WITHOUT_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;
|
||||
// [0] is expected to be the program name, fill it in from the real argv.
|
||||
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 the parameter isn't given, use the env variable.
|
||||
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.
|
||||
// If icu_data_dir is empty here, it will load the 'minimal' data.
|
||||
if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
|
||||
@ -2095,7 +2062,7 @@ int Start(int argc, char** argv) {
|
||||
#if HAVE_OPENSSL
|
||||
{
|
||||
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);
|
||||
}
|
||||
#ifdef NODE_FIPS_MODE
|
||||
|
@ -30,6 +30,7 @@
|
||||
V(cares_wrap) \
|
||||
V(config) \
|
||||
V(contextify) \
|
||||
V(credentials) \
|
||||
V(domain) \
|
||||
V(fs) \
|
||||
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);
|
||||
#endif
|
||||
|
||||
bool SafeGetenv(const char* key, std::string* text);
|
||||
v8::Local<v8::Object> CreateEnvVarProxy(v8::Local<v8::Context> context,
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> data);
|
||||
@ -734,19 +733,13 @@ void ProcessTitleSetter(v8::Local<v8::Name> property,
|
||||
const v8::PropertyCallbackInfo<void>& info);
|
||||
|
||||
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
||||
void SetGid(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
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);
|
||||
#define NODE_IMPLEMENTS_POSIX_CREDENTIALS 1
|
||||
#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);
|
||||
|
||||
} // namespace node
|
||||
|
@ -88,7 +88,9 @@ extern char** environ;
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
extern bool linux_at_secure;
|
||||
namespace per_process {
|
||||
extern bool linux_at_secure;
|
||||
} // namespace per_process
|
||||
} // namespace node
|
||||
|
||||
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);
|
||||
for (; auxv->a_type != AT_NULL; auxv++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,6 @@ typedef int mode_t;
|
||||
#include <pthread.h>
|
||||
#include <sys/resource.h> // getrlimit, setrlimit
|
||||
#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
|
||||
|
||||
namespace node {
|
||||
@ -42,7 +36,6 @@ using v8::Float64Array;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::HeapStatistics;
|
||||
using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Name;
|
||||
@ -254,331 +247,6 @@ void Uptime(const FunctionCallbackInfo<Value>& args) {
|
||||
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,
|
||||
const PropertyCallbackInfo<Value>& info) {
|
||||
char buffer[512];
|
||||
|
@ -15,7 +15,6 @@ using v8::Integer;
|
||||
using v8::Isolate;
|
||||
using v8::KeyCollectionMode;
|
||||
using v8::Local;
|
||||
using v8::NewStringType;
|
||||
using v8::Object;
|
||||
using v8::ONLY_CONFIGURABLE;
|
||||
using v8::ONLY_ENUMERABLE;
|
||||
@ -172,17 +171,6 @@ void WatchdogHasPendingSigint(const FunctionCallbackInfo<Value>& args) {
|
||||
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) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
@ -232,8 +220,6 @@ void Initialize(Local<Object> target,
|
||||
env->SetMethodNoSideEffect(target, "watchdogHasPendingSigint",
|
||||
WatchdogHasPendingSigint);
|
||||
|
||||
env->SetMethod(target, "safeGetenv", SafeGetenv);
|
||||
|
||||
env->SetMethod(target, "enqueueMicrotask", EnqueueMicrotask);
|
||||
|
||||
Local<Object> constants = Object::New(env->isolate());
|
||||
|
@ -9,7 +9,7 @@ const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const isMainThread = common.isMainThread;
|
||||
const kMaxModuleCount = isMainThread ? 59 : 82;
|
||||
const kMaxModuleCount = isMainThread ? 60 : 82;
|
||||
|
||||
assert(list.length <= kMaxModuleCount,
|
||||
`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 {
|
||||
getHiddenValue,
|
||||
setHiddenValue,
|
||||
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
|
||||
safeGetenv
|
||||
arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex
|
||||
} = internalBinding('util');
|
||||
|
||||
for (const oneEnv in process.env) {
|
||||
assert.strictEqual(
|
||||
safeGetenv(oneEnv),
|
||||
process.env[oneEnv]
|
||||
);
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
getHiddenValue({}, kArrowMessagePrivateSymbolIndex),
|
||||
undefined);
|
||||
|
Loading…
x
Reference in New Issue
Block a user