• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 namespace node {
21 
22 using v8::Array;
23 using v8::Context;
24 using v8::FunctionCallbackInfo;
25 using v8::HandleScope;
26 using v8::Isolate;
27 using v8::Local;
28 using v8::MaybeLocal;
29 using v8::Object;
30 using v8::String;
31 using v8::TryCatch;
32 using v8::Uint32;
33 using v8::Value;
34 
linux_at_secure()35 bool 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 
48 namespace credentials {
49 
50 #if defined(__linux__)
51 // Returns true if the current process only has the passed-in capability.
HasOnly(int capability)52 bool 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.
SafeGetenv(const char * key,std::string * text,std::shared_ptr<KVStore> env_vars,v8::Isolate * isolate)76 bool 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 
124 fail:
125   text->clear();
126   return false;
127 }
128 
SafeGetenv(const FunctionCallbackInfo<Value> & args)129 static 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 
143 static const uid_t uid_not_found = static_cast<uid_t>(-1);
144 static const gid_t gid_not_found = static_cast<gid_t>(-1);
145 
uid_by_name(const char * name)146 static 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 
name_by_uid(uid_t uid)160 static 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 
gid_by_name(const char * name)179 static 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.
194 static 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 
uid_by_name(Isolate * isolate,Local<Value> value)215 static 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 
gid_by_name(Isolate * isolate,Local<Value> value)225 static 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 
GetUid(const FunctionCallbackInfo<Value> & args)235 static 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 
GetGid(const FunctionCallbackInfo<Value> & args)242 static 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 
GetEUid(const FunctionCallbackInfo<Value> & args)249 static 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 
GetEGid(const FunctionCallbackInfo<Value> & args)256 static 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 
SetGid(const FunctionCallbackInfo<Value> & args)263 static 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 
SetEGid(const FunctionCallbackInfo<Value> & args)282 static 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 
SetUid(const FunctionCallbackInfo<Value> & args)301 static 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 
SetEUid(const FunctionCallbackInfo<Value> & args)320 static 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 
GetGroups(const FunctionCallbackInfo<Value> & args)339 static 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 
SetGroups(const FunctionCallbackInfo<Value> & args)361 static 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 
InitGroups(const FunctionCallbackInfo<Value> & args)391 static 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 
RegisterExternalReferences(ExternalReferenceRegistry * registry)435 void 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 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)454 static 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 
485 NODE_BINDING_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)
486 NODE_BINDING_EXTERNAL_REFERENCE(credentials,
487                                 node::credentials::RegisterExternalReferences)
488