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);
}