3 # Log failed logins in the sql database
4 # Works with mysql, postgresql and Oracle
5 # It will read the sql parameters from the admin.conf file
8 # log_badlogins <radius.log> [<admin.conf>] [all]
12 # admin.conf: /usr/local/dialup_admin/conf/admin.conf
13 # all: no. Go to the end of the file. Don't read it all.
15 use Date::Manip qw(ParseDate UnixDate);
20 $conf=shift||'/usr/local/dialup_admin/conf/admin.conf';
21 $all_file=shift||'no';
23 # Uncomment to force inserts even if there are sql errors. That can
24 # help in case there is one sql query which stops the whole failed
25 # logins logging system from working
29 # CHANGE THESE TO MATCH YOUR SETUP
31 #$regexp = 'from client localhost port 135|from client blabla ';
32 $tmpfile='/var/tmp/sql.input';
38 or die "Could not open configuration file\n";
41 ($key,$val)=(split /:\s*/,$_);
42 # Fixme : recursivly solve %{.*} replacement for $val
43 # Fixme: Conf should be put in an associative array
44 $sql_type = $val if ($key eq 'sql_type');
45 $sql_server = $val if ($key eq 'sql_server');
46 $sql_username = $val if ($key eq 'sql_username');
47 $sql_password = $val if ($key eq 'sql_password');
48 $sql_database = $val if ($key eq 'sql_database');
49 $sql_accounting_table = $val if ($key eq 'sql_accounting_table');
50 $realm_strip = $val if ($key eq 'general_strip_realms');
51 $realm_del = $val if ($key eq 'general_realm_delimiter');
52 $realm_for = $val if ($key eq 'general_realm_format');
53 $domain = $val if ($key eq 'general_domain');
54 $sql_timeout = $val if ($key eq 'sql_connect_timeout');
55 $sql_extra = $val if ($key eq 'sql_extra_servers');
56 $sqlcmd = $val if ($key eq 'sql_command');
57 $clients= $val if ($key eq 'general_clients_conf');
61 open CLIENTS, "<$clients"
62 or die "Could not open $clients file\n";
67 if (!/^\s*$/ && /=/) {
68 ($key,$val)=(split /\s*=\s*/,$_);
69 $client_short = $val if ($key eq 'shortname');
72 s/.*client\s+([^\s]*)\s+\{.*$/\1/;
73 if (/^\d+\.\d+\.\d+\.\d+/) {
76 if (/\./ || /localhost/) {
79 $name = $_.".".$domain;
81 $addr = gethostbyname $name;
82 ($a,$b,$c,$d)=unpack('C4',$addr);
83 $client = "$a.$b.$c.$d";
84 #DEBUG# print $name." = ".$client."\n";
88 $client_array{$client_short} .= $client;
95 $realm_del = '@' if ($realm_del eq '');
96 $realm_for = 'suffix' if ($realm_for eq '');
97 if ($sql_type eq 'mysql'){
98 $pass = ($sql_password ne '') ? "-p$sql_password" : '';
101 $pass = $sql_password;
103 $pass =~ s/(\W)/\\$1/g;
104 die "SQL server not defined\n" if ($sql_server eq '');
106 die "sql_command directive is not set in admin.conf\n" if ($sqlcmd eq '');
107 die "sql command '$sqlcmd' not found or does not seem to be executable\n" if (! -x $sqlcmd);
110 $opt = "-O connect_timeout=$sql_timeout" if ($sql_timeout);
111 $opt .= " -f" if ($force);
112 @servers = (split /\s+/,$sql_extra) if ($sql_extra ne '');
113 unshift @servers, $sql_server;
116 or die "Could not open file $file\n";
117 if ($verbose > 1) { print STDOUT "DEBUG: Opened $file\n" }
119 seek LOG, 0, 2 if ($all_file eq 'no');
122 if ($verbose > 1) { print STDOUT "DEBUG: Reading $file\n" }
125 next if ($regexp ne '' && !/$regexp/);
127 $user = $nas = $port = $caller = '-';
128 if (/Login incorrect/){
129 if (/Login incorrect \((.+?)\):/){
130 $cause = "Login-Incorrect ($1)";
131 if ($verbose > 1) { print STDOUT "DEBUG: Login-Incorrect ($1)\n" }
133 $cause='Login-Incorrect';
134 if ($verbose > 1) { print STDOUT "DEBUG: Login-Incorrect\n" }
138 elsif (/Invalid user/){
139 if (/Invalid user \((.+?)\):/){
140 $cause = "Invalid-User ($1)";
142 $cause='Invalid-User';
146 elsif (/Multiple logins/){
148 $cause='Multiple-Logins (MPP Attempt)';
150 $cause='Multiple-Logins';
154 elsif (/(Outside allowed timespan \(.+?\)):/){
159 $date = (split / : /,$_)[0];
160 $date2 = ParseDate($date);
162 ($year,$mon,$mday,$hour,$min,$sec)=UnixDate($date2,'%Y','%m','%d','%H','%M','%S');
164 $time = "$year-$mon-$mday $hour:$min:$sec";
165 if (/\[([\w\-\.\!\@\s]+?)\]\s+\(from (.+?)\)/){
167 ($nas,$port) = (split /\s+/,$2)[1,3];
168 if ($2 =~ /cli (.+?)$/){
172 elsif (/\[([\w\-\.\!\@\s]+?)\/.+?\]\s+\(from (.+?)\)/){
174 ($nas,$port) = (split /\s+/,$2)[1,3];
175 if ($2 =~ /cli (.+?)$/){
179 $caller='' if (!defined($caller));
180 $user =~s/[^\w\-\.\d\!\@\s]//g;
181 $nas =~s/[^\w\.\-]//g;
183 $addr = $client_array{$nas};
184 if ($user ne '' && $realm_strip eq 'yes'){
185 ($one,$two) = (split /$realm_del/, $user)[0,1];
187 $user = ($realm_for eq 'suffix') ? $one : $two;
190 foreach $server (@servers){
191 unlink "$tmpfile.$server" if ($delete{$server});
192 open TMP, ">>$tmpfile.$server"
193 or die "Could not open temporary file\n";
194 $ctx = Digest::MD5->new;
199 $ctx->add('badlogin');
200 $uniqueid = $ctx->hexdigest;
201 print TMP "ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='YYYY-MM-DD HH24:MI:SS.FF TZH:TZM';\n" if ($sql_type eq 'oracle');
202 #DEBUG# print "INSERT INTO $sql_accounting_table (UserName,AcctUniqueId,NASIPAddress,NASPortId,AcctStartTime,AcctStopTime,AcctSessionTime,AcctInputOctets,AcctOutputOctets,CallingStationId,AcctTerminateCause) VALUES ('$user','$uniqueid','$addr','$port','$time','$time','0','0','0','$caller','$cause');\n";
203 print TMP "INSERT INTO $sql_accounting_table (UserName,AcctSessionId,AcctUniqueId,NASIPAddress,NASPortId,AcctStartTime,AcctStopTime,AcctSessionTime,AcctInputOctets,AcctOutputOctets,CallingStationId,AcctTerminateCause) VALUES ('$user','$uniqueid','$uniqueid','$addr','$port','$time','$time','0','0','0','$caller','$cause');\n";
205 $command = "$sqlcmd -h$server $opt -u$sql_username $pass $sql_database <$tmpfile.$server" if ($sql_type eq 'mysql');
206 $command = "$sqlcmd -U $sql_username -f $tmpfile.$server $sql_database" if ($sql_type eq 'pg');
207 $command = "$sqlcmd $sql_username/$pass" . "@" . "$sql_database <$tmpfile.$server" if ($sql_type eq 'oracle');
208 $command = "$sqlcmd '$server' '$sql_port' '' '$sql_username' '$sql_pass' <$tmpfile.$server" if ($sql_type eq 'sqlrelay');
209 if ($verbose > 1) { print STDOUT "DEBUG: Sending datafile $tmpfile.$server to \"$sql_type\" database\n" }
211 if ($verbose > 1) { print STDOUT "DEBUG: Sent data to \"$sql_type\" database\n" }
214 $delete{$server} = ($exit == 0) ? 1 : 0;
215 print STDERR "ERROR: SQL query failed for host $server\n" if ($exit != 0);
220 if ($all_file ne 'once') {