File: //usr/syno/ipsec/libexec/verify
#!/usr/bin/python
#
# Copyright (C) 2012 Paul Wouters <pwouters@redhat.com>
#
# Based on old perl and shell code:
# Copyright (C) 2003 Sam Sgro <sam@freeswan.org>
# Copyright (C) 2005-2008 Michael Richardson <mcr@xelerance.com>
# Copyright (C) 2005-2009 Paul Wouters <paul@xelerance.com>
#
# Based on "verify" from the FreeS/WAN distribution, (C) 2001 Michael
# Richardson <mcr@freeswan.org>
#
# 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 <http://www.fsf.org/copyleft/gpl.txt>.
#
# 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.
import os, sys, subprocess, glob
retcode = 0
confdir = os.getenv("IPSEC_CONFS")
if not confdir:
# should get substituted by 'make programs'
confdir = "/usr/syno/ipsec/libexec"
ipsecbin = "/usr/sbin/ipsec"
if not os.path.isfile(ipsecbin):
# hopefully somewhere in our path then
ipsecbin = "ipsec"
if not os.path.isfile("%s/ipsec.conf"%confdir):
try:
p = subprocess.Popen("%s --config"%ipsecbin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
output = output.strip()
if os.path.isfile("%s/ipsec.conf"%output):
confdir = output
except:
sys.exit("Failed to find ipsec.conf")
# Should we print in colour by default?
colour = 0
try:
p = subprocess.Popen("consoletype", stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
output = output.strip()
if output in ("vc","tty","pty"):
colour = 1
except:
try:
p = subprocess.Popen("tput colors", stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if int(output) > 0:
colour = 1
except:
pass
def printfun(text):
# suppress newline
sys.stdout.write("%-50s"%text)
def print_result(rcode, rtext):
global colour
global retcode
OK = '\033[92m'
WARN = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
if rcode == "FAIL":
retcode = 1
if not rtext:
rtext = "FAILED"
if colour:
print "\t[%s%s%s]"%(FAIL,rtext,ENDC)
else:
print "\t[%s]"%rtext
elif rcode == "WARN":
# don't log warnings until we implemented IKE over TCP
# retcode = 1
if not rtext:
rtext = "WARNING"
if colour:
print "\t[%s%s%s]"%(WARN,rtext,ENDC)
else:
print "\t[%s]"%rtext
elif rcode == "OK":
if not rtext:
rtext = "OK"
if colour:
print "\t[%s%s%s]"%(OK,rtext,ENDC)
else:
print "\t[%s]"%rtext
else:
print "INTERNAL ERROR - unknown rcode:%s"%rcode
# 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 dns zone: $_[0]";
# deprecated;
# }
#
#
# printfun " Looking for TXT in forward dns zone: $_[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 dns zone: $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 dns zone: $gwrev";
# deprecated;
# $print_deprecated = 1;
#
# }
# # 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 dns zone: $gwrev";
# run "host -t txt $gwrev";
# ($txtpresent)=grep /X-IPsec-Server/,@out;
# errchk "$txtpresent", "MISSING";
# }
#
# }
#}
def plutocheck():
printfun("Checking that pluto is running")
p = subprocess.Popen(["pidof", "pluto"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if not output:
print_result("FAIL","FAILED")
return
else:
print_result("OK","OK")
# only if pluto is running, do the listen tests
udp500check()
tcp500check()
udp4500check()
tcp4500check()
tcp10000check()
# This is pretty broken, as you can enable forwarding specificaly via iptables as well
# It also won't find/exclude all kinds of interfaces. Also, lots of people have one
# ethernet and one wifi interface, but don't want to bridge these.
# So, mostly left here for historic reasons - candidate to be changed/removed
def forwardcheck():
try:
output = open("/proc/net/dev","r").read().strip()
except:
printfun("Checking for multiple interfaces")
print_result("FAIL","UNEXPECTED KERNEL DEV LIST")
count = 0
for line in output.split("\n"):
if ":" in line:
if not "lo:" in line and not "ipsec" in line and not "mast" in line and not "virbr:" in line:
# let's count this as a real physical interface
count += 1
if count > 1:
printfun("Two or more interfaces found, checking IP forwarding")
try:
output = open("/proc/sys/net/ipv4/ip_forward","r").read().strip()
except:
print_result("FAIL","MISSING ip_forward proc file")
return
if output == "1":
print_result("OK","OK")
else:
print_result("FAIL","FAILED")
def rpfiltercheck():
fail = 0
printfun("Checking rp_filter")
for dirname in glob.glob("/proc/sys/net/ipv4/conf/*"):
val = open("%s/rp_filter"%dirname,"r").read().strip()
if val == "1":
if fail == 0:
print_result("FAIL","ENABLED")
fail = 1
printfun(" %s/rp_filter"%dirname)
print_result("FAIL","ENABLED")
if fail == 0:
print_result("OK","OK")
# Check if any NAT or MASQUERADE would accidentally mess up our packets
def natcheck():
printfun("Checking NAT and MASQUERADEing")
print_result("WARN","TEST INCOMPLETE")
# run iptables -t nat -L -n
# grep for NAT or MASQ
# for KLIPS
# grep over /proc/net/ipsec_eroute check for 'tun0x' then find overlap
# for NETKEY
# grep over ip xfrm state/policy and find overlap
def cmdchecks():
printfun("Checking 'ip' command")
if not os.path.isfile("/sbin/ip") and not os.path.isfile("/usr/sbin/ip"):
print_result("FAIL","FAILED")
p = subprocess.Popen(["ip", "xfrm"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if not "XFRM-OBJECT" in err:
print_result("FAIL","IP XFRM BROKEN")
else:
print_result("OK","OK")
printfun("Checking 'iptables' command")
if not os.path.isfile("/sbin/iptables") and not os.path.isfile("/usr/sbin/iptables"):
print_result("WARN","MISSING")
else:
print_result("OK","OK")
def udp500check():
printfun(" Pluto listening for IKE on udp 500")
try:
p = subprocess.Popen(["ss", "-n", "-l", "-u", "sport = :500"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if ":500" in output:
print_result("OK","OK")
else:
print_result("FAIL","FAILED")
except:
print_result("FAIL","FAILED")
# not yet implemented in *swan
def tcp500check():
printfun(" Pluto listening for IKE on tcp 500")
try:
p = subprocess.Popen(["ss", "-n", "-l", "-t", "sport = :500"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if ":500" in output:
print_result("OK","OK")
else:
print_result("WARN","NOT IMPLEMENTED")
except:
print_result("WARN","NOT IMPLEMENTED")
def udp4500check():
printfun(" Pluto listening for IKE/NAT-T on udp 4500")
try:
p = subprocess.Popen(["ss", "-n", "-l", "-u", "sport = :4500"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if ":4500" in output:
print_result("OK","OK")
else:
print_result("FAIL","DISABLED")
except:
print_result("FAIL","DISABLED")
# not yet implemented in *swan
def tcp4500check():
printfun(" Pluto listening for IKE/NAT-T on tcp 4500")
try:
p = subprocess.Popen(["ss", "-n", "-l", "-t", "sport = :4500"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if ":4500" in output:
print_result("OK","OK")
else:
print_result("WARN","NOT IMPLEMENTED")
except:
print_result("WARN","NOT IMPLEMENTED")
# not yet implemented in *swan
def tcp10000check():
printfun(" Pluto listening for IKE on tcp 10000 (cisco)")
try:
p = subprocess.Popen(["ss", "-n", "-l", "-t", "sport = :10000"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if ":10000" in output:
print_result("OK","OK")
else:
print_result("WARN","NOT IMPLEMENTED")
except:
print_result("WARN","NOT IMPLEMENTED")
def installstartcheck():
print "Checking if IPsec got installed and started correctly:\n"
printfun("Version check and ipsec on-path")
try:
p = subprocess.Popen(["ipsec", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if "swan" in output:
print_result("OK","OK")
print output.replace("Linux","").strip()
else:
print_result("FAIL","FAILED")
except:
print_result("FAIL","FAILED")
printfun("Checking for IPsec support in kernel")
if not os.path.isfile("/proc/net/ipsec_eroute") and not os.path.isfile("/proc/net/pfkey"):
print_result("FAIL","FAILED")
if "no kernel code presently loaded" in output:
print("\n The ipsec service should be started before running 'ipsec verify'\n")
return
else:
print_result("OK","OK")
# we know we have some stack, so continue
if os.path.isfile("/proc/net/ipsec_eroute"):
installcheckklips()
else:
installchecknetkey()
def installcheckklips():
# NAT-T patch check
printfun(" KLIPS: checking for NAT Traversal support")
try:
natt = open("/sys/module/ipsec/parameters/natt_available","r").read().strip()
if natt == "1":
print_result("WARN","OLD STYLE")
elif natt == "2":
print_result("OK","OK")
else:
print_result("FAIL","UNKNOWN CODE")
except:
print_result("WARN","OLD/MISSING")
# OCF patch check
printfun(" KLIPS: checking for OCF crypto offload support ")
try:
ocf = open("/sys/module/ipsec/parameters/ocf_available","r").read().strip()
if ocf == "1":
print_result("OK","OK")
else:
print_result("FAIL","UNKNOWN CODE")
except:
print_result("WARN","N/A")
# SAref patch check
saref = ""
printfun(" KLIPS: IPsec SAref kernel support")
try:
saref = open("/proc/net/ipsec/saref","r").read().strip()
if "refinfo patch applied" in saref:
print_result("OK","OK")
else:
print_result("WARN","N/A")
except:
print_result("WARN","N/A")
# SAref bind patch check
printfun(" KLIPS: IPsec SAref Bind kernel support")
if "bindref patch applied" in saref:
print_result("OK","OK")
else:
print_result("WARN","N/A")
def installchecknetkey():
print (" NETKEY: Testing XFRM related proc values")
for option in ( "send_redirects", "accept_redirects"):
printfun(" ICMP default/%s"%option)
try:
redir = open("/proc/sys/net/ipv4/conf/default/%s"%option,"r").read().strip()
except:
print_result("FAIL","VERY BROKEN KERNEL")
return
if redir == "0":
print_result("OK","OK")
else:
print_result("FAIL","NOT DISABLED")
print("\n Disable /proc/sys/net/ipv4/conf/*/%s or NETKEY will cause act on or cause sending of bogus ICMP redirects!\n"%option)
printfun(" XFRM larval drop")
try:
larval = open("/proc/sys/net/core/xfrm_larval_drop","r").read().strip()
except:
print_result("FAIL","OLD OR BROKEN KERNEL")
return
if larval == "1":
print_result("OK","OK")
else:
print_result("FAIL","NOT ENABLED")
def randomdevcheck():
printfun("Hardware random device check")
if os.path.isfile("/dev/hw_random") or os.path.isfile("/dev/hwrng"):
p = subprocess.Popen(["pidof", "rngd"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if not output:
p = subprocess.Popen(["pidof", "clrngd"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
if not output:
print_result("FAIL","NO RUNNING (cl)rngd DAEMON FOR HW")
else:
print_result("OK","CLRNGD")
else:
print_result("OK","RNGD")
else:
print_result("OK","N/A")
def main():
installstartcheck()
randomdevcheck()
forwardcheck()
rpfiltercheck()
plutocheck()
natcheck()
cmdchecks()
if retcode:
sys.exit("\nipsec verify: encountered errors")
if __name__ == "__main__":
main()
## old perl code not yet ported
##
## sub checktunnel {
## $csource=$_[0]; $cdest=$_[1]; $ctun=$_[2]; $all="0.0.0.0/0";
##
## printfun "Checking $ctun from $csource to $cdest";
## run "iptables -t nat -L POSTROUTING -n";
## @out=grep !/(Chain POSTROUTING|target)/, @out;
## foreach (@out) {
## ( $target, $prot, $opt, $source, $dest ) = split(' ',$_);
## if(((($source eq $csource) || ($source eq $all)) && (($dest eq $cdest) || ($dest = $all))) && $target eq "ACCEPT")
## {
## errchk "@out";
## $reterr = 1;
## }
## else
## {
## @err="$target from $source to $dest kills tunnel $source -> $cdest\n";
## errchk "","FAILED";
## $reterr = 1;
## }
## }
## }
##
## printfun "Checking NAT and MASQUERADEing";
## # This assumes KLIPS eroute information, we should add support
## # for NETKEY, but ip xfrm is very annoying to parse
## if(( -e "/proc/net/nf_conntrack" || -e "/proc/net/ip_conntrack")
## && -e "/proc/net/ipsec_eroute" )
## {
## run "iptables -t nat -L -n";
## if(grep /(NAT|MASQ)/, @out)
## {
## printf "\n";
## open("cat", "/proc/net/ipsec_eroute");
## foreach(grep /tun0x/, <cat>)
## {
## @eroute=split(' ',$_);
## checktunnel $eroute[1], $eroute[3], $eroute[5];
## }
## }
## else
## {
## errchk "1";
## }
## }
## else
## {
## errchk "OK";
## }
## }
## }
##
##
## 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 $_;
## }
## }
##
##
## # 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;
## cmdchecks;
## run "ipsec addconn --configsetup";
## ($oe)=grep(/oe=\'yes\'/, @out);
## if( $oe) {
## dnschecks;
## if($print_deprecated)
## {
## print "
##
## 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.\n";
## }
## } else {
## printfun "Opportunistic Encryption Support";
## warnchk "", "DISABLED";
## }
## }
## # finally, run the config file through the parser checks
## run "ipsec auto addconn --checkconfig";
## }
## exit $reterr