Browse Source

child_process: Support setting uid/gid by name

v0.7.4-release
isaacs 14 years ago
committed by Ryan Dahl
parent
commit
435ece5058
  1. 99
      src/node_child_process.cc
  2. 6
      src/node_child_process.h
  3. 46
      test/disabled/test-child-process-uid-gid.js

99
src/node_child_process.cc

@ -15,6 +15,8 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pwd.h> /* getpwnam() */
#include <grp.h> /* getgrnam() */
#if defined(__FreeBSD__ ) || defined(__OpenBSD__)
#include <sys/wait.h>
#endif
@ -26,6 +28,8 @@
extern char **environ;
# endif
#include <limits.h> /* PATH_MAX */
namespace node {
using namespace v8;
@ -100,7 +104,11 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
!args[0]->IsString() ||
!args[1]->IsArray() ||
!args[2]->IsString() ||
!args[3]->IsArray()) {
!args[3]->IsArray() ||
!args[4]->IsArray() ||
!args[5]->IsBoolean() ||
!(args[6]->IsInt32() || args[6]->IsString()) ||
!(args[7]->IsInt32() || args[7]->IsString())) {
return ThrowException(Exception::Error(String::New("Bad argument.")));
}
@ -158,8 +166,33 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
int fds[3];
int uid = args[6]->ToInteger()->Value();
int gid = args[7]->ToInteger()->Value();
char *custom_uname = NULL;
int custom_uid = -1;
if (args[6]->IsNumber()) {
custom_uid = args[6]->Int32Value();
} else if (args[6]->IsString()) {
String::Utf8Value pwnam(args[6]->ToString());
custom_uname = (char *)calloc(sizeof(char), pwnam.length() + 1);
strncpy(custom_uname, *pwnam, pwnam.length() + 1);
} else {
return ThrowException(Exception::Error(
String::New("setuid argument must be a number or a string")));
}
char *custom_gname = NULL;
int custom_gid = -1;
if (args[7]->IsNumber()) {
custom_gid = args[7]->Int32Value();
} else if (args[7]->IsString()) {
String::Utf8Value grnam(args[7]->ToString());
custom_gname = (char *)calloc(sizeof(char), grnam.length() + 1);
strncpy(custom_gname, *grnam, grnam.length() + 1);
} else {
return ThrowException(Exception::Error(
String::New("setgid argument must be a number or a string")));
}
int r = child->Spawn(argv[0],
argv,
@ -168,8 +201,13 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
fds,
custom_fds,
do_setsid,
uid,
gid);
custom_uid,
custom_uname,
custom_gid,
custom_gname);
if (custom_uname != NULL) free(custom_uname);
if (custom_gname != NULL) free(custom_gname);
for (i = 0; i < argv_length; i++) free(argv[i]);
delete [] argv;
@ -246,7 +284,9 @@ int ChildProcess::Spawn(const char *file,
int custom_fds[3],
bool do_setsid,
int custom_uid,
int custom_gid) {
char *custom_uname,
int custom_gid,
char *custom_gname) {
HandleScope scope;
assert(pid_ == -1);
assert(!ev_is_active(&child_watcher_));
@ -292,16 +332,59 @@ int ChildProcess::Spawn(const char *file,
_exit(127);
}
if (custom_gid != -1 && setgid(custom_gid)) {
static char buf[PATH_MAX + 1];
int gid = -1;
if (custom_gid != -1) {
gid = custom_gid;
} else if (custom_gname != NULL) {
struct group grp, *grpp = NULL;
int err = getgrnam_r(custom_gname,
&grp,
buf,
PATH_MAX + 1,
&grpp);
if (err || grpp == NULL) {
perror("getgrnam_r()");
_exit(127);
}
gid = grpp->gr_gid;
}
int uid = -1;
if (custom_uid != -1) {
uid = custom_uid;
} else if (custom_uname != NULL) {
struct passwd pwd, *pwdp = NULL;
int err = getpwnam_r(custom_uname,
&pwd,
buf,
PATH_MAX + 1,
&pwdp);
if (err || pwdp == NULL) {
perror("getpwnam_r()");
_exit(127);
}
uid = pwdp->pw_uid;
}
if (gid != -1 && setgid(gid)) {
perror("setgid()");
_exit(127);
}
if (custom_uid != -1 && setuid(custom_uid)) {
if (uid != -1 && setuid(uid)) {
perror("setuid()");
_exit(127);
}
if (custom_fds[0] == -1) {
close(stdin_pipe[1]); // close write end
dup2(stdin_pipe[0], STDIN_FILENO);

6
src/node_child_process.h

@ -65,8 +65,10 @@ class ChildProcess : ObjectWrap {
int stdio_fds[3],
int custom_fds[3],
bool do_setsid,
int uid,
int gid);
int custom_uid,
char *custom_uname,
int custom_gid,
char *custom_gname);
// Simple syscall wrapper. Does not disable the watcher. onexit will be
// called still.

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

@ -1,13 +1,20 @@
// 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();
var assert = require("assert");
var spawn = require("child_process").spawn;
var fs = require('fs');
var myUid = process.getuid();
var myGid = process.getgid();
if (myUid != 0) {
console.error('must be run as root, otherwise the gid/uid setting will fail.');
process.exit(1);
}
// get a different user.
// don't care who it is, as long as it's not root
var passwd = fs.readFileSync('/etc/passwd', 'utf8');
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(":");
@ -20,22 +27,31 @@ for (var i = 0, l = passwd.length; i < l; i ++) {
break;
}
}
if (!otherUid && !otherGid) throw new Error("failed getting passwd info.");
if (!otherUid && !otherGid) throw new Error('failed getting passwd info.');
console.error("name, id, gid = %j", [otherName, otherUid, otherGid]);
console.error('name, id, gid = %j', [otherName, otherUid, otherGid]);
var whoNumber = child_process.spawn("id",[], {uid:otherUid,gid:otherGid}),
assert = require("assert");
var whoNumber = spawn('id', [], { uid: otherUid, gid: otherGid });
var whoName = spawn('id', [], { uid: otherName, gid: otherGid });
whoNumber.stdout.buf = "byNumber:";
whoNumber.stdout.on("data", onData);
whoNumber.stdout.buf = 'byNumber:';
whoName.stdout.buf = 'byName:';
whoNumber.stdout.on('data', onData);
whoName.stdout.on('data', onData);
function onData (c) { this.buf += c; }
whoNumber.on("exit", onExit);
whoName.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);
var expr = new RegExp("^(byName|byNumber):uid=" +
otherUid +
"\\(" +
otherName +
"\\) gid=" +
otherGid +
"\\(");
assert.ok(buf.match(expr), "uid and gid should match " + otherName);
}

Loading…
Cancel
Save