Skip to content

Commit 3690aaa

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

File tree

5 files changed

+186
-2
lines changed

5 files changed

+186
-2
lines changed

doc/api/process.md

+75
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: v18.0.0
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: v18.0.0
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

+1
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

+36-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ process.cwd = wrappedCwd;
1010

1111
if (credentials.implementsPosixCredentials) {
1212
const wrapped = wrapPosixCredentialSetters(credentials);
13-
13+
1414
process.initgroups = wrapped.initgroups;
1515
process.setgroups = wrapped.setgroups;
1616
process.setegid = wrapped.setegid;
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 [oldRuid, oldEuid, oldSuid] = credentials.getresuid();
80+
if (ruid === -1) ruid = oldRuid;
81+
else {
82+
validateId(ruid, 'ruid');
83+
if (typeof ruid === 'number') ruid |= 0;
84+
}
85+
86+
if (euid === -1) euid = oldEuid;
87+
else {
88+
validateId(euid, 'euid');
89+
if (typeof euid === 'number') euid |= 0;
90+
}
91+
92+
if (suid === -1) suid = oldSuid;
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

+42
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,43 @@ 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 = ToV8Value(env->context(), std::vector<uid_t>{ruid, euid, suid});
384+
args.GetReturnValue().Set(array.ToLocalChecked());
385+
}
386+
387+
static void SetRESUid(const FunctionCallbackInfo<Value>& args) {
388+
Environment* env = Environment::GetCurrent(args);
389+
CHECK(env->owns_process_state());
390+
391+
CHECK_EQ(args.Length(), 3);
392+
for (int i = 0; i < 3; i++) {
393+
CHECK(args[i]->IsUint32() || args[i]->IsString());
394+
}
395+
396+
uid_t ruid = uid_by_name(env->isolate(), args[0]);
397+
uid_t euid = uid_by_name(env->isolate(), args[1]);
398+
uid_t suid = uid_by_name(env->isolate(), args[2]);
399+
400+
if (ruid == uid_not_found || euid == uid_not_found ||
401+
suid == uid_not_found) {
402+
// Tells JS to throw ERR_INVALID_CREDENTIAL
403+
int flag = 0b1000;
404+
if (ruid == uid_not_found) flag |= 0b0001;
405+
if (euid == uid_not_found) flag |= 0b0010;
406+
if (suid == uid_not_found) flag |= 0b0100;
407+
args.GetReturnValue().Set(flag);
408+
} else if (setresuid(ruid, euid, suid)) {
409+
env->ThrowErrnoException(errno, "setresuid");
410+
} else {
411+
args.GetReturnValue().Set(0);
412+
}
413+
}
414+
378415
static void InitGroups(const FunctionCallbackInfo<Value>& args) {
379416
Environment* env = Environment::GetCurrent(args);
380417

@@ -429,6 +466,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
429466
registry->Register(GetEGid);
430467
registry->Register(GetGroups);
431468

469+
registry->Register(SetRESUid);
470+
registry->Register(GetRESUid);
471+
432472
registry->Register(InitGroups);
433473
registry->Register(SetEGid);
434474
registry->Register(SetEUid);
@@ -454,6 +494,7 @@ static void Initialize(Local<Object> target,
454494
env->SetMethodNoSideEffect(target, "getgid", GetGid);
455495
env->SetMethodNoSideEffect(target, "getegid", GetEGid);
456496
env->SetMethodNoSideEffect(target, "getgroups", GetGroups);
497+
env->SetMethodNoSideEffect(target, "getresuid", GetRESUid);
457498

458499
if (env->owns_process_state()) {
459500
env->SetMethod(target, "initgroups", InitGroups);
@@ -462,6 +503,7 @@ static void Initialize(Local<Object> target,
462503
env->SetMethod(target, "setgid", SetGid);
463504
env->SetMethod(target, "setuid", SetUid);
464505
env->SetMethod(target, "setgroups", SetGroups);
506+
env->SetMethod(target, "setresuid", SetRESUid);
465507
}
466508
#endif // NODE_IMPLEMENTS_POSIX_CREDENTIALS
467509
}

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

+32
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)