]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - config/guardian/guardian.pl
Guardian Paket angefangen, zum Testen muss Snort funktionieren ...
[people/pmueller/ipfire-2.x.git] / config / guardian / guardian.pl
1 #!/usr/bin/perl
2 # V 1.7
3 # Read the readme file for changes
4 #
5
6 $OS=`uname`;
7 chomp $OS;
8 print "OS shows $OS\n";
9
10 require 'getopts.pl';
11
12 &Getopts ('hc:d');
13 if (defined($opt_h)) {
14 print "Guardian v1.7 \n";
15 print "guardian.pl [-hd] <-c config>\n";
16 print " -h shows help\n";
17 print " -d run in debug mode (doesn't fork, output goes to STDOUT)\n";
18 print " -c specifiy a configuration file other than the default (/etc/guardian.conf)\n";
19 exit;
20 }
21 &load_conf;
22 &sig_handler_setup;
23
24 print "My ip address and interface are: $hostipaddr $interface\n";
25
26 if ($hostipaddr !~ /\d+\.\d+\.\d+\.\d+/) {
27 print "This ip address is bad : $hostipaddr\n";
28 die "I need a good host ipaddress\n";
29 }
30
31 $networkaddr = $hostipaddr;
32 $networkaddr =~ s/\d+$/0/;
33 $gatewayaddr = $hostipaddr;
34 $gatewayaddr =~ s/\d+$/$hostgatewaybyte/;
35 $broadcastaddr = $hostipaddr;
36 $broadcastaddr =~ s/\d+$/255/;
37 &build_ignore_hash;
38
39 # This is the target hash. If a packet was destened to any of these, then the
40 # sender of that packet will get denied, unless it is on the ignore list..
41
42 %targethash = ( "$networkaddr" => 1,
43 "$broadcastaddr" => 1,
44 "0" => 1, # This is what gets sent to &checkem if no
45 # destination was found.
46 "$hostipaddr" => 1);
47
48 if ( -e $targetfile ) {
49 &load_targetfile;
50 }
51
52 if (!defined($opt_d)) {
53 print "Becoming a daemon..\n";
54 &daemonize;
55 } else { print "Running in debug mode..\n"; }
56
57 open (ALERT, $alert_file) or die "can't open alert file: $alert_file: $!\n";
58 seek (ALERT, 0, 2); # set the position to EOF.
59 # this is the same as a tail -f :)
60 $counter=0;
61 for (;;) {
62 sleep 1;
63 if (seek(ALERT,0,1)){
64 while (<ALERT>) {
65 chop;
66 if (/snort/) { #syslog file
67 if (defined($opt_d)) {print "$_\n";}
68 # This is *much* cleaner, and should work on all systems
69 @foo=split(/\s+/,$_);
70 ($junk,$reason) = split (/\]:/,$_,2);
71 @array=();
72 foreach $str (@foo) {
73 if ($str=~/(\d+\.\d+\.\d+\.\d+)/) {
74 $array[$#array+1]=$1;
75 # write_log ("Found $array[$#array]\n");
76 }
77 }
78 if ($array[1] eq "") { $array[1]=0; }
79 # this should work if snort didn't report the target address.
80 # $array[1] should be the target address (portscans don't show this)
81 &checkem ($array[0], $array[1], $reason);
82 } else { # snort.alert type file for backwards compat
83 if (defined($opt_d)) {print "$_\n";}
84 if (/\[\*\*\]\s+(.*)\s+\[\*\*\]/){
85 $type=$1;
86 }
87 if (/(\d+\.\d+\.\d+\.\d+):\d+ -\> (\d+\.\d+\.\d+\.\d+):\d+/) {
88 &checkem ($1, $2, $type);
89 }
90 }
91 }
92 }
93 # Run this stuff every 30 seconds..
94 if ($counter == 30) {
95 &remove_blocks; # This might get moved elsewhere, depending on how much load
96 # it puts on the system..
97 &check_log_name;
98 $counter=0;
99 } else { $counter=$counter+1; }
100 }
101
102 sub check_log_name {
103 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
104 $atime,$mtime,$ctime,$blksize,$blocks) = stat($alert_file);
105 if ($size < $previous_size) { # The filesize is smaller than last
106 close (ALERT); # we checked, so we need to reopen it
107 open (ALERT, "$alert_file"); # This should still work in our main while
108 $previous_size=$size; # loop (I hope)
109 write_log ("Log filename changed. Reopening $alert_file\n");
110 } else {
111 $previous_size=$size;
112 }
113 }
114
115
116 sub checkem {
117 my ($source, $dest,$type) = @_;
118 my $flag=0;
119 my $date = localtime();
120 return 1 if ($source eq $hostipaddr); # this should prevent is from nuking
121 # ourselves
122 return 1 if ($source eq $gatewayaddr); # or our gateway
123 if ($ignore{$source} == 1) { # check our ignore list..
124 &write_log("$date: ");
125 &write_log("$source\t$type\n");
126 &write_log("Ignoring attack because $source is in my ignore list\n");
127 return 1;
128 }
129 # if the offending packet was sent to us, the network, or the broadcast, then
130 if ($targethash{$dest} == 1) {
131 &write_log("$date: ");
132 &ipchain ($source, $dest, $type);
133 }
134 # you will see this if the destination was not in the $targethash, and the
135 # packet was not ignored before the target check..
136 else {
137 &write_log ("Odd.. source = $source, dest = $dest - No action done.\n");
138 if (defined ($opt_d)) {
139 foreach $key (keys %targethash) {
140 &write_log ("targethash{$key} = $targethash{$key}\n");
141 }
142 }
143 }
144 }
145
146 sub ipchain {
147 my ($source, $dest, $type) = @_;
148 &write_log ("$source\t$type\n");
149 if ($hash{$source} eq "") {
150 &write_log ("Running '$blockpath $source $interface'\n");
151 system ("$blockpath $source $interface");
152 $hash{$source} = time() + $TimeLimit;
153 } else {
154 # We have already blocked this one, but snort detected another attack. So
155 # we should update the time blocked..
156 $hash{$source} = time() + $TimeLimit;
157 }
158 }
159
160 sub build_ignore_hash {
161 # This would cause is to ignore all broadcasts if it
162 # got set.. However if unset, then the attacker could spoof the packet to make
163 # it look like it came from the network, and a reply to the spoofed packet
164 # could be seen if the attacker were on the local network.
165 # $ignore{$networkaddr}=1;
166
167 # same thing as above, just with the broadcast instead of the network.
168 # $ignore{$broadcastaddr}=1;
169 my $count =0;
170 $ignore{$gatewayaddr}=1;
171 $ignore{$hostipaddr}=1;
172 if ($ignorefile ne "") {
173 open (IGNORE, $ignorefile);
174 while (<IGNORE>) {
175 chop;
176 next if (/\#/); #skip comments
177 next if (/^\s*$/); # and blank lines
178 $ignore{$_}=1;
179 $count++;
180 }
181 close (IGNORE);
182 print "Loaded $count addresses from $ignorefile\n";
183 } else {
184 print "No ignore file was loaded!\n";
185 }
186 }
187
188 sub load_conf {
189 if ($opt_c eq "") {
190 $opt_c = "/etc/guardian.conf";
191 }
192 if (! -e $opt_c) {
193 die "Need a configuration file.. please use to the -c option to name a
194 configuration file\n";
195 }
196 open (CONF, $opt_c) or die "Cannot read the config file $opt_c, $!\n";
197 while (<CONF>) {
198 chop;
199 next if (/^\s*$/); #skip blank lines
200 next if (/^#/); # skip comment lines
201 if (/LogFile\s+(.*)/) {
202 $logfile = $1;
203 }
204 if (/Interface\s+(.*)/) {
205 $interface = $1;
206 }
207 if (/AlertFile\s+(.*)/) {
208 $alert_file = $1;
209 }
210 if (/IgnoreFile\s+(.*)/) {
211 $ignorefile = $1;
212 }
213 if (/TargetFile\s+(.*)/) {
214 $targetfile = $1;
215 }
216 if (/TimeLimit\s+(.*)/) {
217 $TimeLimit = $1;
218 }
219 if (/HostIpAddr\s+(.*)/) {
220 $hostipaddr = $1;
221 }
222 if (/HostGatewayByte\s+(.*)/) {
223 $hostgatewaybyte = $1;
224 }
225 # if (/ipchainsPath\s+(.*)/) {
226 # $ipchains_path = $1;
227 # }
228 }
229 if ($interface eq "") {
230 die "Fatal! Interface is undefined.. Please define it in $opt_o with keyword Interface\n";
231 }
232 if ($alert_file eq "") {
233 print "Warning! AlertFile is undefined.. Assuming /var/log/snort.alert\n";
234 $alert_file="/var/log/snort.alert";
235 }
236 if ($hostipaddr eq "") {
237 print "Warning! HostIpAddr is undefined! Attempting to guess..\n";
238 $hostipaddr = &get_ip($interface);
239 print "Got it.. your HostIpAddr is $hostipaddr\n";
240 }
241 if ($ignorefile eq "") {
242 print "Warning! IgnoreFile is undefined.. going with default ignore list (hostname and gateway)!\n";
243 }
244 if ($hostgatewaybyte eq "") {
245 print "Warning! HostGatewayByte is undefined.. gateway will not be in ignore list!\n";
246 }
247 if ($logfile eq "") {
248 print "Warning! LogFile is undefined.. Assuming debug mode, output to STDOUT\n";
249 $opt_d = 1;
250 }
251 if (! -w $logfile) {
252 print "Warning! Logfile is not writeable! Engaging debug mode, output to STDOUT\n";
253 $opt_d = 1;
254 }
255 foreach $mypath (split (/:/, $ENV{PATH})) {
256 if (-x "$mypath/guardian_block.sh") {
257 $blockpath = "$mypath/guardian_block.sh";
258 }
259 if (-x "$mypath/guardian_unblock.sh") {
260 $unblockpath = "$mypath/guardian_unblock.sh";
261 }
262 }
263 if ($blockpath eq "") {
264 print "Error! Could not find guardian_block.sh. Please consult the README. \n";
265 exit;
266 }
267 if ($unblockpath eq "") {
268 print "Warning! Could not find guardian_unblock.sh. Guardian will not be\n";
269 print "able to remove blocked ip addresses. Please consult the README file\n";
270 }
271 if ($TimeLimit eq "") {
272 print "Warning! Time limit not defined. Defaulting to absurdly long time limit\n";
273 $TimeLimit = 999999999;
274 }
275 }
276
277
278 sub write_log {
279 my $message = $_[0];
280 if (defined($opt_d)) { # we are in debug mode, and not daemonized
281 print STDOUT $message;
282 } else {
283 open (LOG, ">>$logfile");
284 print LOG $message;
285 close (LOG);
286 }
287 }
288
289
290
291 sub daemonize {
292 my ($home);
293 if (fork()) {
294 # parent
295 exit(0);
296 } else {
297 # child
298 &write_log ("Guardian process id $$\n");
299 $home = (getpwuid($>))[7] || die "No home directory!\n";
300 chdir($home); # go to my homedir
301 setpgrp(0,0); # become process leader
302 close(STDOUT);
303 close(STDIN);
304 close(STDERR);
305 print "Testing...\n";
306 }
307 }
308
309 sub get_ip {
310 my ($interface) = $_[0];
311 my $ip;
312 open (IFCONFIG, "/bin/netstat -iee |grep $interface -A7 |");
313 while (<IFCONFIG>) {
314 if ($OS eq "FreeBSD") {
315 if (/inet (\d+\.\d+\.\d+\.\d+)/) {
316 $ip = $1;
317 }
318 }
319 if ($OS eq "Linux") {
320 if (/inet addr:(\d+\.\d+\.\d+\.\d+)/) {
321 $ip = $1;
322 }
323 }
324 }
325 close (IFCONFIG);
326 if ($ip eq "") { die "Couldn't figure out the ip address\n"; }
327 $ip;
328 }
329
330 sub sig_handler_setup {
331 $SIG{TERM} = \&clean_up_and_exit; # kill
332 $SIG{QUIT} = \&clean_up_and_exit; # kill -3
333 # $SIG{HUP} = \&flush_and_reload; # kill -1
334 }
335
336 sub remove_blocks {
337 my $source;
338 my $time = time();
339 foreach $source (keys %hash) {
340 if ($hash{$source} < $time) {
341 &call_unblock ($source, "expiring block of $source\n");
342 delete ($hash{$source});
343 }
344 }
345 }
346
347 sub call_unblock {
348 my ($source, $message) = @_;
349 &write_log ("$message");
350 system ("$unblockpath $source $interface");
351 }
352
353 sub clean_up_and_exit {
354 my $source;
355 &write_log ("received kill sig.. shutting down\n");
356 foreach $source (keys %hash) {
357 &call_unblock ($source, "removing $source for shutdown\n");
358 }
359 exit;
360 }
361
362 sub load_targetfile {
363 my $count = 0;
364 open (TARG, "$targetfile") or die "Cannot open $targetfile\n";
365 while (<TARG>) {
366 chop;
367 next if (/\#/); #skip comments
368 next if (/^\s*$/); # and blank lines
369 $targethash{$_}=1;
370 $count++;
371 }
372 close (TARG);
373 print "Loaded $count addresses from $targetfile\n";
374 }