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