From 3f619407fe1e597657b598383d0b5003a064311b Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 17 Mar 2021 13:48:51 +0100 Subject: [PATCH] src: allow CAP_NET_BIND_SERVICE in SafeGetenv This commit updates SafeGetenv to check if the current process has the effective capability cap_net_bind_service set, and if so allows environment variables to be read. The motivation for this change is a use-case where Node is run in a container, and the is a requirement to be able to listen to ports below 1024. This is done by setting the capability of cap_net_bind_service. In addition there is a need to set the environment variable `NODE_EXTRA_CA_CERTS`. But currently this environment variable will not be read when the capability has been set on the executable. PR-URL: https://github.com/nodejs/node/pull/37727 Reviewed-By: Anna Henningsen Reviewed-By: Richard Lau Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- src/node_credentials.cc | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/node_credentials.cc b/src/node_credentials.cc index fa3dfa48a3ceb2..de0ba3c8be2b1e 100644 --- a/src/node_credentials.cc +++ b/src/node_credentials.cc @@ -11,6 +11,10 @@ #if !defined(_MSC_VER) #include // setuid, getuid #endif +#ifdef __linux__ +#include +#include +#endif // __linux__ namespace node { @@ -33,11 +37,42 @@ bool linux_at_secure = false; namespace credentials { -// Look up environment variable unless running as setuid root. +#if defined(__linux__) +// Returns true if the current process only has the passed-in capability. +bool HasOnly(int capability) { + DCHECK(cap_valid(capability)); + + struct __user_cap_data_struct cap_data[2]; + struct __user_cap_header_struct cap_header_data = { + _LINUX_CAPABILITY_VERSION_3, + getpid()}; + + + if (syscall(SYS_capget, &cap_header_data, &cap_data) != 0) { + return false; + } + if (capability < 32) { + return cap_data[0].permitted == + static_cast(CAP_TO_MASK(capability)); + } + return cap_data[1].permitted == + static_cast(CAP_TO_MASK(capability)); +} +#endif + +// Look up the environment variable and allow the lookup if the current +// process only has the capability CAP_NET_BIND_SERVICE set. If the current +// process does not have any capabilities set and the process is running as +// setuid root then lookup will not be allowed. bool SafeGetenv(const char* key, std::string* text, Environment* env) { #if !defined(__CloudABI__) && !defined(_WIN32) +#if defined(__linux__) + if ((!HasOnly(CAP_NET_BIND_SERVICE) && per_process::linux_at_secure) || + getuid() != geteuid() || getgid() != getegid()) +#else if (per_process::linux_at_secure || getuid() != geteuid() || getgid() != getegid()) +#endif goto fail; #endif