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: 170 201 84.6 %
Date: 2019-09-15 22:29:17 Branches: 106 198 53.5 %

Line Branch Exec Source
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::Function;
19
using v8::FunctionCallbackInfo;
20
using v8::HandleScope;
21
using v8::Isolate;
22
using v8::Local;
23
using v8::MaybeLocal;
24
using v8::NewStringType;
25
using v8::Object;
26
using v8::String;
27
using v8::TryCatch;
28
using v8::Uint32;
29
using v8::Value;
30
31
namespace per_process {
32
bool linux_at_secure = false;
33
}  // namespace per_process
34
35
namespace credentials {
36
37
// Look up environment variable unless running as setuid root.
38
56010
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
39
#if !defined(__CloudABI__) && !defined(_WIN32)
40


112020
  if (per_process::linux_at_secure || getuid() != geteuid() ||
41
56010
      getgid() != getegid())
42
    goto fail;
43
#endif
44
45
56010
  if (env != nullptr) {
46
15857
    HandleScope handle_scope(env->isolate());
47
15857
    TryCatch ignore_errors(env->isolate());
48
31714
    MaybeLocal<String> value = env->env_vars()->Get(
49
        env->isolate(),
50
15857
        String::NewFromUtf8(env->isolate(), key, NewStringType::kNormal)
51
47571
            .ToLocalChecked());
52
15857
    if (value.IsEmpty()) goto fail;
53
5271
    String::Utf8Value utf8_value(env->isolate(), value.ToLocalChecked());
54
5271
    if (*utf8_value == nullptr) goto fail;
55
5271
    *text = std::string(*utf8_value, utf8_value.length());
56

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

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

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


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


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


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


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


6
  CHECK(args[0]->IsUint32() || args[0]->IsString());
337


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