GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_credentials.cc Lines: 188 221 85.1 %
Date: 2020-09-06 22:14:11 Branches: 99 182 54.4 %

Line Branch Exec Source
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
15
namespace node {
16
17
using v8::Array;
18
using v8::Context;
19
using v8::FunctionCallbackInfo;
20
using v8::HandleScope;
21
using v8::Isolate;
22
using v8::Local;
23
using v8::MaybeLocal;
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.
37
50512
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
38
#if !defined(__CloudABI__) && !defined(_WIN32)
39


101024
  if (per_process::linux_at_secure || getuid() != geteuid() ||
40
50512
      getgid() != getegid())
41
    goto fail;
42
#endif
43
44
50512
  if (env != nullptr) {
45
14866
    HandleScope handle_scope(env->isolate());
46
14866
    TryCatch ignore_errors(env->isolate());
47
29732
    MaybeLocal<String> maybe_value = env->env_vars()->Get(
48
        env->isolate(),
49
29732
        String::NewFromUtf8(env->isolate(), key).ToLocalChecked());
50
    Local<String> value;
51
14866
    if (!maybe_value.ToLocal(&value)) goto fail;
52
4965
    String::Utf8Value utf8_value(env->isolate(), value);
53
4965
    if (*utf8_value == nullptr) goto fail;
54
4965
    *text = std::string(*utf8_value, utf8_value.length());
55

4965
    return true;
56
  }
57
58
  {
59
66868
    Mutex::ScopedLock lock(per_process::env_var_mutex);
60
61
35646
    size_t init_sz = 256;
62
66868
    MaybeStackBuffer<char, 256> val;
63
35646
    int ret = uv_os_getenv(key, *val, &init_sz);
64
65
35646
    if (ret == UV_ENOBUFS) {
66
      // Buffer is not large enough, reallocate to the updated init_sz
67
      // and fetch env value again.
68
      val.AllocateSufficientStorage(init_sz);
69
      ret = uv_os_getenv(key, *val, &init_sz);
70
    }
71
72
35646
    if (ret >= 0) {  // Env key value fetch success.
73
4424
      *text = *val;
74
4424
      return true;
75
    }
76
  }
77
78
fail:
79
41123
  text->clear();
80
41123
  return false;
81
}
82
83
10011
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
84
30033
  CHECK(args[0]->IsString());
85
10011
  Environment* env = Environment::GetCurrent(args);
86
10011
  Isolate* isolate = env->isolate();
87
14926
  Utf8Value strenvtag(isolate, args[0]);
88
14926
  std::string text;
89
10011
  if (!SafeGetenv(*strenvtag, &text, env)) return;
90
  Local<Value> result =
91
9830
      ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
92
9830
  args.GetReturnValue().Set(result);
93
}
94
95
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
96
97
static const uid_t uid_not_found = static_cast<uid_t>(-1);
98
static const gid_t gid_not_found = static_cast<gid_t>(-1);
99
100
4
static uid_t uid_by_name(const char* name) {
101
  struct passwd pwd;
102
  struct passwd* pp;
103
  char buf[8192];
104
105
4
  errno = 0;
106
4
  pp = nullptr;
107
108

4
  if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
109
2
    return pp->pw_uid;
110
111
2
  return uid_not_found;
112
}
113
114
static char* name_by_uid(uid_t uid) {
115
  struct passwd pwd;
116
  struct passwd* pp;
117
  char buf[8192];
118
  int rc;
119
120
  errno = 0;
121
  pp = nullptr;
122
123
  if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
124
      pp != nullptr) {
125
    return strdup(pp->pw_name);
126
  }
127
128
  if (rc == 0) errno = ENOENT;
129
130
  return nullptr;
131
}
132
133
4
static gid_t gid_by_name(const char* name) {
134
  struct group pwd;
135
  struct group* pp;
136
  char buf[8192];
137
138
4
  errno = 0;
139
4
  pp = nullptr;
140
141

4
  if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
142
    return pp->gr_gid;
143
144
4
  return gid_not_found;
145
}
146
147
#if 0  // For future use.
148
static const char* name_by_gid(gid_t gid) {
149
  struct group pwd;
150
  struct group* pp;
151
  char buf[8192];
152
  int rc;
153
154
  errno = 0;
155
  pp = nullptr;
156
157
  if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
158
      pp != nullptr) {
159
    return strdup(pp->gr_name);
160
  }
161
162
  if (rc == 0)
163
    errno = ENOENT;
164
165
  return nullptr;
166
}
167
#endif
168
169
4
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
170
4
  if (value->IsUint32()) {
171
    return static_cast<uid_t>(value.As<Uint32>()->Value());
172
  } else {
173
8
    Utf8Value name(isolate, value);
174
4
    return uid_by_name(*name);
175
  }
176
}
177
178
5
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
179
5
  if (value->IsUint32()) {
180
2
    return static_cast<gid_t>(value.As<Uint32>()->Value());
181
  } else {
182
8
    Utf8Value name(isolate, value);
183
4
    return gid_by_name(*name);
184
  }
185
}
186
187
313
static void GetUid(const FunctionCallbackInfo<Value>& args) {
188
313
  Environment* env = Environment::GetCurrent(args);
189
313
  CHECK(env->has_run_bootstrapping_code());
190
  // uid_t is an uint32_t on all supported platforms.
191
939
  args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
192
313
}
193
194
298
static void GetGid(const FunctionCallbackInfo<Value>& args) {
195
298
  Environment* env = Environment::GetCurrent(args);
196
298
  CHECK(env->has_run_bootstrapping_code());
197
  // gid_t is an uint32_t on all supported platforms.
198
894
  args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
199
298
}
200
201
2
static void GetEUid(const FunctionCallbackInfo<Value>& args) {
202
2
  Environment* env = Environment::GetCurrent(args);
203
2
  CHECK(env->has_run_bootstrapping_code());
204
  // uid_t is an uint32_t on all supported platforms.
205
6
  args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
206
2
}
207
208
2
static void GetEGid(const FunctionCallbackInfo<Value>& args) {
209
2
  Environment* env = Environment::GetCurrent(args);
210
2
  CHECK(env->has_run_bootstrapping_code());
211
  // gid_t is an uint32_t on all supported platforms.
212
6
  args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
213
2
}
214
215
1
static void SetGid(const FunctionCallbackInfo<Value>& args) {
216
1
  Environment* env = Environment::GetCurrent(args);
217
1
  CHECK(env->owns_process_state());
218
219
1
  CHECK_EQ(args.Length(), 1);
220

5
  CHECK(args[0]->IsUint32() || args[0]->IsString());
221
222
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
223
224
1
  if (gid == gid_not_found) {
225
    // Tells JS to throw ERR_INVALID_CREDENTIAL
226
2
    args.GetReturnValue().Set(1);
227
  } else if (setgid(gid)) {
228
    env->ThrowErrnoException(errno, "setgid");
229
  } else {
230
    args.GetReturnValue().Set(0);
231
  }
232
1
}
233
234
1
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
235
1
  Environment* env = Environment::GetCurrent(args);
236
1
  CHECK(env->owns_process_state());
237
238
1
  CHECK_EQ(args.Length(), 1);
239

5
  CHECK(args[0]->IsUint32() || args[0]->IsString());
240
241
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
242
243
1
  if (gid == gid_not_found) {
244
    // Tells JS to throw ERR_INVALID_CREDENTIAL
245
2
    args.GetReturnValue().Set(1);
246
  } else if (setegid(gid)) {
247
    env->ThrowErrnoException(errno, "setegid");
248
  } else {
249
    args.GetReturnValue().Set(0);
250
  }
251
1
}
252
253
2
static void SetUid(const FunctionCallbackInfo<Value>& args) {
254
2
  Environment* env = Environment::GetCurrent(args);
255
2
  CHECK(env->owns_process_state());
256
257
2
  CHECK_EQ(args.Length(), 1);
258

10
  CHECK(args[0]->IsUint32() || args[0]->IsString());
259
260
2
  uid_t uid = uid_by_name(env->isolate(), args[0]);
261
262
2
  if (uid == uid_not_found) {
263
    // Tells JS to throw ERR_INVALID_CREDENTIAL
264
2
    args.GetReturnValue().Set(1);
265
1
  } else if (setuid(uid)) {
266
1
    env->ThrowErrnoException(errno, "setuid");
267
  } else {
268
    args.GetReturnValue().Set(0);
269
  }
270
2
}
271
272
2
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
273
2
  Environment* env = Environment::GetCurrent(args);
274
2
  CHECK(env->owns_process_state());
275
276
2
  CHECK_EQ(args.Length(), 1);
277

10
  CHECK(args[0]->IsUint32() || args[0]->IsString());
278
279
2
  uid_t uid = uid_by_name(env->isolate(), args[0]);
280
281
2
  if (uid == uid_not_found) {
282
    // Tells JS to throw ERR_INVALID_CREDENTIAL
283
2
    args.GetReturnValue().Set(1);
284
1
  } else if (seteuid(uid)) {
285
1
    env->ThrowErrnoException(errno, "seteuid");
286
  } else {
287
    args.GetReturnValue().Set(0);
288
  }
289
2
}
290
291
2
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
292
2
  Environment* env = Environment::GetCurrent(args);
293
2
  CHECK(env->has_run_bootstrapping_code());
294
295
2
  int ngroups = getgroups(0, nullptr);
296
2
  if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups");
297
298
4
  std::vector<gid_t> groups(ngroups);
299
300
2
  ngroups = getgroups(ngroups, groups.data());
301
2
  if (ngroups == -1)
302
    return env->ThrowErrnoException(errno, "getgroups");
303
304
2
  groups.resize(ngroups);
305
2
  gid_t egid = getegid();
306
2
  if (std::find(groups.begin(), groups.end(), egid) == groups.end())
307
    groups.push_back(egid);
308
2
  MaybeLocal<Value> array = ToV8Value(env->context(), groups);
309
2
  if (!array.IsEmpty())
310
4
    args.GetReturnValue().Set(array.ToLocalChecked());
311
}
312
313
1
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
314
1
  Environment* env = Environment::GetCurrent(args);
315
316
1
  CHECK_EQ(args.Length(), 1);
317
2
  CHECK(args[0]->IsArray());
318
319
2
  Local<Array> groups_list = args[0].As<Array>();
320
1
  size_t size = groups_list->Length();
321
1
  MaybeStackBuffer<gid_t, 64> groups(size);
322
323
2
  for (size_t i = 0; i < size; i++) {
324
2
    gid_t gid = gid_by_name(
325
6
        env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked());
326
327
2
    if (gid == gid_not_found) {
328
      // Tells JS to throw ERR_INVALID_CREDENTIAL
329
3
      args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
330
1
      return;
331
    }
332
333
1
    groups[i] = gid;
334
  }
335
336
  int rc = setgroups(size, *groups);
337
338
  if (rc == -1) return env->ThrowErrnoException(errno, "setgroups");
339
340
  args.GetReturnValue().Set(0);
341
}
342
343
1
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
344
1
  Environment* env = Environment::GetCurrent(args);
345
346
1
  CHECK_EQ(args.Length(), 2);
347

5
  CHECK(args[0]->IsUint32() || args[0]->IsString());
348

5
  CHECK(args[1]->IsUint32() || args[1]->IsString());
349
350
1
  Utf8Value arg0(env->isolate(), args[0]);
351
  gid_t extra_group;
352
  bool must_free;
353
  char* user;
354
355
2
  if (args[0]->IsUint32()) {
356
    user = name_by_uid(args[0].As<Uint32>()->Value());
357
    must_free = true;
358
  } else {
359
1
    user = *arg0;
360
1
    must_free = false;
361
  }
362
363
1
  if (user == nullptr) {
364
    // Tells JS to throw ERR_INVALID_CREDENTIAL
365
    return args.GetReturnValue().Set(1);
366
  }
367
368
1
  extra_group = gid_by_name(env->isolate(), args[1]);
369
370
1
  if (extra_group == gid_not_found) {
371
1
    if (must_free) free(user);
372
    // Tells JS to throw ERR_INVALID_CREDENTIAL
373
2
    return args.GetReturnValue().Set(2);
374
  }
375
376
  int rc = initgroups(user, extra_group);
377
378
  if (must_free) free(user);
379
380
  if (rc) return env->ThrowErrnoException(errno, "initgroups");
381
382
  args.GetReturnValue().Set(0);
383
}
384
385
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
386
387
4403
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
388
4403
  registry->Register(SafeGetenv);
389
390
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
391
4403
  registry->Register(GetUid);
392
4403
  registry->Register(GetEUid);
393
4403
  registry->Register(GetGid);
394
4403
  registry->Register(GetEGid);
395
4403
  registry->Register(GetGroups);
396
397
4403
  registry->Register(InitGroups);
398
4403
  registry->Register(SetEGid);
399
4403
  registry->Register(SetEUid);
400
4403
  registry->Register(SetGid);
401
4403
  registry->Register(SetUid);
402
4403
  registry->Register(SetGroups);
403
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
404
4403
}
405
406
453
static void Initialize(Local<Object> target,
407
                       Local<Value> unused,
408
                       Local<Context> context,
409
                       void* priv) {
410
453
  Environment* env = Environment::GetCurrent(context);
411
453
  Isolate* isolate = env->isolate();
412
413
453
  env->SetMethod(target, "safeGetenv", SafeGetenv);
414
415
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
416
1359
  READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials");
417
453
  env->SetMethodNoSideEffect(target, "getuid", GetUid);
418
453
  env->SetMethodNoSideEffect(target, "geteuid", GetEUid);
419
453
  env->SetMethodNoSideEffect(target, "getgid", GetGid);
420
453
  env->SetMethodNoSideEffect(target, "getegid", GetEGid);
421
453
  env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
422
423
453
  if (env->owns_process_state()) {
424
41
    env->SetMethod(target, "initgroups", InitGroups);
425
41
    env->SetMethod(target, "setegid", SetEGid);
426
41
    env->SetMethod(target, "seteuid", SetEUid);
427
41
    env->SetMethod(target, "setgid", SetGid);
428
41
    env->SetMethod(target, "setuid", SetUid);
429
41
    env->SetMethod(target, "setgroups", SetGroups);
430
  }
431
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
432
453
}
433
434
}  // namespace credentials
435
}  // namespace node
436
437
4472
NODE_MODULE_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)
438

17822
NODE_MODULE_EXTERNAL_REFERENCE(credentials,
439
                               node::credentials::RegisterExternalReferences)