commit-inetutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Inetutils branch, master, updated. inetutils-1_9_1-230-g7d483


From: Mats Erik Andersson
Subject: [SCM] GNU Inetutils branch, master, updated. inetutils-1_9_1-230-g7d48334
Date: Thu, 03 Jan 2013 13:17:09 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Inetutils ".

The branch, master has been updated
       via  7d4833481e9e9fc5998c66fadd666e727b169323 (commit)
      from  fb9dcf487b3cac5bf6d32923241193bb967a9cb4 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.savannah.gnu.org/cgit/inetutils.git/commit/?id=7d4833481e9e9fc5998c66fadd666e727b169323


commit 7d4833481e9e9fc5998c66fadd666e727b169323
Author: Mats Erik Andersson <address@hidden>
Date:   Wed Jan 2 19:28:35 2013 +0100

    tftpd: Chrooted mode.

diff --git a/ChangeLog b/ChangeLog
index 9131746..3b56f74 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2013-01-02  Mats Erik Andersson  <address@hidden>
+
+       tftpd: Chrooted mode.
+
+       * src/tftpd.c: Include <grp.h>, <pwd.h>, <xalloc.h>.
+       (chrootdir, group, user): New variables.
+       (DEFAULT_GROUP, DEFAULT_USER): New macros.
+       (options): New options `-g/--group', `-s/--secure-dir',
+       and `-u/--user'.  Put new options in separate group.
+       (parse_opt): Parameter `arg' is now in use.
+       <`g', `s', `u'>: New cases.
+       (main): Assign default values to `group', `user'.
+       If chrooted mode was specified, execute chroot(),
+       setgid(), and setuid().
+       * tests/tftp.sh (do_secure_setting): New variable.
+       Set to true only for the super-user, then activating
+       two further read tests.
+       (REDIRECT): Set also for empty VERBOSE.
+
+       * doc/inetutils.texi <tftpd invocation>: Fill with
+       sensible content for old and new capabilities.
+
 2012-12-27  Mats Erik Andersson  <address@hidden>
 
        traceroute: Conform to standard application set-up.
diff --git a/doc/inetutils.texi b/doc/inetutils.texi
index 663744a..b0375f1 100644
--- a/doc/inetutils.texi
+++ b/doc/inetutils.texi
@@ -3587,22 +3587,157 @@ access is refused.
 @chapter @command{tftpd}: TFTP server
 @cindex tftpd
 
address@hidden is intended to be invoked via @command{inetd}
+at all times.
+
 @noindent
 Synopsis:
 
 @example
-tftpd address@hidden@dots{}
+tftpd address@hidden address@hidden @dots{}]
 @end example
 
 @table @option
address@hidden -g @var{group}
address@hidden address@hidden
address@hidden -g
address@hidden --group
+Specify group membership of the process owner.
+This is used only along with the option @option{-s}.
+The default choice is @samp{nogroup}.
+
 @item -l
address@hidden --logging
address@hidden -l
address@hidden --logging
 Enable logging.
 
 @item -n
address@hidden --nonexistent
address@hidden -n
address@hidden --nonexistent
 Supress negative acknowledgement of requests for nonexistent relative
 filenames.
+
address@hidden -s @var{dir}
address@hidden address@hidden
address@hidden -s
address@hidden --secure-dir
+Let the serving process change its root directory to @var{dir}
+before attending to any requests.
+This directory is not observable by any client, but improves
+server isolation, since servable contents must be located
+below this chrooted directory @var{dir}.
+
address@hidden -u @var{user}
address@hidden address@hidden
address@hidden -u
address@hidden --user
+Specify the process owner for serving requests.
+Only relevant along with the option @option{-s}.
+The default name is @samp{nobody}.
 @end table
 
address@hidden Directory prefixes
address@hidden validation}
+
+In addition to options, an invocation of @command{tftpd} can
+specify an optional list of directory prefixes.
+These are approved of according to two principles:
+
address@hidden @bullet
address@hidden
+Relative pathnames are ignored.
+
address@hidden
+At most twenty prefixes are approved, the rest is discarded.
address@hidden itemize
+
address@hidden
+A request for a file is decided upon as a consequence
+of evaluating these criteria:
+
address@hidden @bullet
address@hidden
+Every file request containing the substring @samp{/../} is denied,
+as is a file name beginning with @samp{../}.
+
address@hidden
+Write requests must specify absolute locations.
+
address@hidden
+A file request, if specified as an @emph{absolute} pathname,
+must begin with one of the approved directory prefixes,
+should at least one such prefix have been accepted.
+
address@hidden
+In the absence of a prefix collection, any absolute pathname is
+accepted, should the corresponding file exist.
+
address@hidden
+A file request, if specified as a @emph{relative} name,
+will only be searched for below the acceptable prefixes,
+should at least one such prefix have been approved.
+
address@hidden
+A request for a relatively named file, is denied in the absence
+of approved directory prefixes.
+
address@hidden
+The resulting file must be world readable, or world writable,
+for a read request, or a write request, to succeed.
address@hidden itemize
+
address@hidden Use cases
address@hidden setup cases}
+
+The standard use case is an entry in @file{/etc/inetd.conf} like
+
address@hidden
+tftp dgram tcp4 wait root nobody /usr/sbin/tftpd \
address@hidden        } tftpd /tftpboot /altboot
address@hidden example
+
address@hidden
+This would allow the TFTP client to use any of
+
address@hidden
+get kernel
+get /tftpboot/kernel
+get kernel.alt
+get /altboot/kernel.alt
+get /etc/motd
address@hidden smallexample
+
address@hidden
+given that @file{/tftpboot/kernel} and @file{/altboot/kernel.alt} exist.
+Observe that also @file{/etc/motd} is accessible, inspite there being
+no explicit mention of standard file locations.
+
+A stronger mode of running a TFTP server is to use the `secure mode',
+meaning that the serving process is running in a chrooted mode.
+Then a suitable configuration could be
+
address@hidden
+tftp dgram tcp4 wait root nobody /usr/sbin/tftpd \
address@hidden        } tftpd --secure-dir=/srv/tftp-root  /tftpboot /altboot
address@hidden example
+
address@hidden
+Supposing that the files @file{/srv/tftp-root/tftpboot/kernel}
+and @file{/srv/tftp-root/altboot/kernel.alt} were available,
+all the previously suggested client requests for a kernel would
+still be granted, but now any request for @file{/etc/motd}
+would be declined, and would get a reply `File not found' back.
+
+The chrooted setting is denying access outside of
address@hidden/srv/tftp-root}, yet is not indicating this lock-in
+to the client, and is thus improving server isolation.
+Since neither of @option{-u} and @option{-g} were specified,
+the configuration reproduced above will in fact have the
+transmitting server process running with the default
+owner set to @samp{nobody:nogroup}.
+
 @node rshd invocation
 @chapter @command{rshd}: Remote shell server
 @cindex rshd
diff --git a/src/tftpd.c b/src/tftpd.c
index 004803d..d648733 100644
--- a/src/tftpd.c
+++ b/src/tftpd.c
@@ -79,10 +79,13 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
 
 #include "tftpsubs.h"
 
 #include <unused-parameter.h>
+#include <xalloc.h>
 #include <argp.h>
 #include <progname.h>
 #include <libinetutils.h>
@@ -98,6 +101,17 @@ void usage (void);
 static int peer;
 static int rexmtval = TIMEOUT;
 static int maxtimeout = 5 * TIMEOUT;
+static char *chrootdir = NULL;
+static char *group;
+static char *user;
+
+#ifndef DEFAULT_GROUP
+# define DEFAULT_GROUP "nogroup"
+#endif
+
+#ifndef DEFAULT_USER
+# define DEFAULT_USER  "nobody"
+#endif
 
 /* Some systems define PKTSIZE in <arpa/tftp.h>.  */
 #ifndef PKTSIZE
@@ -133,16 +147,30 @@ static const char *verifyhost (struct sockaddr_storage *, 
socklen_t);
 
 
 static struct argp_option options[] = {
+#define GRP 0
   { "logging", 'l', NULL, 0,
-    "enable logging", 1},
+    "enable logging", GRP+1},
   { "nonexistent", 'n', NULL, 0,
     "supress negative acknowledgement of requests for "
-    "nonexistent relative filenames", 1},
+    "nonexistent relative filenames", GRP+1},
+#undef GRP
+#define GRP 10
+  { NULL, 0, NULL, 0, "", GRP},
+  { "group", 'g', "GRP", 0,
+    "set group of process owner, used with '-s' and "
+    "defaults to 'nogroup'", GRP+1},
+  { "secure-dir", 's', "DIR", 0,
+    "change root directory to DIR before searching and "
+    "serving content", GRP+1},
+  { "user", 'u', "USR", 0,
+    "set name of process owner, used with '-s' and "
+    "defaults to 'nobody'", GRP+1},
+#undef GRP
   { NULL, 0, NULL, 0, NULL, 0}
 };
 
 static error_t
-parse_opt (int key, char *arg _GL_UNUSED_PARAMETER,
+parse_opt (int key, char *arg,
           struct argp_state *state _GL_UNUSED_PARAMETER)
 {
   switch (key)
@@ -151,10 +179,24 @@ parse_opt (int key, char *arg _GL_UNUSED_PARAMETER,
       logging = 1;
       break;
 
+    case 'g':
+      free (group);
+      group = xstrdup (arg);
+      break;
+
     case 'n':
       suppress_naks = 1;
       break;
 
+    case 's':
+      chrootdir = xstrdup (arg);
+      break;
+
+    case 'u':
+      free (user);
+      user = xstrdup (arg);
+      break;
+
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -180,6 +222,9 @@ main (int argc, char *argv[])
   int on, n;
   struct sockaddr_storage sin;
 
+  group = xstrdup (DEFAULT_GROUP);
+  user = xstrdup (DEFAULT_USER);
+
   set_program_name (argv[0]);
   iu_argp_init ("tftpd", default_program_authors);
   argp_parse (&argp, argc, argv, 0, &index, NULL);
@@ -202,12 +247,52 @@ main (int argc, char *argv[])
        }
     }
 
+  if (chrootdir && *chrootdir)
+    {
+      struct passwd *pwd = NULL;
+      struct group *grp = NULL;
+
+      /* Ignore user and group setting for non-root invokations.  */
+      if (!getuid())
+       {
+         pwd = getpwnam (user);
+         if (!pwd)
+           {
+             syslog (LOG_ERR, "getpwnam('%s'): %m", user);
+             exit (EXIT_FAILURE);
+           }
+
+         grp = getgrnam (group);
+         if (!grp)
+           {
+             syslog (LOG_ERR, "getgrnam('%s'): %m", group);
+             exit (EXIT_FAILURE);
+           }
+       }
+
+      if (chroot (chrootdir) || chdir ("/"))
+       {
+         syslog (LOG_ERR, "chroot('%s'): %m", chrootdir);
+         exit (EXIT_FAILURE);
+       }
+
+      if (pwd && grp)
+       {
+         if (setgid (grp->gr_gid) || setuid (pwd->pw_uid))
+           {
+             syslog (LOG_ERR, "setgid/setuid: %m");
+             exit (EXIT_FAILURE);
+           }
+       }
+    }
+
   on = 1;
   if (ioctl (0, FIONBIO, &on) < 0)
     {
-      syslog (LOG_ERR, "ioctl(FIONBIO): %m\n");
+      syslog (LOG_ERR, "ioctl(FIONBIO): %m");
       exit (EXIT_FAILURE);
     }
+
   fromlen = sizeof (from);
   n = recvfrom (0, buf, sizeof (buf), 0, (struct sockaddr *) &from, &fromlen);
   if (n < 0)
diff --git a/tests/tftp.sh b/tests/tftp.sh
index 6efe305..93a041d 100755
--- a/tests/tftp.sh
+++ b/tests/tftp.sh
@@ -30,6 +30,20 @@
 #
 #    OpenBSD uses /etc/services directly, not via /etc/nsswitch.conf.
 
+#
+# Currently implemented tests (10 or 12 in total):
+#
+#  * Read three files in binary mode, from 127.0.0.1 and ::1,
+#    needing one, two, and multiple data packets, respectively.
+#
+#  * Read one moderate size ascii file from 127.0.0.1 and ::1.
+#
+#  * Reload configuration and read a small binary file twice.
+#
+#  * (root only) Reload configuration for chrooted mode.
+#    Read one binary file with a relative name, and one ascii
+#    file with absolute location.
+
 . ./tools.sh
 
 $need_dd || exit_no_dd
@@ -95,6 +109,10 @@ USER=`func_id_user`
 
 # Late supplimentary subtest.
 do_conf_reload=true
+do_secure_setting=true
+
+# Disable chrooted mode for non-root invocation.
+test `func_id_uid` -eq 0 || do_secure_setting=false
 
 # Random base directory at testing time.
 TMPDIR=`$MKTEMP -d $PWD/tmp.XXXXXXXXXX` ||
@@ -195,7 +213,7 @@ write_conf ||
 # would like to suppress the verbose output.  The variable
 # REDIRECT is set to '2>/dev/null' in non-verbose mode.
 #
-test -n "${VERBOSE+yes}" || REDIRECT='2>/dev/null'
+test -n "$VERBOSE" || REDIRECT='2>/dev/null'
 
 eval "$INETD -d -p'$INETD_PID' '$INETD_CONF' $REDIRECT &"
 
@@ -385,6 +403,53 @@ else
     $silence echo >&2 'Informational: Inhibiting config reload test.'
 fi
 
+if $do_secure_setting; then
+    # Allow an underprivileged process owner to read files.
+    chmod g=rx,o=rx $TMPDIR
+
+    cat > "$INETD_CONF" <<-EOF
+       $PORT dgram ${PROTO}4 wait $USER $TFTPD   tftpd -l -s $TMPDIR /tftp-test
+       $PORT dgram ${PROTO}6 wait $USER $TFTPD   tftpd -l -s $TMPDIR /tftp-test
+       EOF
+
+    # Let inetd reload configuration.
+    kill -HUP $inetd_pid
+
+    # Test two files: file-small and asciifile.txt
+    #
+    addr=`echo "$ADDRESSES" | $SED 's/ .*//'`
+    name=`echo "$FILELIST" | $SED 's/ .*//'`
+    rm -f "$name" "$ASCIIFILE"
+    EFFORTS=`expr $EFFORTS + 2`
+
+    echo "binary
+get $name
+ascii
+get /tftp-test/$ASCIIFILE" | \
+    eval "$TFTP" ${VERBOSE:+-v} "$addr" $PORT $bucket
+
+    cmp "$TMPDIR/tftp-test/$name" "$name" 2>/dev/null
+    result=$?
+    if test $? -ne 0; then
+       $silence echo >&2 "Failed chrooted access to $name."
+       RESULT=$result
+    else
+       $silence echo >&2 "Success with chrooted access to $name."
+       SUCCESSES=`expr $SUCCESSES + 1`
+    fi
+    cmp "$TMPDIR/tftp-test/$ASCIIFILE" "$ASCIIFILE" 2>/dev/null
+    result=$?
+    if test $? -ne 0; then
+       $silence echo >&2 "Failed chrooted access to /tftp-test/$ASCIIFILE."
+       RESULT=$result
+    else
+       $silence echo >&2 "Success with chrooted /tftp-test/$ASCIIFILE."
+       SUCCESSES=`expr $SUCCESSES + 1`
+    fi
+else
+    $silence echo >&2 'Informational: Inhibiting chroot test.'
+fi
+
 # Minimal clean up. Main work in posttesting().
 $silence echo
 test $RESULT -eq 0 && $silence false \

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog          |   22 ++++++++
 doc/inetutils.texi |  137 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/tftpd.c        |   93 ++++++++++++++++++++++++++++++++++--
 tests/tftp.sh      |   67 +++++++++++++++++++++++++-
 4 files changed, 313 insertions(+), 6 deletions(-)


hooks/post-receive
-- 
GNU Inetutils 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]