On Thu, 19 Jul 2001, Pekka Savola wrote:
> As there have been some buffer overflows recently (and despite best
> intentions, there will be more sooner or later), I thought to bring up an
> idea..
>
> I've sent a patch to be able to drop root privs after getting the socket
> with -U switch.
>
> However, this is rather cumbersome to use.
>
> Perhaps if you don't specify -U root, and uid=pcap exists, tcpdump would
> automatically setuid to pcap. (exception is if tcpdump binary is
> setuid, then setuid to the running user as before).
>
> This way all packet dumping would always be made with non-privileged
> account, unless explicitly otherwise requested.

Well, I happened to make time to hack this.

autoheader, autoconf, configure --with-user=pcap, add pcap user and there
you go..

It depends on the previous droproot patch (by Jarno Huuskonen) (also
attached if curious).

-- 
Pekka Savola                 "Tell me of difficulties surmounted,
Netcore Oy                   not those you stumble over and fall"
Systems. Networks. Security.  -- Robert Jordan: A Crown of Swords
--- tcpdump-3.6.2/configure.in  Wed Jan 17 20:29:58 2001
+++ tcpdump-3.6.2.drop/configure.in     Sun Jul 22 14:20:32 2001
@@ -76,6 +76,15 @@
 )
 
 
+AC_ARG_WITH(user, [  --with-user=USERNAME    drop privileges by default to USERNAME])
+AC_MSG_CHECKING([whether to drop root privileges by default])
+if test ! -z "$with_user" ; then
+        AC_DEFINE_UNQUOTED(WITH_USER, "$withval")
+       AC_MSG_RESULT(to \"$withval\")
+else
+       AC_MSG_RESULT(no)
+fi
+
 CFLAGS="$CFLAGS -Dss_family=__ss_family -Dss_len=__ss_len"
 AC_MSG_CHECKING([whether to enable ipv6])
 AC_ARG_ENABLE(ipv6,
--- tcpdump-3.6.2/acconfig.h    Tue Oct 24 03:56:48 2000
+++ tcpdump-3.6.2.drop/acconfig.h       Sun Jul 22 14:07:16 2001
@@ -84,3 +84,6 @@
 #undef u_int16_t
 #undef u_int32_t
 #undef u_int8_t
+
+/* define if should drop privileges by default */
+#undef WITH_USER
--- tcpdump-3.6.2/tcpdump.c     Sun Jul 22 11:22:55 2001
+++ tcpdump-3.6.2.drop/tcpdump.c        Sun Jul 22 13:56:09 2001
@@ -463,6 +463,13 @@
                printer = lookup_printer(pcap_datalink(pd));
                pcap_userdata = 0;
        }
+#ifdef WITH_USER
+       /* if run as root, drop root; protect against remote sec problems */
+       if (getuid() == 0 || geteuid() == 0) {
+               droproot(WITH_USER);
+               /* does not return if fails */
+       }
+#endif
        if (RFileName == NULL) {
                (void)fprintf(stderr, "%s: listening on %s\n",
                    program_name, device);
--- tcpdump-3.6.2/tcpdump.1     Sun Jul 22 11:22:55 2001
+++ tcpdump-3.6.2.drop/tcpdump.1        Sun Jul 22 14:24:36 2001
@@ -249,6 +249,10 @@
 .I user
 and group ID to the primary group of
 .IR user .
+
+.B Note! 
+This vendor's implementation automatically drops the privileges to user ``pcap''
+if nothing else is specified.
 .TP
 .B \-T
 Force packets selected by "\fIexpression\fP" to be interpreted the
diff -uNr tcpdump-3.6.1.orig/tcpdump.1 tcpdump-3.6.1/tcpdump.1
--- tcpdump-3.6.1.orig/tcpdump.1        Sat Jan 13 22:39:52 2001
+++ tcpdump-3.6.1/tcpdump.1     Sat Jan 13 22:42:11 2001
@@ -70,6 +70,10 @@
 .I algo:secret
 ]
 [
+.B \-U
+.I username
+]
+[
 .I expression
 ]
 .br
@@ -245,6 +249,12 @@
 .TP
 .B \-u
 Print undecoded NFS handles.
+.TP
+.B \-U
+Drops root privileges and changes user ID to
+.I username
+and group ID to the primary group of
+.IR username .
 .TP
 .B \-v
 (Slightly more) verbose output.  For example, the time to live,
diff -uNr tcpdump-3.6.1.orig/tcpdump.c tcpdump-3.6.1/tcpdump.c
--- tcpdump-3.6.1.orig/tcpdump.c        Sat Jan 13 22:39:52 2001
+++ tcpdump-3.6.1/tcpdump.c     Sat Jan 13 22:43:35 2001
@@ -51,7 +51,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <ctype.h>
-
+#include <pwd.h>
 
 #include "interface.h"
 #include "addrtoname.h"
@@ -152,6 +152,24 @@
 extern int opterr;
 extern char *optarg;
 
+/* Drop root privileges */
+void droproot(const char *username)
+{
+       struct passwd *pw = NULL;
+       pw = getpwnam( username );
+       if ( pw ) {
+               if ( initgroups(username, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 
+|| setuid(pw->pw_uid) != 0 ) {
+                       fprintf(stderr, "Couldn't change to '%.32s' uid=%d gid=%d\n", 
+username, 
+                                                       pw->pw_uid, pw->pw_gid);
+                       exit(1);
+               }
+       }
+       else {
+               fprintf(stderr, "Couldn't find user '%.32s'\n", username);
+               exit(1);
+       }
+}
+
 int
 main(int argc, char **argv)
 {
@@ -163,6 +181,7 @@
        RETSIGTYPE (*oldhandler)(int);
        u_char *pcap_userdata;
        char ebuf[PCAP_ERRBUF_SIZE];
+       char *username = NULL;
 
        cnt = -1;
        device = NULL;
@@ -183,7 +202,7 @@
        
        opterr = 0;
        while (
-           (op = getopt(argc, argv, "ac:deE:fF:i:lm:nNOpqr:Rs:StT:uvw:xXY")) != -1)
+           (op = getopt(argc, argv, "ac:deE:fF:i:lm:nNOpqr:Rs:StT:uU:vw:xXY")) != -1)
                switch (op) {
 
                case 'a':
@@ -313,6 +332,17 @@
                case 'u':
                        ++uflag;
                        break;
+               
+               case 'U':
+                       if ( optarg ) {
+                               username = strdup(optarg);
+                       }
+                       else {
+                               fprintf(stderr, "%s: Need username after -U\n", 
+program_name);
+                               usage();
+                               /* NOTREACHED */
+                       }
+                       break;
                        
                case 'v':
                        ++vflag;
@@ -357,7 +387,13 @@
                 * Also, this prevents the user from reading anyone's
                 * trace file.
                 */
-               setuid(getuid());
+               if ( username ) {
+                       droproot( username );
+               }
+               else {
+                       if ( setgid(getgid()) != 0 || setuid(getuid()) != 0 )
+                               fprintf(stderr, "Warning: setgid/setuid failed!\n");
+               }
 
                pd = pcap_open_offline(RFileName, ebuf);
                if (pd == NULL)
@@ -388,7 +424,13 @@
                /*
                 * Let user own process after socket has been opened.
                 */
-               setuid(getuid());
+               if ( username ) {
+                       droproot( username );
+               }
+               else {
+                       if ( setgid(getgid()) != 0 || setuid(getuid()) != 0 )
+                               fprintf(stderr, "Warning: setgid/setuid failed !\n");
+               }
        }
        if (infile)
                cmdbuf = read_infile(infile);
@@ -506,6 +548,6 @@
        (void)fprintf(stderr,
 "\t\t[ -i interface ] [ -r file ] [ -s snaplen ]\n");
        (void)fprintf(stderr,
-"\t\t[ -T type ] [ -w file ] [ expression ]\n");
+"\t\t[ -T type ] [ -w file ] [-U username] [ expression ]\n");
        exit(-1);
 }

Reply via email to