Index: CHROOT =================================================================== RCS file: /chest/cvsroot/rssh/CHROOT,v --- CHROOT 5 Sep 2004 17:48:56 -0000 1.3 +++ CHROOT 27 Nov 2005 19:20:42 -0000 @@ -148,7 +148,8 @@ /lib/libnss_files.so.? into the chroot jail. Without it, the scp command failed, complaining that my user ID was an unknown user. If you use LDAP authentication on the server, you will probably need to also copy -libnss_ldap.so.? into your chroot jail. +libnss_ldap.so.? into your chroot jail. Similar requirements likely exist for +other authentication methods... Logging @@ -190,10 +191,10 @@ Individual jails for different users ------------------------------------ -In v2.1, you are now able to specify the location of different chroot jails +Since v2.1, you are now able to specify the location of different chroot jails for different users, whether or not to chroot individual users, etc. You might be tempted to create individual jails for each different user, and you -can do that, BUT... It's probably a bad idea. +can do that, BUT... it's probably a very bad idea. There are several reasons for this. First, it uses lots of disk space, because you need to copy the libraries and such into each one (though in some @@ -203,11 +204,12 @@ above) in each of the chroot jails. However, the number of sockets syslog can listen to is generally limited. On most Linux systems, that limit is a maximum of 20, though it can be changed by editing the source code to syslogd. -So if you go with individual chroot jails, you either need to be satisfied -without logging for most of your users, or recompile syslogd and manually -configure it to open a socket in each of your users' jails. And while I -haven't done it, I suspect creating large numbers of sockets for syslogd to -listen to will make logging to syslog somewhat painful. +On other platforms, this may not even be possible. So if you go with +individual chroot jails, you either need to be satisfied without logging for +most of your users, or recompile syslogd and manually configure it to open a +socket in each of your users' jails. And while I haven't done it, I suspect +creating large numbers of sockets for syslogd to listen to will make logging +to syslog somewhat painful. My recommendation is, if you really really really need individual chroot jails, use them. But if you can, configure a single jail where the majority @@ -221,9 +223,9 @@ Setting up a proper chroot jail can be difficult, and it is always a very environment-specific operation. Because of this, I can not and will not -provide any sort of support for setting up chroot jails. However, in the near -future, I will make sure there is a mailing list for rssh, where you can ask -questions. +provide any sort of support for setting up chroot jails. However, there is a +mailing list for rssh, where you can ask questions. See the rssh homepage for +details. If you are having problems, make sure that you have logging set up properly, if nothing else. Doing so will help you determine what kind of problems you Index: ChangeLog =================================================================== RCS file: /chest/cvsroot/rssh/ChangeLog,v --- ChangeLog 30 Dec 2004 20:58:09 -0000 1.6 +++ ChangeLog 27 Nov 2005 19:23:09 -0000 @@ -1,3 +1,11 @@ +2.3.0 + + - modified chroot_helper to parse the config file, to avoid arbitrary + chroot() (and thus root compromise) + - numerous documentation updates + - fix for va_start()/va_end()-related segfault on 64-bit architecture + - small bit of code cleanup + 2.2.3 - added checks for command execution arguments to scp, rdist, rsync Index: Makefile.am =================================================================== RCS file: /chest/cvsroot/rssh/Makefile.am,v --- Makefile.am 19 Jun 2004 08:16:15 -0000 1.3 +++ Makefile.am 27 Nov 2005 21:34:12 -0000 @@ -6,7 +6,7 @@ libexec_PROGRAMS = rssh_chroot_helper nodist_rssh_SOURCES = main.c pathnames.h config.h rssh_SOURCES = rssh.h rsshconf.h rsshconf.c log.c log.h util.c util.h argvec.c argvec.h -rssh_chroot_helper_SOURCES = rssh_chroot_helper.c log.c log.h argvec.c argvec.h +rssh_chroot_helper_SOURCES = rssh_chroot_helper.c log.c log.h argvec.c argvec.h util.c util.h rsshconf.c rsshconf.h rssh.h dist_sysconf_DATA = rssh.conf man_MANS = rssh.1 rssh.conf.5 EXTRA_DIST = $(man_MANS) CHROOT SECURITY rssh.spec mkchroot.sh conf_convert.sh @@ -16,3 +16,6 @@ install-exec-hook: chmod u+s $(libexecdir)/rssh_chroot_helper + +rpm: + rpmbuild -tb `echo @PACKAGE_STRING@.tar.gz|tr " " "-"` Index: NEWS =================================================================== RCS file: /chest/cvsroot/rssh/NEWS,v --- NEWS 15 Mar 2004 21:01:37 -0000 1.1.1.1 +++ NEWS 27 Nov 2005 19:23:54 -0000 @@ -0,0 +1, @@ +Automake's insistance on the existence of this file is very lame. Index: SECURITY =================================================================== RCS file: /chest/cvsroot/rssh/SECURITY,v --- SECURITY 22 Jan 2005 21:44:17 -0000 1.5 +++ SECURITY 27 Nov 2005 20:18:28 -0000 @@ -1,6 +1,27 @@ SECURITY -------- +Please note: The man page now has detailed information about security concerns +and some tips for setting up rssh securely. If you haven't read these, please +do so now. What follows is mostly a summary of specific security issues which +have affected rssh since I started developing it. + +Nov 27, 2005 + +Max Vozeler reported a scary problem whereby rssh_chroot_helper can be +exploited to chroot to arbitrary directories and thereby gain root access. +The 2.3.0 release of rssh fixes this problem by forcing the chroot helper +program to re-parse the config file instead of allowing the chroot home to be +specified on the command line. Thus users not listed can not use it to chroot +(or will chroot to the default location, which hopefully the sysadmin has set +up securely), and users who are listed will be chrooted to the directories +where they are supposed to go only. + +Numerous people reported a problem with the way I used va_start/va_end in +log.c which causes a segfault on 64-bit Linux platforms. I don't know if this +is exploitable, but it's fixed. Given the previously mentioned root +compromise, this probably doesn't matter much... ;-) + Dec 3, 2004 Jason Wies has reported a problem whereby rssh can be bypassed in conjunction Index: aclocal.m4 =================================================================== RCS file: /chest/cvsroot/rssh/aclocal.m4,v --- aclocal.m4 5 Sep 2004 17:48:56 -0000 1.2 +++ aclocal.m4 27 Nov 2005 17:58:56 -0000 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.8.3 -*- Autoconf -*- +# generated automatically by aclocal 1.9.2 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 # Free Software Foundation, Inc. @@ -33,14 +33,14 @@ # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. -AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.8"]) +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION so it can be traced. # This function is AC_REQUIREd by AC_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], - [AM_AUTOMAKE_VERSION([1.8.3])]) + [AM_AUTOMAKE_VERSION([1.9.2])]) # AM_AUX_DIR_EXPAND @@ -108,7 +108,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997, 2000, 2001, 2003 Free Software Foundation, Inc. +# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -145,8 +145,8 @@ fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then - AC_MSG_ERROR([conditional "$1" was never defined. -Usually this means the macro was only invoked conditionally.]) + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) fi])]) # serial 7 -*- Autoconf -*- @@ -266,9 +266,14 @@ grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings - # (even with -Werror). So we grep stderr for any message - # that says an option was ignored. - if grep 'ignoring option' conftest.err >/dev/null 2>&1; then :; else + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi @@ -314,7 +319,8 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -350,27 +356,21 @@ else continue fi - grep '^DEP_FILES *= *[[^ @%:@]]' < "$mf" > /dev/null || continue - # Extract the definition of DEP_FILES from the Makefile without - # running `make'. + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` - test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" - # We invoke sed twice because it is the simplest approach to - # changing $(DEPDIR) to its actual value in the expansion. - for file in `sed -n ' - /^DEP_FILES = .*\\\\$/ { - s/^DEP_FILES = // - :loop - s/\\\\$// - p - n - /\\\\$/ b loop - p - } - /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue @@ -425,7 +425,7 @@ # This macro actually does too much some checks are only needed if # your package does certain things. But this isn't really a big deal. -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 # Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify @@ -501,7 +501,6 @@ AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) -AM_MISSING_PROG(AMTAR, tar) AM_PROG_INSTALL_SH AM_PROG_INSTALL_STRIP AC_REQUIRE([AM_PROG_MKDIR_P])dnl @@ -510,7 +509,9 @@ AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl - +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES(CC)], @@ -753,13 +754,21 @@ # this.) AC_DEFUN([AM_PROG_MKDIR_P], [if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then - # Keeping the `.' argument allows $(mkdir_p) to be used without - # argument. Indeed, we sometimes output rules like + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in # $(mkdir_p) $(somedir) - # where $(somedir) is conditionally defined. - # (`test -n '$(somedir)' && $(mkdir_p) $(somedir)' is a more - # expensive solution, as it forces Make to start a sub-shell.) - mkdir_p='mkdir -p -- .' + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' else # On NextStep and OpenStep, the `mkdir' command does not # recognize any option. It will interpret all options as @@ -925,3 +934,111 @@ INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 1 + + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + Index: configure.ac =================================================================== RCS file: /chest/cvsroot/rssh/configure.ac,v --- configure.ac 30 Dec 2004 20:58:09 -0000 1.5 +++ configure.ac 27 Nov 2005 17:16:33 -0000 @@ -1,5 +1,5 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(rssh, 2.2.3, [rssh-discuss at lists dot sourceforge dot net]) +AC_INIT(rssh, 2.3.0, [rssh-discuss at lists dot sourceforge dot net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([rssh.1]) AM_CONFIG_HEADER([config.h]) Index: log.c =================================================================== RCS file: /chest/cvsroot/rssh/log.c,v --- log.c 10 Oct 2004 16:59:46 -0000 1.2 +++ log.c 19 Nov 2005 16:10:12 -0000 @@ -156,6 +156,7 @@ /* try to print msg to buffer, until we succeed or fail conclusively */ va_start( arglist, msg ); retc = vsnprintf( format_temp, length, msg, arglist ); + va_end( arglist ); /* * Check retc to make sure it fit account for differences in libc @@ -171,7 +172,9 @@ "Could not allocate mem in log_msg(), log.c"); exit(1); } + va_start( arglist, msg ); vsnprintf( format_temp, retc + 1, msg, arglist ); + va_end( arglist ); } /* if retc == -1, we must be compiled under pre-C99 libc */ while ( retc == -1 ){ @@ -183,11 +186,10 @@ exit(1); } memset( format_temp, 0, length ); + va_start( arglist, msg ); retc = vsnprintf( format_temp, length, msg, arglist ); + va_end( arglist ); } - - /* clean up arglist and log the string */ - va_end( arglist ); syslog((facility | level), "%s", format_temp); } Index: main.c.in =================================================================== RCS file: /chest/cvsroot/rssh/main.c.in,v --- main.c.in 30 Dec 2004 20:58:09 -0000 1.6 +++ main.c.in 27 Nov 2005 22:36:19 -0000 @@ -82,7 +82,7 @@ char *progname; char *username; char *version = "@PACKAGE_STRING@"; -char *copyr = "Copyright 2002-4 Derek D. Martin <@PACKAGE_BUGREPORT@>"; +char *copyr = "Copyright 2002-5 Derek D. Martin <@PACKAGE_BUGREPORT@>"; /* MAIN PROGRAM */ int main( int argc, char **argv ) @@ -116,7 +116,7 @@ log_open(); /* process the config file */ - if ( !(read_shell_config(&opts, PATH_RSSH_CONFIG)) ){ + if ( !(read_shell_config(&opts, PATH_RSSH_CONFIG, 1)) ){ log_set_priority(LOG_ERR); log_msg("there were errors processing configuration file!"); } @@ -188,7 +188,6 @@ /* if we need to do chroot processing, do it */ if ( opts->shell_flags & RSSH_USE_CHROOT ){ /* create vector of pointers to command line arguments */ - root = opts->chroot_path; /* * we don't call build_arg_vector() here, because expanding @@ -205,30 +204,26 @@ } argvec[0] = PATH_CHROOT_HELPER; - argvec[1] = root; /* which one is it? */ if ( !(strcmp(*cmd, PATH_SCP)) ) - argvec[2] = "1"; + argvec[1] = "1"; else if ( !(strcmp(*cmd, PATH_SFTP_SERVER)) ) - argvec[2] = "2"; + argvec[1] = "2"; else if ( !(strcmp(*cmd, PATH_CVS)) ) - argvec[2] = "3"; + argvec[1] = "3"; else if ( !(strcmp(*cmd, PATH_RDIST)) ) - argvec[2] = "4"; + argvec[1] = "4"; else if ( !(strcmp(*cmd, PATH_RSYNC)) ) - argvec[2] = "5"; + argvec[1] = "5"; else { log_set_priority(LOG_ERR); log_msg("fatal error identifying the correct command " "(this should never happen)"); exit(1); } - if ( !(homedir = extract_root(root, uinfo.pw_dir)) ) - homedir = strdup("/"); - argvec[3] = homedir; - argvec[4] = cmdline; - argvec[5] = NULL; + argvec[2] = cmdline; + argvec[3] = NULL; /* change the command to run to the chroot helper */ *cmd = PATH_CHROOT_HELPER; @@ -243,11 +238,9 @@ } /* stuff the args into the buffer */ - snprintf(temp, len, "%s \"%s\" %s \"%s\" %s", + snprintf(temp, len, "%s %s \"%s\"", PATH_CHROOT_HELPER, - root, - argvec[2], - homedir, + argvec[1], cmdline); /* now log 'em */ Index: rssh.1 =================================================================== RCS file: /chest/cvsroot/rssh/rssh.1,v --- rssh.1 30 Dec 2004 20:58:09 -0000 1.3 +++ rssh.1 27 Nov 2005 21:18:49 -0000 @@ -1,21 +1,21 @@ .\" No comment! .\" -.TH RSSH 1 "7 Jul 2003" "man pages" "Derek D. Martin" +.TH RSSH 1 "27 Nov 2005" "man pages" "Derek D. Martin" .SH NAME rssh \- restricted secure shell allowing only scp and/or sftp .SH SYNOPSIS .B rssh -.I -c scp|sftp-server .RI [ " options... " ] " " [ " ... " ] .br +.B rssh -v .SH DESCRIPTION .B rssh -is a restricted shell for providing limited access to a host via \fIssh\fP(1), +is a restricted shell for providing limited access to a host via \fBssh\fP(1), allowing a user whose shell is configured to .B rssh -to use one or more of the command(s) \fIscp\fP(1) or \fIsftp\fP(1) -\fIcvs\fP(1), \fIrdist\fP(1), and \fIrsync\fP(1), and -.B only +to use one or more of the command(s) \fBscp\fP(1), \fBsftp\fP(1) +\fBcvs\fP(1), \fBrdist\fP(1), and \fBrsync\fP(1), and +.I only those commands. It is intended primarily to work with OpenSSH (see http://www.openssh.com), but may work with other implementations. .P @@ -29,15 +29,15 @@ .RE .P If invoked with the -.I -v +.B -v option, .B rssh will report its version, and exit. All other arguments to .B rssh -are those specified by the remote \fIssh\fP(1) client, and aren't of much +are those specified by the remote \fBssh\fP(1) client, and aren't of much concern to the average user. The arguments provided must be what a shell on -the remote end would receive in order to pass control to \fIscp\fP(1) or -\fIsftp\fP(1). If +the remote end would receive in order to pass control to \fBscp\fP(1), +\fBsftp\fP(1), etc. If .B rssh receives arguments which do not conform, it will emit an error message and exit. If the program the user is trying to run is not allowed, or contains syntax @@ -45,62 +45,93 @@ will also emit an error and exit. .P .B rssh -has a configuration file, rssh.conf(5), which allows some of the behavior of +has a configuration file, \fIrssh.conf\fP(5), which allows some of the +behavior of .B rssh to be customized. See that man page for details. - .SH SECURITY NOTES +.I Read this section with exceptional care, or you may put your system at risk! +.SS Using rssh With CVS +If you are using \fBrssh\fP to allow CVS access, it should be noted that it is +not possible to prevent a user who is very familiar with CVS from bypassing +\fBrssh\fP and getting a shell, unless the user does not have write access in +the repository. Obviously, the user must have write access to the repository +in order to update it, which allows them to upload arbitrary programs into the +repository. CVS provides several mechanisms for executing such arbitrary +programs... The only reasonably safe way to use \fBrssh\fP with CVS is to use +the chroot jail facilities to place the CVS repository within a chroot jail. +Please see below and all relevant documentation for details of how to set up +chroot jails. Note that \fIusers will still be able to get shell access +within the jail\fP; the only protection which is provided is that they can not +escape the jail. I have been pursuaded to retain support for CVS because this +protection is better than no protection. +.I You have been warned. Use CVS at your own risk. +.SS Potential root Compromise With Old Versions -.SS Command Line Parser - -As of +Before \fBrssh 2.3.0\fP, if a regular user had shell access to a machine where .B rssh -version 2.2.3, the program must parse out the complete command line to avoid -command line options which cause the execution of arbitrary programs (and -hence bypass the security of \fBrssh\fP). In order to keep the program source -code sane, the parser is a little over-zealous about matching command line -options. In practice, this probably will not be an issue, but in theory it is -possible. -.P -If you run into a problem where -.B rssh -refuses to run, claiming to be rejecting insecure command line options which -were not specified, try changing your command line such that all \fIshort\fP -options are specified as single-letter option flags (e.g. -e -p instead of --ep) and make sure you separate arguments from their respective options by a -space (e.g. -p 123 instead of -p123). In virtually all cases, this should -solve the problem. Admittedly, an exhaustive search was not performed, but no -problematical cases were found which were likely to be common. -.P -The alternative would have been to include a complete command-line parser for -rcp, rdist, and rsync; this was way out of the scope of this project. In -practice, the existing parser should suffice. If, however, you find cases -where it does not, please post details to the rssh mailing list. Details -about how to post to the mailing list can be found at the rssh homepage. +was installed, a root compromise was possible due to +.B rssh_chroot_helper +allowing a user to arbitrarily \fBchroot\fP(2) to anywhere on the filesystem. +It is possible to mitigate this attack against affected versions of +.B rssh +using strict access controls to files, by making sure that the user can not +write to any file on the same partition as system executables, and that any +partition where they can write files does not allow execution of SUID +programs. As of \fBrssh 2.3.0\fP, this attack has been prevented by +preventing arbitrary chroot(), \fIif your jail is set up securely\fP. In +particular, make sure that regular users can not write to directories inside +the jail which contain the copied binaries. That should be obvious, but it +needs to be said. Though it should not be strictly necessary, to further +protect your system from possible compromise, it is also advisable to follow +the section below, entitled "Safeguards Against Bypassing rssh". .SS Safeguards Against Bypassing rssh - .B rssh is designed to interact with several other programs. Even if rssh is -completely bug-free, changes in those other programs could possibly result in methods -to circumvent the protection that +completely bug-free, changes in those other programs could possibly result in +methods to circumvent the protection that .B rssh -is intended to provide. \fBIt is important for you, the system administrator, +is intended to provide. \fIIt is important for you, the system administrator, to stay current on the services you make available with rssh, to be sure that -these commands do\fP \fInot\fP \fBprovide mechanisms to allow the user to run -arbitrary commands.\fP Also, while the goal of every release is to be bug -free, no one is perfect... There may be undiscovered bugs in +these commands do not provide mechanisms to allow the user to run arbitrary +commands.\fP Also, while the goal of every release is to be bug free, no one +is perfect... There may be undiscovered bugs in .B rssh which might allow a user to circumvent it. .P You can protect your system from those who would take advantage of such -weaknesses. There are three basic steps: - -.nf - 1. place your users in a chroot jail - 2. mount their home filesystem with the noexec option - 3. use standard file permissions appropriately -.fi - +weaknesses. This is not required for \fBrssh\fP to work properly, but it is a +really good idea. There are six basic steps: +.RS +.TP +1. +protect all non-administrator accounts with rssh (i.e. no regular user should have shell access to the server) +.TP +2. +place your users in a chroot jail +.TP +3. +limit the binaries which live in the jail to the absolute minimum required +.TP +4. +mount their home filesystem with the noexec/nosuid option (i.e. use +separate partitions in the jail for user home directories and all other files, +if possible/reasonable) +.TP +5. +create a group for rssh users, and limit executable access to the binaries to +users in that group. +.TP +6. +use standard file permissions carefully and appropriately +.RE +.P +If possible, make sure that no regular user has any kind of shell access to +the system other than through \fBrssh\fP. Otherwise, users with shell access +could potentially exploit undiscovered bugs in +.B rssh_chroot_helper +to gain root access to the server. +.P .B rssh gives the system administrator the ability to place the users in a chroot jail. See details in the man page for @@ -113,31 +144,78 @@ are trying to provide. This prevents them from running standard system commands. .P -Then, make sure the user's files are on a seperate filesystem from your -system's executables. Make sure you mount this filesystem using the -.I noexec -option, if your operating system provides one. This prevents the users from +Then, make sure the user's files inside the jail are on a seperate filesystem +from your system's executables. If possible in your environment, make sure +you mount this filesystem using the +.IR noexec " and " nosuid +options, if your operating system provides them. This prevents the users from being able to execute programs which they have uploaded to the target machine -(e.g. using scp) which might otherwise be executable. +(e.g. using scp) which might otherwise be executable, and prevents SUID +programs from respecting the SUID bits. Note that these options necessitate +the users' files are on separate partitions from the binaries and libraries +that live in the jail. Therefore you will need at least 2 partitions for your +jail to do this properly (one for the system binaries in the jail, the other +for the user directories). +.P +Additionally, create a group, for example "rsshuser", for rssh users. Put all +your users who will be restricted by rssh in that group. Set the ownership +and permissions on rssh and rssh_chroot_helper so that only those users can +execute them. The following commands should illustrate: +.P +.RS +.B # groupadd rsshuser +.br +.B # chown root:rsshuser rssh rssh_chroot_helper +.br +.B # chmod 550 rssh +.br +.B # chmod 4550 rssh_chroot_helper +.br +.RE .P Lastly, use standard Unix/POSIX file permissions to ensure they can not access files they should not be able to within the chroot jail. +.SS Command Line Parser +As of +.B rssh +version 2.2.3, the program must parse out the complete command line to avoid +command line options which cause the execution of arbitrary programs (and +hence bypass the security of \fBrssh\fP). In order to keep the program source +code sane, the parser is a little over-zealous about matching command line +options. In practice, this probably will not be an issue, but in theory it is +possible. +.P +If you run into a problem where +.B rssh +refuses to run, claiming to be rejecting insecure command line options which +were not specified, try changing your command line such that all \fIshort\fP +options are specified as single-letter option flags (e.g. -e -p instead of +-ep) and make sure you separate arguments from their respective options by a +space (e.g. -p 123 instead of -p123). In virtually all cases, this should +solve the problem. Admittedly, an exhaustive search was not performed, but no +problematical cases were found which were likely to be common. +.P +The alternative would have been to include a complete command-line parser for +rcp, rdist, and rsync; this was way out of the scope of this project. In +practice, the existing parser should suffice. If, however, you find cases +where it does not, please post details to the rssh mailing list. Details +about how to post to the mailing list can be found at the rssh homepage. .SS "OpenSSH Versions and Bypassing rssh" -Prior to OpenSSH 3.5, \fIsshd\fP(8) will generally attempt to parse files in +Prior to OpenSSH 3.5, \fBsshd\fP(8) will generally attempt to parse files in the user's home directory, and may also try to run a start-up script from the user's .I $HOME/.ssh directory. .B rssh does not make use of the user's environment in any way. The relevant command -is executed by calling \fIexecv\fP(3) with the full path to the command, as +is executed by calling \fBexecv\fP(3) with the full path to the command, as specified at compile time. It does not depend upon the user's PATH variable, or on any other environment variable. .P There are, however, several problems that can arise. This is due entirely to the way the OpenSSH Project's sshd works, and is in no way the fault of \fBrssh\fP. For example, one problem which might exist is that, according to -the \fIsshd\fP(8) man page from at least some releases of OpenSSH, the +the \fBsshd\fP(8) man page from at least some releases of OpenSSH, the commands listed in the .I $HOME/.ssh/rc file are executed with @@ -161,7 +239,7 @@ .I is vulnerable to this attack, there is a workaround for this problem, though it is pretty restrictive. -.B "The user's home directory absolutely must *not* be writable by the user." +.I "The user's home directory absolutely must not be writable by the user." If it is, the user can use sftp to remove the directory or rename it, and then create a new one, and fill it up with whatever environment files they like. For providing file uploads, this means a user-writable directory must be created for @@ -193,7 +271,41 @@ and disable the default of static compilation. .SH BUGS None. =8^) +.SS A Note About Getting Help +If you are having trouble getting +.B rssh +working, or you think you've found a bug, please use the mailing list, and +.I do not e-mail me +\fIdirectly\fP. +You must sign up for the list in order to post. Information about how to sign +up is available on the rssh homepage. If you mail me directly with questions, +I will almost certainly ignore you, or at the very least ask you to repost +your question on the mailing list. Please also feel free to provide feedback +about rssh on the mailing list, whether positive or negative (especially +negative). +.SS Security Problems +The only exception to the above is if you believe you have found a security +problem with \fBrssh\fP. If that is the case, then please \fIdo\fP contact me +privately. If you are unable to find my direct contact info, post a message on +the mailing list requesting that I contact you about a potential security +problem. Security problems should be dealt with privately, so that the threat +can be properly assessed, and so as not to needlessly endanger the +installations of \fBrssh\fP in production environments. I take security +problems seriously, and will work to resolve them as quickly as possible. +.SS N.B.: +Before you e-mail me (or the mailing list) with questions, be sure to +.I THOROUGHLY +read all of the following files: README, INSTALL, CHROOT, SECURITY. All of +these files are distributed with the rssh source code, as well as all binary +packages of \fBrssh\fP. If you downloaded a binary package, these files +should be located wherever your distribution keeps its documentation files +(usually /usr/share/doc/rssh-version/ or something similar). Also +.I THOROUGHLY +read the man pages for \fBrssh\fP(1), and \fBrssh.conf\fP(5). Finally, if you +are still having problems, read the FAQ at +http://www.pizzashack.org/rssh/faq.shtml. If it is clear to me that you have +not read these documents, I will ignore you. In most cases, these documents +will already have everything you need to get rssh working, and I won't be able +to explain it any better on a mailing list than I did in those documents... .SH SEE ALSO -\fIrssh.conf\fP(5), \fIsshd\fP(8), \fIssh\fP(1), \fIscp\fP(1), \fIsftp\fP(1). - - +\fBrssh.conf\fP(5), \fBsshd\fP(8), \fBssh\fP(1), \fBscp\fP(1), \fBsftp\fP(1). Index: rssh.conf =================================================================== RCS file: /chest/cvsroot/rssh/rssh.conf,v --- rssh.conf 19 Jun 2004 08:16:15 -0000 1.3 +++ rssh.conf 27 Nov 2005 21:28:36 -0000 @@ -19,10 +19,11 @@ # the chroot jail will be located. # # if you DO NOT want to chroot users, LEAVE THIS COMMENTED OUT. -# You can quote anywhere, but quotes not required unless path contains a -# space... as in this example. +# chrootpath = /usr/local/chroot -#chrootpath = "/usr/local/chroot dir" +# You can quote anywhere, but quotes not required unless the path contains a +# space... as in this example. +#chrootpath = "/usr/local/my chroot" ########################################## # EXAMPLES of configuring per-user options @@ -30,10 +31,6 @@ #user=rudy:077:00010: # the path can simply be left out to not chroot #user=rudy:077:00010 # the ending colon is optional -#spaces in the path must be quoted... -#user=rudy:011:00001:"/usr/local/chroot dir" # scp with chroot -#user=rudy:011:00010:"/usr/local/chroot dir" # sftp with chroot -#user=rudy:011:00011:"/usr/local/chroot dir" # both with chroot #user=rudy:011:00100: # cvs, with no chroot #user=rudy:011:01000: # rdist, with no chroot #user=rudy:011:10000: # rsync, with no chroot @@ -41,8 +38,14 @@ #user=rudy:01"1:00001:/usr/local/chroot" # or somewhere in the middle, freak! #user=rudy:'011:00001:/usr/local/chroot' # single quotes too +# if your chroot_path contains spaces, it must be quoted... +# In the following examples, the chroot_path is "/usr/local/my chroot" +#user=rudy:011:00001:"/usr/local/my chroot" # scp with chroot +#user=rudy:011:00010:"/usr/local/my chroot" # sftp with chroot +#user=rudy:011:00011:"/usr/local/my chroot" # both with chroot + # Spaces before or after the '=' are fine, but spaces in chrootpath need # quotes. -#user = "rudy:011:00001:/usr/local/chroot dir" -#user = "rudy:011:00001:/usr/local/chroot dir" # neither do comments at line end +#user = "rudy:011:00001:/usr/local/my chroot" +#user = "rudy:011:00001:/usr/local/my chroot" # neither do comments at line end Index: rssh.conf.5.in =================================================================== RCS file: /chest/cvsroot/rssh/rssh.conf.5.in,v --- rssh.conf.5.in 20 Mar 2004 03:00:12 -0000 1.2 +++ rssh.conf.5.in 28 Nov 2005 16:54:56 -0000 @@ -78,7 +78,8 @@ Causes \fBrssh\fP (actually a helper program) to call the .I chroot() system call, changing the root of the file system to whatever directory is -specified. For example: +specified. Note that the value on the right hand side of the equal sign is +the name of a directory, not a command. For example: .P chrootpath=/usr/chroot .P @@ -89,15 +90,24 @@ distribution for hints about how to do this. See also the \fIchroot\fP(2) man page. .P -If the user's home directory (as specified in /etc/password) is underneath the -path specified by this keyword, then the user will be chdir'd into their home -directory. If it is not, then they will be chdir'd to the root of the chroot -jail. +If the user's home directory (as specified in \fI/etc/passwd\fP) is underneath +the path specified by this keyword, then the user will be chdir'd into their +home directory. If it is not, then they will be chdir'd to the root of the +chroot jail. +.P +In other words, if the jail is \fI/chroot\fP, and your user's home directory +is \fI/chroot/home/user\fP, then once \fBrssh_chroot_helper\fP changes the +root of the system, it will cd into \fI/home/user\fP inside the jail. +However, if your user's home directory is given as \fI/home/user\fP in +\fI/etc/passwd\fP, then even if that directory exists in the jail, the chroot +helper will not try to cd there. The user's normal home directory must live +inside the jail for this to work. .RE +.P .B user .RS The user keyword allows for the configuration of options on a per-user basis. -.B THIS KEYWORD OVERRIDES ALL OTHER KEYWORDS FOR THE SPECIFIED USER. +.I THIS KEYWORD OVERRIDES ALL OTHER KEYWORDS FOR THE SPECIFIED USER. That is, if you use a user keyword for user foo, then foo will use only the settings in that user line, and not any of the settings set with the keywords above. The user keyword's argument consists of a group of fields separated by @@ -119,7 +129,9 @@ .RE .B path .RS -The path to which this user should be chrooted +The \fIdirectory\fP to which this user should be chrooted (this is not a +command, it is a directory name). See \fBchroot_path\fP above for complete +details. .RE .P For example, you might have something like this: @@ -131,7 +143,7 @@ specified, the user will .I not be chrooted, -.B regardless of default options set with the keywords above. +.I regardless of default options set with the keywords above. If you wanted this user to be chrooted, you would need to specify the chroot path explicitly, even if it should be the same as that set using the chrootpath keyword. Remember that if there are spaces in the path, you need Index: rssh_chroot_helper.c =================================================================== RCS file: /chest/cvsroot/rssh/rssh_chroot_helper.c,v --- rssh_chroot_helper.c 3 Apr 2004 08:45:14 -0000 1.4 +++ rssh_chroot_helper.c 28 Nov 2005 16:38:41 -0000 @@ -31,20 +31,45 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ #include +#ifdef HAVE_STDLIB_H #include +#endif /* HAVE_STDLIB_H */ +#ifdef HAVE_UNISTD_H #include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_ERRNO_H #include +#endif /* HAVE_ERRNO_H */ +#ifdef HAVE_STRING_H #include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_LIBGEN_H #include +#endif /* HAVE_LIBGEN_H */ +#ifdef HAVE_SYSLOG_H #include +#endif /* HAVE_SYSLOG_H */ +#ifdef HAVE_PWD_H #include +#endif /* HAVE_PWD_H */ +#ifdef HAVE_SYS_TYPES_H #include +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_STAT_H #include +#endif /* HAVE_SYS_STAT_H */ + /* LOCAL INCLUDES */ +#include "rssh.h" +#include "rsshconf.h" #include "pathnames.h" #include "log.h" +#include "util.h" #include "argvec.h" /* GLOBAL VARIABLES */ @@ -58,32 +83,20 @@ /* FILE SCOPE FUNCTIONS */ -char *get_username( void ) -{ - struct passwd *temp; - - if ( !(temp = getpwuid(getuid()) ) ) return NULL; - return temp->pw_name; -} - - void ch_start_logging( void ) { - /* set up logging */ + /* set up logging - username should be set before this is called */ if ( log_init ) return; - username = get_username(); log_set_facility(LOG_USER); log_set_priority(LOG_INFO); log_open(); - log_msg("new session for %s, UID=%d", - username ? username : "unknown user", - getuid()); + log_msg("new session for %s, UID=%d", username, getuid()); /* all log messages from this point on are errors */ log_set_priority(LOG_ERR); log_init = 1; } -void ch_fatal_syscall( char *func, char *arg, char *strerr ) +void ch_fatal_error( char *func, char *arg, char *strerr ) { /* drop privileges */ @@ -101,45 +114,124 @@ int main( int argc, char **argv ) { struct stat s; + ShellOptions_t opts; long int cmd; char *conv; char **argvec; + char *cmd_path; + char *homedir; + struct passwd uinfo; + struct passwd *temp; + + /* + * Unfortunately, in order to maintain security, a lot of the code + * from the rssh main program must be duplicated. Specifically, the + * config file must be parsed to get the chroot path, in order to + * prevent a user from being able to chroot() arbitrarily, which leads + * to easy root compromise if a user has shell access to the system. + * build_arg_vector() must be done here instead of in the main + * program, in order to prevent a directory transversal attack. + */ + + /* + * As a possible future security enhancement, it should be possible to + * have rssh_chroot_helper authenticate cryptographically that it was + * exec()'d by rssh. If rssh is SGID to some rssh-users group, it + * could store the public key for rssh_chroot_helper in a file which + * is only readable by that group. Since rssh_chroot_helper is + * already SUID root, it could store its private key in some file that + * is only readable by root. Obviously, care should be taken that no + * user who has shell access to the system can become a member of the + * rssh-users group. The only thing stopping me from coding that now + * is my lack of knowledge of cryptographic programming. + * + * As a further precaution, all users whose accounts will be protected + * by rssh should be in the rssh-users group, and both rssh and + * rssh_chroot_helper should be executable only by that group. This + * can be done now, even without any cryptography. + */ + + /* THIS CODE IS EXPOSED AS ROOT! */ + + /* initialize variables to defaults */ + opts.rssh_umask = 022; + opts.shell_flags = 0; + opts.chroot_path = NULL; /* figure out our name, and give it to the log module */ progname = strdup(log_make_ident(basename(argv[0]))); + /* get user's passwd info */ + if ( (temp = getpwuid(getuid())) ){ + uinfo = *temp; + username = uinfo.pw_name; + } + else + /* this probably should never happen */ + username = "unknown user!"; + /* make sure we have enough arguments, or exit with error */ - if ( argc < 5 ) - /* cheating, since this isn't a system call problem... */ - ch_fatal_syscall("rssh_chroot_helper", "invalid argument(s)", - "not enough arguments"); + if ( argc != 3 ) ch_fatal_error(progname, "invalid arguments", + "wrong number of arguments"); + + /* process the config file, don't log */ + if ( !(read_shell_config(&opts, PATH_RSSH_CONFIG, 0)) ){ + ch_fatal_error("read_shell_config()", PATH_RSSH_CONFIG, + "errors processing configuration file!"); + } /* - * argv[1] is the directory to chroot to. Check to make sure it + * opts.chroot_path is directory to chroot to. Check to make sure it * exists. If it does, chroot and drop privileges, and cd to it. */ - if ( stat(argv[1], &s) == -1 ) - ch_fatal_syscall("stat()", argv[1], strerror(errno)); - if ( chroot(argv[1]) == -1 ) - ch_fatal_syscall("chroot()", argv[1], strerror(errno)); + if ( stat(opts.chroot_path, &s) == -1 ) + ch_fatal_error("stat()", argv[1], strerror(errno)); + if ( chroot(opts.chroot_path) == -1 ) + ch_fatal_error("chroot()", argv[1], strerror(errno)); + + /* END OF CODE EXPOSED AS ROOT! */ setuid(getuid()); ch_start_logging(); - if ( !(argvec = build_arg_vector(argv[4], 0)) ) - ch_fatal_syscall("build_arg_vector()", argv[4], - "bad expansion"); + log_msg("user's home dir is %s", uinfo.pw_dir); - /* make sure we can change directory to the user's dir */ - if ( chdir(argv[3]) == -1 ){ - log_msg("could not cd to user's home dir: %s", argv[3]); - if ( chdir("/") ) - ch_fatal_syscall("chdir()", "/", strerror(errno)); + /* get the user's home dir */ + if ( !(homedir = extract_root(opts.chroot_path, uinfo.pw_dir)) ){ + log_msg("couldn't find %s in chroot jail", uinfo.pw_dir); + homedir = strdup("/"); } - /* argv[2] is "1" if scp, "2" if sftp, ... */ - cmd = strtol(argv[2], &conv, 10); + log_msg("chrooted to %s", opts.chroot_path); + log_msg("changing working directory to %s (inside jail)", homedir); + + /* cd into / to avoid possibility of breaking out of the jail */ + if ( chdir("/") ) + ch_fatal_error("chdir()", "/", strerror(errno)); + + /* make sure we can change directory to the user's dir */ + if ( chdir(homedir) == -1 ) + log_msg("could not cd to user's home dir: %s", homedir); + + if ( !(argvec = build_arg_vector(argv[2], 0)) ) + ch_fatal_error("build_arg_vector()", argv[2], + "bad expansion"); + + /* + * This is the old way to figure out what program to run. Since we're + * re-parsing the config file in rssh_chroot helper, we could get rid + * of this and redetermine it from the command line, and re-parse + * whether or not it's ok to run that command. But, I don't think + * that's really necessary, nor worth the effort. A user who is + * restricted by rssh will not be able to gain access to manipulate + * this on the command line, and a user who has full shell access + * can't gain anything they couldn't already do by manipulating it... + * so it seems OK to leave as is. + */ + + /* argv[1] is "1" if scp, "2" if sftp, ... */ + cmd = strtol(argv[1], &conv, 10); if ( *conv ){ log_msg("command identifier contained invalid chars"); exit(2); @@ -148,19 +240,19 @@ /* ok... what were we supposed to run? */ switch (cmd){ case 1: - argv[3] = PATH_SCP; + cmd_path = PATH_SCP; break; case 2: - argv[3] = PATH_SFTP_SERVER; + cmd_path = PATH_SFTP_SERVER; break; case 3: - argv[3] = PATH_CVS; + cmd_path = PATH_CVS; break; case 4: - argv[3] = PATH_RDIST; + cmd_path = PATH_RDIST; break; case 5: - argv[3] = PATH_RSYNC; + cmd_path = PATH_RSYNC; break; default: log_msg("invalid command specified"); @@ -168,10 +260,10 @@ } /* now run it */ - execv(argv[3], argvec); + execv(cmd_path, argvec); /* we only get here if the exec fails */ - ch_fatal_syscall("execv()", argv[3], strerror(errno)); + ch_fatal_error("execv()", cmd_path, strerror(errno)); /* and we never get here, but it shuts gcc up */ exit(1); } Index: rsshconf.c =================================================================== RCS file: /chest/cvsroot/rssh/rsshconf.c,v --- rsshconf.c 30 Dec 2004 20:58:09 -0000 1.4 +++ rsshconf.c 27 Nov 2005 22:35:43 -0000 @@ -78,6 +78,7 @@ NULL }; +int log=0; /* flag to tell config parser to stop processing config file */ static bool got_user_config = FALSE; @@ -124,16 +125,17 @@ /* EXTERNALLY VISIBLE FUNCTIONS */ /* returns FALSE if there was an error, TRUE if not */ -int read_shell_config( ShellOptions_t *opts, const char *filename ) +int read_shell_config( ShellOptions_t *opts, const char *filename, int do_log ) { FILE *cfg_file; /* config file ptr */ int linenum; /* cfg file line counter */ int status = TRUE; /* were all the cfg lines good? */ char line[CFG_LINE_LEN + 1]; /* buffer to hold region */ + log = do_log; memset(line, 0, CFG_LINE_LEN + 1); cfg_file = fopen(filename, "r"); - if (!cfg_file) { + if (!cfg_file && log ) { log_set_priority(LOG_WARNING); log_msg("config file (%s) missing, using defaults", filename); opts->shell_flags = RSSH_ALLOW_SCP; @@ -169,8 +171,10 @@ *newline = '\0'; else { /* there is no newline - log the error and find the EOL */ - log_set_priority(LOG_ERR); - log_msg("line %d: line too long", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("line %d: line too long", lineno); + } while ( fgets(tmp, CFG_LINE_LEN, cfg_file) ){ if ( (newline = strchr(line, '\n')) ) break; @@ -234,9 +238,12 @@ return TRUE; default: /* the keyword is unknown */ - log_set_priority(LOG_ERR); - log_msg("line %d: syntax error parsing config file", lineno); - if ( keywrd[0] ) + if (log){ + log_set_priority(LOG_ERR); + log_msg("line %d: syntax error parsing config file", + lineno); + } + if ( keywrd[0] && log ) log_msg("unknown keyword: %s", keywrd); return FALSE; } @@ -320,8 +327,10 @@ /* initialize strings and pointers */ memset(buf, 0, buflen); if ( !(copy = strdup(str)) ){ - log_set_priority(LOG_ERR); - log_msg("OOM error in get_token() (fatal)"); + if (log){ + log_set_priority(LOG_ERR); + log_msg("OOM error in get_token() (fatal)"); + } exit(1); } start = copy; @@ -421,11 +430,14 @@ const int lineno ) { if ( !eat_comment(line) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } - log_set_priority(LOG_INFO); - log_msg("allowing scp to all users"); + if (log){ + log_set_priority(LOG_INFO); + log_msg("allowing scp to all users"); + } opts->shell_flags |= RSSH_ALLOW_SCP; return TRUE; } @@ -445,11 +457,14 @@ int pos; if ( !(pos = eat_comment(line)) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } - log_set_priority(LOG_INFO); - log_msg("allowing sftp to all users"); + if (log){ + log_set_priority(LOG_INFO); + log_msg("allowing sftp to all users"); + } opts->shell_flags |= RSSH_ALLOW_SFTP; return TRUE; } @@ -470,11 +485,14 @@ int pos; if ( !(pos = eat_comment(line)) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } - log_set_priority(LOG_INFO); - log_msg("allowing cvs to all users"); + if (log){ + log_set_priority(LOG_INFO); + log_msg("allowing cvs to all users"); + } opts->shell_flags |= RSSH_ALLOW_CVS; return TRUE; } @@ -495,12 +513,15 @@ int pos; if ( !(pos = eat_comment(line)) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } log_set_priority(LOG_INFO); - log_msg("allowing rdist to all users"); - opts->shell_flags |= RSSH_ALLOW_RDIST; + if (log){ + log_msg("allowing rdist to all users"); + opts->shell_flags |= RSSH_ALLOW_RDIST; + } return TRUE; } @@ -520,11 +541,14 @@ int pos; if ( !(pos = eat_comment(line)) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } - log_set_priority(LOG_INFO); - log_msg("allowing rsync to all users"); + if (log){ + log_set_priority(LOG_INFO); + log_msg("allowing rsync to all users"); + } opts->shell_flags |= RSSH_ALLOW_RSYNC; return TRUE; } @@ -546,7 +570,7 @@ */ if ( !(temp = (char *)malloc(CFG_LINE_LEN + 1)) ){ - log_msg("fatal error: can't allocate space for chroot path"); + if (log) log_msg("fatal error: can't allocate space for chroot path"); exit(1); } /* get_asgn_param() eats trailing comments, so we won't */ @@ -557,8 +581,10 @@ /* get rid of any old value for chroot path, assign new one */ if ( opts->chroot_path ) free(opts->chroot_path); - log_set_priority(LOG_INFO); - log_msg("chrooting all users to %s", temp); + if (log){ + log_set_priority(LOG_INFO); + log_msg("chrooting all users to %s", temp); + } /* we must not free temp, since opts points to it */ opts->chroot_path = temp; opts->shell_flags |= RSSH_USE_CHROOT; @@ -576,8 +602,10 @@ int pos; if ( !(temp = (char *)malloc(CFG_LINE_LEN + 1)) ){ - log_set_priority(LOG_ERR); - log_msg("fatal error: can't allocate space for log facility"); + if (log){ + log_set_priority(LOG_ERR); + log_msg("fatal error: can't allocate space for log facility"); + } exit(1); } /* this eats trailing comments */ @@ -747,17 +775,20 @@ free(temp); if ( !eat_comment(line + pos) ){ - log_msg("line %d: syntax error parsing config file", lineno); + if (log) log_msg("line %d: syntax error parsing config file", + lineno); return FALSE; } if ( facname ){ log_set_priority(LOG_INFO); - log_msg("setting log facility to %s", facname); + if (log) log_msg("setting log facility to %s", facname); log_set_facility(fac); return TRUE; } - log_msg("line %d: unknown log facility specified", lineno); - log_set_facility(LOG_USER); + if (log){ + log_msg("line %d: unknown log facility specified", lineno); + log_set_facility(LOG_USER); + } return FALSE; } @@ -770,8 +801,10 @@ int mask; /* umask */ if ( !(temp = (char *)malloc(CFG_LINE_LEN + 1)) ){ - log_set_priority(LOG_ERR); - log_msg("fatal error: can't allocate space in process_umask()"); + if (log){ + log_set_priority(LOG_ERR); + log_msg("fatal error: can't allocate space in process_umask()"); + } exit(1); } /* this eats trailing comments */ @@ -782,15 +815,19 @@ /* convert the umask to a number */ if ( !validate_umask(temp, &mask) ){ - log_set_priority(LOG_WARNING); - log_msg("line %d: invalid umask specified, using default 077", + if (log){ + log_set_priority(LOG_WARNING); + log_msg("line %d: invalid umask specified, using default 077", lineno); + } opts->rssh_umask = 077; free(temp); return FALSE; } - log_set_priority(LOG_INFO); - log_msg("setting umask to %#o", mask); + if (log){ + log_set_priority(LOG_INFO); + log_msg("setting umask to %#o", mask); + } opts->rssh_umask = mask; free(temp); return TRUE; @@ -817,7 +854,7 @@ /* make space for user options */ if ( !(temp = (char *)malloc(CFG_LINE_LEN + 1)) ){ - log_msg("fatal error: can't allocate space for user options"); + if (log) log_msg("fatal error: can't allocate space for user options"); exit(1); } @@ -831,8 +868,11 @@ /* now process individual config bits of temp */ if ( !(pos = get_token(temp, user, CFG_LINE_LEN + 1, TRUE, TRUE )) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing config file, line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing config file, line %d", + lineno); + } return FALSE; } @@ -845,10 +885,13 @@ * user lines we don't care about... */ if ( (strcmp(user, username)) ) return TRUE; - log_set_priority(LOG_INFO); - log_msg("line %d: configuring user %s", lineno, user); + if (log){ + log_set_priority(LOG_INFO); + log_msg("line %d: configuring user %s", lineno, user); + } if ( !(len = eat_colon(temp + pos)) ){ - log_msg("syntax error parsing config file: line %d ", lineno); + if (log) log_msg("syntax error parsing config file: line %d ", + lineno); return FALSE; } pos += len; @@ -856,28 +899,35 @@ /* do the umask, but validate it last, since it's non-fatal */ if ( !(len = get_token(temp + pos, mask, CFG_LINE_LEN + 1, TRUE, FALSE)) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing user umask, line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing user umask, line %d", lineno); + } return FALSE; } pos += len; /* do the access bits */ if ( !(len = eat_colon(temp + pos)) ){ - log_msg("syntax error parsing config file: line %d ", lineno); + if (log) log_msg("syntax error parsing config file: line %d ", + lineno); return FALSE; } pos += len; if ( !(len = get_token(temp + pos, axs, CFG_LINE_LEN + 1, TRUE, FALSE)) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing user access, line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing user access, line %d", lineno); + } return FALSE; } if ( !validate_access(axs, &allow_sftp, &allow_scp, &allow_cvs, &allow_rdist, &allow_rsync) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing access bits, line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing access bits, line %d", lineno); + } return FALSE; } pos += len; @@ -886,7 +936,7 @@ if ( !(len = eat_colon(temp + pos)) ) goto cleanup; pos += len; if ( !(path = (char *)malloc(CFG_LINE_LEN + 1)) ){ - log_msg("fatal error: can't allocate space for chroot path"); + if (log) log_msg("fatal error: can't allocate space for chroot path"); exit(1); } if ( !(len = get_token(temp + pos, path, CFG_LINE_LEN + 1, @@ -900,20 +950,26 @@ /* make sure nothing is left */ while ( *(temp + pos) != '\0' && isspace(*(temp + pos)) ) pos++; if ( *(temp + pos) != '\0' ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing user config: line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing user config: line %d", lineno); + } return FALSE; } /* now finally validate the umask */ if ( !validate_umask(mask, &tmpmask) ){ - log_set_priority(LOG_WARNING); - log_msg("line %d: invalid umask specified, using default", + if (log){ + log_set_priority(LOG_WARNING); + log_msg("line %d: invalid umask specified, using default", lineno); + } tmpmask = 077; } - log_set_priority(LOG_INFO); - log_msg("setting %s's umask to %#o", user, tmpmask); + if (log){ + log_set_priority(LOG_INFO); + log_msg("setting %s's umask to %#o", user, tmpmask); + } opts->rssh_umask = tmpmask; /* set the rest of the parameters */ @@ -922,27 +978,27 @@ opts->shell_flags = 0; /* now set the user-specific flags */ if ( allow_scp ){ - log_msg("allowing scp to user %s", user); + if (log) log_msg("allowing scp to user %s", user); opts->shell_flags |= RSSH_ALLOW_SCP; } if ( allow_sftp ){ - log_msg("allowing sftp to user %s", user); + if (log) log_msg("allowing sftp to user %s", user); opts->shell_flags |= RSSH_ALLOW_SFTP; } if ( allow_cvs ){ - log_msg("allowing cvs to user %s", user); + if (log) log_msg("allowing cvs to user %s", user); opts->shell_flags |= RSSH_ALLOW_CVS; } if ( allow_rdist ){ - log_msg("allowing rdist to user %s", user); + if (log) log_msg("allowing rdist to user %s", user); opts->shell_flags |= RSSH_ALLOW_RDIST; } if ( allow_rsync ){ - log_msg("allowing rsync to user %s", user); + if (log) log_msg("allowing rsync to user %s", user); opts->shell_flags |= RSSH_ALLOW_RSYNC; } if ( path ){ - log_msg("chrooting %s to %s", user, path); + if (log) log_msg("chrooting %s to %s", user, path); opts->shell_flags |= RSSH_USE_CHROOT; } opts->chroot_path = path; @@ -961,22 +1017,30 @@ /* make sure '=' is next token, otherwise syntax error */ if ( (pos = eat_assignment(line)) <= 0 ){ - log_set_priority(LOG_ERR); - log_msg("error parsing config file at line %d: " + if (log){ + log_set_priority(LOG_ERR); + log_msg("error parsing config file at line %d: " "assignment expected", lineno); + } return FALSE; } /* get the string parameter of the assignment */ if ( !(len = get_token((line + pos), buf, buflen, FALSE, FALSE)) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing config file, line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing config file, line %d", + lineno); + } return FALSE; } pos += len; /* check for ending comment */ if ( !eat_comment(line + pos) ){ - log_set_priority(LOG_ERR); - log_msg("syntax error parsing config file at line %d", lineno); + if (log){ + log_set_priority(LOG_ERR); + log_msg("syntax error parsing config file at line %d", + lineno); + } return FALSE; } return pos; Index: rsshconf.h =================================================================== RCS file: /chest/cvsroot/rssh/rsshconf.h,v --- rsshconf.h 15 Mar 2004 21:01:37 -0000 1.1.1.1 +++ rsshconf.h 27 Nov 2005 22:34:54 -0000 @@ -49,7 +49,7 @@ /* EXTERNALLY VISIBLE FUNCTION DECLARATIONS */ -int read_shell_config( ShellOptions_t *opts, const char *filename ); +int read_shell_config( ShellOptions_t *opts, const char *filename, int log ); #endif /* _rssh_config_h */ Index: util.c =================================================================== RCS file: /chest/cvsroot/rssh/util.c,v --- util.c 30 Dec 2004 20:58:09 -0000 1.4 +++ util.c 27 Nov 2005 17:01:52 -0000 @@ -122,12 +122,13 @@ /* * opt_exist() - takes a string representing a command line, and a single - * character representing a command-line option to search for. - * If the option exists in the command line, return TRUE. This - * function is a little over-zealous about returning a match, - * but in this case that is better than not being zealous - * enough. And frankly, I don't want to spend the time required - * to get it 100% right. It's not worth the effort. + * character representing a command-line option flag (e.g. "-S") + * to search for. If the option exists in the command line, + * return TRUE. This function is a little over-zealous about + * returning a match, but in this case that is better than not + * being zealous enough. And frankly, I don't want to spend the + * time required to get it 100% right. It's not worth the + * effort. */ bool opt_exist(char *cl, char opt) { @@ -209,6 +210,11 @@ } if ( check_command(cl, opts, PATH_CVS, RSSH_ALLOW_CVS) ) + if ( opt_exist(cl, 'e') ){ + fprintf(stderr, "\ninsecure -e option not allowed."); + log_msg("insecure -e option in cvs command line!"); + return NULL; + } return PATH_CVS; if ( check_command(cl, opts, PATH_RDIST, RSSH_ALLOW_RDIST) ){ @@ -321,4 +327,16 @@ return TRUE; } +/* + * get_username() - get the username of the user, or return unknown + * + */ +char *get_username( void ) +{ + struct passwd *temp; + + if ( !(temp = getpwuid(getuid()) ) ) return "unknown user!"; + return temp->pw_name; +} + Index: util.h =================================================================== RCS file: /chest/cvsroot/rssh/util.h,v --- util.h 30 Dec 2004 20:58:09 -0000 1.4 +++ util.h 19 Nov 2005 16:24:33 -0000 @@ -38,6 +38,7 @@ int validate_umask( const char *temp, int *mask ); int validate_access( const char *temp, bool *allow_sftp, bool *allow_scp, bool *allow_cvs, bool *allow_rdist, bool *allow_rsync ); -bool opt_exist(char *cl, char opt); +bool opt_exist( char *cl, char opt ); +char *get_username( void ); #endif /* _util_h */