Allow process.setuid() and process.setgid() to accept string names in lieu of numeric IDs
authorPeter Griess <pg@std.in>
Wed, 19 May 2010 00:40:44 +0000 (17:40 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Wed, 19 May 2010 00:41:10 +0000 (17:41 -0700)
src/node.cc
test/disabled/test-setuidgid.js [new file with mode: 0644]

index 4cff11b..d1718ca 100644 (file)
@@ -14,6 +14,8 @@
 #include <dlfcn.h> /* dlopen(), dlsym() */
 #include <sys/types.h>
 #include <unistd.h> /* setuid, getuid */
+#include <pwd.h> /* getpwnam() */
+#include <grp.h> /* getgrnam() */
 
 #include <node_buffer.h>
 #include <node_io_watcher.h>
@@ -90,6 +92,10 @@ static ev_async eio_want_poll_notifier;
 static ev_async eio_done_poll_notifier;
 static ev_idle  eio_poller;
 
+// Buffer for getpwnam_r(), getgrpam_r(); keep this scoped at file-level rather
+// than method-level to avoid excess stack usage.
+static char getbuf[1024];
+
 // We need to notify V8 when we're idle so that it can run the garbage
 // collector. The interface to this is V8::IdleNotification(). It returns
 // true if the heap hasn't be fully compacted, and needs to be run again.
@@ -1152,11 +1158,29 @@ static Handle<Value> SetGid(const Arguments& args) {
       String::New("setgid requires 1 argument")));
   }
 
-  Local<Integer> given_gid = args[0]->ToInteger();
-  int gid = given_gid->Int32Value();
+  int gid;
+  if (args[0]->IsNumber()) {
+    gid = args[0]->Int32Value();
+  } else if (args[0]->IsString()) {
+    String::Utf8Value grpnam(args[0]->ToString());
+    struct group grp, *grpp = NULL;
+    int err;
+
+    if ((err = getgrnam_r(*grpnam, &grp, getbuf, sizeof(getbuf), &grpp)) ||
+        grpp == NULL) {
+      return ThrowException(ErrnoException(errno, "getgrnam_r"));
+    }
+
+    gid = grpp->gr_gid;
+  } else {
+    return ThrowException(Exception::Error(
+      String::New("setgid argument must be a number or a string")));
+  }
+
   int result;
   if ((result = setgid(gid)) != 0) {
-    return ThrowException(Exception::Error(String::New(strerror(errno))));
+    return ThrowException(ErrnoException(errno, "setgid"));
   }
   return Undefined();
 }
@@ -1169,11 +1193,29 @@ static Handle<Value> SetUid(const Arguments& args) {
           String::New("setuid requires 1 argument")));
   }
 
-  Local<Integer> given_uid = args[0]->ToInteger();
-  int uid = given_uid->Int32Value();
+  int uid;
+
+  if (args[0]->IsNumber()) {
+    uid = args[0]->Int32Value();
+  } else if (args[0]->IsString()) {
+    String::Utf8Value pwnam(args[0]->ToString());
+    struct passwd pwd, *pwdp = NULL;
+    int err;
+
+    if ((err = getpwnam_r(*pwnam, &pwd, getbuf, sizeof(getbuf), &pwdp)) ||
+        pwdp == NULL) {
+      return ThrowException(ErrnoException(errno, "getpwnam_r"));
+    }
+
+    uid = pwdp->pw_uid;
+  } else {
+    return ThrowException(Exception::Error(
+      String::New("setuid argument must be a number or a string")));
+  }
+
   int result;
   if ((result = setuid(uid)) != 0) {
-    return ThrowException(Exception::Error(String::New(strerror(errno))));
+    return ThrowException(ErrnoException(errno, "setuid"));
   }
   return Undefined();
 }
diff --git a/test/disabled/test-setuidgid.js b/test/disabled/test-setuidgid.js
new file mode 100644 (file)
index 0000000..c34864b
--- /dev/null
@@ -0,0 +1,23 @@
+// Requires special privlages
+require('../common');
+var assert = require('assert');
+
+var oldgid = process.getgid();
+process.setgid('nobody');
+var newgid = process.getgid();
+assert.notEqual(newgid, oldgid, 'gids expected to be different');
+
+var olduid = process.getuid();
+process.setuid('nobody');
+var newuid = process.getuid();
+assert.notEqual(newuid, olduid, 'uids expected to be different');
+
+try {
+  process.setuid('nobody1234');
+} catch (e) {
+  assert.equal(
+    e.message,
+    'failed to resolve group',
+    'unexpected error message'
+  );
+}