You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
5.4 KiB
187 lines
5.4 KiB
#!/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/smb_audit.log';
|
|
$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);
|
|
|