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