Skip to content

Commit 87270fd

Browse files
committed
process: add get/set resuid
This PR adds support for getresuid and setresuid for js side
1 parent 9843885 commit 87270fd

File tree

5 files changed

+186
-1
lines changed

5 files changed

+186
-1
lines changed

doc/api/process.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,36 @@ if (process.getuid) {
19661966
This function is only available on POSIX platforms (i.e. not Windows or
19671967
Android).
19681968
1969+
## `process.getresuid()`
1970+
1971+
<!-- YAML
1972+
added: REPLACEME
1973+
-->
1974+
1975+
The `process.getresuid()` method returns an array with the real, effective,
1976+
and saved user IDs.
1977+
1978+
* Returns: {integer\[]}
1979+
1980+
```mjs
1981+
import process from 'process';
1982+
1983+
if (process.getresuid) {
1984+
console.log(process.getresuid()); // [ 0, 0, 0 ]
1985+
}
1986+
```
1987+
1988+
```cjs
1989+
const process = require('process');
1990+
1991+
if (process.getresuid) {
1992+
console.log(process.getresuid()); // [ 0, 0, 0 ]
1993+
}
1994+
```
1995+
1996+
This function is only available on POSIX platforms (i.e. not Windows or
1997+
Android).
1998+
19691999
## `process.hasUncaughtExceptionCaptureCallback()`
19702000
19712001
<!-- YAML
@@ -3333,6 +3363,51 @@ This function is only available on POSIX platforms (i.e. not Windows or
33333363
Android).
33343364
This feature is not available in [`Worker`][] threads.
33353365
3366+
## `process.setresuid(ruid, euid, suid)`
3367+
3368+
<!-- YAML
3369+
added: REPLACEME
3370+
-->
3371+
3372+
The `process.setresuid(ruid, euid, suid)` method sets the real, effective,
3373+
and saved user IDs.
3374+
3375+
* `ruid` {string|number} The real user ID
3376+
* `euid` {string|number} The effective user ID
3377+
* `suid` {string|number} The saved user ID
3378+
3379+
```mjs
3380+
import process from 'process';
3381+
3382+
if (process.getresuid && process.setresuid) {
3383+
console.log(`Current ids: ${process.getresuid()[0]}`);
3384+
try {
3385+
process.setresuid(501, 501, 501);
3386+
console.log(`New ids: ${process.getresuid()}`);
3387+
} catch (err) {
3388+
console.log(`Failed to set ids: ${err}`);
3389+
}
3390+
}
3391+
```
3392+
3393+
```cjs
3394+
const process = require('process');
3395+
3396+
if (process.getresuid && process.setresuid) {
3397+
console.log(`Current ids: ${process.getresuid()[0]}`);
3398+
try {
3399+
process.setresuid(501, 501, 501);
3400+
console.log(`New ids: ${process.getresuid()}`);
3401+
} catch (err) {
3402+
console.log(`Failed to set ids: ${err}`);
3403+
}
3404+
}
3405+
```
3406+
3407+
This function is only available on POSIX platforms (i.e. not Windows or
3408+
Android).
3409+
This feature is not available in [`Worker`][] threads.
3410+
33363411
## `process.setSourceMapsEnabled(val)`
33373412
33383413
<!-- YAML

lib/internal/bootstrap/node.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ if (credentials.implementsPosixCredentials) {
175175
process.getgid = credentials.getgid;
176176
process.getegid = credentials.getegid;
177177
process.getgroups = credentials.getgroups;
178+
process.getresuid = credentials.getresuid;
178179
}
179180

180181
// Setup the callbacks that node::AsyncWrap will call when there are hooks to

lib/internal/bootstrap/switches/does_own_process_state.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ if (credentials.implementsPosixCredentials) {
1717
process.seteuid = wrapped.seteuid;
1818
process.setgid = wrapped.setgid;
1919
process.setuid = wrapped.setuid;
20+
process.setresuid = wrapped.setresuid;
2021
}
2122

2223
// ---- keep the attachment of the wrappers above so that it's easier to ----
@@ -45,7 +46,8 @@ function wrapPosixCredentialSetters(credentials) {
4546
setegid: _setegid,
4647
seteuid: _seteuid,
4748
setgid: _setgid,
48-
setuid: _setuid
49+
setuid: _setuid,
50+
setresuid: _setresuid
4951
} = credentials;
5052

5153
function initgroups(user, extraGroup) {
@@ -73,6 +75,37 @@ function wrapPosixCredentialSetters(credentials) {
7375
}
7476
}
7577

78+
function setresuid(ruid, euid, suid) {
79+
const ids = credentials.getresuid();
80+
if (ruid === -1) ruid = ids[0];
81+
else {
82+
validateId(ruid, 'ruid');
83+
if (typeof ruid === 'number') ruid |= 0;
84+
}
85+
86+
if (euid === -1) euid = ids[1];
87+
else {
88+
validateId(euid, 'euid');
89+
if (typeof euid === 'number') euid |= 0;
90+
}
91+
92+
if (suid === -1) suid = ids[2];
93+
else {
94+
validateId(suid, 'suid');
95+
if (typeof suid === 'number') suid |= 0;
96+
}
97+
98+
// Result is 0 on success, 0b1xxx if credential is unknown.
99+
const result = _setresuid(ruid, euid, suid);
100+
if (result >= 0b1000) {
101+
const failures = [];
102+
if (result & 0b0001) failures.push(ruid);
103+
if (result & 0b0010) failures.push(euid);
104+
if (result & 0b0100) failures.push(suid);
105+
throw new ERR_UNKNOWN_CREDENTIAL('User', failures);
106+
}
107+
}
108+
76109
function wrapIdSetter(type, method) {
77110
return function(id) {
78111
validateId(id, 'id');
@@ -96,6 +129,7 @@ function wrapPosixCredentialSetters(credentials) {
96129
return {
97130
initgroups,
98131
setgroups,
132+
setresuid,
99133
setegid: wrapIdSetter('Group', _setegid),
100134
seteuid: wrapIdSetter('User', _seteuid),
101135
setgid: wrapIdSetter('Group', _setgid),

src/node_credentials.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,44 @@ static void SetGroups(const FunctionCallbackInfo<Value>& args) {
375375
args.GetReturnValue().Set(0);
376376
}
377377

378+
static void GetRESUid(const FunctionCallbackInfo<Value>& args) {
379+
Environment* env = Environment::GetCurrent(args);
380+
CHECK(env->has_run_bootstrapping_code());
381+
uid_t ruid, euid, suid;
382+
getresuid(&ruid, &euid, &suid);
383+
MaybeLocal<Value> array =
384+
ToV8Value(env->context(), std::vector<uid_t>{ruid, euid, suid});
385+
args.GetReturnValue().Set(array.ToLocalChecked());
386+
}
387+
388+
static void SetRESUid(const FunctionCallbackInfo<Value>& args) {
389+
Environment* env = Environment::GetCurrent(args);
390+
CHECK(env->owns_process_state());
391+
392+
CHECK_EQ(args.Length(), 3);
393+
for (int i = 0; i < 3; i++) {
394+
CHECK(args[i]->IsUint32() || args[i]->IsString());
395+
}
396+
397+
uid_t ruid = uid_by_name(env->isolate(), args[0]);
398+
uid_t euid = uid_by_name(env->isolate(), args[1]);
399+
uid_t suid = uid_by_name(env->isolate(), args[2]);
400+
401+
if (ruid == uid_not_found || euid == uid_not_found ||
402+
suid == uid_not_found) {
403+
// Tells JS to throw ERR_INVALID_CREDENTIAL
404+
int flag = 0b1000;
405+
if (ruid == uid_not_found) flag |= 0b0001;
406+
if (euid == uid_not_found) flag |= 0b0010;
407+
if (suid == uid_not_found) flag |= 0b0100;
408+
args.GetReturnValue().Set(flag);
409+
} else if (setresuid(ruid, euid, suid)) {
410+
env->ThrowErrnoException(errno, "setresuid");
411+
} else {
412+
args.GetReturnValue().Set(0);
413+
}
414+
}
415+
378416
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
379417
Environment* env = Environment::GetCurrent(args);
380418

@@ -429,6 +467,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
429467
registry->Register(GetEGid);
430468
registry->Register(GetGroups);
431469

470+
registry->Register(SetRESUid);
471+
registry->Register(GetRESUid);
472+
432473
registry->Register(InitGroups);
433474
registry->Register(SetEGid);
434475
registry->Register(SetEUid);
@@ -454,6 +495,7 @@ static void Initialize(Local<Object> target,
454495
env->SetMethodNoSideEffect(target, "getgid", GetGid);
455496
env->SetMethodNoSideEffect(target, "getegid", GetEGid);
456497
env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
498+
env->SetMethodNoSideEffect(target, "getresuid", GetRESUid);
457499

458500
if (env->owns_process_state()) {
459501
env->SetMethod(target, "initgroups", InitGroups);
@@ -462,6 +504,7 @@ static void Initialize(Local<Object> target,
462504
env->SetMethod(target, "setgid", SetGid);
463505
env->SetMethod(target, "setuid", SetUid);
464506
env->SetMethod(target, "setgroups", SetGroups);
507+
env->SetMethod(target, "setresuid", SetRESUid);
465508
}
466509
#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS
467510
}

test/parallel/test-process-uid-gid.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ if (common.isWindows) {
3030
assert.strictEqual(process.getgid, undefined);
3131
assert.strictEqual(process.setuid, undefined);
3232
assert.strictEqual(process.setgid, undefined);
33+
assert.strictEqual(process.getresuid, undefined);
34+
assert.strictEqual(process.setresuid, undefined);
3335
return;
3436
}
3537

@@ -51,6 +53,30 @@ assert.throws(() => {
5153
message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'
5254
});
5355

56+
assert.throws(() => {
57+
process.setresuid({}, 0, 0);
58+
}, {
59+
code: 'ERR_INVALID_ARG_TYPE',
60+
message: 'The "ruid" argument must be one of type ' +
61+
'number or string. Received an instance of Object'
62+
});
63+
64+
assert.throws(() => {
65+
process.setresuid(0, {}, 0);
66+
}, {
67+
code: 'ERR_INVALID_ARG_TYPE',
68+
message: 'The "euid" argument must be one of type ' +
69+
'number or string. Received an instance of Object'
70+
});
71+
72+
assert.throws(() => {
73+
process.setresuid(0, 0, {});
74+
}, {
75+
code: 'ERR_INVALID_ARG_TYPE',
76+
message: 'The "suid" argument must be one of type ' +
77+
'number or string. Received an instance of Object'
78+
});
79+
5480
// Passing -0 shouldn't crash the process
5581
// Refs: https://github.com/nodejs/node/issues/32750
5682
try { process.setuid(-0); } catch {}
@@ -63,6 +89,7 @@ if (process.getuid() !== 0) {
6389
// Should not throw.
6490
process.getgid();
6591
process.getuid();
92+
process.getresuid();
6693

6794
assert.throws(
6895
() => { process.setgid('nobody'); },
@@ -73,6 +100,11 @@ if (process.getuid() !== 0) {
73100
() => { process.setuid('nobody'); },
74101
/(?:EPERM, .+|User identifier does not exist: nobody)$/
75102
);
103+
104+
assert.throws(
105+
() => { process.setresuid('nobody', 1, 2); },
106+
/(?:EPERM, .+|Group identifier does not exist: \[ 'nobody' \])$/
107+
);
76108
return;
77109
}
78110

0 commit comments

Comments
 (0)