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