diff -up tcpdump-4.2.1/tcpdump.1.in.eperm tcpdump-4.2.1/tcpdump.1.in --- tcpdump-4.2.1/tcpdump.1.in.eperm 2012-05-16 15:46:55.009494388 +0200 +++ tcpdump-4.2.1/tcpdump.1.in 2012-05-16 15:47:16.860299598 +0200 @@ -214,6 +214,9 @@ have the name specified with the flag, with a number after it, starting at 1 and continuing upward. The units of \fIfile_size\fP are millions of bytes (1,000,000 bytes, not 1,048,576 bytes). + +Note that when used with \fB\-Z\fR option (enabled by default), privileges +are dropped before opening first savefile. .TP .B \-d Dump the compiled packet-matching code in a human readable form to @@ -650,7 +653,9 @@ but before opening any savefiles for out and the group ID to the primary group of .IR user . .IP -This behavior can also be enabled by default at compile time. +This behavior is enabled by default (\fB\-Z tcpdump\fR), and can +be disabled by \fB\-Z root\fR. + .IP "\fI expression\fP" .RS selects which packets will be dumped. diff -up tcpdump-4.2.1/tcpdump.c.eperm tcpdump-4.2.1/tcpdump.c --- tcpdump-4.2.1/tcpdump.c.eperm 2012-05-16 15:46:28.321732801 +0200 +++ tcpdump-4.2.1/tcpdump.c 2012-05-16 15:46:42.642604795 +0200 @@ -1289,9 +1289,27 @@ main(int argc, char **argv) * Switching to the -Z user ID only after opening the first * savefile doesn't handle the general case. */ - if (getuid() == 0 || geteuid() == 0) { - if (username || chroot_dir) - droproot(username, chroot_dir); + + /* If user is running tcpdump as root and wants to write to the savefile, + * we will check if -C is set and if it is, we will drop root + * privileges right away and consequent call to pcap_dump_open() + * will most likely fail for the first file. If -C flag is not set we + * will create file as root then change ownership of file to proper + * user(default tcpdump) and drop root privileges. + */ + int chown_flag = 0; + if (WFileName && (getuid() == 0 || geteuid() == 0)) { + if (Cflag != 0) { + if (username || chroot_dir) + droproot(username, chroot_dir); + } else { + chown_flag = 1; + } + } else { + if (getuid() == 0 || geteuid() == 0) { + if (username || chroot_dir) + droproot(username, chroot_dir); + } } #endif /* WIN32 */ @@ -1312,6 +1330,40 @@ main(int argc, char **argv) MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0); p = pcap_dump_open(pd, dumpinfo.CurrentFileName); + + /* Change ownership of file and drop root privileges */ + if (chown_flag) { + struct passwd pwd; + struct passwd *p_pwd; + char *username_buf; + long initlen; + size_t len; + + initlen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (initlen == -1) { + len = 1024; + } else { + len = (size_t) initlen; + } + + username_buf = (char *) malloc(len * sizeof(char)); + if (username_buf == NULL) { + error("malloc of username_buf"); + } + + getpwnam_r(username, &pwd, username_buf, len, &p_pwd); + if (p_pwd == NULL) { + error("Couldn't find user '%s'", username); + } + + chown(dumpinfo.CurrentFileName, pwd.pw_uid, pwd.pw_gid); + + if (username || chroot_dir) + droproot(username, chroot_dir); + + free(username_buf); + } + if (p == NULL) error("%s", pcap_geterr(pd)); if (Cflag != 0 || Gflag != 0) {