Browse Source

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.
v0.7.4-release
isaacs 14 years ago
committed by Ryan Dahl
parent
commit
6f5d95de6d
  1. 14
      lib/child_process.js
  2. 28
      src/node_child_process.cc
  3. 4
      src/node_child_process.h
  4. 41
      test/disabled/test-child-process-uid-gid.js

14
lib/child_process.js

@ -185,21 +185,27 @@ ChildProcess.prototype.kill = function(sig) {
ChildProcess.prototype.spawn = function(path, args, options, customFds) { ChildProcess.prototype.spawn = function(path, args, options, customFds) {
args = args || []; args = args || [];
var cwd, env, setsid; var cwd, env, setsid, uid, gid;
if (!options || options.cwd === undefined && if (!options || options.cwd === undefined &&
options.env === undefined && options.env === undefined &&
options.customFds === undefined) { options.customFds === undefined &&
options.gid === undefined &&
options.uid === undefined) {
// Deprecated API: (path, args, options, env, customFds) // Deprecated API: (path, args, options, env, customFds)
cwd = ''; cwd = '';
env = options || process.env; env = options || process.env;
customFds = customFds || [-1, -1, -1]; customFds = customFds || [-1, -1, -1];
setsid = false; setsid = false;
uid = -1;
gid = -1;
} else { } else {
// Recommended API: (path, args, options) // Recommended API: (path, args, options)
cwd = options.cwd || ''; cwd = options.cwd || '';
env = options.env || process.env; env = options.env || process.env;
customFds = options.customFds || [-1, -1, -1]; customFds = options.customFds || [-1, -1, -1];
setsid = options.setsid ? true : false; setsid = options.setsid ? true : false;
uid = options.hasOwnProperty("uid") ? options.uid : -1;
gid = options.hasOwnProperty("gid") ? options.gid : -1;
} }
var envPairs = []; var envPairs = [];
@ -214,7 +220,9 @@ ChildProcess.prototype.spawn = function(path, args, options, customFds) {
cwd, cwd,
envPairs, envPairs,
customFds, customFds,
setsid); setsid,
uid,
gid);
this.fds = fds; this.fds = fds;
if (customFds[0] === -1 || customFds[0] === undefined) { if (customFds[0] === -1 || customFds[0] === undefined) {

28
src/node_child_process.cc

@ -158,7 +158,18 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
int fds[3]; 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]); for (i = 0; i < argv_length; i++) free(argv[i]);
delete [] argv; delete [] argv;
@ -233,7 +244,9 @@ int ChildProcess::Spawn(const char *file,
char **env, char **env,
int stdio_fds[3], int stdio_fds[3],
int custom_fds[3], int custom_fds[3],
bool do_setsid) { bool do_setsid,
int custom_uid,
int custom_gid) {
HandleScope scope; HandleScope scope;
assert(pid_ == -1); assert(pid_ == -1);
assert(!ev_is_active(&child_watcher_)); assert(!ev_is_active(&child_watcher_));
@ -276,6 +289,17 @@ int ChildProcess::Spawn(const char *file,
case 0: // Child. case 0: // Child.
if (do_setsid && setsid() < 0) { if (do_setsid && setsid() < 0) {
perror("setsid"); 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) { if (custom_fds[0] == -1) {

4
src/node_child_process.h

@ -64,7 +64,9 @@ class ChildProcess : ObjectWrap {
char **env, char **env,
int stdio_fds[3], int stdio_fds[3],
int custom_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 // Simple syscall wrapper. Does not disable the watcher. onexit will be
// called still. // called still.

41
test/disabled/test-child-process-uid-gid.js

@ -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…
Cancel
Save