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: 171 203 84.2 %
Date: 2019-05-05 22:32:45 Branches: 107 202 53.0 %

Line Branch Exec Source
1
#include "node_internals.h"
2
3
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
4
#include <grp.h>  // getgrnam()
5
#include <pwd.h>  // getpwnam()
6
#endif            // NODE_IMPLEMENTS_POSIX_CREDENTIALS
7
8
#if !defined(_MSC_VER)
9
#include <unistd.h>  // setuid, getuid
10
#endif
11
12
namespace node {
13
14
using v8::Array;
15
using v8::Context;
16
using v8::Function;
17
using v8::FunctionCallbackInfo;
18
using v8::HandleScope;
19
using v8::Isolate;
20
using v8::Local;
21
using v8::MaybeLocal;
22
using v8::NewStringType;
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.
36
50096
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
37
#if !defined(__CloudABI__) && !defined(_WIN32)
38


100192
  if (per_process::linux_at_secure || getuid() != geteuid() ||
39
50096
      getgid() != getegid())
40
    goto fail;
41
#endif
42
43
50096
  if (env != nullptr) {
44
14007
    HandleScope handle_scope(env->isolate());
45
14007
    TryCatch ignore_errors(env->isolate());
46
28014
    MaybeLocal<String> value = env->env_vars()->Get(
47
        env->isolate(),
48
14007
        String::NewFromUtf8(env->isolate(), key, NewStringType::kNormal)
49
56028
            .ToLocalChecked());
50
14007
    if (value.IsEmpty()) goto fail;
51
4731
    String::Utf8Value utf8_value(env->isolate(), value.ToLocalChecked());
52
4731
    if (*utf8_value == nullptr) goto fail;
53
4731
    *text = std::string(*utf8_value, utf8_value.length());
54

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

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

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


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


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


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


12
  CHECK(args[0]->IsUint32() || args[0]->IsString());
265
266
2
  uid_t uid = uid_by_name(env->isolate(), args[0]);
267
268
2
  if (uid == uid_not_found) {
269
    // Tells JS to throw ERR_INVALID_CREDENTIAL
270
2
    args.GetReturnValue().Set(1);
271
1
  } else if (seteuid(uid)) {
272
1
    env->ThrowErrnoException(errno, "seteuid");
273
  } else {
274
    args.GetReturnValue().Set(0);
275
  }
276
2
}
277
278
2
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
279
2
  Environment* env = Environment::GetCurrent(args);
280
2
  CHECK(env->has_run_bootstrapping_code());
281
282
2
  int ngroups = getgroups(0, nullptr);
283
2
  if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups");
284
285
2
  std::vector<gid_t> groups(ngroups);
286
287
2
  ngroups = getgroups(ngroups, groups.data());
288
2
  if (ngroups == -1)
289
    return env->ThrowErrnoException(errno, "getgroups");
290
291
2
  groups.resize(ngroups);
292
2
  gid_t egid = getegid();
293
2
  if (std::find(groups.begin(), groups.end(), egid) == groups.end())
294
    groups.push_back(egid);
295
2
  MaybeLocal<Value> array = ToV8Value(env->context(), groups);
296
2
  if (!array.IsEmpty())
297
4
    args.GetReturnValue().Set(array.ToLocalChecked());
298
}
299
300
1
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
301
1
  Environment* env = Environment::GetCurrent(args);
302
303
1
  CHECK_EQ(args.Length(), 1);
304
2
  CHECK(args[0]->IsArray());
305
306
2
  Local<Array> groups_list = args[0].As<Array>();
307
1
  size_t size = groups_list->Length();
308
1
  gid_t* groups = new gid_t[size];
309
310
2
  for (size_t i = 0; i < size; i++) {
311
    gid_t gid = gid_by_name(
312
6
        env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked());
313
314
2
    if (gid == gid_not_found) {
315
1
      delete[] groups;
316
      // Tells JS to throw ERR_INVALID_CREDENTIAL
317
3
      args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
318
2
      return;
319
    }
320
321
1
    groups[i] = gid;
322
  }
323
324
  int rc = setgroups(size, groups);
325
  delete[] 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
4646
static void Initialize(Local<Object> target,
377
                       Local<Value> unused,
378
                       Local<Context> context,
379
                       void* priv) {
380
4646
  Environment* env = Environment::GetCurrent(context);
381
4646
  Isolate* isolate = env->isolate();
382
383
4646
  env->SetMethod(target, "safeGetenv", SafeGetenv);
384
385
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
386
13938
  READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials");
387
4646
  env->SetMethodNoSideEffect(target, "getuid", GetUid);
388
4646
  env->SetMethodNoSideEffect(target, "geteuid", GetEUid);
389
4646
  env->SetMethodNoSideEffect(target, "getgid", GetGid);
390
4646
  env->SetMethodNoSideEffect(target, "getegid", GetEGid);
391
4646
  env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
392
393
4646
  if (env->owns_process_state()) {
394
4469
    env->SetMethod(target, "initgroups", InitGroups);
395
4469
    env->SetMethod(target, "setegid", SetEGid);
396
4469
    env->SetMethod(target, "seteuid", SetEUid);
397
4469
    env->SetMethod(target, "setgid", SetGid);
398
4469
    env->SetMethod(target, "setuid", SetUid);
399
4469
    env->SetMethod(target, "setgroups", SetGroups);
400
  }
401
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
402
4646
}
403
404
}  // namespace credentials
405
}  // namespace node
406
407
4524
NODE_MODULE_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)