1#include "env-inl.h" 2#include "node_external_reference.h" 3#include "node_internals.h" 4#include "util-inl.h" 5 6#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS 7#include <grp.h> // getgrnam() 8#include <pwd.h> // getpwnam() 9#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS 10 11#if !defined(_MSC_VER) 12#include <unistd.h> // setuid, getuid 13#endif 14#ifdef __linux__ 15#include <linux/capability.h> 16#include <sys/auxv.h> 17#include <sys/syscall.h> 18#endif // __linux__ 19 20namespace node { 21 22using v8::Array; 23using v8::Context; 24using v8::FunctionCallbackInfo; 25using v8::HandleScope; 26using v8::Isolate; 27using v8::Local; 28using v8::MaybeLocal; 29using v8::Object; 30using v8::String; 31using v8::TryCatch; 32using v8::Uint32; 33using v8::Value; 34 35bool linux_at_secure() { 36 // This could reasonably be a static variable, but this way 37 // we can guarantee that this function is always usable 38 // and returns the correct value, e.g. even in static 39 // initialization code in other files. 40#ifdef __linux__ 41 static const bool value = getauxval(AT_SECURE); 42 return value; 43#else 44 return false; 45#endif 46} 47 48namespace credentials { 49 50#if defined(__linux__) 51// Returns true if the current process only has the passed-in capability. 52bool HasOnly(int capability) { 53 DCHECK(cap_valid(capability)); 54 55 struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3]; 56 struct __user_cap_header_struct cap_header_data = { 57 _LINUX_CAPABILITY_VERSION_3, 58 getpid()}; 59 60 61 if (syscall(SYS_capget, &cap_header_data, &cap_data) != 0) { 62 return false; 63 } 64 65 static_assert(arraysize(cap_data) == 2); 66 return cap_data[CAP_TO_INDEX(capability)].permitted == 67 static_cast<unsigned int>(CAP_TO_MASK(capability)) && 68 cap_data[1 - CAP_TO_INDEX(capability)].permitted == 0; 69} 70#endif 71 72// Look up the environment variable and allow the lookup if the current 73// process only has the capability CAP_NET_BIND_SERVICE set. If the current 74// process does not have any capabilities set and the process is running as 75// setuid root then lookup will not be allowed. 76bool SafeGetenv(const char* key, 77 std::string* text, 78 std::shared_ptr<KVStore> env_vars, 79 v8::Isolate* isolate) { 80#if !defined(__CloudABI__) && !defined(_WIN32) 81#if defined(__linux__) 82 if ((!HasOnly(CAP_NET_BIND_SERVICE) && linux_at_secure()) || 83 getuid() != geteuid() || getgid() != getegid()) 84#else 85 if (linux_at_secure() || getuid() != geteuid() || getgid() != getegid()) 86#endif 87 goto fail; 88#endif 89 90 if (env_vars != nullptr) { 91 DCHECK_NOT_NULL(isolate); 92 HandleScope handle_scope(isolate); 93 TryCatch ignore_errors(isolate); 94 MaybeLocal<String> maybe_value = env_vars->Get( 95 isolate, String::NewFromUtf8(isolate, key).ToLocalChecked()); 96 Local<String> value; 97 if (!maybe_value.ToLocal(&value)) goto fail; 98 String::Utf8Value utf8_value(isolate, value); 99 if (*utf8_value == nullptr) goto fail; 100 *text = std::string(*utf8_value, utf8_value.length()); 101 return true; 102 } 103 104 { 105 Mutex::ScopedLock lock(per_process::env_var_mutex); 106 107 size_t init_sz = 256; 108 MaybeStackBuffer<char, 256> val; 109 int ret = uv_os_getenv(key, *val, &init_sz); 110 111 if (ret == UV_ENOBUFS) { 112 // Buffer is not large enough, reallocate to the updated init_sz 113 // and fetch env value again. 114 val.AllocateSufficientStorage(init_sz); 115 ret = uv_os_getenv(key, *val, &init_sz); 116 } 117 118 if (ret >= 0) { // Env key value fetch success. 119 *text = *val; 120 return true; 121 } 122 } 123 124fail: 125 text->clear(); 126 return false; 127} 128 129static void SafeGetenv(const FunctionCallbackInfo<Value>& args) { 130 CHECK(args[0]->IsString()); 131 Environment* env = Environment::GetCurrent(args); 132 Isolate* isolate = env->isolate(); 133 Utf8Value strenvtag(isolate, args[0]); 134 std::string text; 135 if (!SafeGetenv(*strenvtag, &text, env->env_vars(), isolate)) return; 136 Local<Value> result = 137 ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked(); 138 args.GetReturnValue().Set(result); 139} 140 141#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS 142 143static const uid_t uid_not_found = static_cast<uid_t>(-1); 144static const gid_t gid_not_found = static_cast<gid_t>(-1); 145 146static uid_t uid_by_name(const char* name) { 147 struct passwd pwd; 148 struct passwd* pp; 149 char buf[8192]; 150 151 errno = 0; 152 pp = nullptr; 153 154 if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) 155 return pp->pw_uid; 156 157 return uid_not_found; 158} 159 160static char* name_by_uid(uid_t uid) { 161 struct passwd pwd; 162 struct passwd* pp; 163 char buf[8192]; 164 int rc; 165 166 errno = 0; 167 pp = nullptr; 168 169 if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 && 170 pp != nullptr) { 171 return strdup(pp->pw_name); 172 } 173 174 if (rc == 0) errno = ENOENT; 175 176 return nullptr; 177} 178 179static gid_t gid_by_name(const char* name) { 180 struct group pwd; 181 struct group* pp; 182 char buf[8192]; 183 184 errno = 0; 185 pp = nullptr; 186 187 if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr) 188 return pp->gr_gid; 189 190 return gid_not_found; 191} 192 193#if 0 // For future use. 194static const char* name_by_gid(gid_t gid) { 195 struct group pwd; 196 struct group* pp; 197 char buf[8192]; 198 int rc; 199 200 errno = 0; 201 pp = nullptr; 202 203 if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 && 204 pp != nullptr) { 205 return strdup(pp->gr_name); 206 } 207 208 if (rc == 0) 209 errno = ENOENT; 210 211 return nullptr; 212} 213#endif 214 215static uid_t uid_by_name(Isolate* isolate, Local<Value> value) { 216 if (value->IsUint32()) { 217 static_assert(std::is_same<uid_t, uint32_t>::value); 218 return value.As<Uint32>()->Value(); 219 } else { 220 Utf8Value name(isolate, value); 221 return uid_by_name(*name); 222 } 223} 224 225static gid_t gid_by_name(Isolate* isolate, Local<Value> value) { 226 if (value->IsUint32()) { 227 static_assert(std::is_same<gid_t, uint32_t>::value); 228 return value.As<Uint32>()->Value(); 229 } else { 230 Utf8Value name(isolate, value); 231 return gid_by_name(*name); 232 } 233} 234 235static void GetUid(const FunctionCallbackInfo<Value>& args) { 236 Environment* env = Environment::GetCurrent(args); 237 CHECK(env->has_run_bootstrapping_code()); 238 // uid_t is an uint32_t on all supported platforms. 239 args.GetReturnValue().Set(static_cast<uint32_t>(getuid())); 240} 241 242static void GetGid(const FunctionCallbackInfo<Value>& args) { 243 Environment* env = Environment::GetCurrent(args); 244 CHECK(env->has_run_bootstrapping_code()); 245 // gid_t is an uint32_t on all supported platforms. 246 args.GetReturnValue().Set(static_cast<uint32_t>(getgid())); 247} 248 249static void GetEUid(const FunctionCallbackInfo<Value>& args) { 250 Environment* env = Environment::GetCurrent(args); 251 CHECK(env->has_run_bootstrapping_code()); 252 // uid_t is an uint32_t on all supported platforms. 253 args.GetReturnValue().Set(static_cast<uint32_t>(geteuid())); 254} 255 256static void GetEGid(const FunctionCallbackInfo<Value>& args) { 257 Environment* env = Environment::GetCurrent(args); 258 CHECK(env->has_run_bootstrapping_code()); 259 // gid_t is an uint32_t on all supported platforms. 260 args.GetReturnValue().Set(static_cast<uint32_t>(getegid())); 261} 262 263static void SetGid(const FunctionCallbackInfo<Value>& args) { 264 Environment* env = Environment::GetCurrent(args); 265 CHECK(env->owns_process_state()); 266 267 CHECK_EQ(args.Length(), 1); 268 CHECK(args[0]->IsUint32() || args[0]->IsString()); 269 270 gid_t gid = gid_by_name(env->isolate(), args[0]); 271 272 if (gid == gid_not_found) { 273 // Tells JS to throw ERR_INVALID_CREDENTIAL 274 args.GetReturnValue().Set(1); 275 } else if (setgid(gid)) { 276 env->ThrowErrnoException(errno, "setgid"); 277 } else { 278 args.GetReturnValue().Set(0); 279 } 280} 281 282static void SetEGid(const FunctionCallbackInfo<Value>& args) { 283 Environment* env = Environment::GetCurrent(args); 284 CHECK(env->owns_process_state()); 285 286 CHECK_EQ(args.Length(), 1); 287 CHECK(args[0]->IsUint32() || args[0]->IsString()); 288 289 gid_t gid = gid_by_name(env->isolate(), args[0]); 290 291 if (gid == gid_not_found) { 292 // Tells JS to throw ERR_INVALID_CREDENTIAL 293 args.GetReturnValue().Set(1); 294 } else if (setegid(gid)) { 295 env->ThrowErrnoException(errno, "setegid"); 296 } else { 297 args.GetReturnValue().Set(0); 298 } 299} 300 301static void SetUid(const FunctionCallbackInfo<Value>& args) { 302 Environment* env = Environment::GetCurrent(args); 303 CHECK(env->owns_process_state()); 304 305 CHECK_EQ(args.Length(), 1); 306 CHECK(args[0]->IsUint32() || args[0]->IsString()); 307 308 uid_t uid = uid_by_name(env->isolate(), args[0]); 309 310 if (uid == uid_not_found) { 311 // Tells JS to throw ERR_INVALID_CREDENTIAL 312 args.GetReturnValue().Set(1); 313 } else if (setuid(uid)) { 314 env->ThrowErrnoException(errno, "setuid"); 315 } else { 316 args.GetReturnValue().Set(0); 317 } 318} 319 320static void SetEUid(const FunctionCallbackInfo<Value>& args) { 321 Environment* env = Environment::GetCurrent(args); 322 CHECK(env->owns_process_state()); 323 324 CHECK_EQ(args.Length(), 1); 325 CHECK(args[0]->IsUint32() || args[0]->IsString()); 326 327 uid_t uid = uid_by_name(env->isolate(), args[0]); 328 329 if (uid == uid_not_found) { 330 // Tells JS to throw ERR_INVALID_CREDENTIAL 331 args.GetReturnValue().Set(1); 332 } else if (seteuid(uid)) { 333 env->ThrowErrnoException(errno, "seteuid"); 334 } else { 335 args.GetReturnValue().Set(0); 336 } 337} 338 339static void GetGroups(const FunctionCallbackInfo<Value>& args) { 340 Environment* env = Environment::GetCurrent(args); 341 CHECK(env->has_run_bootstrapping_code()); 342 343 int ngroups = getgroups(0, nullptr); 344 if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups"); 345 346 std::vector<gid_t> groups(ngroups); 347 348 ngroups = getgroups(ngroups, groups.data()); 349 if (ngroups == -1) 350 return env->ThrowErrnoException(errno, "getgroups"); 351 352 groups.resize(ngroups); 353 gid_t egid = getegid(); 354 if (std::find(groups.begin(), groups.end(), egid) == groups.end()) 355 groups.push_back(egid); 356 MaybeLocal<Value> array = ToV8Value(env->context(), groups); 357 if (!array.IsEmpty()) 358 args.GetReturnValue().Set(array.ToLocalChecked()); 359} 360 361static void SetGroups(const FunctionCallbackInfo<Value>& args) { 362 Environment* env = Environment::GetCurrent(args); 363 364 CHECK_EQ(args.Length(), 1); 365 CHECK(args[0]->IsArray()); 366 367 Local<Array> groups_list = args[0].As<Array>(); 368 size_t size = groups_list->Length(); 369 MaybeStackBuffer<gid_t, 64> groups(size); 370 371 for (size_t i = 0; i < size; i++) { 372 gid_t gid = gid_by_name( 373 env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked()); 374 375 if (gid == gid_not_found) { 376 // Tells JS to throw ERR_INVALID_CREDENTIAL 377 args.GetReturnValue().Set(static_cast<uint32_t>(i + 1)); 378 return; 379 } 380 381 groups[i] = gid; 382 } 383 384 int rc = setgroups(size, *groups); 385 386 if (rc == -1) return env->ThrowErrnoException(errno, "setgroups"); 387 388 args.GetReturnValue().Set(0); 389} 390 391static void InitGroups(const FunctionCallbackInfo<Value>& args) { 392 Environment* env = Environment::GetCurrent(args); 393 394 CHECK_EQ(args.Length(), 2); 395 CHECK(args[0]->IsUint32() || args[0]->IsString()); 396 CHECK(args[1]->IsUint32() || args[1]->IsString()); 397 398 Utf8Value arg0(env->isolate(), args[0]); 399 gid_t extra_group; 400 bool must_free; 401 char* user; 402 403 if (args[0]->IsUint32()) { 404 user = name_by_uid(args[0].As<Uint32>()->Value()); 405 must_free = true; 406 } else { 407 user = *arg0; 408 must_free = false; 409 } 410 411 if (user == nullptr) { 412 // Tells JS to throw ERR_INVALID_CREDENTIAL 413 return args.GetReturnValue().Set(1); 414 } 415 416 extra_group = gid_by_name(env->isolate(), args[1]); 417 418 if (extra_group == gid_not_found) { 419 if (must_free) free(user); 420 // Tells JS to throw ERR_INVALID_CREDENTIAL 421 return args.GetReturnValue().Set(2); 422 } 423 424 int rc = initgroups(user, extra_group); 425 426 if (must_free) free(user); 427 428 if (rc) return env->ThrowErrnoException(errno, "initgroups"); 429 430 args.GetReturnValue().Set(0); 431} 432 433#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS 434 435void RegisterExternalReferences(ExternalReferenceRegistry* registry) { 436 registry->Register(SafeGetenv); 437 438#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS 439 registry->Register(GetUid); 440 registry->Register(GetEUid); 441 registry->Register(GetGid); 442 registry->Register(GetEGid); 443 registry->Register(GetGroups); 444 445 registry->Register(InitGroups); 446 registry->Register(SetEGid); 447 registry->Register(SetEUid); 448 registry->Register(SetGid); 449 registry->Register(SetUid); 450 registry->Register(SetGroups); 451#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS 452} 453 454static void Initialize(Local<Object> target, 455 Local<Value> unused, 456 Local<Context> context, 457 void* priv) { 458 SetMethod(context, target, "safeGetenv", SafeGetenv); 459 460#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS 461 Environment* env = Environment::GetCurrent(context); 462 Isolate* isolate = env->isolate(); 463 464 READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials"); 465 SetMethodNoSideEffect(context, target, "getuid", GetUid); 466 SetMethodNoSideEffect(context, target, "geteuid", GetEUid); 467 SetMethodNoSideEffect(context, target, "getgid", GetGid); 468 SetMethodNoSideEffect(context, target, "getegid", GetEGid); 469 SetMethodNoSideEffect(context, target, "getgroups", GetGroups); 470 471 if (env->owns_process_state()) { 472 SetMethod(context, target, "initgroups", InitGroups); 473 SetMethod(context, target, "setegid", SetEGid); 474 SetMethod(context, target, "seteuid", SetEUid); 475 SetMethod(context, target, "setgid", SetGid); 476 SetMethod(context, target, "setuid", SetUid); 477 SetMethod(context, target, "setgroups", SetGroups); 478 } 479#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS 480} 481 482} // namespace credentials 483} // namespace node 484 485NODE_BINDING_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize) 486NODE_BINDING_EXTERNAL_REFERENCE(credentials, 487 node::credentials::RegisterExternalReferences) 488