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: 150 182 82.4 %
Date: 2019-03-02 22:23:06 Branches: 92 180 51.1 %

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


103892
  if (per_process::linux_at_secure || getuid() != geteuid() ||
36
51946
      getgid() != getegid())
37
    goto fail;
38
#endif
39
40
  {
41
51946
    Mutex::ScopedLock lock(per_process::env_var_mutex);
42
51946
    if (const char* value = getenv(key)) {
43
13051
      *text = value;
44
13051
      return true;
45
38895
    }
46
  }
47
48
fail:
49
38895
  text->clear();
50
38895
  return false;
51
}
52
53
8867
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
54
26601
  CHECK(args[0]->IsString());
55
8867
  Isolate* isolate = args.GetIsolate();
56
8867
  Utf8Value strenvtag(isolate, args[0]);
57
13342
  std::string text;
58
17734
  if (!SafeGetenv(*strenvtag, &text)) return;
59
  Local<Value> result =
60
8950
      ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
61
13425
  args.GetReturnValue().Set(result);
62
}
63
64
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
65
66
static const uid_t uid_not_found = static_cast<uid_t>(-1);
67
static const gid_t gid_not_found = static_cast<gid_t>(-1);
68
69
4
static uid_t uid_by_name(const char* name) {
70
  struct passwd pwd;
71
  struct passwd* pp;
72
  char buf[8192];
73
74
4
  errno = 0;
75
4
  pp = nullptr;
76
77

4
  if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
78
2
    return pp->pw_uid;
79
80
2
  return uid_not_found;
81
}
82
83
static char* name_by_uid(uid_t uid) {
84
  struct passwd pwd;
85
  struct passwd* pp;
86
  char buf[8192];
87
  int rc;
88
89
  errno = 0;
90
  pp = nullptr;
91
92
  if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
93
      pp != nullptr) {
94
    return strdup(pp->pw_name);
95
  }
96
97
  if (rc == 0) errno = ENOENT;
98
99
  return nullptr;
100
}
101
102
4
static gid_t gid_by_name(const char* name) {
103
  struct group pwd;
104
  struct group* pp;
105
  char buf[8192];
106
107
4
  errno = 0;
108
4
  pp = nullptr;
109
110

4
  if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
111
    return pp->gr_gid;
112
113
4
  return gid_not_found;
114
}
115
116
#if 0  // For future use.
117
static const char* name_by_gid(gid_t gid) {
118
  struct group pwd;
119
  struct group* pp;
120
  char buf[8192];
121
  int rc;
122
123
  errno = 0;
124
  pp = nullptr;
125
126
  if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
127
      pp != nullptr) {
128
    return strdup(pp->gr_name);
129
  }
130
131
  if (rc == 0)
132
    errno = ENOENT;
133
134
  return nullptr;
135
}
136
#endif
137
138
4
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
139
4
  if (value->IsUint32()) {
140
    return static_cast<uid_t>(value.As<Uint32>()->Value());
141
  } else {
142
4
    Utf8Value name(isolate, value);
143
4
    return uid_by_name(*name);
144
  }
145
}
146
147
5
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
148
5
  if (value->IsUint32()) {
149
2
    return static_cast<gid_t>(value.As<Uint32>()->Value());
150
  } else {
151
4
    Utf8Value name(isolate, value);
152
4
    return gid_by_name(*name);
153
  }
154
}
155
156
300
static void GetUid(const FunctionCallbackInfo<Value>& args) {
157
  // uid_t is an uint32_t on all supported platforms.
158
900
  args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
159
300
}
160
161
289
static void GetGid(const FunctionCallbackInfo<Value>& args) {
162
  // gid_t is an uint32_t on all supported platforms.
163
867
  args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
164
289
}
165
166
2
static void GetEUid(const FunctionCallbackInfo<Value>& args) {
167
  // uid_t is an uint32_t on all supported platforms.
168
6
  args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
169
2
}
170
171
2
static void GetEGid(const FunctionCallbackInfo<Value>& args) {
172
  // gid_t is an uint32_t on all supported platforms.
173
6
  args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
174
2
}
175
176
1
static void SetGid(const FunctionCallbackInfo<Value>& args) {
177
1
  Environment* env = Environment::GetCurrent(args);
178
1
  CHECK(env->owns_process_state());
179
180
1
  CHECK_EQ(args.Length(), 1);
181


6
  CHECK(args[0]->IsUint32() || args[0]->IsString());
182
183
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
184
185
1
  if (gid == gid_not_found) {
186
    // Tells JS to throw ERR_INVALID_CREDENTIAL
187
2
    args.GetReturnValue().Set(1);
188
  } else if (setgid(gid)) {
189
    env->ThrowErrnoException(errno, "setgid");
190
  } else {
191
    args.GetReturnValue().Set(0);
192
  }
193
1
}
194
195
1
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
196
1
  Environment* env = Environment::GetCurrent(args);
197
1
  CHECK(env->owns_process_state());
198
199
1
  CHECK_EQ(args.Length(), 1);
200


6
  CHECK(args[0]->IsUint32() || args[0]->IsString());
201
202
1
  gid_t gid = gid_by_name(env->isolate(), args[0]);
203
204
1
  if (gid == gid_not_found) {
205
    // Tells JS to throw ERR_INVALID_CREDENTIAL
206
2
    args.GetReturnValue().Set(1);
207
  } else if (setegid(gid)) {
208
    env->ThrowErrnoException(errno, "setegid");
209
  } else {
210
    args.GetReturnValue().Set(0);
211
  }
212
1
}
213
214
2
static void SetUid(const FunctionCallbackInfo<Value>& args) {
215
2
  Environment* env = Environment::GetCurrent(args);
216
2
  CHECK(env->owns_process_state());
217
218
2
  CHECK_EQ(args.Length(), 1);
219


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


12
  CHECK(args[0]->IsUint32() || args[0]->IsString());
239
240
2
  uid_t uid = uid_by_name(env->isolate(), args[0]);
241
242
2
  if (uid == uid_not_found) {
243
    // Tells JS to throw ERR_INVALID_CREDENTIAL
244
2
    args.GetReturnValue().Set(1);
245
1
  } else if (seteuid(uid)) {
246
1
    env->ThrowErrnoException(errno, "seteuid");
247
  } else {
248
    args.GetReturnValue().Set(0);
249
  }
250
2
}
251
252
2
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
253
2
  Environment* env = Environment::GetCurrent(args);
254
255
2
  int ngroups = getgroups(0, nullptr);
256
2
  if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups");
257
258
2
  std::vector<gid_t> groups(ngroups);
259
260
2
  ngroups = getgroups(ngroups, groups.data());
261
2
  if (ngroups == -1)
262
    return env->ThrowErrnoException(errno, "getgroups");
263
264
2
  groups.resize(ngroups);
265
2
  gid_t egid = getegid();
266
2
  if (std::find(groups.begin(), groups.end(), egid) == groups.end())
267
    groups.push_back(egid);
268
2
  MaybeLocal<Value> array = ToV8Value(env->context(), groups);
269
2
  if (!array.IsEmpty())
270
4
    args.GetReturnValue().Set(array.ToLocalChecked());
271
}
272
273
1
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
274
1
  Environment* env = Environment::GetCurrent(args);
275
276
1
  CHECK_EQ(args.Length(), 1);
277
2
  CHECK(args[0]->IsArray());
278
279
2
  Local<Array> groups_list = args[0].As<Array>();
280
1
  size_t size = groups_list->Length();
281
1
  gid_t* groups = new gid_t[size];
282
283
2
  for (size_t i = 0; i < size; i++) {
284
    gid_t gid = gid_by_name(
285
6
        env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked());
286
287
2
    if (gid == gid_not_found) {
288
1
      delete[] groups;
289
      // Tells JS to throw ERR_INVALID_CREDENTIAL
290
3
      args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
291
2
      return;
292
    }
293
294
1
    groups[i] = gid;
295
  }
296
297
  int rc = setgroups(size, groups);
298
  delete[] groups;
299
300
  if (rc == -1) return env->ThrowErrnoException(errno, "setgroups");
301
302
  args.GetReturnValue().Set(0);
303
}
304
305
1
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
306
1
  Environment* env = Environment::GetCurrent(args);
307
308
1
  CHECK_EQ(args.Length(), 2);
309


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


6
  CHECK(args[1]->IsUint32() || args[1]->IsString());
311
312
1
  Utf8Value arg0(env->isolate(), args[0]);
313
  gid_t extra_group;
314
  bool must_free;
315
  char* user;
316
317
2
  if (args[0]->IsUint32()) {
318
    user = name_by_uid(args[0].As<Uint32>()->Value());
319
    must_free = true;
320
  } else {
321
1
    user = *arg0;
322
1
    must_free = false;
323
  }
324
325
1
  if (user == nullptr) {
326
    // Tells JS to throw ERR_INVALID_CREDENTIAL
327
    return args.GetReturnValue().Set(1);
328
  }
329
330
1
  extra_group = gid_by_name(env->isolate(), args[1]);
331
332
1
  if (extra_group == gid_not_found) {
333
1
    if (must_free) free(user);
334
    // Tells JS to throw ERR_INVALID_CREDENTIAL
335
2
    return args.GetReturnValue().Set(2);
336
  }
337
338
  int rc = initgroups(user, extra_group);
339
340
  if (must_free) free(user);
341
342
  if (rc) return env->ThrowErrnoException(errno, "initgroups");
343
344
  args.GetReturnValue().Set(0);
345
}
346
347
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
348
349
4408
static void Initialize(Local<Object> target,
350
                       Local<Value> unused,
351
                       Local<Context> context,
352
                       void* priv) {
353
4408
  Environment* env = Environment::GetCurrent(context);
354
4408
  Isolate* isolate = env->isolate();
355
356
4408
  env->SetMethod(target, "safeGetenv", SafeGetenv);
357
358
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
359
13224
  READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials");
360
4408
  env->SetMethodNoSideEffect(target, "getuid", GetUid);
361
4408
  env->SetMethodNoSideEffect(target, "geteuid", GetEUid);
362
4408
  env->SetMethodNoSideEffect(target, "getgid", GetGid);
363
4408
  env->SetMethodNoSideEffect(target, "getegid", GetEGid);
364
4408
  env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
365
366
4408
  if (env->owns_process_state()) {
367
4243
    env->SetMethod(target, "initgroups", InitGroups);
368
4243
    env->SetMethod(target, "setegid", SetEGid);
369
4243
    env->SetMethod(target, "seteuid", SetEUid);
370
4243
    env->SetMethod(target, "setgid", SetGid);
371
4243
    env->SetMethod(target, "setuid", SetUid);
372
4243
    env->SetMethod(target, "setgroups", SetGroups);
373
  }
374
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
375
4408
}
376
377
}  // namespace credentials
378
}  // namespace node
379
380
4292
NODE_MODULE_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)