setuid/setgid ruby scripts 
Author Message
 setuid/setgid ruby scripts

Hey folks,

Yesterday I needed to make a program setgid, but as you probably know,
you can't (at least, not under linux) just set the setuid or setgid
bits, as it won't be honored for #! scripts.

Anyway, my first solution was to embed a ruby script in an executable
and have the executable run it. That worked, but was a little bit
annoying to write, and lost the advantage of being easily modifiable
(easily==no recompile, etc).

So, I made a "better" version that was just:
#include <unistd.h>
int main(int argc, char *argv[]) {
  const char *script = "/absolute/path/to/ruby/script.rb"
  execv(script, argv);

Quote:
}

And then setuid the result.

Anyway, I was just wondering if anyone has ever needed to make a ruby
script setuid or setgid before--and if so, how did you go about doing
it?

Any comments about this solution in general? Seems like it's possible
that there could be some sort of potential exploit through environment
poisoning or something, but I can't think of anything off-hand. (In
this example, it was setgid to a specific single-task group, so I'm not
too worried about it; but as a general case, it would be more important
to think about...)

(For the curious, the actual application is a nethack restore-saved-game
script, for those of us who sometimes like to cheat at nethack (mostly
my wife, but sometimes me too!). Code attached.  ;)

--

OpenPGP FP: C99E DF40 54F6 B625 FD48  B509 A3DE 8D79 541F F830

  nethack_restore.cpp
< 1K Download

  nethack_restore-script.rb
< 1K Download


Sat, 19 Nov 2005 09:29:16 GMT  
 setuid/setgid ruby scripts
Hi,

At Tue, 3 Jun 2003 10:29:16 +0900,

Quote:

> So, I made a "better" version that was just:
> #include <unistd.h>
> int main(int argc, char *argv[]) {
>   const char *script = "/absolute/path/to/ruby/script.rb"
>   execv(script, argv);
> }
> And then setuid the result.

A perl script creates such wrappers can be found in Camel book.

Quote:
> Anyway, I was just wondering if anyone has ever needed to make a ruby
> script setuid or setgid before--and if so, how did you go about doing
> it?

I had, but suid/sgid security issue has not been discussed
about enough yet.

Index: Makefile.in
===================================================================
RCS file: /cvs/ruby/src/ruby/Makefile.in,v
retrieving revision 1.44
diff -u -2 -p -r1.44 Makefile.in
--- Makefile.in 21 May 2003 11:51:55 -0000      1.44





 PROGRAM=$(RUBY_INSTALL_NAME)$(EXEEXT)
+SPROGRAM=$(SRUBY_INSTALL_NAME)$(EXEEXT)
 RUBY = $(RUBY_INSTALL_NAME)





+



   version.h

Index: configure.in
===================================================================
RCS file: /cvs/ruby/src/ruby/configure.in,v
retrieving revision 1.173
diff -u -2 -p -r1.173 configure.in
--- configure.in        1 Jun 2003 12:58:56 -0000       1.173

              getpgrp setpgrp getpgid setpgid getgroups getpriority getrlimit\
              dlopen sigprocmask sigaction _setjmp setsid telldir seekdir fchmod\
-             mktime timegm cosh sinh tanh)
+             mktime timegm cosh sinh tanh group_member)
 AC_STRUCT_TIMEZONE

 AC_SUBST(setup)

+case $target_os in
+cygwin*|mswin*|mingw*|djgpp*|os2_emx*|human*);;
+*)
+  AC_CACHE_CHECK([for setuid/setgid supported],
+    rb_cv_setugid,
+    [AC_TRY_CPP([
+#include <sys/stat.h>
+#ifndef S_ISUID
+#ifndef S_ISGID
+#error setuid/setgid not supported
+#endif
+#endif
+    ],
+    rb_cv_setugid=yes,
+    rb_cv_setugid=no)])
+  if test "$rb_cv_setugid" = yes; then
+    AC_LIBOBJ([suiddmy])
+  fi
+  ;;
+esac
+
 if test "$prefix" = NONE; then

 AC_SUBST(LIBRUBY_DLDFLAGS)
 AC_SUBST(RUBY_INSTALL_NAME)
+AC_SUBST(SRUBY_INSTALL_NAME)
 AC_SUBST(rubyw_install_name)

 RUBY_INSTALL_NAME="${ri_prefix}ruby${ri_suffix}"
+SRUBY_INSTALL_NAME="${ri_prefix}suidruby${ri_suffix}"
 case "$target_os" in
   cygwin*|mingw*)
Index: file.c
===================================================================
RCS file: /cvs/ruby/src/ruby/file.c,v
retrieving revision 1.146
diff -u -2 -p -r1.146 file.c
--- file.c      19 May 2003 05:41:07 -0000      1.146

 }

-static int
+#ifndef HAVE_GROUP_MEMBER
+int
 group_member(gid)

     return Qfalse;
 }
+#endif

 #ifndef S_IXUGO
Index: ruby.c
===================================================================
RCS file: /cvs/ruby/src/ruby/ruby.c,v
retrieving revision 1.77
diff -u -2 -p -r1.77 ruby.c
--- ruby.c      22 Apr 2003 11:58:08 -0000      1.77

 #include <stdio.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <ctype.h>

                rb_io_binmode(f);
        }
+#elif defined S_ISUID || defined S_ISGID
+       if (script) {
+           ruby_check_loadable(f);
+       }
 #endif
     }
Index: suid.c
===================================================================
RCS file: suid.c
diff -N suid.c
--- /dev/null   1 Jan 1970 00:00:00 -0000

+/**********************************************************************
+
+  suid.c -
+
+  $Author$
+  $Date$
+  created at: Wed Aug 29 13:45:03 JST 2001
+
+  This file is covered under the Ruby's license (see the file
+  COPYING).
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubyio.h"
+#include <errno.h>
+#include <sys/stat.h>
+
+#if !defined S_ISUID && !defined S_ISGID
+#error "setuid/setgid not supported"
+#endif
+
+void
+ruby_check_loadable(f)
+    VALUE f;
+{
+    OpenFile *fptr;
+    struct stat st;
+    int setid = 0;
+    int uid, euid, gid, egid;
+#ifdef HAVE_SETRESUID
+    int suid;
+#endif
+#ifdef HAVE_SETRESGID
+    int sgid;
+#endif
+
+    rb_check_type(f, T_FILE);
+    GetOpenFile(f, fptr);
+    if (fstat(fileno(fptr->f), &st) == -1) {
+       rb_sys_fail(fptr->path);
+    }
+
+#ifdef HAVE_SETRESUID
+    getresuid(&uid, &euid, &suid);
+#else
+    uid = (int)getuid();
+    euid = (int)geteuid();
+#endif
+#ifdef HAVE_SETRESGID
+    getresgid(&gid, &egid, &sgid);
+#else
+    gid = (int)getgid();
+    egid = (int)getegid();
+#endif
+
+    if (uid != 0) {
+       if ((
+#if defined S_IRUSR || defined S_IXUSR
+           (st.st_uid == uid) ?
+           !(st.st_mode & (0
+#ifdef S_IRUSR
+                           |S_IRUSR
+#endif
+#ifdef S_IXUSR
+                           |S_IXUSR
+#endif
+               )) :
+#endif
+#if defined S_IRGRP || defined S_IXGRP
+           group_member(st.st_gid) ?
+           !(st.st_mode & (0
+#ifdef S_IRGRP
+                           |S_IRGRP
+#endif
+#ifdef S_IXGRP
+                           |S_IXGRP
+#endif
+               )) :
+#endif
+#if defined S_IROTH || defined S_IXOTH
+           !(st.st_mode & (0
+#ifdef S_IROTH
+                           |S_IROTH
+#endif
+#ifdef S_IXOTH
+                           |S_IXOTH
+#endif
+               ))
+#else
+           0
+#endif
+           )
+#if defined S_ISUID && defined S_IWGRP
+           || !(~st.st_mode & (S_ISUID|S_IWGRP))
+#endif
+#if defined S_ISUID && defined S_IWOTH
+           || !(~st.st_mode & (S_ISUID|S_IWOTH))
+#endif
+#if defined S_ISGID && defined S_IWOTH
+           || !(~st.st_mode & (S_ISGID|S_IWOTH))
+#endif
+           ) {
+           errno = EACCES;
+           rb_load_fail(fptr->path);
+       }
+    }
+
+#ifdef S_ISGID
+    if (st.st_mode & S_ISGID) {
+# if defined HAVE_SETEGID
+       setegid(st.st_gid);
+# elif defined HAVE_SETREGID
+       setregid(-1, st.st_gid);
+# else
+       setgid(st.st_gid);
+# endif
+       setid = 1;
+    }
+    else
+#endif
+#if defined HAVE_SETRESGID && defined HAVE_SETEGID
+       setegid(sgid);
+#else
+       setgid(gid);
+#endif
+
+#ifdef S_ISUID
+    if (st.st_mode & S_ISUID) {
+# if defined HAVE_SETEUID
+       seteuid(st.st_uid);
+# elif defined HAVE_SETREUID
+       setreuid(-1, st.st_uid);
+# else
+       setuid(st.st_uid);
+# endif
+       setid = 1;
+    }
+    else
+#endif
+#if defined HAVE_SETRESUID && defined HAVE_SETEUID
+       seteuid(suid);
+#else
+       setuid(uid);
+#endif
+
+    if (!setid) {
+       rb_loaderror("not setuid/setgid -- %s", fptr->path);
+    }
+}
Index: suiddmy.c
===================================================================
RCS file: suiddmy.c
diff -N suiddmy.c
--- /dev/null   1 Jan 1970 00:00:00 -0000

+void
+ruby_check_loadable()
+{
+}

--
Nobu Nakada



Sat, 19 Nov 2005 10:48:45 GMT  
 setuid/setgid ruby scripts
Hi,

In message "Re: setuid/setgid ruby scripts"

|> Anyway, I was just wondering if anyone has ever needed to make a ruby
|> script setuid or setgid before--and if so, how did you go about doing
|> it?
|
|I had, but suid/sgid security issue has not been discussed
|about enough yet.

A wrapper is a friend of you.

My grandpa once told me that never try suidruby, before he died 30
years ago.  At that time, I didn't understand what he meant.

                                                        matz.



Sat, 19 Nov 2005 15:26:23 GMT  
 setuid/setgid ruby scripts
Thank you, for that totally ridiculous image at 8:00am... that made my morning!

And before he died, he gasped, "Do not believe IBM, token ring will fail,
invest
in 3com..."

"I will avenge you..."


Quote:
>My grandpa once told me that never try suidruby, before he died 30
>years ago.  At that time, I didn't understand what he meant.

>                                                         matz.



Sat, 19 Nov 2005 15:35:24 GMT  
 
 [ 4 post ] 

 Relevant Pages 

1. setgid with Tcl scripts

2. secure setuid scripts with rexx

3. Creating a setuid script

4. setuid Python scripts

5. python setuid script

6. setuid python scripts

7. setuid script and rcslib.py (from Demo/pdist)

8. problem with expect & setuid script

9. help: expect script with setuid

10. Tutorials on setuid/seteuid scripting

11. Expect and setuid scripts

12. Need help w/ SetUID expect scripts on Solaris

 

 
Powered by phpBB® Forum Software