#!/usr/bin/perl ########################################################################### # # Program : Log Analyzer for DansGuardian # Author : Jimmy Myrick (jmyrick@tiger1.tiger.org) # Version : .1.1 # Released : July 6, 2002 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Heck, if you like it though and want to send me something, that's # ok too. # # Change history is available here for now: # http://www.tiger.org/technology/dg # ########################################################################### ########################################################################### # #Cambiar para apuntar al directorio de logs del DansGuardian # NOTA: El / inicial ES NECESARIO!! # ########################################################################### $logdir = '/var/log/dansguardian/'; ########################################################################### # # Nombre del archivo de logueo. Cambiar segun el prefijo de sus archivos # Por defecto es access.log y no deberia ser modificado. # # Cualquier archivo en $logdir que coincida con el prefijo en $logfile y # este comprimido con gzip con la extension .gz sera tambien leido # Los resultados se mostraran en orden cronologico inverso al nombre de # los archivos. # # Ejemplo: # Si tiene los archivos: access.log access.log.0.gz access.log.1.gz # se mostraran primero los resultados contenidos en access.log.1.gz # seguidos por los contenidos en access.log.0.gz y luego los en # access.log # # El programa no ejecuta ningun ordenamiento y los resultados se muestran # en el orden en que se encuentran en el archivo. Si sus resultados aparecen # fuera de secuencia controle los nombres/fechas para asegurarse que son # comprimidos y rotados correctamente. Si usa newsyslog.conf bajo FreeBSD # esto no sera un problema. # ########################################################################### $logfile = 'access.log'; ########################################################################### # # Si necesita los modulos perl abajo indicados, descarguelos y # descomprimalos en algun directorio, Lugo haga cd al directorio y ejecute # los comandos: perl Makefile.pl; make; make test; make install # # Si necesita instrucciones adicionales: # http://www.cpan.org/modules/INSTALL.html # Puede obtener los archivos en: # http://www.cpan.org/authors/id/LDS/CGI.pm-2.81.tar.gz # ########################################################################### use CGI; ########################################################################### # # Esto es necesario para descomprimir los archivos de log en linea. # Para obtenerlo: # http://www.cpan.org/authors/id/PMQS/Compress-Zlib-1.16.tar.gz # ########################################################################### use Compress::Zlib; ########################################################################### # # Esto determinara automagicamente de donde es llamado el programa. # En caso negativo quite la marca de comentario de la primera linea, # cambie el contenido al nombre de su servidor y path y comente la # segunda linea. # Puede usar los comandos de restriccion del apache para limitar el # acceso a este archivo, si lo desea. # ########################################################################### #$cgipath = 'http://www.internal.net/cgi-bin/dglog-es.pl'; $cgipath = $ENV{SCRIPT_NAME}; ########################################################################### # # NO DEBERIA SER NECESARIO MODIFICAR NADA DEBAJO DE ESTA LINEA # ########################################################################### $q = new CGI; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon = $mon + 1; # mon starts at 0 $year = $year + 1900; # year needs 1900 added $pagename = 'Analizador de Registros para DansGuardian'; $a = $q->param('a'); if ($a eq 'i') { # Inquiry into logs # Estos son los valores que enviara el usuario a travez del navegador $sIP = "TODO"; # direccion IP $sUN = "TODO"; # nombre de usuario $sURL = "TODO"; # URL to show or trace a denied site - this is the URL to trace $sSD = "TODO"; # Complete start date $sSDY = "TODO"; # Start date year $sSDM = "TODO"; # Start date month $sSDD = "TODO"; # Start date day $sED = "TODO"; # Complete end date $sEDY = "TODO"; # End date year $sEDM = "TODO"; # End date month $sEDD = "TODO"; # End date day $sA = "TODO"; # Action $sSumCnt = "20"; # Number of summary sites to show $sSumDen = "off"; # Show denied summary? on/off $sSumAlw = "off"; # Show allowed summary? on/off $sL = "off"; # Turn URL's into links? on/off $sZ = "off"; # Examine gziped files? on/off $sIP = &validateIP($q->param('sIP')) if $q->param('sIP') ne ""; $sUN = $q->param('sUN') if $q->param('sUN') ne ""; $sURL = $q->param('sURL') if $q->param('sURL') ne ""; if ($q->param('sSDY') ne "" && $q->param('sSDM') ne "" && $q->param('sSDD') ne "" && $q->param('sEDY') ne "" && $q->param('sEDM') ne "" && $q->param('sEDD') ne "") { $sSDY = $q->param('sSDY'); $sSDM = $q->param('sSDM'); $sSDD = $q->param('sSDD'); $sEDY = $q->param('sEDY'); $sEDM = $q->param('sEDM'); $sEDD = $q->param('sEDD'); $sSD = $sSDY.'.'.$sSDM.'.'.$sSDD; $sSD = convertDate($sSD); $sED = $sEDY.'.'.$sEDM.'.'.$sEDD; $sED = convertDate($sED); if ($sSD > $sED) { $msg = "Fecha Final es anterior a Fecha Inicial"; &printMenu; } } $sA = &validateAction($q->param('sA')) if $q->param('sA') ne ""; # Action $sSumCnt = &validateSummary($q->param('sSumCnt')) if $q->param('sSumCnt') ne ""; $sSumDen = $q->param('sSumDen') if $q->param('sSumDen') eq 'on'; $sSumAlw = $q->param('sSumAlw') if $q->param('sSumAlw') eq 'on'; $sL = $q->param('sL') if $q->param('sL') eq 'on'; $sZ = $q->param('sZ') if $q->param('sZ') eq 'on'; # Need a few global variables to keep from passing back and forth a bunch $linesRead, $allowTotal, $blockTotal, $grandTotal = 0; &searchLog; } elsif ($a eq 'h') { &displayHelp; } else { &printMenu; } ############# sub searchLog ############# { my $first = 0; &printHeader; print ""; print "Informe para :
Fecha Inicial: $sSD | Fecha Final: $sED | Usuario : $sUN | IP: $sIP | Accion: $sA | URL: $sURL
\n"; print ""; opendir(D, $logdir); @files = grep {/^$logfile/} readdir(D); @files = sort {$b cmp $a} @files; closedir(D); foreach $file (@files) { if ($file =~ /\.gz/) { if ($sZ eq 'on') { if ($first == 0) { print "Ignoring gzip logfile(s) in $logdir: "; $first = 1; } print "$file | "; next; } $gz = gzopen($logdir.$file,r); if (!$gz) { $msg = "No se puede abrir $logdir$file. Controle Permisos."; &printMenu; } while ($gz->gzreadline($line)) { &checkLine($line); } $gz->gzclose; } else { print "

"; unless (open(F,$logdir.$file)) { $msg = "No se puede abrir $logdir$file. Controle Permisos."; &printMenu; } while ($line = ) { &checkLine($line); } close(F); } } if ($sSumAlw eq "on" && $allowTotal != 0) { &showSummarySites($allowTotal,'PERMITIDO',$sSumCnt,%topSites); } if ($sSumDen eq "on" && $blockTotal != 0) { &showSummarySites($blockTotal,'DENEGADO',$sSumCnt,%blockSites); } print "


Coincidencias totales: $grandTotal | Total de Registros: $linesRead
"; print "
Volver al Menu
"; } ############# sub checkLine ############# { my ($line) = @_; $linesRead++; # Print out a '.' every 1000 log file lines read. Keep browser connect alive if (($linesRead % 1000) == 0) { print " "; } ($date,$time,$user,$ip,$url,$toeol) = split(/ /,$line,6); # Rule out the easy matches first return if ($sIP ne "TODO" && $sIP ne $ip); return if ($sUN ne "TODO" && $sUN ne $user); # Don't do a date comparison unless we are told to if ($sSD ne "TODO" || $sED ne "TODO") { $dgDate = &convertDate($date); return if (!($dgDate ge $sSD && $dgDate le $sED)); } $url =~ /(\w+):\/\/([\w\.-]+)\/?(\S*)/; $protocol = $1; # HTTP, FTP $baseurl = $2; # domain part without http:// or ftp:// return if ($sURL ne "TODO" && $sURL ne $baseurl); $toeol =~ /(\*.+\*)? ?(.+)? (\w+) (\d+)$/; $action = $1; # *DENIED# or *EXCEPTION* etc., if exists $reason = $2; # Reason for #1 if exists $method = $3; # method (GET POST) $size = $4; # size if ($sA ne "TODO") { return if ($sA eq "denAll" && $action ne "*DENIED*"); return if ($sA eq "excAll" && $action ne "*EXCEPTION*"); return if ($sA eq "denSite" && !($reason =~ /^Banned site/)); return if ($sA eq "denRegURL" && !($reason =~ /^Banned Regular Expression URL/)); return if ($sA eq "denPhrase" && !($reason =~ /^Banned Phrase/)); return if ($sA eq "denCombPhrase" && !($reason =~ /^Banned combination phrase/)); return if ($sA eq "denWeightPhrase" && !($reason =~ /^Weighted phrase limit/)); return if ($sA eq "denExt" && !($reason =~ /^Banned extension/)); return if ($sA eq "denMIME" && !($reason =~ /^Banned MIME Type/)); return if ($sA eq "denICRA" && !($reason =~ /^ICRA/)); return if ($sA eq "denBlanketIP" && !($reason =~ /^Blanket IP Block/)); return if ($sA eq "excSite" && !($reason =~ /^Exception site/)); return if ($sA eq "excPhrase" && !($reason =~ /^Exception phrase/)); return if ($sA eq "excCombPhrase" && !($reason =~ /^Combination exception phrase/)); } # Need to do a count for grandTotal if allowed OR denied summary selected if ($sSumAlw eq "on" || $sSumDen eq "on") { if ($action ne '*DENIED*') { $allowTotal++; $grandTotal++; # Don't waste memory if didn't want this, but need to count for grandTotal $topSites{$baseurl}++ if $sSumAlw eq "on"; } else { $blockTotal++; $grandTotal++; # Don't waste memory if didn't want this, but need to count for grandTotal $blockSites{$baseurl}++ if $sSumDen eq "on"; } } else { print "$date   $time   "; print "$ip   $user
"; if ($sL eq 'on') { print "$url $method $size
"; } else { print "$url $method $size
"; } if ($action ne "" && $reason ne "") { print "$action : $reason

"; } else { print "

"; } $grandTotal++; } } #################### sub showSummarySites { #################### my ($subTotal, $whatToShow, $topNum, %sites) = @_; my $count = 1; print "
"; foreach $key (sort {$sites{$b} <=> $sites{$a}} keys %sites) { if ($count <= $topNum) { print ""; print ""; print ""; $count++; } break; } print ""; print "

Primeros $topNum $whatToShow
Posicion URL Cantidad \% de
$whatToShow
\% del
Total
Investigar
$count.  "; if ($sL eq 'on') { print "$key"; } else { print "$key"; } print "$sites{$key}"; printf("   %2.2f  ",($sites{$key}/$subTotal)*100); print ""; printf("   %2.2f",($sites{$key}/$grandTotal)*100); print " Ver

Total Registros $whatToShow (solo los primeros $topNum) : $subTotal


"; } ################### sub validateSummary ################### { my ($count) = @_; if ($count < 0 || $count > 100) { $count = 20; } return($count); } ############## sub validateIP ############## { my ($checkIP) = @_; if ($checkIP eq 'TODO') { return('TODO'); } elsif ($checkIP =~ /^((2([0-4]\d|5[0-5])|1?\d{1,2})(\.|$)){4}/) { return ($checkIP); } else { $msg = "Direccion IP invalida."; &printMenu; } } ################## sub validateAction { ################## my ($action) = @_; # Need to make the actions a hash and reference them that way # Make it easier to add/modify and can validate that way too # Maybe later. if ($action eq "none") { return ("TODO"); } return ($action); } ############### sub convertDate { ############### my ($workDate) = @_; ($year, $mon, $day) = split(/\./,$workDate); if (length($mon) == 1) { $mon = '0'.$mon; } if (length($day) == 1) { $day = '0'.$day; } if (($mon ge "01" && $mon le "12") && ($day ge "01" && $day le "31") && ($year ge "2000" && $year le "2035")) { $goodDate = $year.$mon.$day; return ($goodDate); } else { $msg = "Fecha Invalida"; &printMenu; } } ############### sub buildSelect ############### { my ($start, $end, $type) = @_; my $x = 0; ## print "