From: nobu.nokada@... Date: 2003-06-03T11:48:45+09:00 Subject: Re: setuid/setgid ruby scripts Hi, At Tue, 3 Jun 2003 10:29:16 +0900, Wesley J Landaker wrote: > So, I made a "better" version that was just: > #include > 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. > 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 +++ Makefile.in 3 Jun 2003 02:27:18 -0000 @@ -36,7 +36,9 @@ MAINLIBS = @MAINLIBS@ RUBY_INSTALL_NAME=@RUBY_INSTALL_NAME@ +SRUBY_INSTALL_NAME=@SRUBY_INSTALL_NAME@ RUBY_SO_NAME=@RUBY_SO_NAME@ EXEEXT = @EXEEXT@ PROGRAM=$(RUBY_INSTALL_NAME)$(EXEEXT) +SPROGRAM=$(SRUBY_INSTALL_NAME)$(EXEEXT) RUBY = $(RUBY_INSTALL_NAME) MINIRUBY = @MINIRUBY@ @@ -119,4 +121,8 @@ $(PROGRAM): $(LIBRUBY) $(MAINOBJ) $( $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINLIBS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@ +$(SPROGRAM): $(LIBRUBY) $(MAINOBJ) $(EXTOBJS) suid.@OBJEXT@ + @rm -f $@ + $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(OUTFLAG)$@ $(MAINLIBS) $(MAINOBJ) $(EXTOBJS) suid.@OBJEXT@ $(LIBRUBYARG) $(LIBS) + $(LIBRUBY_A): $(OBJS) dmyext.@OBJEXT@ @AR@ rcu $@ $(OBJS) dmyext.@OBJEXT@ @@ -360,2 +366,3 @@ variable.@OBJEXT@: variable.c ruby.h con version.@OBJEXT@: version.c ruby.h config.h defines.h intern.h missing.h \ version.h +suid.@OBJEXT@: suid.c missing.h ruby.h rubyio.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 +++ configure.in 1 Jun 2003 16:16:56 -0000 @@ -379,5 +379,5 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid 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_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff, @@ -993,4 +993,25 @@ esac 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 +#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 prefix=$ac_default_prefix @@ -1213,4 +1234,5 @@ AC_SUBST(LIBRUBY_LDSHARED) AC_SUBST(LIBRUBY_DLDFLAGS) AC_SUBST(RUBY_INSTALL_NAME) +AC_SUBST(SRUBY_INSTALL_NAME) AC_SUBST(rubyw_install_name) AC_SUBST(RUBYW_INSTALL_NAME) @@ -1245,4 +1267,5 @@ test "$program_suffix" != NONE && 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 +++ file.c 19 May 2003 06:06:56 -0000 @@ -425,5 +425,6 @@ rb_file_lstat(obj) } -static int +#ifndef HAVE_GROUP_MEMBER +int group_member(gid) GETGROUPS_T gid; @@ -454,4 +455,5 @@ 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 +++ ruby.c 22 Apr 2003 15:40:17 -0000 @@ -25,4 +25,5 @@ #include #include +#include #include @@ -793,4 +794,8 @@ load_file(fname, script) 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 3 Oct 2001 01:55:06 -0000 @@ -0,0 +1,150 @@ +/********************************************************************** + + 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 +#include + +#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 +++ suiddmy.c 6 Sep 2001 10:25:58 -0000 @@ -0,0 +1,4 @@ +void +ruby_check_loadable() +{ +} -- Nobu Nakada