summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2011-01-10 18:20:12 -0800
committerRyan Dahl <ry@tinyclouds.org>2011-01-11 13:54:51 -0800
commit435ece505897c8931548162a1ccc5389e3fc558c (patch)
tree06abab8d6f9da4537c861cc0d7078100e9d8d7d8
parentb92329667ff1f32a112e277bbb3cd6bc192c6f6d (diff)
downloadnodejs-435ece505897c8931548162a1ccc5389e3fc558c.tar.gz
nodejs-435ece505897c8931548162a1ccc5389e3fc558c.tar.bz2
nodejs-435ece505897c8931548162a1ccc5389e3fc558c.zip
child_process: Support setting uid/gid by name
-rw-r--r--src/node_child_process.cc99
-rw-r--r--src/node_child_process.h6
-rw-r--r--test/disabled/test-child-process-uid-gid.js46
3 files changed, 126 insertions, 25 deletions
diff --git a/src/node_child_process.cc b/src/node_child_process.cc
index 9ba7527b2..6c5c1eb07 100644
--- a/src/node_child_process.cc
+++ b/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);
diff --git a/src/node_child_process.h b/src/node_child_process.h
index eec27d551..05fedb5f0 100644
--- a/src/node_child_process.h
+++ b/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.
diff --git a/test/disabled/test-child-process-uid-gid.js b/test/disabled/test-child-process-uid-gid.js
index 71513c0e0..b63e2255d 100644
--- a/test/disabled/test-child-process-uid-gid.js
+++ b/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);
}