summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/internal/bootstrap/node.js26
-rw-r--r--lib/internal/modules/cjs/loader.js2
-rw-r--r--lib/internal/process/main_thread_only.js73
-rw-r--r--lib/os.js2
-rw-r--r--node.gyp1
-rw-r--r--src/bootstrapper.cc11
-rw-r--r--src/env.cc2
-rw-r--r--src/node.cc59
-rw-r--r--src/node_binding.cc1
-rw-r--r--src/node_credentials.cc395
-rw-r--r--src/node_internals.h17
-rw-r--r--src/node_main.cc6
-rw-r--r--src/node_process.cc332
-rw-r--r--src/node_util.cc14
-rw-r--r--test/parallel/test-bootstrap-modules.js2
-rw-r--r--test/parallel/test-safe-get-env.js19
-rw-r--r--test/parallel/test-util-internal.js10
17 files changed, 498 insertions, 474 deletions
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index 35565769d2..33d79689d0 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -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();
}
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index b70171cc5d..bf6a9d4029 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -34,7 +34,7 @@ const {
internalModuleReadJSON,
internalModuleStat
} = internalBinding('fs');
-const { safeGetenv } = internalBinding('util');
+const { safeGetenv } = internalBinding('credentials');
const {
makeRequireFunction,
requireDepth,
diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js
index b8b7a454dc..5f53f34084 100644
--- a/lib/internal/process/main_thread_only.js
+++ b/lib/internal/process/main_thread_only.js
@@ -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) {
-
- process.initgroups = function initgroups(user, extraGroup) {
+function wrapPosixCredentialSetters(credentials) {
+ const {
+ initgroups: _initgroups,
+ setgroups: _setgroups,
+ setegid: _setegid,
+ seteuid: _seteuid,
+ setgid: _setgid,
+ setuid: _setuid
+ } = credentials;
+
+ 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
};
diff --git a/lib/os.js b/lib/os.js
index 2c806908ee..28e1d7797f 100644
--- a/lib/os.js
+++ b/lib/os.js
@@ -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';
diff --git a/node.gyp b/node.gyp
index db8443d813..b337de0a42 100644
--- a/node.gyp
+++ b/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',
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 2c8212177b..0a1cfed210 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.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(),
diff --git a/src/env.cc b/src/env.cc
index ed6717d684..1e65dfdac8 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -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(
diff --git a/src/node.cc b/src/node.cc
index d830b846c9..58722f23b3 100644
--- a/src/node.cc
+++ b/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
diff --git a/src/node_binding.cc b/src/node_binding.cc
index 4a47c744c2..1e78f7c17f 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -30,6 +30,7 @@
V(cares_wrap) \
V(config) \
V(contextify) \
+ V(credentials) \
V(domain) \
V(fs) \
V(fs_event_wrap) \
diff --git a/src/node_credentials.cc b/src/node_credentials.cc
new file mode 100644
index 0000000000..0f202dfb75
--- /dev/null
+++ b/src/node_credentials.cc
@@ -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)
diff --git a/src/node_internals.h b/src/node_internals.h
index d9a72a10a4..2c6aad5b96 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -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
diff --git a/src/node_main.cc b/src/node_main.cc
index bea1af7bdd..7107aea8c1 100644
--- a/src/node_main.cc
+++ b/src/node_main.cc
@@ -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;
}
}
diff --git a/src/node_process.cc b/src/node_process.cc
index ccd8ce2fd9..b9376953e2 100644
--- a/src/node_process.cc
+++ b/src/node_process.cc
@@ -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];
diff --git a/src/node_util.cc b/src/node_util.cc
index f6ebee895f..f7412d92bc 100644
--- a/src/node_util.cc
+++ b/src/node_util.cc
@@ -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());
diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js
index 8f1592084c..5c1693ca89 100644
--- a/test/parallel/test-bootstrap-modules.js
+++ b/test/parallel/test-bootstrap-modules.js
@@ -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')
diff --git a/test/parallel/test-safe-get-env.js b/test/parallel/test-safe-get-env.js
new file mode 100644
index 0000000000..0bee9971db
--- /dev/null
+++ b/test/parallel/test-safe-get-env.js
@@ -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]
+ );
+}
diff --git a/test/parallel/test-util-internal.js b/test/parallel/test-util-internal.js
index 4b78cefc6e..f16ccfdbdc 100644
--- a/test/parallel/test-util-internal.js
+++ b/test/parallel/test-util-internal.js
@@ -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);