child_process: Support setting uid/gid by name
This commit is contained in:
parent
b92329667f
commit
435ece5058
@ -15,6 +15,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h> /* getpwnam() */
|
||||||
|
#include <grp.h> /* getgrnam() */
|
||||||
#if defined(__FreeBSD__ ) || defined(__OpenBSD__)
|
#if defined(__FreeBSD__ ) || defined(__OpenBSD__)
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#endif
|
#endif
|
||||||
@ -26,6 +28,8 @@
|
|||||||
extern char **environ;
|
extern char **environ;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
#include <limits.h> /* PATH_MAX */
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
using namespace v8;
|
using namespace v8;
|
||||||
@ -100,7 +104,11 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
|
|||||||
!args[0]->IsString() ||
|
!args[0]->IsString() ||
|
||||||
!args[1]->IsArray() ||
|
!args[1]->IsArray() ||
|
||||||
!args[2]->IsString() ||
|
!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.")));
|
return ThrowException(Exception::Error(String::New("Bad argument.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +166,33 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
|
|||||||
|
|
||||||
int fds[3];
|
int fds[3];
|
||||||
|
|
||||||
int uid = args[6]->ToInteger()->Value();
|
char *custom_uname = NULL;
|
||||||
int gid = args[7]->ToInteger()->Value();
|
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],
|
int r = child->Spawn(argv[0],
|
||||||
argv,
|
argv,
|
||||||
@ -168,8 +201,13 @@ Handle<Value> ChildProcess::Spawn(const Arguments& args) {
|
|||||||
fds,
|
fds,
|
||||||
custom_fds,
|
custom_fds,
|
||||||
do_setsid,
|
do_setsid,
|
||||||
uid,
|
custom_uid,
|
||||||
gid);
|
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]);
|
for (i = 0; i < argv_length; i++) free(argv[i]);
|
||||||
delete [] argv;
|
delete [] argv;
|
||||||
@ -246,7 +284,9 @@ int ChildProcess::Spawn(const char *file,
|
|||||||
int custom_fds[3],
|
int custom_fds[3],
|
||||||
bool do_setsid,
|
bool do_setsid,
|
||||||
int custom_uid,
|
int custom_uid,
|
||||||
int custom_gid) {
|
char *custom_uname,
|
||||||
|
int custom_gid,
|
||||||
|
char *custom_gname) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
assert(pid_ == -1);
|
assert(pid_ == -1);
|
||||||
assert(!ev_is_active(&child_watcher_));
|
assert(!ev_is_active(&child_watcher_));
|
||||||
@ -292,16 +332,59 @@ int ChildProcess::Spawn(const char *file,
|
|||||||
_exit(127);
|
_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()");
|
perror("setgid()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (custom_uid != -1 && setuid(custom_uid)) {
|
if (uid != -1 && setuid(uid)) {
|
||||||
perror("setuid()");
|
perror("setuid()");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (custom_fds[0] == -1) {
|
if (custom_fds[0] == -1) {
|
||||||
close(stdin_pipe[1]); // close write end
|
close(stdin_pipe[1]); // close write end
|
||||||
dup2(stdin_pipe[0], STDIN_FILENO);
|
dup2(stdin_pipe[0], STDIN_FILENO);
|
||||||
|
@ -65,8 +65,10 @@ class ChildProcess : ObjectWrap {
|
|||||||
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 custom_uid,
|
||||||
int gid);
|
char *custom_uname,
|
||||||
|
int custom_gid,
|
||||||
|
char *custom_gname);
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
// must be run as sudo, otherwise the gid/uid setting will fail.
|
var assert = require("assert");
|
||||||
var child_process = require("child_process"),
|
var spawn = require("child_process").spawn;
|
||||||
constants = require("constants"),
|
var fs = require('fs');
|
||||||
passwd = require("fs").readFileSync("/etc/passwd", "utf8"),
|
|
||||||
myUid = process.getuid(),
|
var myUid = process.getuid();
|
||||||
myGid = process.getgid();
|
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.
|
// get a different user.
|
||||||
// don't care who it is, as long as it's not root
|
// 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/);
|
passwd = passwd.trim().split(/\n/);
|
||||||
|
|
||||||
for (var i = 0, l = passwd.length; i < l; i ++) {
|
for (var i = 0, l = passwd.length; i < l; i ++) {
|
||||||
if (passwd[i].charAt(0) === "#") continue;
|
if (passwd[i].charAt(0) === "#") continue;
|
||||||
passwd[i] = passwd[i].split(":");
|
passwd[i] = passwd[i].split(":");
|
||||||
@ -20,22 +27,31 @@ for (var i = 0, l = passwd.length; i < l; i ++) {
|
|||||||
break;
|
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}),
|
var whoNumber = spawn('id', [], { uid: otherUid, gid: otherGid });
|
||||||
assert = require("assert");
|
var whoName = spawn('id', [], { uid: otherName, gid: otherGid });
|
||||||
|
|
||||||
whoNumber.stdout.buf = "byNumber:";
|
whoNumber.stdout.buf = 'byNumber:';
|
||||||
whoNumber.stdout.on("data", onData);
|
whoName.stdout.buf = 'byName:';
|
||||||
|
whoNumber.stdout.on('data', onData);
|
||||||
|
whoName.stdout.on('data', onData);
|
||||||
function onData (c) { this.buf += c; }
|
function onData (c) { this.buf += c; }
|
||||||
|
|
||||||
whoNumber.on("exit", onExit);
|
whoNumber.on("exit", onExit);
|
||||||
|
whoName.on("exit", onExit);
|
||||||
|
|
||||||
function onExit (code) {
|
function onExit (code) {
|
||||||
var buf = this.stdout.buf;
|
var buf = this.stdout.buf;
|
||||||
console.log(buf);
|
console.log(buf);
|
||||||
var expr = new RegExp("^byNumber:uid="+otherUid+"\\("+
|
var expr = new RegExp("^(byName|byNumber):uid=" +
|
||||||
otherName+"\\) gid="+otherGid+"\\(");
|
otherUid +
|
||||||
|
"\\(" +
|
||||||
|
otherName +
|
||||||
|
"\\) gid=" +
|
||||||
|
otherGid +
|
||||||
|
"\\(");
|
||||||
assert.ok(buf.match(expr), "uid and gid should match " + otherName);
|
assert.ok(buf.match(expr), "uid and gid should match " + otherName);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user