#!/usr/bin/perl -w use File::Tail; use DBI; use URI; use Getopt::Long; use strict; our %opts = (); # Set default options $opts{log} = '/var/log/messages'; $opts{debug} = 0; $opts{dbhost} = 'localhost'; $opts{dbname} = 'samba_log'; $opts{dbuser} = 'samba'; $opts{dbpass} = 'samba'; our @exclude = (); # get command line arguments GetOptions( "debug=i" => \$opts{debug}, "log=s" => \$opts{squidlog}, "dbhost=s" => \$opts{dbhost}, "dbname=s" => \$opts{dbname}, "dbuser=s" => \$opts{dbuser}, "dbpass=s" => \$opts{dbpass}, "exclude=s" => \@exclude ); @exclude = split(/,/,join(',',@exclude)); # Disable output buffering select(STDOUT); $| = 1; select(STDERR); $| = 1; open STDERR, '>&STDOUT'; # Set process name $0 = 'samba-db-logd'; # Get hostname our $host = `hostname`; chomp($host); ### Subroutines # Print messages on stderr # for debuging purpose sub printlog { my $msg = shift; print "$msg\n"; return; } # Connect to the database sub db_connect { my $dbh = DBI->connect("DBI:mysql:database=$opts{dbname};host=$opts{dbhost}", $opts{dbuser}, $opts{dbpass}, {RaiseError => 1}); die "Couldn't connect to database\n" unless ($dbh); $dbh->do("SET NAMES 'UTF8';") or die "Couldn't set names to UTF-8: ".DBI::errstr; return $dbh; } # escape chars for MySQL queries sub mysql_escape { my $string = shift; $string =~ s|'|\\'|g; return $string; } # Prepare query once sub prepare_query { my $dbh = shift; my $q = "INSERT INTO audit ". "(samba_host,date_day,date_time,username,client_ip,client_name,". "action,access_mode,status,share,file_src,file_dst)". "VALUES(?,?,?,?,?,?,?,?,?,?,?,?);"; my $qh = $dbh->prepare($q); return $qh; } my $dbh = db_connect; my $qh = prepare_query($dbh); # Open log file printlog("opening log file") if ($opts{debug} ge 1); my $tail = File::Tail->new(name=>$opts{log}, maxinterval=>15); while (defined(my $line=$tail->read)){ chomp($line); my ($username, $client_ip, $client_name, $share, $action, $status, $access_mode, $file_src, $file_dst) = undef; # Skip logging if listed in --exclude next if (grep { $action eq $_ } @exclude); # Oct 12 17:20:24 sme8 smbd[11176]: admin|192.168.7.50|pc10-45|intranet|mkdir|Nouveau dossier if ($line =~ m/^\w+\s\d+\s\d+:\d+:\d+\s\w+\ssmbd\[\d+\]:\s+(\w+)\|(\d+\.\d+\.\d+\.\d+)\|([\w\.]+)\|(\w+)\|(\w+)/){ $username = $1; $client_ip = $2; $client_name = $3; $share = $4; $action = $5; } else{ printlog("Couldn't parse this line: $line"); next; } my @other = split /\|/, $line; if (($action eq 'opendir') || ($action eq 'chdir') || ($action eq 'connect') || ($action eq 'disconnect') || ($action eq 'close'){ # Oct 12 17:20:24 sme8 smbd[11176]: admin|192.168.7.50|pc11-45|intranet|opendir|ok|./ $status = $other[5]; $file_src = $other[6]; $access_mode = 'r'; } elsif ($action eq 'chdir'){ #Oct 19 19:14:52 sme8 smbd[2241]: admin|192.168.7.50|pc11-45|intranet|chdir|ok|chdir|/ $status = $other[5]; $file_src = $other[7]; $access_mode = 'r'; } elsif (($action eq 'rmdir') || ($action eq 'mkdir') || ($action eq 'unlink')){ $status = $other[5]; $file_src = $other[6]; $access_mode = 'w'; } elsif ($action eq 'open'){ # Oct 12 17:20:28 sme8 smbd[11176]: admin|192.168.7.50|pc10-45|intranet|open|ok|r|Nouveau document $status = $other[5]; $access_mode = $other[6]; $file_src = $other[7]; } elsif ($action eq 'rename'){ # Oct 12 17:20:28 sme8 smbd[11176]: admin|192.168.7.50|pc10-45|intranet|rename|ok|./Nouveau document|Nouveau document 2 $status = $other[5]; $file_src = $other[6]; $file_dst = $other[7]; $access_mode = 'w'; } my ($sec,$min,$hour,$day,$mon,$year) = localtime; $year += 1900; $mon += 1; my $date = $year.'-'.$mon.'-'.$day; my $time = $hour.':'.$min.':'.$sec; # MySQL escape # Shouldn't be needed, but just in case logs contains junk $username = mysql_escape($username); $client_ip = mysql_escape($client_ip); $client_name = mysql_escape($client_name); $share = mysql_escape($share); $action = mysql_escape($action); $access_mode = mysql_escape($access_mode); $status = mysql_escape($status); $file_src = mysql_escape($file_src); $file_dst = mysql_escape($file_dst) if (defined $file_dst); # File names may appear with a space at the end in the logs $file_src =~ s/\s+$//; $file_dst =~ s/\s+$// if (defined $file_dst); if ($opts{debug} ge 2){ my $msg = "New audit entry:\ndate: $date\nhour: $time\nusername: $username\n". "client_ip: $client_ip\nclient_name: $client_name\nshare: $share\n". "action: $action\nstatus: $status\nfile_src: $file_src\naccess_mode: $access_mode"; $msg .= "\nfile_dst: $file_dst" if (defined $file_dst); $msg .= "\n"; printlog($msg); } $qh->execute($host,$date,$time,$username,$client_ip,$client_name,$action, $access_mode,$status,$share,$file_src,$file_dst) || die "Database error: ".$qh->errstr; } exit(0);