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: 43 176 24.4 %
Date: 2019-02-01 22:03:38 Branches: 16 180 8.9 %

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
1804
bool SafeGetenv(const char* key, std::string* text) {
34
#if !defined(__CloudABI__) && !defined(_WIN32)
35


3608
  if (per_process::linux_at_secure || getuid() != geteuid() ||
36
1804
      getgid() != getegid())
37
    goto fail;
38
#endif
39
40
  {
41
1804
    Mutex::ScopedLock lock(per_process::env_var_mutex);
42
1804
    if (const char* value = getenv(key)) {
43
230
      *text = value;
44
230
      return true;
45
1574
    }
46
  }
47
48
fail:
49
1574
  text->clear();
50
1574
  return false;
51
}
52
53
328
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
54
984
  CHECK(args[0]->IsString());
55
328
  Isolate* isolate = args.GetIsolate();
56
328
  Utf8Value strenvtag(isolate, args[0]);
57
492
  std::string text;
58
656
  if (!SafeGetenv(*strenvtag, &text)) return;
59
  Local<Value> result =
60
328
      ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
61
492
  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
static uid_t uid_by_name(const char* name) {
70
  struct passwd pwd;
71
  struct passwd* pp;
72
  char buf[8192];
73
74
  errno = 0;
75
  pp = nullptr;
76
77
  if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
78
    return pp->pw_uid;
79
80
  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
static gid_t gid_by_name(const char* name) {
103
  struct group pwd;
104
  struct group* pp;
105
  char buf[8192];
106
107
  errno = 0;
108
  pp = nullptr;
109
110
  if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
111
    return pp->gr_gid;
112
113
  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
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
139
  if (value->IsUint32()) {
140
    return static_cast<uid_t>(value.As<Uint32>()->Value());
141
  } else {
142
    Utf8Value name(isolate, value);
143
    return uid_by_name(*name);
144
  }
145
}
146
147
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
148
  if (value->IsUint32()) {
149
    return static_cast<gid_t>(value.As<Uint32>()->Value());
150
  } else {
151
    Utf8Value name(isolate, value);
152
    return gid_by_name(*name);
153
  }
154
}
155
156
270
static void GetUid(const FunctionCallbackInfo<Value>& args) {
157
  // uid_t is an uint32_t on all supported platforms.
158
810
  args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
159
270
}
160
161
269
static void GetGid(const FunctionCallbackInfo<Value>& args) {
162
  // gid_t is an uint32_t on all supported platforms.
163
807
  args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
164
269
}
165
166
static void GetEUid(const FunctionCallbackInfo<Value>& args) {
167
  // uid_t is an uint32_t on all supported platforms.
168
  args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
169
}
170
171
static void GetEGid(const FunctionCallbackInfo<Value>& args) {
172
  // gid_t is an uint32_t on all supported platforms.
173
  args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
174
}
175
176
static void SetGid(const FunctionCallbackInfo<Value>& args) {
177
  Environment* env = Environment::GetCurrent(args);
178
  CHECK(env->is_main_thread());
179
180
  CHECK_EQ(args.Length(), 1);
181
  CHECK(args[0]->IsUint32() || args[0]->IsString());
182
183
  gid_t gid = gid_by_name(env->isolate(), args[0]);
184
185
  if (gid == gid_not_found) {
186
    // Tells JS to throw ERR_INVALID_CREDENTIAL
187
    args.GetReturnValue().Set(1);
188
  } else if (setgid(gid)) {
189
    env->ThrowErrnoException(errno, "setgid");
190
  } else {
191
    args.GetReturnValue().Set(0);
192
  }
193
}
194
195
static void SetEGid(const FunctionCallbackInfo<Value>& args) {
196
  Environment* env = Environment::GetCurrent(args);
197
  CHECK(env->is_main_thread());
198
199
  CHECK_EQ(args.Length(), 1);
200
  CHECK(args[0]->IsUint32() || args[0]->IsString());
201
202
  gid_t gid = gid_by_name(env->isolate(), args[0]);
203
204
  if (gid == gid_not_found) {
205
    // Tells JS to throw ERR_INVALID_CREDENTIAL
206
    args.GetReturnValue().Set(1);
207
  } else if (setegid(gid)) {
208
    env->ThrowErrnoException(errno, "setegid");
209
  } else {
210
    args.GetReturnValue().Set(0);
211
  }
212
}
213
214
static void SetUid(const FunctionCallbackInfo<Value>& args) {
215
  Environment* env = Environment::GetCurrent(args);
216
  CHECK(env->is_main_thread());
217
218
  CHECK_EQ(args.Length(), 1);
219
  CHECK(args[0]->IsUint32() || args[0]->IsString());
220
221
  uid_t uid = uid_by_name(env->isolate(), args[0]);
222
223
  if (uid == uid_not_found) {
224
    // Tells JS to throw ERR_INVALID_CREDENTIAL
225
    args.GetReturnValue().Set(1);
226
  } else if (setuid(uid)) {
227
    env->ThrowErrnoException(errno, "setuid");
228
  } else {
229
    args.GetReturnValue().Set(0);
230
  }
231
}
232
233
static void SetEUid(const FunctionCallbackInfo<Value>& args) {
234
  Environment* env = Environment::GetCurrent(args);
235
  CHECK(env->is_main_thread());
236
237
  CHECK_EQ(args.Length(), 1);
238
  CHECK(args[0]->IsUint32() || args[0]->IsString());
239
240
  uid_t uid = uid_by_name(env->isolate(), args[0]);
241
242
  if (uid == uid_not_found) {
243
    // Tells JS to throw ERR_INVALID_CREDENTIAL
244
    args.GetReturnValue().Set(1);
245
  } else if (seteuid(uid)) {
246
    env->ThrowErrnoException(errno, "seteuid");
247
  } else {
248
    args.GetReturnValue().Set(0);
249
  }
250
}
251
252
static void GetGroups(const FunctionCallbackInfo<Value>& args) {
253
  Environment* env = Environment::GetCurrent(args);
254
255
  int ngroups = getgroups(0, nullptr);
256
  if (ngroups == -1) return env->ThrowErrnoException(errno, "getgroups");
257
258
  std::vector<gid_t> groups(ngroups);
259
260
  ngroups = getgroups(ngroups, groups.data());
261
  if (ngroups == -1)
262
    return env->ThrowErrnoException(errno, "getgroups");
263
264
  groups.resize(ngroups);
265
  gid_t egid = getegid();
266
  if (std::find(groups.begin(), groups.end(), egid) == groups.end())
267
    groups.push_back(egid);
268
  MaybeLocal<Value> array = ToV8Value(env->context(), groups);
269
  if (!array.IsEmpty())
270
    args.GetReturnValue().Set(array.ToLocalChecked());
271
}
272
273
static void SetGroups(const FunctionCallbackInfo<Value>& args) {
274
  Environment* env = Environment::GetCurrent(args);
275
276
  CHECK_EQ(args.Length(), 1);
277
  CHECK(args[0]->IsArray());
278
279
  Local<Array> groups_list = args[0].As<Array>();
280
  size_t size = groups_list->Length();
281
  gid_t* groups = new gid_t[size];
282
283
  for (size_t i = 0; i < size; i++) {
284
    gid_t gid = gid_by_name(
285
        env->isolate(), groups_list->Get(env->context(), i).ToLocalChecked());
286
287
    if (gid == gid_not_found) {
288
      delete[] groups;
289
      // Tells JS to throw ERR_INVALID_CREDENTIAL
290
      args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
291
      return;
292
    }
293
294
    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
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
306
  Environment* env = Environment::GetCurrent(args);
307
308
  CHECK_EQ(args.Length(), 2);
309
  CHECK(args[0]->IsUint32() || args[0]->IsString());
310
  CHECK(args[1]->IsUint32() || args[1]->IsString());
311
312
  Utf8Value arg0(env->isolate(), args[0]);
313
  gid_t extra_group;
314
  bool must_free;
315
  char* user;
316
317
  if (args[0]->IsUint32()) {
318
    user = name_by_uid(args[0].As<Uint32>()->Value());
319
    must_free = true;
320
  } else {
321
    user = *arg0;
322
    must_free = false;
323
  }
324
325
  if (user == nullptr) {
326
    // Tells JS to throw ERR_INVALID_CREDENTIAL
327
    return args.GetReturnValue().Set(1);
328
  }
329
330
  extra_group = gid_by_name(env->isolate(), args[1]);
331
332
  if (extra_group == gid_not_found) {
333
    if (must_free) free(user);
334
    // Tells JS to throw ERR_INVALID_CREDENTIAL
335
    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
164
static void Initialize(Local<Object> target,
350
                       Local<Value> unused,
351
                       Local<Context> context,
352
                       void* priv) {
353
164
  Environment* env = Environment::GetCurrent(context);
354
164
  Isolate* isolate = env->isolate();
355
356
164
  env->SetMethod(target, "safeGetenv", SafeGetenv);
357
358
#ifdef NODE_IMPLEMENTS_POSIX_CREDENTIALS
359
492
  READONLY_TRUE_PROPERTY(target, "implementsPosixCredentials");
360
164
  env->SetMethodNoSideEffect(target, "getuid", GetUid);
361
164
  env->SetMethodNoSideEffect(target, "geteuid", GetEUid);
362
164
  env->SetMethodNoSideEffect(target, "getgid", GetGid);
363
164
  env->SetMethodNoSideEffect(target, "getegid", GetEGid);
364
164
  env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
365
366
164
  if (env->is_main_thread()) {
367
164
    env->SetMethod(target, "initgroups", InitGroups);
368
164
    env->SetMethod(target, "setegid", SetEGid);
369
164
    env->SetMethod(target, "seteuid", SetEUid);
370
164
    env->SetMethod(target, "setgid", SetGid);
371
164
    env->SetMethod(target, "setuid", SetUid);
372
164
    env->SetMethod(target, "setgroups", SetGroups);
373
  }
374
#endif  // NODE_IMPLEMENTS_POSIX_CREDENTIALS
375
164
}
376
377
}  // namespace credentials
378
}  // namespace node
379
380
164
NODE_MODULE_CONTEXT_AWARE_INTERNAL(credentials, node::credentials::Initialize)