Chapter 4. User Activity
4.4. Tracking File and Network Operations
4.4.2. Tracking Operations in Unix
~DF40DE.tmp 11/08/1999 07:29:56p FILE_ACTION_REMOVED
~DF6E5C.tmp 11/08/1999 07:29:56p FILE_ACTION_ADDED
~DF6E66.tmp 11/08/1999 07:29:56p FILE_ACTION_ADDED
~DF6E5C.tmp 11/08/1999 07:29:56p FILE_ACTION_REMOVED
Unfortunately, the tracking of network operations under NT/2000 is not nearly as impressive. Ideally, as an administrator you'd like to know which process (and therefore which user) has opened a network port.
Unfortunately, I know of no Perl module or free third−party command−line tool that can provide this information. There does exist a single commercial command−line tool called TCPVstat that can show us the network−connection−to−process mapping. TCPVstat is found in the TCPView Professional Edition package available at http://www.winternals.com.
If we are only able to use free tools, then we'll have to make do with a simple listing of the currently open network ports on our system. For that, we'll use another module by Ramdane called Win32::IpHelp.
Here's code to print this information:
use Win32::IpHelp;
# note: the case of "IpHelp" is signficant in this call my $iobj = new Win32::IpHelp;
# populates list of hash of hashes
$iobj−>GetTcpTable(\@table,1);
foreach $entry (@table){
print $entry−>{LocalIP}−>{Value} . ":" . $entry−>{LocalPort}−>{Value}. " −> ";
print $entry−>{RemoteIP}−>{Value} . ":" . $entry−>{RemotePort}−>{Value}."\n";
}
Let's see how we'd perform the same tasks from within the Unix world.
4.4.2. Tracking Operations in Unix
To handle the tracking of both file and network operations in Unix, we can use a single approach. This is one of few times in this book where calling a separate executable is clearly the superior method. Vic Abell has given an amazing gift to the system administration world by writing and maintaining a program called lsof (LiSt Open Files) that can be found at ftp://vic.cc.purdue.edu/pub/tools/unix/lsof. lsof can show in detail all of the currently open files and network connections on a Unix machine. One of the things that make it truly amazing is its portability. The latest version as of this writing runs on at least 18 flavors of Unix and supports several OS versions for each flavor.
Here's a snippet of lsof 's output showing one of the processes I am running. lsof tends to output very long lines, so I've inserted a blank line between each line of output to make the distinctions clear:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME netscape 21065 dnb cwd VDIR 172,2891 8192 12129 /home/dnb
netscape 21065 dnb txt VREG 172,1246 14382364 656749 /net/arch−solaris (fileserver:/vol/systems/arch−solaris) netscape 21065 dnb txt VREG 32,6 54656 35172 /usr (/dev/dsk/c0t0d0s6)
netscape 21065 dnb txt VREG 32,6 146740 6321 /usr/lib/libelf.so.1 netscape 21065 dnb txt VREG 32,6 69292 102611 /usr (/dev/dsk/c0t0d0s6)
netscape 21065 dnb txt VREG 32,6 21376 79751 /usr/lib/locale/en_US/en_US.so.1
The Five−Minute RCS Tutorial (Perl for System Administration)
4.4.2. Tracking Operations in Unix 149
netscape 21065 dnb txt VREG 32,6 19304 5804 /usr/lib/libmp.so.2
netscape 21065 dnb txt VREG 32,6 98284 22860 /usr/openwin/lib/libICE.so.6 netscape 21065 dnb txt VREG 32,6 46576 22891 /usr/openwin/lib/libSM.so.6 netscape 21065 dnb txt VREG 32,6 1014020 5810 /usr/lib/libc.so.1
netscape 21065 dnb txt VREG 32,6 105788 5849 /usr/lib/libm.so.1 netscape 21065 dnb txt VREG 32,6 721924 5806 /usr/lib/libnsl.so.1 netscape 21065 dnb txt VREG 32,6 166196 5774 /usr/lib/ld.so.1
netscape 21065 dnb 0u VCHR 24,3 0t73 5863 /devices/pseudo/pts@0:3−> ttcompat−>ldterm−>ptem−>pts netscape 21065 dnb 3u VCHR 13,12 0t0 5821 /devices/pseudo/mm@0:zero
netscape 21065 dnb 7u FIFO 0x6034d264 0t1 47151 PIPE−>0x6034d1e0
netscape 21065 dnb 8u inet 0x6084cb68 0xfb210ec TCP host.ccs.neu.edu:46575−> host2.ccs.neu.edu:6000 (ESTABLISHED) netscape 21065 dnb 29u inet 0x60642848 0t215868 TCP host.ccs.neu.edu:46758−> www.mindbright.se:80 (CLOSE_WAIT)
In the previous output you can see some of the power this command provides. We can see the current working directory (VDIR), regular files (VREG), character devices (VCHR), pipes (FIFO), and network connections (inet) opened by this process.
The easiest way to use lsof from Perl is to invoke its special "field" mode (−F ). In this mode, its output is broken up into specially labeled and delimited fields, instead of the ps −like columns show above. This makes parsing the output a cinch.
There is one quirk to the output. It is organized into what the author calls "process sets" and "file sets." A process set is a set of field entries referring to a single process; a file set is a similar set for a file. This all makes more sense if we turn on field mode with the 0 option. Fields are then delimited with NUL (ASCII 0) characters and sets with NL (ASCII 12). Here's the same group of lines as those above, this time in field mode (NUL is represented as ^@):
p21065^@cnetscape^@u6700^@Ldnb^@
fcwd^@a ^@l ^@tVDIR^@D0x2b00b4b^@s8192^@i12129^@n/home/dnb^@
ftxt^@a ^@l ^@tVREG^@D0x2b004de^@s14382364^@i656749^@n/net/arch−solaris (fileserver:/vol/systems/arch−solaris)^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s54656^@i35172^@n/usr (/dev/dsk/c0t0d0s6)^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s146740^@i6321^@n/usr/lib/libelf.so.1^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s40184^@i6089^@n/usr (/dev/dsk/c0t0d0s6)^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s69292^@i102611^@n/usr (/dev/dsk/c0t0d0s6)^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s21376^@i79751^@n/usr/lib/locale/en_US/en_US.so.1^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s19304^@i5804^@n/usr/lib/libmp.so.2^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s98284^@i22860^@n/usr/openwin/lib/libICE.so.6^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s46576^@i22891^@n/usr/openwin/lib/libSM.so.6^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s1014020^@i5810^@n/usr/lib/libc.so.1^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s105788^@i5849^@n/usr/lib/libm.so.1^@
4.4.2. Tracking Operations in Unix 150
ftxt^@a ^@l ^@tVREG^@D0x800006^@s721924^@i5806^@n/usr/lib/libnsl.so.1^@
ftxt^@a ^@l ^@tVREG^@D0x800006^@s166196^@i5774^@n/usr/lib/ld.so.1^@
f0^@au^@l ^@tVCHR^@D0x600003^@o73^@i5863^@n/devices/pseudo/pts@0:3−>ttcompat−>ldterm−>ptem−>pts^@
f3^@au^@l ^@tVCHR^@D0x34000c^@o0^@i5821^@n/devices/pseudo/mm@0:zero^@
f7^@au^@l ^@tFIFO^@d0x6034d264^@o1^@i47151^@nPIPE−>0x6034d1e0^@
f8^@au^@l ^@tinet^@d0x6084cb68^@o270380692^@PTCP^@nhost.ccs.neu.edu:46575−> host2.ccs.neu.edu:6000^@TST=ESTABLISHED^@
f29^@au^@l ^@tinet^@d0x60642848^@o215868^@PTCP^@nhost.ccs.neu.edu:46758−> www.mindbright.se:80^@TST=CLOSE_WAIT^@
Let's take this output apart. The first line is a process set (we can tell because it begins with the letter p):
p21065^@cnetscape^@u6700^@Ldnb^@
Each field begins with a letter identifying the field's contents ( p for pid, c for command, u for uid, and L for login) and ends with a delimiter character. Together the fields on this line make up a process set. All of the lines that follow, up until the next process set, describe the open files/network connections of the process described by this process set.
Let's put this mode to use. If we wanted to show all of the open files on a system and the pids that are using them, we could use code like this:
use Text::Wrap;
$lsofexec = "/usr/local/bin/lsof"; # location of lsof executable
# (F)ield mode, NUL (0) delim, show (L)ogin, file (t)ype and file (n)ame
$lsofflag = "−FL0tn";
open(LSOF,"$lsofexec $lsofflag|") or die "Unable to start $lsof:$!\n";
while(<LSOF>){
# deal with a process set if (substr($_,0,1) eq "p"){
($pid,$login) = split(/\0/);
$pid = substr($pid,1,length($pid));
}
# deal with a file set, note: we are only interested # in "regular" files
if (substr($_,0,5) eq "tVREG"){
($type,$pathname) = split(/\0/);
# a process may have the same path name open twice, # these two lines make sure we only record it once next if ($seen{$pathname} eq $pid);
$seen{$pathname} = $pid;
$pathname = substr($pathname,1,length($pathname));
push(@{$paths{$pathname}},$pid);
} }
close(LSOF);
for (sort keys %paths){
print "$_:\n";
print wrap("\t","\t",join(" ",@{$paths{$_}})),"\n";
}
The Five−Minute RCS Tutorial (Perl for System Administration)
4.4.2. Tracking Operations in Unix 151
This code instructs lsof to show only a few of its possible fields. We iterate through its output, collecting filenames and pids in a hash of lists. When we've received all of the output, we print the filenames in a nicely formatted pid list (thanks to David Muir Sharnoff's Text::Wrap module):
/usr (/dev/dsk/c0t0d0s6):
For our last example of tracking Unix file and network operations, let's return to an earlier example, where we attempted to find IRC bots running on a system. There are more reliable ways to find network daemons like bots than looking at the process table. A user may be able to hide the name of a bot by renaming the
executable, but she or he will have to work a lot harder to hide the open network connection. More often than not, this connection is to a server running on TCP ports 6660−7000. lsof makes looking for these
processes easy:
$lsofexec = "/usr/local/bin/lsof";
$lsofflag = "−FL0c −iTCP:6660−7000";
# this is a hash slice being used to preload a hash table, the
# existence of whose keys we'll check later. Usually this gets written
# like this:
# %approvedclients = ("ircII" => undef, "xirc" => undef, ...);
# (but this is a cool idiom popularized by Mark−Jason Dominus)
@approvedclients{"ircII","xirc","pirc"} = ( );
open(LSOF,"$lsofexec $lsofflag|") or die "Unable to start $lsof:$!\n";
while(<LSOF>){
($pid,$command,$login) = /p(\d+)\000 c(.+)\000 L(\w+)\000/x;
warn "$login using an unapproved client called $command (pid $pid)!\n"
unless (exists $approvedclients{$command});
}
close(LSOF);
This is the simplest check we can make. It will catch users who rename eggdrop to pine or −tcsh, and it will catch those users who don't even attempt to hide their bot, but it suffers from a similar flaw to our other approach. If a user is smart enough, she or he may rename their bot to something on our "approved clients"
list. To continue the cat−and−mouse game we could take at least two more steps:
•
Use lsof to check that the file opened for that executable really is the file we expect it to be, and not
4.4.2. Tracking Operations in Unix 152
some random binary in a user filesystem.
•
Use our process control methods to check that the user is running this program from an existing shell.
If this is the only process running for a user (i.e., they've logged off but still left it running), it is probably a daemon and hence a bot.
This cat−and−mouse game brings us to a point that will help wrap up the chapter. In Chapter 3, "User Accounts", we mentioned that users are fundamentally unpredictable. They do things systems administrators don't anticipate. There is an old saying: "Nothing is foolproof because fools are so ingenious." It is important to come to grips with this fact as you program Perl for user administration. You'll write more robust programs as a result. When one of your programs goes "blooey" because a user did something unexpected, you'll be able to sit back calmly and admire the ingenuity.
4.3. Unix Process Control 4.5. Module Information
for This Chapter
Copyright © 2001 O'Reilly & Associates. All rights reserved.
Chapter 4: User Activity
4.5. Module Information for This Chapter
Module CPAN ID Version
Mac::Processes (ships with MacPerl; a modified version is available in the
Mac−Glue package) CNANDOR 1.01
Win32::API 0.011
Win32::ISync (found at http://www.generation.net/~aminer/Perl/) 1.11 Win32::IProc (found at http://www.generation.net/~aminer/Perl/) 1.32 Win32::Setupsup (found at ftp://ftp.roth.net/pub/NTPerl/Others/SetupSup/ or
http://Jenda.Krynicky.cz) 980320
Win32::Lanman (found at ftp://ftp.roth.net/pub/ntperl/Others/Lanman/) 1.05 Win32::OLE (ships with ActiveState Perl) JDB 1.11
Proc::ProcessTable DURIST 0.26
Win32::AdvNotify (found at http://www.generation.net/~aminer/Perl/) 1.01
Data::Dumper GSAR 2.101
Win32::IpHelp (found at http://www.generation.net/~aminer/Perl/) 1.02
Text::Wrap (ships with Perl) MUIR 98.112902
The Five−Minute RCS Tutorial (Perl for System Administration)
4.5. Module Information for This Chapter 153