Database Logging Daemon for samba on SME Server
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

#!/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);