Added ability to set lock screen to setbg
[clearscm.git] / bin / bice.pl
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: bice.pl,v $
6
7 Report breakin attempts to this domain
8
9 =head1 VERSION
10
11 =over
12
13 =item Author
14
15 Andrew DeFaria <Andrew@ClearSCM.com>
16
17 =item Revision
18
19 $Revision: 1.3 $
20
21 =item Created:
22
23 Fri Mar 18 01:14:38 PST 2005
24
25 =item Modified:
26
27 $Date: 2013/05/30 15:35:27 $
28
29 =back
30
31 =head1 SYNOPSIS
32
33  Usage: bice [-u|sage] [-v|erbose] [-d|ebug] [-nou|pdate] [-nom|ail]
34              [-f|ilename <filename> ]
35
36  Where:
37    -u|sage     Print this usage
38    -v|erbose:  Verbose mode (Default: -verbose)
39    -nou|pdate: Don't update security logfile file (Default: -update)
40    -nom|ail:   Don't send emails (Default: -mail)
41    -f|ilename: Open alternate messages file (Default: /var/log/auth.log)
42
43 =head1 DESCRIPTION
44
45 This script will look at the security logfile for attempted breakins and then 
46 use whois to report them to the upstream provider.
47
48 =cut
49
50 use strict;
51 use warnings;
52
53 use FindBin;
54 use Getopt::Long;
55
56 use lib "$FindBin::Bin/../lib";
57
58 use Display;
59 use Mail;
60 use Utils;
61
62 use Fcntl ':flock'; # import LOCK_* constants
63
64 my $security_logfile = '/var/log/auth.log';
65
66 # Customize these variables
67 my $domain   = 'DeFaria.com';
68 my $contact  = 'Andrew@DeFaria.com';
69 my $location = 'Santa Clara, California, USA';
70 my $UTC      = 'UTC-8';
71 my $mailhost = $domain;
72 # End customize these variables
73
74 my $verbose;
75 my $update    = 1;
76 my $email     = 1;
77 my $hostname  = `hostname`;
78 chomp $hostname;
79
80 if ($hostname =~ /(\w*)\./) {
81   $hostname = $1;
82 } # if
83
84 sub AddToIPTables (@) {
85   my (@ips) = @_;
86
87   # We shouldn't need to weed out duplicate but ya never know  
88   my $ipfilename = '/etc/ipblock';
89
90   my $result = open my $ipfile, '<', $ipfilename;
91
92   my (%ips, @oldips);
93
94   if ($result) {
95     @oldips = <$ipfile>; 
96
97     close $ipfile if $ipfile;
98
99     chomp @oldips;
100   } # if
101
102   map { $ips{$_} = 1 } @oldips;
103   map { $ips{$_} = 1 } <@ips>;
104
105   open $ipfile, '>', "$ipfilename"
106     or error "Unable to open $ipfilename - $!", 1;
107
108   foreach (sort keys %ips) {
109     print $ipfile "$_\n";
110   } # foreach
111
112   close $ipfile;
113
114   # Recreate the BICE chain
115   `/sbin/iptables -F BICE`;
116   `/sbin/iptables -X BICE`;
117   `/sbin/iptables -N BICE`;
118
119   # Add all new @ips to iptables
120   `/sbin/iptables -A BICE -s $_ -p tcp -j DROP` foreach (sort keys %ips);
121
122   return;
123 } # AddToIPTables
124
125 # Use whois(1) to get the email addresses of the responsible parties for an IP
126 # address. Note that a hash is used to eliminate duplicates.
127 sub GetEmailAddresses ($) {
128   my ($ip) = @_;
129
130   # List of whois servers to try
131   my @whois_list = (
132     '',
133     'whois.arin.net',
134     'whois.nsiregistry.net',
135     'whois.opensrs.net',
136     'whois.networksolutions.com',
137   );
138
139   my %email_addresses;
140
141   foreach (@whois_list) {
142     my @lines;
143
144     if ($_ eq "") {
145       @lines = grep { /.*\@.*/ } `whois $ip`;
146     } else {
147       @lines = grep {/.*\@.*/ } `whois -h $_ $ip`;
148     } # if
149
150     foreach (@lines) {
151       my @fields = split /:/, $_;
152
153       $_ = $fields [@fields - 1];
154
155       if (/(\S+\@\S[\.\S]+)/) {
156         $email_addresses{$1} = "";
157       } # if
158     } # foreach
159
160     # Break out of loop if we found email addresses
161     last unless keys %email_addresses;
162   } # foreach
163
164   return keys %email_addresses;
165 } # GetEmailAddresses
166
167 # Send email to the responsible parties.
168 sub SendEmail ($$$$$) {
169   my ($to, $subject, $message, $ip, $violations) = @_;
170
171   if ($email) {
172     verbose "Reporting $ip ($violations violations) to $to";
173   } else {
174     verbose "Would have reported $ip ($violations violations) to $to";
175     return;
176   } # if
177
178   mail (
179     from    => "BICE\@$domain",
180     to      => $to,
181     #cc      => $contact,
182     subject => $subject,
183     mode    => 'html',
184     data    => $message,
185   );
186 } # SendEmail
187
188 sub processLogfile () {
189   my %violations;
190
191   # Note: Normally you must be root to open up $security_logfile
192   open my $readlog, '<', $security_logfile
193     or error "Unable to open $security_logfile - $!", 1;
194
195   flock $readlog, LOCK_EX
196     or error "Unable to flock $security_logfile", 1;
197
198   my @lines;
199
200   while (<$readlog>) {
201     my $newline = $_;
202
203     if (/^(\S+\s+\S+\s+\S+)\s+.*Invalid user (\w+) from (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {
204       my %violation = $violations{$3} ? %{$violations{$3}} : %_;
205
206       push @{$violation{$2}}, $1;
207
208       $violations{$3} = \%violation;
209
210       $newline =~ s/Invalid user/INVALID USER/;
211     } elsif (/^(\S+\s+\S+\s+\S+)\s+.*authentication failure.*ruser=(\S+).*rhost=(\S+)/) {
212       my %violation = $violations{$3} ? %{$violations{$3}} : %_;
213
214       push @{$violation{$2}}, $1;
215
216       $violations{$3} = \%violation;
217
218       $newline =~ s/authentication failure/AUTHENTICATION FAILURE/;
219     } elsif (/^(\S+\s+\S+\s+\S+)\s+.*Failed password for (\w+) from (\d{1,3}\.\d{1,3}\.d{1,3}\.d{1,3})/) {
220       my %violation = $violations{$3} ? %{$violations{$3}} : %_;
221
222       push @{$violation{$2}}, $1;
223
224       $violations{$3} = \%violation;
225
226       $newline =~ s/Failed password/FAILED PASSWORD/;
227     } # if
228
229     push @lines, $newline; 
230   } # while
231
232   return %violations unless $update;
233
234   flock $readlog, LOCK_UN
235     or error "Unable to unlock $security_logfile", 1;
236
237   close $readlog;
238
239   open my $writelog, '>', $security_logfile
240     or error "Unable to open $security_logfile for writing - $!", 1;
241
242   flock $writelog, LOCK_EX
243     or error "Unable to flock $security_logfile", 1;
244
245   print $writelog $_ foreach @lines;
246
247   flock $writelog, LOCK_UN
248     or error "Unable to unlock $security_logfile", 1;
249
250   close $writelog;
251
252   return %violations;
253 } # processLogfile
254
255 # Report breakins to the authorities.
256 sub ReportBreakins () {
257   my %violations = processLogfile;
258
259   my $nbrViolations = keys %violations;
260
261   if ($nbrViolations == 0) {
262     verbose 'No violations found';
263   } elsif ($nbrViolations == 1) {
264     verbose '1 site attempting to violate our perimeter';
265   } else {
266     verbose "$nbrViolations sites attempting to violate our perimeter";
267   } # if
268
269   foreach (sort keys %violations) {
270     my $ip = $_;
271
272     my $attempts;
273
274     $attempts += @{$violations{$ip}{$_}} foreach (keys %{$violations{$ip}});
275
276     my @emails   = GetEmailAddresses $ip;
277
278     unless (@emails) {
279       verbose 'Unable to find any responsible parties for detected breakin '
280             . "attempts from IP $ip ($attempts breakin attempts)";
281       next;
282     } # unless
283
284     my $to      = join ',', @emails;
285     my $subject = "Illegal attempts to break into $domain from your domain";
286     my $message = <<"END";
287 <p>Somebody from your domain with an IP Address of <b>$ip</b> has been
288 attempting to break into my domain, <b>$domain</b>. <u>Breaking into somebody
289 else's computer is illegal and criminal prosecution can result!</u> As a
290 responsible ISP it is in your best interests to investigate such activity and to
291 shutdown any such illegal activity as it is a violation of law and most likely a
292 violation of your user level agreement. It is expected that you will investigate
293 this and send the result and/or disposition of your investigation back to
294 $contact. <font color=red><b>If you fail to do so then criminal prosecution may
295 result!</b></font></p>
296
297 <p>Please be aware that <b>none</b> of these attempts to breakin have been
298 successful - this system is configured such that only trusted users are allowed
299 to log in as they must provide authenticated keys in advance. So your attempts
300 have been wholly unsuccessful. Still, this does not diminish the illegality nor
301 the ability of us to pursue this matter in a court of law.</p>
302
303 <p>There were a total of $attempts attempts to break into $domain. The following
304 is a report of the breakin attempts from IP Address $ip along with the usernames
305 attempted and the time of the attempt:</p>
306
307 <p>Note: $domain is located in $location. All times are $UTC:</p>
308
309 <ol>
310 END
311     # Report users
312     foreach my $user (sort keys %{$violations{$ip}}) {
313       if (@{$violations{$ip}{$user}} == 1) {
314         $message .= "<li>The user <b>$user</b> attempted access on $violations{$ip}{$user}[0]</li>";
315       } else {
316         $message .= "<li>The user <b>$user</b> attemped access on the following date/times:</li>"; 
317         $message .= "<ol>";
318         $message .= "<li>$_</li>" foreach (@{$violations{$ip}{$user}});
319         $message .= "</ol>";
320       } # if
321     } # foreach
322
323     $message .= '</ol><p>Your prompt attention to this matter is expected '
324               . 'and will be appreciated.</p>';
325     SendEmail $to, $subject, $message, $ip, $attempts;
326   } # foreach
327
328   AddToIPTables keys %violations;
329 } # ReportBreakins
330
331 ## Main
332
333 # Get options
334 GetOptions (
335   'verbose', sub { set_verbose },
336   'debug',   sub { set_debug },
337   'usage',   sub { Usage },
338   'update!', \$update,
339   'mail!',   \$email,
340   'file=s',  \$security_logfile,
341 ) || Usage;
342
343 Usage 'Must specify filename'
344   unless $security_logfile;
345
346 ReportBreakins;
347
348 =pod
349
350 =head1 CONFIGURATION AND ENVIRONMENT
351
352 DEBUG: If set then $debug is set to this level.
353
354 VERBOSE: If set then $verbose is set to this level.
355
356 TRACE: If set then $trace is set to this level.
357
358 =head1 DEPENDENCIES
359
360 =head2 Perl Modules
361
362 L<FindBin>
363
364 L<Getopt::Long|Getopt::Long>
365
366 L<Fcntl>
367
368 =head2 ClearSCM Perl Modules
369
370 =begin man 
371
372  Display
373  Mail
374  Utils
375
376 =end man
377
378 =begin html
379
380 <blockquote>
381 <<a href="http://clearscm.com/php/scm_man.php?file=lib/Display.pm">Display</a><br>
382 <a href="http://clearscm.com/php/scm_man.php?file=lib/Mail.pm">Mail</a><br>
383 a href="http://clearscm.com/php/scm_man.php?file=lib/Utils.pm">Utils</a><br>
384 </blockquote>
385
386 =end html
387
388 =head1 BUGS AND LIMITATIONS
389
390 There are no known bugs in this script
391
392 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
393
394 =head1 LICENSE AND COPYRIGHT
395
396 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.
397
398 =cut