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: 184 215 85.6 %
Date: 2020-07-19 22:14:24 Branches: 96 178 53.9 %

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
55982
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
38
#if !defined(__CloudABI__) && !defined(_WIN32)
39


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

5406
    return true;
56
  }
57
58
  {
59
74625
    Mutex::ScopedLock lock(per_process::env_var_mutex);
60
39782
    if (const char* value = getenv(key)) {
61
4939
      *text = value;
62
4939
      return true;
63
    }
64
  }
65
66
fail:
67
45637
  text->clear();
68
45637
  return false;
69
}
70
71
10897
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
72
32691
  CHECK(args[0]->IsString());
73
10897
  Environment* env = Environment::GetCurrent(args);
74
10897
  Isolate* isolate = env->isolate();
75
16257
  Utf8Value strenvtag(isolate, args[0]);
76
16257
  std::string text;
77
10897
  if (!SafeGetenv(*strenvtag, &text, env)) return;
78
  Local<Value> result =
79
10720
      ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
80
10720
  args.GetReturnValue().Set(result);
81
}
82
83
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
84
85
static const uid_t uid_not_found = static_cast<uid_t>(-1);
86
static const gid_t gid_not_found = static_cast<gid_t>(-1);
87
88
4
static uid_t uid_by_name(const char* name) {
89
  struct passwd pwd;
90
  struct passwd* pp;
91
  char buf[8192];
92
93
4
  errno = 0;
94
4
  pp = nullptr;
95
96

4
  if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
97
2
    return pp->pw_uid;
98
99
2
  return uid_not_found;
100
}
101
102
static char* name_by_uid(uid_t uid) {
103
  struct passwd pwd;
104
  struct passwd* pp;
105
  char buf[8192];
106
  int rc;
107
108
  errno = 0;
109
  pp = nullptr;
110
111
  if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
112
      pp != nullptr) {
113
    return strdup(pp->pw_name);
114
  }
115
116
  if (rc == 0) errno = ENOENT;
117
118
  return nullptr;
119
}
120
121
4
static gid_t gid_by_name(const char* name) {
122
  struct group pwd;
123
  struct group* pp;
124
  char buf[8192];
125
126
4
  errno = 0;
127
4
  pp = nullptr;
128
129

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

5
  CHECK(args[0]->IsUint32() || args[0]->IsString());
209
210
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
211
212
1
  if (gid == gid_not_found) {
213
    // Tells JS to throw ERR_INVALID_CREDENTIAL
214
2
    args.GetReturnValue().Set(1);
215
  } else if (setgid(gid)) {
216
    env->ThrowErrnoException(errno, "setgid");
217
  } else {
218
    args.GetReturnValue().Set(0);
219
  }
220
1
}
221
222
1
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
223
1
  Environment* env = Environment::GetCurrent(args);
224
1
  CHECK(env->owns_process_state());
225
226
1
  CHECK_EQ(args.Length(), 1);
227

5
  CHECK(args[0]->IsUint32() || args[0]->IsString());
228
229
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
230
231
1
  if (gid == gid_not_found) {
232
    // Tells JS to throw ERR_INVALID_CREDENTIAL
233
2
    args.GetReturnValue().Set(1);
234
  } else if (setegid(gid)) {
235
    env->ThrowErrnoException(errno, "setegid");
236
  } else {
237
    args.GetReturnValue().Set(0);
238
  }
239
1
}
240
241
2
static void SetUid(const FunctionCallbackInfo<Value>& args) {
242
2
  Environment* env = Environment::GetCurrent(args);
243
2
  CHECK(env->owns_process_state());
244
245
2
  CHECK_EQ(args.Length(), 1);
246

10
  CHECK(args[0]->IsUint32() || args[0]->IsString());
247
248
2
  uid_t uid = uid_by_name(env->isolate(), args[0]);
249
250
2
  if (uid == uid_not_found) {
251
    // Tells JS to throw ERR_INVALID_CREDENTIAL
252
2
    args.GetReturnValue().Set(1);
253
1
  } else if (setuid(uid)) {
254
1
    env->ThrowErrnoException(errno, "setuid");
255
  } else {
256
    args.GetReturnValue().Set(0);
257
  }
258
2
}
259
260
2
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
261
2
  Environment* env = Environment::GetCurrent(args);
262
2
  CHECK(env->owns_process_state());
263
264
2
  CHECK_EQ(args.Length(), 1);
265

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

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

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

19890
NODE_MODULE_EXTERNAL_REFERENCE(credentials,
427
                               node::credentials::RegisterExternalReferences)