#!/bin/sh # # run tripwire on a list of remote system from a secure system (i.e. no network # services whatsoever from it and is physically secure). # # (c) Yunliang Yu # TODO: # rewrite in perl # add email to tw.hosts to monitor hosts from other depts # # siggen -1 runtw # #set -x # # 0 3 * * * $HOME/run/runtw >> $HOME/run/log 2>&1 ####IFS=space,tab,newline## IFS=" " PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/etc:/usr/local/bin:$HOME/bin export IFS PATH umask 077 ########################### for i in $@; do case $i in -n*) atype="hosts";; -os*) atype="os";; -*) echo "Usage: $0 [-n/-os host/os ...]"; exit;; ?*) if test -z "$atype"; then echo "Usage: $0 [-n/-os host/os ...]"; exit fi if test "$atype" = "hosts"; then givenhosts="$givenhosts $i"; else givenoses="$givenoses $i"; fi;; esac done #echo "$givenhosts X $givenoses"; exit; echo "====== START `date` ===="; BASEDIR="`dirname $0`"; #echo $BASEDIR; exit CONFIG="tw.hosts" CONFIGtmp="${CONFIG}.tmp" SSHconf="$HOME/.ssh/config" siggen="sparc64linux/siggen -1 -q"; tripwire="sparc64linux/tripwire -i 2"; # make sure it's not suid for thishost thishost="`uname -n`"; # put it in $CONFIG file too #date="`date '+%m%d%y_%H%M'`"; # for testing #date="`date '+%a'`"; # for easy weely rotating date="`date '+%d'`"; # for easy monthly rotating rep="report"; mailto="yu@math.duke.edu xyz@math.duke.edu"; webhf="root@web.math.duke.edu:/usr/local/httpd/htdocs/tw/$date.html"; # use .htaccess runs=15; # number of cocurrent ssh connections to run if cd $BASEDIR; then BASEDIR="`pwd`"; if test ! -f $CONFIG; then echo "*** CONFIG file $CONFIG doesn't exist, quit" | tee -a $rep; cat $rep |Mail -s "Tripwire Error" $mailto exit; fi else echo "*** Can't cd to $BASEDIR, quit" | tee -a $rep; cat $rep |Mail -s "Tripwire Error" $mailto exit; fi echo -e "######## Date: `date` ###########\n">$rep; if tty -s; then # it is a tty, do not sleep :; else if test -d locks; then lkcount=`ls locks|wc -w`; if test $lkcount -ne 0; then echo "*** Locks exist from previous run, quit" | tee -a $rep; ls locks | tee -a $rep; cat $rep |Mail -s "Tripwire Error" $mailto exit; fi fi dt="`netstat -in|awk '{s+=$4+$8}END{print s}'`"; # a random # dt="`expr \( $dt + $$ \) % 7200`"; # up to 2 hours if test -z "$givenhosts" -a -z "$givenoses"; then echo "++++sleep for $dt seconds+++++"; sleep $dt fi fi echo "Host *">$SSHconf echo " RSAAuthentication yes">>$SSHconf echo " StrictHostKeyChecking no">>$SSHconf; # for the first few runs echo " BatchMode yes">>$SSHconf echo " EscapeChar none">>$SSHconf echo " Compression yes">>$SSHconf echo " ConnectionAttempts 2">>$SSHconf; # ? echo " FallBackToRsh no">>$SSHconf echo " ForwardX11 no">>$SSHconf echo " ForwardAgent no">>$SSHconf echo " KeepAlive yes">>$SSHconf #echo " ClearAllForwardings yes">>$SSHconf echo " GatewayPorts no">>$SSHconf trap "killall ssh-agent; rm -rf $CONFIGtmp $SSHconf .twzyuyme* >/dev/null 2>&1; exit 1" 0 1 2 3 15 eval `ssh-agent -s`; # start ssh-agent in current shell cat .usercomment|ssh-add -p; # add the secret key #sh # get a interactive shell for testing if test ! -d databases; then mkdir databases fi if test ! -d logs; then mkdir logs fi if test -z "$givenhosts" -a -z "$givenoses"; then rm -f logs/*_$date >/dev/null 2>&1; # clean up fi if test ! -d locks; then mkdir locks; # use file locks to overcome the wait; problem below fi cp /dev/null $CONFIGtmp; egrep '^[a-zA-Z0-9]' $CONFIG | \ while read rh t d x; do # remotehost ostype rundir trash h=$rh; # from old code if test -z "$d"; then echo "*** config line error: $rh $t $d" | tee -a $rep; continue; elif test ! -d $t; then # note $d must be a dir only root can write in it echo "*** os type $t doesn't have a binary directory" | tee -a $rep; continue; fi if test -n "$givenhosts" -o -n "$givenoses"; then dothish=; for i in $givenhosts null; do if test "$i" = "$h";then dothish="yes"; break; fi done for i in $givenoses null; do if test "$i" = "$t";then dothish="yes"; break; fi done if test -z "$dothish"; then continue; fi rm -f logs/${h}_$date >/dev/null 2>&1; fi echo "$rh $t $d">>$CONFIGtmp done while read rh t d; do # remotehost ostype rundir h=$rh; # from old code cd $BASEDIR; # just to be sure lk="locks/$h"; log="logs/${h}_$date"; echo "===== START $rh `date` ====" | tee -a $log; if test -f $lk; then echo "*** Lock $lk exists, skip" | tee -a $log; continue; fi dbfile="tw.db"; dbnew="tw.db_new"; #dt="`date '+%S%M'`"; #dt="`netstat -in|awk '{s+=$4+$8}END{print s}'`"; # a random #, not unique enough #dir=".twzyuyme$dt$$"; # unique tmp, not unique enough dir=".twzyuyme$h$$"; # unique tmp mkdir $dir cp $t/tripwire $dir/twzyuyme isthishost=0; if test "$h" = "$thishost"; then isthishost=1; fi cmd="nice ./twzyuyme -i 2"; # echo or nice if test $isthishost -eq 1; then #cmd="nice $tripwire "; # need to setuid to read all files? cmd="nice $HOME/bin/tripwire -i 2 "; # suid version for this host ONLY touch $dir/$dbfile $dir/$dbnew; # fool the ownership fi sig="`$siggen $dir/twzyuyme`"; cmd="$cmd -tsig $sig"; dbconf="$t/tw.config"; if test -f "$t/tw.config.$h"; then dbconf="$t/tw.config.$h"; fi cmd="$cmd -c - -initialize"; #cmd="cat"; # testing echo "++++ssh tripwire in bg $log+++++"; { # need to use () to run in a subshell here? echo "++++ssh tripwire in bg $log+++++"; touch $lk; # the lock file could be used to hold the pid dd="$d/$dir"; # careful echo "++++#1 scp dir $dir to $rh:$dd+++++"; if test $isthishost -eq 1; then cp -r $dir $d; else timer -300 scp -q -r $dir root@$rh:$d; fi if test $? -ne 0; then rm -rf $dir; rm -f $lk; echo "*** scp $dir to $rh failed"; exit; fi rm -rf $dir; echo "++++#2 cmd: $cmd+++++"; if test $isthishost -eq 1; then cat $dbconf | (cd $dd && $cmd); # run as not root, could not read some files cd $BASEDIR; # cd back to where the program starts else cat $dbconf | timer -7200 ssh -x -l root $rh "cd $dd && $cmd"; # -n does not pass stdin fi echo "++++#3 scp tripwire dbfile from $rh+++++"; sig="`grep '#### MD5 for ' $log|awk '{print $6}'`"; # hope not buffered dbf="databases/${dbfile}_$h"; if test -f $dbf -o -f ${dbf}.gz; then dbf="${dbf}_new"; fi if test $isthishost -eq 1; then cp $dd/$dbfile $dbf else timer -600 scp -q root@$rh:$dd/$dbfile $dbf; fi sig1="`$siggen $dbf`";# 1 extra space at the end gzip -f $dbf if test "$sig " = "$sig1"; then echo "** Md5 on $dbf matches for $rh"; else echo "*** Md5 on $dbf NOT match for $rh: $sig vs $sig1"; fi echo "++++clean up $dd+++++"; if test $isthishost -eq 1; then rm -rf $d/.twzyuyme* else timer -60 ssh -n -x -l root $rh "/bin/rm -rf $d/.twzyuyme*"; fi rm -f $lk echo "++++$rh END `date`+++++"; } > $log 2>&1 & echo "++++check runs++++"; while :; do lkcount=`ls locks|wc -w`; if test $lkcount -lt $runs; then break; # how to deal with some hung ssh/scp processes? fi sleep 120; done done<$CONFIGtmp #done&1 # it's a perl5 script fi echo "++++check for changes now `date`++++"; while read rh t d; do h=$rh; # from old code cd $BASEDIR; # just to be sure log="logs/${h}_$date"; dbold="databases/tw.db_${h}.gz" dbnew="databases/tw.db_${h}_new.gz" dbconf="$t/tw.config"; if test -f "$t/tw.config.$h"; then dbconf="$t/tw.config.$h"; fi if test ! -f $dbold -o ! -f $dbnew; then echo "### Phase 4:" >> $log 2>&1 continue; fi dbsame="databases/comdb.$t.gz"; if test ! -f $dbsame; then dbsame=""; else dbsame="-s $dbsame"; fi echo "++++ check $rh $dbconf $dbold $dbnew $dbsame ++++"; $tripwire -c $dbconf -d $dbold -n $dbnew $dbsame >> $log 2>&1 echo "++++$rh END `date`+++++" >> $log 2>&1 done<$CONFIGtmp echo "++++parse it now `date`++++";# use $CONFIG could be in perl cat logs/*_$date |egrep '^\*\*\* ' >>$rep; # egrep all error msg first echo>>$rep; while read rh t d; do h=$rh; # from old code log="logs/${h}_$date"; if test ! -f $log; then echo "*** No log file for $rh" | tee -a $rep; fi done<$CONFIGtmp echo>>$rep; while read rh t d; do h=$rh; # from old code log="logs/${h}_$date"; if test ! -f $log; then continue; fi sig="`grep '#### MD5 for ' $log`";# data collecting sih="`grep '### Phase 4:' $log`"; # find diffs if test -z "X$sig" -o -z "$sih"; then # X: not check if new dbase OK cat $log>>$rep; echo>>$rep; fi done<$CONFIGtmp echo>>$rep ta=".tmp1"; tb=".tmp2"; tc=".tmp3"; # temp files echo>$ta; echo>$tb; echo>$tc; while read rh t d; do h=$rh; # from old code log="logs/${h}_$date"; if test ! -f $log; then continue; fi sig="`grep '#### MD5 for ' $log`"; sih="`grep '### Phase 4:' $log`"; if test -z "X$sig" -o -z "$sih"; then continue; # already taken care above fi #cat $log>>$rep; awk 'BEGIN{p=0;n=0;}{ if(p == 0){#first line p=1; if($0 ~ /^===== START /){ printf("\n%s\n",$3,$0)>>ta; printf("\n%s\n",$3,$0)>>tb; printf("%s go go",$3,$3,$3)>>tc; }else{ print>>ta; print>>tb; } }else if(p == 1 && $0 ~ /^### Phase 4:/){ p=2; }else if(p == 2 && NF > 0 && $1 != "###"){ if($0 ~ /^\+\+\+\+/){ printf("++++ %d Lines ++++\n",n)>>tb; printf(" %d\n",n)>>tc; exit; } n++; if($0 ~ /^[acd].*\/(ping|login|netstat|ps|ls|ifconfig|df|libc.a|libc.so|cc|gcc|.rhosts|hosts.equiv|passwd|group|yp|.login|.cshrc|.profile|.forward|authorized_keys)$/){ if($0 !~ /\/(ls\-|.*\.cfdisab|.*\.cfedit|src\/|texmf|mqueue|binding|ypfiles|help|doc)/){ print>>ta; } }else if($0 ~ /^[acd].*\/(in.rshd|in.rlogind|in.telnetd|in.ftpd|wu.ftpd|in.named|in.tftpd|sshd|sshd.|ssh|scp|ypbind|ypserv|inetd|cron|crond|sendmail|dhcpd)$/){ print>>ta; }else if($0 ~ /^a.*\/(tcp:|udp:)/){ if(split($NF,A,":")){ X[A[1]]=X[A[1]] " " A[2]; } #print>>ta; }else if($0 ~ /^[acd].* \/(tmp\/.*|dev|etc|root|usr\/bin|bin|sbin|usr\/sbin|yu1)\// || $0 ~ /^[acd].*\/(spool|cron|\.\..*)\//){ if($0 !~ /\/(ticlts|.*\.save|ntp|defaultprinter|.*\.bak|issue|snmpd|dsk|rdsk|.*\.cfedi|.twzyu|config|texmf|FIFO|mtab|mnttab|src|cfengine|pcmcia)/){ if($0 !~ /\/(ticots|.*\.fc|.*\.pid|saf|.*pipe|.*\.lock|aliases.|xtab|dumpdates|ssh_random|printer|mail|.*\.cfdis|mqueue|\.netscape)/){ if($0 !~ /\/(nologin|\.FVWM2|ioctl|.*\.cfsa|math.|nis.|usr\/man\/)/){ print>>tb; } } } } } }END{for(x in X){printf("added %s:%s\n",x,X[x])>>tb}}' ta=$ta tb=$tb tc=$tc $log >>$rep; echo>>$ta; echo>>$tb; done<$CONFIGtmp echo "============== Critical Changes ====================">>$rep; cat $ta>>$rep; echo "============== Misc Changes ========================">>$rep; cat $tb>>$rep; #cat $rep |Mail -s "Tripwire Report" $mailto { cat< Tripwire Report
Critical Changes go
Misc Changes go
EOF22
sort $tc
echo "
" cat $rep cat< EOF33 } | cat> $ta chmod a+r $ta timer -600 scp -q -p $ta $webhf && webhfgood="[OK]" rm -f $ta $tb $tc { cat<