child_process: Add gid/uid flags to spawn config
This is mostly working, but not completely ideal for two reasons. 1. Rather than emitting an error on the ChildProcess object when the setgid/setuid fails, it is simply printing the error to stderr and exiting. The same happens with the cwd, so that's not completely terrible. 2. I don't have a good test for this. It fails with an EPERM if you try to change the uid or gid as a non-root user.
This commit is contained in:
parent
b7419dfaad
commit
6f5d95de6d
@ -185,21 +185,27 @@ ChildProcess.prototype.kill = function(sig) {
|
||||
ChildProcess.prototype.spawn = function(path, args, options, customFds) {
|
||||
args = args || [];
|
||||
|
||||
var cwd, env, setsid;
|
||||
var cwd, env, setsid, uid, gid;
|
||||
if (!options || options.cwd === undefined &&
|
||||
options.env === undefined &&
|
||||
options.customFds === undefined) {
|
||||
options.customFds === undefined &&
|
||||
options.gid === undefined &&
|
||||
options.uid === undefined) {
|
||||
// Deprecated API: (path, args, options, env, customFds)
|
||||
cwd = '';
|
||||
env = options || process.env;
|
||||
customFds = customFds || [-1, -1, -1];
|
||||
setsid = false;
|
||||
uid = -1;
|
||||
gid = -1;
|
||||
} else {
|
||||
// Recommended API: (path, args, options)
|
||||
cwd = options.cwd || '';
|
||||
env = options.env || process.env;
|
||||
customFds = options.customFds || [-1, -1, -1];
|
||||
setsid = options.setsid ? true : false;
|
||||
uid = options.hasOwnProperty("uid") ? options.uid : -1;
|
||||
gid = options.hasOwnProperty("gid") ? options.gid : -1;
|
||||
}
|
||||
|
||||
var envPairs = [];
|
||||
@ -214,7 +220,9 @@ ChildProcess.prototype.spawn = function(path, args, options, customFds) {
|
||||
cwd,
|
||||
envPairs,
|
||||
customFds,
|
||||
setsid);
|
||||
setsid,
|
||||
uid,
|
||||
gid);
|
||||
this.fds = fds;
|
||||
|
||||
if (customFds[0] === -1 || customFds[0] === undefined) {
|
||||
|
@ -158,7 +158,18 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
|
||||
|
||||
int fds[3];
|
||||
|
||||
int r = child->Spawn(argv[0], argv, cwd, env, fds, custom_fds, do_setsid);
|
||||
int uid = args[6]->ToInteger()->Value();
|
||||
int gid = args[7]->ToInteger()->Value();
|
||||
|
||||
int r = child->Spawn(argv[0],
|
||||
argv,
|
||||
cwd,
|
||||
env,
|
||||
fds,
|
||||
custom_fds,
|
||||
do_setsid,
|
||||
uid,
|
||||
gid);
|
||||
|
||||
for (i = 0; i < argv_length; i++) free(argv[i]);
|
||||
delete [] argv;
|
||||
@ -233,7 +244,9 @@ int ChildProcess::Spawn(const char *file,
|
||||
char **env,
|
||||
int stdio_fds[3],
|
||||
int custom_fds[3],
|
||||
bool do_setsid) {
|
||||
bool do_setsid,
|
||||
int custom_uid,
|
||||
int custom_gid) {
|
||||
HandleScope scope;
|
||||
assert(pid_ == -1);
|
||||
assert(!ev_is_active(&child_watcher_));
|
||||
@ -276,6 +289,17 @@ int ChildProcess::Spawn(const char *file,
|
||||
case 0: // Child.
|
||||
if (do_setsid && setsid() < 0) {
|
||||
perror("setsid");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (custom_gid != -1 && setgid(custom_gid)) {
|
||||
perror("setgid()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (custom_uid != -1 && setuid(custom_uid)) {
|
||||
perror("setuid()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if (custom_fds[0] == -1) {
|
||||
|
@ -64,7 +64,9 @@ class ChildProcess : ObjectWrap {
|
||||
char **env,
|
||||
int stdio_fds[3],
|
||||
int custom_fds[3],
|
||||
bool do_setsid);
|
||||
bool do_setsid,
|
||||
int uid,
|
||||
int gid);
|
||||
|
||||
// Simple syscall wrapper. Does not disable the watcher. onexit will be
|
||||
// called still.
|
||||
|
41
test/disabled/test-child-process-uid-gid.js
Normal file
41
test/disabled/test-child-process-uid-gid.js
Normal file
@ -0,0 +1,41 @@
|
||||
// must be run as sudo, otherwise the gid/uid setting will fail.
|
||||
var child_process = require("child_process"),
|
||||
constants = require("constants"),
|
||||
passwd = require("fs").readFileSync("/etc/passwd", "utf8"),
|
||||
myUid = process.getuid(),
|
||||
myGid = process.getgid();
|
||||
|
||||
// get a different user.
|
||||
// don't care who it is, as long as it's not root
|
||||
passwd = passwd.trim().split(/\n/);
|
||||
for (var i = 0, l = passwd.length; i < l; i ++) {
|
||||
if (passwd[i].charAt(0) === "#") continue;
|
||||
passwd[i] = passwd[i].split(":");
|
||||
var otherName = passwd[i][0];
|
||||
var otherUid = +passwd[i][2];
|
||||
var otherGid = +passwd[i][3];
|
||||
if (otherUid && otherUid !== myUid &&
|
||||
otherGid && otherGid !== myGid &&
|
||||
otherUid > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!otherUid && !otherGid) throw new Error("failed getting passwd info.");
|
||||
|
||||
console.error("name, id, gid = %j", [otherName, otherUid, otherGid]);
|
||||
|
||||
var whoNumber = child_process.spawn("id",[], {uid:otherUid,gid:otherGid}),
|
||||
assert = require("assert");
|
||||
|
||||
whoNumber.stdout.buf = "byNumber:";
|
||||
whoNumber.stdout.on("data", onData);
|
||||
function onData (c) { this.buf += c; }
|
||||
|
||||
whoNumber.on("exit", onExit);
|
||||
function onExit (code) {
|
||||
var buf = this.stdout.buf;
|
||||
console.log(buf);
|
||||
var expr = new RegExp("^byNumber:uid="+otherUid+"\\("+
|
||||
otherName+"\\) gid="+otherGid+"\\(");
|
||||
assert.ok(buf.match(expr), "uid and gid should match "+otherName);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user