#!/usr/bin/perl # mail OE DNS RR info to relevent administrator # # Copyright (C) 2003 Sam Sgro # # Based on "verify" from the FreeS/WAN distribution, (C) 2001 Michael # Richardson # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. See . # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. $me="ipsec verify"; $ENV{'PATH'}="/sbin:/usr/bin:/usr/local/sbin:@IPSEC_SBINDIR@:$ENV{'PATH'}"; if($ENV{'IPSEC_CONFS'}) { $conf=$ENV{'IPSEC_CONFS'} } else { $conf= `ipsec --confdir`; chomp($conf); } # Should we print in colour by default? $ctype=`/sbin/consoletype`; if ( $ctype && !($ctype eq "serial")) { $colour="1"; } sub printfun { print sprintf("%-65s",@_); } # capture STDOUT as @out, STDERR as @err with no temp files. sub run { $command=shift; pipe child_read, parent_write; pipe parent_read, child_write; pipe err_read, err_write; $mypid=fork; if($mypid) { close child_write; close err_write; @out=; @err=; } else { close parent_read; close parent_write; open STDOUT,">&child_write"; open STDERR,">&err_write"; exec $command; print STDERR "Cannot execute command \"$command\": $!\n"; } } # Code to print out [OK], [FAILED]. sub errchk { if (!shift(@_)) { print "\t["; if($colour) { print "\e[1;31m"; } if(@_) { print "@_"; } else { print "FAILED"; } if($colour) { print "\e[0;39m"; } print "]\n"; if(@err) { print @err; } } else { print "\t["; if($colour) { print "\e[1;32m"; } print "OK"; if($colour) { print "\e[0;39m"; } print "]\n"; } } # Code to print out [DEPRECATED] and key restrict message sub deprecated { print "\t["; if($colour) { print "\e[1;33m"; } print "DEPRECATED"; if($colour) { print "\e[0;39m"; } print "]\n RFC 3445 restricts the use of the KEY RR to DNSSEC applications. The use of a KEY record sub-type for Opporunistic Encryption (OE) has been deprecated. TXT records are used to provide all OE functionality. For more information on these changes, see: http://www.freeswan.org/freeswan_snaps/CURRENT-SNAP/doc/upgrading.html\n\n"; } # Verification routines begin here... # # Check DNS Configuration based on a hostname # $1 = Hostname (string) # eg: checkdnshost oetest.freeswan.org sub checkdnshost { run "host -t key $_[0]"; ($keypresent)=grep /(0x4200|16896)/, @out; if($keypresent) { printfun "Looking for KEY in forward map: $_[0]"; deprecated; } printfun "Looking for TXT in forward map: $_[0]"; run "host -t txt $_[0]"; ($txtpresent)=grep /X-IPsec-Server/,@out; errchk "$txtpresent", "MISSING"; } # Check DNS Configuration based on IP address # $1 = IP Address (string) # eg: checkdnsip 127.0.0.2 sub checkdnsip { $fortxt=$_[0]; $revtxt=join('.',reverse(split(/\./, $fortxt))).".in-addr.arpa."; printfun "Looking for TXT in reverse map: $revtxt"; run "host -t txt $revtxt"; ($txtpresent)=grep /X-IPsec-Server/,@out; errchk "$txtpresent", "MISSING"; if($txtpresent) { $txtpresent=~ s/.*X-IPsec-Server\([0-9].*\)=//; $txtpresent=~ s/[\"\ ].*//; $gwip=$txtpresent; chomp($gwip); $gwrev=join('.',reverse(split(/\./, $gwip))).".in-addr.arpa."; # Check for a KEY record for the indicated IPSec GW. run "host -t key $gwrev"; ($keypresent)=grep /(0x4200|16896)/, @out; if($keypresent) { printfun "Looking for KEY in reverse map: $gwrev"; deprecated; } # If the host is its own gateway, then we know we've got a TXT record. if($gwip ne $fortxt) { printfun "Looking for TXT in reverse map: $gwrev"; run "host -t txt $gwrev"; ($txtpresent)=grep /X-IPsec-Server/,@out; errchk "$txtpresent", "MISSING"; } } } sub udp500check { run "netstat -an"; # Flaw: Only taking the first IP address found listening... ($listen)=grep /:500/, @out; if(!$listen) { printfun "Pluto not listening on port udp 500. Check interfaces defintion in ipsec.conf.\n"; } else { ( $rest, $rest, $rest, $defaultsrcip, $rest)=split(' ',$listen); $defaultsrcip=~ s/:500//; run "/bin/ls /proc/sys/net/ipv4/conf"; foreach $net (grep !/(lo|all|default|ipsec|vmnet)/, @check=@out) { if ( -e "/proc/net/ip_fwchains" ) { chomp($net); printfun "Checking IPchains port 500 hole ($defaultsrcip on $net)"; run "ipchains --check input -p udp --src $defaultsrcip 500 --dst 2.3.4.5 500 -i $net"; chomp($out[0]); if( $out[0] eq "accepted" ) { errchk "$out[0]"; } else { errchk "","BLOCKED"; } } elsif( -e "/proc/net/ip_tables_names" ) { # iptables --check will never happen, hack it outselves # use tcpdump? nmap? custom rule? at least check outgoing 500? ping outside activeOE box? # print "Cannot check if IPtables has port 500 hole ($defaultsrcip on $net)"; } } } } sub checktunnel { $csource=$_[0]; $cdest=$_[1]; $ctun=$_[2]; $all="0.0.0.0/0"; printfun "Checking $ctun from $csource to $cdest"; $check=""; run "iptables -t nat -L POSTROUTING -n"; @out=grep !/(Chain POSTROUTING|target)/, @out; foreach (@out) { ( $target, $prot, $opt, $source, $dest ) = split(' ',$_); # iptables does not append /32 netmasks for single hosts. if (!grep /\//, $dest) { $dest ="$dest"."/32"; } if(((($source eq $csource) || ($source eq $all)) && (($dest eq $cdest) || ($dest eq $all))) && ($target eq "MASQUERADE" || $target eq "SNAT")) { @err="$target from $source to $dest kills tunnel $csource -> $cdest\n"; $check="FAILED"; } } if($check eq "FAILED") { errchk "",$check; print "\n"; } else { errchk "@out"; } return $check; } sub installstartcheck { print "Checking your system to see if IPsec got installed and started correctly:\n"; printfun "Version check and ipsec on-path"; run "ipsec --version"; if(!@err) { errchk "@out"; print grep /Linux/, @out; } else { # No reason to receive standard error from this command. If we do, abort! print " \"ipsec --version\" failed! Often, failure means we cannot find the master \"ipsec\" script on your PATH. Here's the error message: "; print @err; print " Verify that FreeS/WAN is installed correctly,\n"; exit 1; } $kernelcheck="Checking for IPsec kernel support"; if ( -e "/proc/net/ipsec_eroute" ) { $kernelcheck.=": found KLIPS"; $support="1"; } elsif ( -e "/proc/net/pfkey" ) { $kernelcheck.=": found 2.6 native"; $support="1"; } # Print out kernel code in use instead of OK. printfun "$kernelcheck"; errchk "$support"; # Wouldn't this test fail if your mucked up your interface definition? printfun "Checking that pluto is running"; run "ipsec whack --status"; errchk "@out"; if (grep /interface/, @out) { udp500check; } } sub tunnelchecks { open("dev", "/proc/net/dev"); if((grep !/(ipsec|lo:|Inter|packets)/, ) > 1) { printfun "Two or more interfaces found, checking IP forwarding"; open("cat", "/proc/sys/net/ipv4/ip_forward"); if( == "0") { @err="If you intend to tunnel packets on behalf of other machines, then set\n/proc/sys/net/ipv4/ip_forward to 1."; errchk ""; } else { errchk "@out"; printfun "Checking NAT and MASQUERADEing"; if( -e "/proc/net/ip_conntrack" ) { run "iptables -t nat -L -n"; if(grep /(NAT|MASQ)/, @out) { printf "\n"; $check=""; open("cat", "/proc/net/ipsec_eroute"); foreach(grep /tun0x/, ) { @eroute=split(' ',$_); $check = "$check".checktunnel $eroute[1], $eroute[3], $eroute[5]; } if(grep /FAILED/,$check) { print " NATting could potentially kill some of your existing tunnels. For more information, visit the following URL: http://lists.freeswan.org/pipermail/users/2002-August/012918.html\n"; } } else { errchk "1"; } } else { errchk "","N/A"; } } } } sub dnschecks { # Check the running hostname. printf "\nOpportunistic Encryption DNS checks:\n"; run "hostname"; ($hostname)=@out; chomp $hostname; checkdnshost $hostname; # Check all the public IP addresses... run "/sbin/ifconfig -a"; foreach (grep /inet addr/,@out) { $_=~ s/^\s*//; @temp=split(/[:\ ]+/, $_); push(@address,$temp[2]); } # Purge all non-routeable IPs... @address=grep !/^(127.*|10.*|172.1[6789]+.*.*|172.2+.*.*|172.3[01]+.*.*|192.168.*.*|169.254.*.*)/,@address; printfun "Does the machine have at least one non-private address?"; errchk @address; foreach(@address=grep !$check{$_}++,@address) { checkdnsip $_; } } # Main function begins here! # Harvest options, ensure they're valid. use Getopt::Long; %optctl = ("host" => \$hostname,"ip" => \$ip, "colour" => \$colour); GetOptions(\%optctl, "host=s" ,"ip=s", "colour!"); # Exit if we get passed an option we don't recognize. if($Getopt::Long::error) { exit; } # If you've passed --host or --ip, do only those checks. if($hostname || $ip) { # Check this --host for OE. if($hostname) { printf "Checking $hostname for Opportunistic Encryption:\n"; checkdnshost $hostname; run "host -t A $hostname"; if(($ipaddr) = grep (/address/i, @out)) { $ipaddr=~ s/.*address\ //; chomp $ipaddr; checkdnsip $ipaddr; } else { printf "$hostname does not resolve to an IP, no reverse lookup tests possible.\n"; } } # Check this IP for OE. if($ip) { printf "Checking IP $ip for Opportunistic Encryption:\n"; checkdnsip $ip; } } else { # Call the default routines... # Root check... if($> != "0") { print "To check this machine, you need to run \"$me\" as root.\n"; exit; } else { installstartcheck; tunnelchecks; dnschecks; } }