Package: httptunnel
Version: 3.3+dfsg-1
Severity: normal
Tags: security,patch,upstream

hts (httptunnel server side program) provides no way to downgrade
privileges after all sockets are open.
That means some hacks (ie: capabilities) need to be done to bind
privileged port (ie: 80) when using unprivileged user (ie: nobody).
Furthermore no privilege are needed once the tunnel is created.

The attached patch add 2 more arguments to hts command line:
  -C, --chroot LOCATION
  -u, --user USERNAME

Privileges are downgraded after tunnel_new_server() call
(chroot,getpwnam+setgid+setuid)
Patch tested and working on 1 Debian testing + 1 Debian unstable (both x86).

-- System Information:
Debian Release: 4.0
 APT prefers unstable
 APT policy: (500, 'unstable')
Architecture: i386 (i686)
Kernel: Linux 2.6.18.3-crazy

Versions of packages file depends on:
ii  libc6                         2.6.1-1    GNU C Library: Shared libraries
diff -Nur httptunnel-3.3+dfsg/hts.1 httptunnel-3.3.new/hts.1
--- httptunnel-3.3+dfsg/hts.1   2008-04-22 22:30:25.000000000 +0200
+++ httptunnel-3.3.new/hts.1    2008-04-22 22:32:57.000000000 +0200
@@ -44,6 +44,12 @@
 .TP
 .B \-p, \-\-pid\-file LOCATION
 write a PID file to LOCATION
+.TP
+.B \-C, \-\-chroot LOCATION
+chroot to LOCATION before serving clients
+.TP
+.B \-u, \-\-user USER
+change user and group identities before serving clients
 .SH AUTHOR
 This manual page was contributed by Teemu Hukkanen <[EMAIL PROTECTED]>,
 and was originally written for the Debian GNU/Linux system.
diff -Nur httptunnel-3.3+dfsg/hts.c httptunnel-3.3.new/hts.c
--- httptunnel-3.3+dfsg/hts.c   2001-02-25 12:56:37.000000000 +0100
+++ httptunnel-3.3.new/hts.c    2008-04-22 01:42:43.000000000 +0200
@@ -13,6 +13,8 @@
 #include <signal.h>
 #include <sys/poll_.h>
 #include <sys/time.h>
+#include <time.h>
+#include <pwd.h>
 
 #include "common.h"
 
@@ -31,6 +33,8 @@
   int strict_content_length;
   int keep_alive;
   int max_connection_age;
+  char *chroot_path;
+  char *user;
 } Arguments;
 
 int debug_level = 0;
@@ -67,6 +71,8 @@
 "  -V, --version                  output version information and exit\n"
 "  -w, --no-daemon                don't fork into the background\n"
 "  -p, --pid-file LOCATION        write a PID file to LOCATION\n"
+"  -C, --chroot LOCATION          chroot to LOCATION before serving clients\n"
+"  -u, --user USERNAME            change user id before serving clients\n"
 "\n"
 "Report bugs to %s.\n",
           me, DEFAULT_HOST_PORT, DEFAULT_KEEP_ALIVE,
@@ -93,6 +99,8 @@
   arg->strict_content_length = FALSE;
   arg->keep_alive = DEFAULT_KEEP_ALIVE;
   arg->max_connection_age = DEFAULT_CONNECTION_MAX_TIME;
+  arg->chroot_path = NULL;
+  arg->user = NULL;
   
   for (;;)
     {
@@ -114,10 +122,12 @@
        { "forward-port", required_argument, 0, 'F' },
        { "content-length", required_argument, 0, 'c' },
        { "max-connection-age", required_argument, 0, 'M' },
+       { "chroot", required_argument, 0, 'C' },
+       { "user", required_argument, 0, 'u' },
        { 0, 0, 0, 0 }
       };
 
-      static const char *short_options = "c:d:F:hk:M:p:sSVw"
+      static const char *short_options = "c:C:d:F:hk:M:p:sSu:Vw"
 #ifdef DEBUG_MODE
        "D:l:"
 #endif
@@ -140,6 +150,10 @@
        case 'c':
          arg->content_length = atoi_with_postfix (optarg);
          break;
+       
+       case 'C':
+         arg->chroot_path = optarg;
+         break;
 
        case 'd':
          arg->device = optarg;
@@ -203,6 +217,10 @@
        case 'p':
          arg->pid_filename = optarg;
          break;
+       
+       case 'u':
+         arg->user = optarg;
+         break;
 
        case 'w':
          arg->use_daemon = FALSE;
@@ -278,6 +296,8 @@
   Arguments arg;
   Tunnel *tunnel;
   FILE *pid_file;
+  uid_t uid;
+  gid_t gid;
 
   parse_arguments (argc, argv, &arg);
 
@@ -307,6 +327,10 @@
   log_notice ("  debug_level = %d", debug_level);
   log_notice ("  pid_filename = %s",
              arg.pid_filename ? arg.pid_filename : "(null)");
+  log_notice ("  chroot_path = %s",
+             arg.chroot_path ? arg.chroot_path : "(null)");
+  log_notice ("  user = %s",
+             arg.user ? arg.user : "(null)");
 
   tunnel = tunnel_new_server (arg.host, arg.port, arg.content_length);
   if (tunnel == NULL)
@@ -315,6 +339,36 @@
       log_exit (1);
     }
 
+  if (arg.user)
+    {
+      struct passwd *pwd;
+      pwd = getpwnam(arg.user);
+      if (!pwd)
+        {
+          log_error ("couldn't chroot to %s", arg.chroot_path);
+          log_exit (1);
+       }
+      uid = pwd->pw_uid;
+      gid = pwd->pw_gid;
+      endpwent();
+    }
+
+  if (arg.chroot_path && chroot(arg.chroot_path))
+    {
+      log_error ("couldn't chroot to %s", arg.chroot_path);
+      log_exit (1);
+    }
+
+  if (arg.user)
+    {
+      if (setgid(gid) || setuid(uid))
+        {
+         log_error ("couldn't change identity to %u:%u", uid, gid);
+         log_exit (1);
+       }
+    }
+
+
   if (tunnel_setopt (tunnel, "strict_content_length",
                     &arg.strict_content_length) == -1)
     log_debug ("tunnel_setopt strict_content_length error: %s",

Reply via email to