#!/usr/bin/perl ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2007 Michael Tremer & Christian Schmidt # # # # 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 3 of the License, or # # (at your option) any later version. # # # # 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. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### use Net::DNS; use File::Copy; use File::Temp qw/ tempfile tempdir /; use strict; # enable only the following on debugging purpose #use warnings; #use CGI::Carp 'fatalsToBrowser'; require '/var/ipfire/general-functions.pl'; require "${General::swroot}/lang.pl"; require "${General::swroot}/header.pl"; require "${General::swroot}/countries.pl"; #workaround to suppress a warning when a variable is used only once my @dummy = ( ${Header::colourgreen}, ${Header::colourblue} ); undef (@dummy); ### ### Initialize variables ### my $sleepDelay = 4; # after a call to ipsecctrl S or R, wait this delay (seconds) before reading status # (let the ipsec do its job) my %netsettings=(); our %cgiparams=(); our %vpnsettings=(); my %checked=(); my %confighash=(); my %cahash=(); my %selected=(); my $warnmessage = ''; my $errormessage = ''; my %color = (); my %mainsettings = (); &General::readhash("${General::swroot}/main/settings", \%mainsettings); &General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color); &General::readhash("${General::swroot}/ethernet/settings", \%netsettings); $cgiparams{'ENABLED'} = 'off'; $cgiparams{'EDIT_ADVANCED'} = 'off'; $cgiparams{'ACTION'} = ''; $cgiparams{'CA_NAME'} = ''; $cgiparams{'DBG_CRYPT'} = ''; $cgiparams{'DBG_PARSING'} = ''; $cgiparams{'DBG_EMITTING'} = ''; $cgiparams{'DBG_CONTROL'} = ''; $cgiparams{'DBG_KLIPS'} = ''; $cgiparams{'DBG_DNS'} = ''; $cgiparams{'DBG_NAT_T'} = ''; $cgiparams{'KEY'} = ''; $cgiparams{'TYPE'} = ''; $cgiparams{'ADVANCED'} = ''; $cgiparams{'INTERFACE'} = ''; $cgiparams{'NAME'} = ''; $cgiparams{'LOCAL_SUBNET'} = ''; $cgiparams{'REMOTE_SUBNET'} = ''; $cgiparams{'REMOTE'} = ''; $cgiparams{'LOCAL_ID'} = ''; $cgiparams{'REMOTE_ID'} = ''; $cgiparams{'REMARK'} = ''; $cgiparams{'PSK'} = ''; $cgiparams{'CERT_NAME'} = ''; $cgiparams{'CERT_EMAIL'} = ''; $cgiparams{'CERT_OU'} = ''; $cgiparams{'CERT_ORGANIZATION'} = ''; $cgiparams{'CERT_CITY'} = ''; $cgiparams{'CERT_STATE'} = ''; $cgiparams{'CERT_COUNTRY'} = ''; $cgiparams{'SUBJECTALTNAME'} = ''; $cgiparams{'CERT_PASS1'} = ''; $cgiparams{'CERT_PASS2'} = ''; $cgiparams{'ROOTCERT_HOSTNAME'} = ''; $cgiparams{'ROOTCERT_COUNTRY'} = ''; $cgiparams{'P12_PASS'} = ''; $cgiparams{'ROOTCERT_ORGANIZATION'} = ''; $cgiparams{'ROOTCERT_HOSTNAME'} = ''; $cgiparams{'ROOTCERT_EMAIL'} = ''; $cgiparams{'ROOTCERT_OU'} = ''; $cgiparams{'ROOTCERT_CITY'} = ''; $cgiparams{'ROOTCERT_STATE'} = ''; &Header::getcgihash(\%cgiparams, {'wantfile' => 1, 'filevar' => 'FH'}); ### ### Useful functions ### sub valid_dns_host { my $hostname = $_[0]; unless ($hostname) { return "No hostname"}; my $res = new Net::DNS::Resolver; my $query = $res->search("$hostname"); if ($query) { foreach my $rr ($query->answer) { ## Potential bug - we are only looking at A records: return 0 if $rr->type eq "A"; } } else { return $res->errorstring; } } ### ### Just return true is one interface is vpn enabled ### sub vpnenabled { return ($vpnsettings{'ENABLED'} eq 'on'); } ### ### old version: maintain serial number to one, without explication. ### this : let the counter go, so that each cert is numbered. ### sub cleanssldatabase { if (open(FILE, ">${General::swroot}/certs/serial")) { print FILE "01"; close FILE; } if (open(FILE, ">${General::swroot}/certs/index.txt")) { print FILE ""; close FILE; } unlink ("${General::swroot}/certs/index.txt.old"); unlink ("${General::swroot}/certs/serial.old"); unlink ("${General::swroot}/certs/01.pem"); } sub newcleanssldatabase { if (! -s "${General::swroot}/certs/serial" ) { open(FILE, ">${General::swroot}/certs/serial"); print FILE "01"; close FILE; } if (! -s ">${General::swroot}/certs/index.txt") { system ("touch ${General::swroot}/certs/index.txt"); } unlink ("${General::swroot}/certs/index.txt.old"); unlink ("${General::swroot}/certs/serial.old"); # unlink ("${General::swroot}/certs/01.pem"); numbering evolves. Wrong place to delete } ### ### Call openssl and return errormessage if any ### sub callssl ($) { my $opt = shift; my $retssl = `/usr/bin/openssl $opt 2>&1`; #redirect stderr my $ret = ''; foreach my $line (split (/\n/, $retssl)) { &General::log("ipsec", "$line") if (0); # 1 for verbose logging $ret .= '
'.$line if ( $line =~ /error|unknown/ ); } if ($ret) { $ret= &Header::cleanhtml($ret); } return $ret ? "$Lang::tr{'openssl produced an error'}: $ret" : '' ; } ### ### Obtain a CN from given cert ### sub getCNfromcert ($) { #&General::log("ipsec", "Extracting name from $_[0]..."); my $temp = `/usr/bin/openssl x509 -text -in $_[0]`; $temp =~ /Subject:.*CN=(.*)[\n]/; $temp = $1; $temp =~ s+/Email+, E+; $temp =~ s/ ST=/ S=/; $temp =~ s/,//g; $temp =~ s/\'//g; return $temp; } ### ### Obtain Subject from given cert ### sub getsubjectfromcert ($) { #&General::log("ipsec", "Extracting subject from $_[0]..."); my $temp = `/usr/bin/openssl x509 -text -in $_[0]`; $temp =~ /Subject: (.*)[\n]/; $temp = $1; $temp =~ s+/Email+, E+; $temp =~ s/ ST=/ S=/; return $temp; } ### ### Combine local subnet and connection name to make a unique name for each connection section ### (this sub is not used now) ### sub makeconnname ($) { my $conn = shift; my $subnet = shift; $subnet =~ /^(.*?)\/(.*?)$/; # $1=IP $2=mask my $ip = unpack('N', &Socket::inet_aton($1)); if (length ($2) > 2) { my $mm = unpack('N', &Socket::inet_aton($2)); while ( ($mm & 1)==0 ) { $ip >>= 1; $mm >>= 1; }; } else { $ip >>= (32 - $2); } return sprintf ("%s-%X", $conn, $ip); } ### ### Write a config file. ### ###Type=Host : GUI can choose the interface used (RED,GREEN,BLUE) and ### the side is always defined as 'left'. ### configihash[14]: 'VHOST' is allowed ### sub writeipsecfiles { my %lconfighash = (); my %lvpnsettings = (); &General::readhasharray("${General::swroot}/vpn/config", \%lconfighash); &General::readhash("${General::swroot}/vpn/settings", \%lvpnsettings); open(CONF, ">${General::swroot}/vpn/ipsec.conf") or die "Unable to open ${General::swroot}/vpn/ipsec.conf: $!"; open(SECRETS, ">${General::swroot}/vpn/ipsec.secrets") or die "Unable to open ${General::swroot}/vpn/ipsec.secrets: $!"; flock CONF, 2; flock SECRETS, 2; print CONF "version 2\n\n"; print CONF "config setup\n"; #create an ipsec Interface for each 'enabled' ones #loop trought configuration and add physical interfaces to the list my $interfaces = "\tinterfaces=\""; foreach my $key (keys %lconfighash) { next if ($lconfighash{$key}[0] ne 'on'); $interfaces .= "%defaultroute " if ($interfaces !~ /defaultroute/ && $lconfighash{$key}[26] eq 'RED'); $interfaces .= "ipsec1=$netsettings{'GREEN_DEV'} " if ($interfaces !~ /ipsec1/ && $lconfighash{$key}[26] eq 'GREEN'); $interfaces .= "ipsec2=$netsettings{'BLUE_DEV'} " if ($interfaces !~ /ipsec2/ && $lconfighash{$key}[26] eq 'BLUE'); $interfaces .= "ipsec3=$netsettings{'ORANGE_DEV'} " if ($interfaces !~ /ipsec3/ && $lconfighash{$key}[26] eq 'ORANGE'); } print CONF $interfaces . "\"\n"; my $plutodebug = ''; # build debug list map ($plutodebug .= $lvpnsettings{$_} eq 'on' ? lc (substr($_,4)).' ' : '', ('DBG_CRYPT','DBG_PARSING','DBG_EMITTING','DBG_CONTROL', 'DBG_KLIPS','DBG_DNS','DBG_NAT_T')); $plutodebug = 'none' if $plutodebug eq ''; # if nothing selected, use 'none'. print CONF "\tklipsdebug=\"none\"\n"; print CONF "\tplutodebug=\"$plutodebug\"\n"; # deprecated in ipsec.conf version 2 #print CONF "\tplutoload=%search\n"; #print CONF "\tplutostart=%search\n"; print CONF "\tuniqueids=yes\n"; print CONF "\tnat_traversal=yes\n"; print CONF "\toverridemtu=$lvpnsettings{'VPN_OVERRIDE_MTU'}\n" if ($lvpnsettings{'VPN_OVERRIDE_MTU'} ne ''); print CONF "\tvirtual_private=%v4:10.0.0.0/8,%v4:172.16.0.0/12,%v4:192.168.0.0/16"; print CONF ",%v4:!$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"; if (length($netsettings{'ORANGE_DEV'}) > 2) { print CONF ",%v4:!$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"; } if (length($netsettings{'BLUE_DEV'}) > 2) { print CONF ",%v4:!$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"; } foreach my $key (keys %lconfighash) { if ($lconfighash{$key}[3] eq 'net') { print CONF ",%v4:!$lconfighash{$key}[11]"; } } print CONF "\n\n"; print CONF "conn %default\n"; print CONF "\tkeyingtries=0\n"; print CONF "\tdisablearrivalcheck=no\n"; print CONF "\n"; if (-f "${General::swroot}/certs/hostkey.pem") { print SECRETS ": RSA ${General::swroot}/certs/hostkey.pem\n" } my $last_secrets = ''; # old the less specifics connections foreach my $key (keys %lconfighash) { next if ($lconfighash{$key}[0] ne 'on'); #remote peer is not set? => use '%any' $lconfighash{$key}[10] = '%any' if ($lconfighash{$key}[10] eq ''); my $localside; if ($lconfighash{$key}[26] eq 'BLUE') { $localside = $netsettings{'BLUE_ADDRESS'}; } elsif ($lconfighash{$key}[26] eq 'GREEN') { $localside = $netsettings{'GREEN_ADDRESS'}; } elsif ($lconfighash{$key}[26] eq 'ORANGE') { $localside = $netsettings{'ORANGE_ADDRESS'}; } else { # it is RED $localside = $lvpnsettings{'VPN_IP'}; } print CONF "conn $lconfighash{$key}[1]\n"; print CONF "\tleft=$localside\n"; print CONF "\tleftnexthop=%defaultroute\n" if ($lconfighash{$key}[26] eq 'RED' && $lvpnsettings{'VPN_IP'} ne '%defaultroute'); print CONF "\tleftsubnet=$lconfighash{$key}[8]\n"; print CONF "\tright=$lconfighash{$key}[10]\n"; if ($lconfighash{$key}[3] eq 'net') { print CONF "\trightsubnet=$lconfighash{$key}[11]\n"; print CONF "\trightnexthop=%defaultroute\n"; } elsif ($lconfighash{$key}[10] eq '%any' && $lconfighash{$key}[14] eq 'on') { #vhost allowed for roadwarriors? print CONF "\trightsubnet=vhost:%no,%priv\n"; } # Local Cert and Remote Cert (unless auth is DN dn-auth) if ($lconfighash{$key}[4] eq 'cert') { print CONF "\tleftcert=${General::swroot}/certs/hostcert.pem\n"; print CONF "\trightcert=${General::swroot}/certs/$lconfighash{$key}[1]cert.pem\n" if ($lconfighash{$key}[2] ne '%auth-dn'); } # Local and Remote IDs print CONF "\tleftid=\"$lconfighash{$key}[7]\"\n" if ($lconfighash{$key}[7]); print CONF "\trightid=\"$lconfighash{$key}[9]\"\n" if ($lconfighash{$key}[9]); # Algorithms if ($lconfighash{$key}[18] && $lconfighash{$key}[19] && $lconfighash{$key}[20]) { print CONF "\tike="; my @encs = split('\|', $lconfighash{$key}[18]); my @ints = split('\|', $lconfighash{$key}[19]); my @groups = split('\|', $lconfighash{$key}[20]); my $comma = 0; foreach my $i (@encs) { foreach my $j (@ints) { foreach my $k (@groups) { if ($comma != 0) { print CONF ","; } else { $comma = 1; } print CONF "$i-$j-modp$k"; } } } if ($lconfighash{$key}[24] eq 'on') { #only proposed algorythms? print CONF "!\n"; } else { print CONF "\n"; } } if ($lconfighash{$key}[21] && $lconfighash{$key}[22]) { print CONF "\tesp="; my @encs = split('\|', $lconfighash{$key}[21]); my @ints = split('\|', $lconfighash{$key}[22]); my $comma = 0; foreach my $i (@encs) { foreach my $j (@ints) { if ($comma != 0) { print CONF ","; } else { $comma = 1; } print CONF "$i-$j"; } } if ($lconfighash{$key}[24] eq 'on') { #only proposed algorythms? print CONF "!\n"; } else { print CONF "\n"; } } if ($lconfighash{$key}[23]) { print CONF "\tpfsgroup=$lconfighash{$key}[23]\n"; } # Lifetimes print CONF "\tikelifetime=$lconfighash{$key}[16]h\n" if ($lconfighash{$key}[16]); print CONF "\tkeylife=$lconfighash{$key}[17]h\n" if ($lconfighash{$key}[17]); # Aggresive mode print CONF "\taggrmode=yes\n" if ($lconfighash{$key}[12] eq 'on'); # Compression print CONF "\tcompress=yes\n" if ($lconfighash{$key}[13] eq 'on'); # Dead Peer Detection print CONF "\tdpddelay=30\n"; print CONF "\tdpdtimeout=120\n"; print CONF "\tdpdaction=$lconfighash{$key}[27]\n"; # Disable pfs ? print CONF "\tpfs=". ($lconfighash{$key}[28] eq 'on' ? "yes\n" : "no\n"); # Build Authentication details: LEFTid RIGHTid : PSK psk my $psk_line; if ($lconfighash{$key}[4] eq 'psk') { $psk_line = ($lconfighash{$key}[7] ? $lconfighash{$key}[7] : $localside) . " " ; $psk_line .= $lconfighash{$key}[9] ? $lconfighash{$key}[9] : $lconfighash{$key}[10]; #remoteid or remote address? $psk_line .= " : PSK '$lconfighash{$key}[5]'\n"; # if the line contains %any, it is less specific than two IP or ID, so move it at end of file. if ($psk_line =~ /%any/) { $last_secrets .= $psk_line; } else { print SECRETS $psk_line; } print CONF "\tauthby=secret\n"; } else { print CONF "\tauthby=rsasig\n"; print CONF "\tleftrsasigkey=%cert\n"; print CONF "\trightrsasigkey=%cert\n"; } # Automatically start only if a net-to-net connection if ($lconfighash{$key}[3] eq 'host') { print CONF "\tauto=add\n"; } else { print CONF "\tauto=start\n"; } print CONF "\n"; }#foreach key print SECRETS $last_secrets if ($last_secrets); close(CONF); close(SECRETS); } ### ### Save main settings ### if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} && $cgiparams{'TYPE'} eq '' && $cgiparams{'KEY'} eq '') { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); unless (&General::validfqdn($cgiparams{'VPN_IP'}) || &General::validip($cgiparams{'VPN_IP'}) || $cgiparams{'VPN_IP'} eq '%defaultroute' ) { $errormessage = $Lang::tr{'invalid input for hostname'}; goto SAVE_ERROR; } unless ($cgiparams{'VPN_DELAYED_START'} =~ /^[0-9]{1,3}$/ ) { #allow 0-999 seconds ! $errormessage = $Lang::tr{'invalid time period'}; goto SAVE_ERROR; } unless ($cgiparams{'VPN_OVERRIDE_MTU'} =~ /^(|[0-9]{1,5})$/ ) { #allow 0-99999 $errormessage = $Lang::tr{'vpn mtu invalid'}; goto SAVE_ERROR; } unless ($cgiparams{'VPN_WATCH'} =~ /^(|off|on)$/ ) { $errormessage = $Lang::tr{'invalid input'}; goto SAVE_ERROR; } map ($vpnsettings{$_} = $cgiparams{$_}, ('ENABLED','DBG_CRYPT','DBG_PARSING','DBG_EMITTING','DBG_CONTROL', 'DBG_KLIPS','DBG_DNS','DBG_NAT_T')); $vpnsettings{'VPN_IP'} = $cgiparams{'VPN_IP'}; $vpnsettings{'VPN_DELAYED_START'} = $cgiparams{'VPN_DELAYED_START'}; $vpnsettings{'VPN_OVERRIDE_MTU'} = $cgiparams{'VPN_OVERRIDE_MTU'}; $vpnsettings{'VPN_WATCH'} = $cgiparams{'VPN_WATCH'}; &General::writehash("${General::swroot}/vpn/settings", \%vpnsettings); &writeipsecfiles(); if (&vpnenabled) { system('/usr/local/bin/ipsecctrl', 'S'); } else { system('/usr/local/bin/ipsecctrl', 'D'); } sleep $sleepDelay; SAVE_ERROR: ### ### Reset all step 2 ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'remove x509'} && $cgiparams{'AREUSURE'} eq 'yes') { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); foreach my $key (keys %confighash) { if ($confighash{$key}[4] eq 'cert') { delete $confighash{$key}; } } while (my $file = glob("${General::swroot}/{ca,certs,crls,private}/*")) { unlink $file } &cleanssldatabase(); if (open(FILE, ">${General::swroot}/vpn/caconfig")) { print FILE ""; close FILE; } &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); system('/usr/local/bin/ipsecctrl', 'R'); sleep $sleepDelay; ### ### Reset all step 1 ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'remove x509'}) { &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); &Header::openbox('100%', 'left', $Lang::tr{'are you sure'}); print <
$Lang::tr{'capswarning'}: $Lang::tr{'resetting the vpn configuration will remove the root ca, the host certificate and all certificate based connections'}
END ; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit (0); ### ### Upload CA Certificate ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'upload ca certificate'}) { &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); if ($cgiparams{'CA_NAME'} !~ /^[a-zA-Z0-9]+$/) { $errormessage = $Lang::tr{'name must only contain characters'}; goto UPLOADCA_ERROR; } if (length($cgiparams{'CA_NAME'}) >60) { $errormessage = $Lang::tr{'name too long'}; goto VPNCONF_ERROR; } if ($cgiparams{'CA_NAME'} eq 'ca') { $errormessage = $Lang::tr{'name is invalid'}; goto UPLOAD_CA_ERROR; } # Check if there is no other entry with this name foreach my $key (keys %cahash) { if ($cahash{$key}[0] eq $cgiparams{'CA_NAME'}) { $errormessage = $Lang::tr{'a ca certificate with this name already exists'}; goto UPLOADCA_ERROR; } } if (ref ($cgiparams{'FH'}) ne 'Fh') { $errormessage = $Lang::tr{'there was no file upload'}; goto UPLOADCA_ERROR; } # Move uploaded ca to a temporary file (my $fh, my $filename) = tempfile( ); if (copy ($cgiparams{'FH'}, $fh) != 1) { $errormessage = $!; goto UPLOADCA_ERROR; } my $temp = `/usr/bin/openssl x509 -text -in $filename`; if ($temp !~ /CA:TRUE/i) { $errormessage = $Lang::tr{'not a valid ca certificate'}; unlink ($filename); goto UPLOADCA_ERROR; } else { move($filename, "${General::swroot}/ca/$cgiparams{'CA_NAME'}cert.pem"); if ($? ne 0) { $errormessage = "$Lang::tr{'certificate file move failed'}: $!"; unlink ($filename); goto UPLOADCA_ERROR; } } my $key = &General::findhasharraykey (\%cahash); $cahash{$key}[0] = $cgiparams{'CA_NAME'}; $cahash{$key}[1] = &Header::cleanhtml(getsubjectfromcert ("${General::swroot}/ca/$cgiparams{'CA_NAME'}cert.pem")); &General::writehasharray("${General::swroot}/vpn/caconfig", \%cahash); system('/usr/local/bin/ipsecctrl', 'R'); sleep $sleepDelay; UPLOADCA_ERROR: ### ### Display ca certificate ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'show ca certificate'}) { &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); if ( -f "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem") { &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); &Header::openbox('100%', 'left', "$Lang::tr{'ca certificate'}:"); my $output = `/usr/bin/openssl x509 -text -in ${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem`; $output = &Header::cleanhtml($output,"y"); print "
$output
\n"; &Header::closebox(); print "
$Lang::tr{'back'}
"; &Header::closebigbox(); &Header::closepage(); exit(0); } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Export ca certificate to browser ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download ca certificate'}) { &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); if ( -f "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem" ) { print "Content-Type: application/force-download\n"; print "Content-Type: application/octet-stream\r\n"; print "Content-Disposition: attachment; filename=$cahash{$cgiparams{'KEY'}}[0]cert.pem\r\n\r\n"; print `/usr/bin/openssl x509 -in ${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem`; exit(0); } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Remove ca certificate (step 2) ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'remove ca certificate'} && $cgiparams{'AREUSURE'} eq 'yes') { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); if ( -f "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem" ) { foreach my $key (keys %confighash) { my $test = `/usr/bin/openssl verify -CAfile ${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem ${General::swroot}/certs/$confighash{$key}[1]cert.pem`; if ($test =~ /: OK/) { # Delete connection system('/usr/local/bin/ipsecctrl', 'D', $key) if (&vpnenabled); unlink ("${General::swroot}/certs/$confighash{$key}[1]cert.pem"); unlink ("${General::swroot}/certs/$confighash{$key}[1].p12"); delete $confighash{$key}; &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); } } unlink ("${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem"); delete $cahash{$cgiparams{'KEY'}}; &General::writehasharray("${General::swroot}/vpn/caconfig", \%cahash); system('/usr/local/bin/ipsecctrl', 'R'); sleep $sleepDelay; } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Remove ca certificate (step 1) ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'remove ca certificate'}) { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); my $assignedcerts = 0; if ( -f "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem" ) { foreach my $key (keys %confighash) { my $test = `/usr/bin/openssl verify -CAfile ${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem ${General::swroot}/certs/$confighash{$key}[1]cert.pem`; if ($test =~ /: OK/) { $assignedcerts++; } } if ($assignedcerts) { &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); &Header::openbox('100%', 'left', $Lang::tr{'are you sure'}); print <
$Lang::tr{'capswarning'} $Lang::tr{'connections are associated with this ca. deleting the ca will delete these connections as well.'}
END ; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit (0); } else { unlink ("${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem"); delete $cahash{$cgiparams{'KEY'}}; &General::writehasharray("${General::swroot}/vpn/caconfig", \%cahash); system('/usr/local/bin/ipsecctrl', 'R'); sleep $sleepDelay; } } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Display root certificate ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'show root certificate'} || $cgiparams{'ACTION'} eq $Lang::tr{'show host certificate'}) { my $output; &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); if ($cgiparams{'ACTION'} eq $Lang::tr{'show root certificate'}) { &Header::openbox('100%', 'left', "$Lang::tr{'root certificate'}:"); $output = `/usr/bin/openssl x509 -text -in ${General::swroot}/ca/cacert.pem`; } else { &Header::openbox('100%', 'left', "$Lang::tr{'host certificate'}:"); $output = `/usr/bin/openssl x509 -text -in ${General::swroot}/certs/hostcert.pem`; } $output = &Header::cleanhtml($output,"y"); print "
$output
\n"; &Header::closebox(); print "
$Lang::tr{'back'}
"; &Header::closebigbox(); &Header::closepage(); exit(0); ### ### Export root certificate to browser ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download root certificate'}) { if ( -f "${General::swroot}/ca/cacert.pem" ) { print "Content-Type: application/force-download\n"; print "Content-Disposition: attachment; filename=cacert.pem\r\n\r\n"; print `/usr/bin/openssl x509 -in ${General::swroot}/ca/cacert.pem`; exit(0); } ### ### Export host certificate to browser ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download host certificate'}) { if ( -f "${General::swroot}/certs/hostcert.pem" ) { print "Content-Type: application/force-download\n"; print "Content-Disposition: attachment; filename=hostcert.pem\r\n\r\n"; print `/usr/bin/openssl x509 -in ${General::swroot}/certs/hostcert.pem`; exit(0); } ### ### Form for generating/importing the caroot+host certificate ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'generate root/host certificates'} || $cgiparams{'ACTION'} eq $Lang::tr{'upload p12 file'}) { if (-f "${General::swroot}/ca/cacert.pem") { $errormessage = $Lang::tr{'valid root certificate already exists'}; goto ROOTCERT_SKIP; } &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); # fill in initial values if ($cgiparams{'ROOTCERT_HOSTNAME'} eq '') { if (-e "${General::swroot}/red/active" && open(IPADDR, "${General::swroot}/red/local-ipaddress")) { my $ipaddr = ; close IPADDR; chomp ($ipaddr); $cgiparams{'ROOTCERT_HOSTNAME'} = (gethostbyaddr(pack("C4", split(/\./, $ipaddr)), 2))[0]; if ($cgiparams{'ROOTCERT_HOSTNAME'} eq '') { $cgiparams{'ROOTCERT_HOSTNAME'} = $ipaddr; } } $cgiparams{'ROOTCERT_COUNTRY'} = $vpnsettings{'ROOTCERT_COUNTRY'} if (!$cgiparams{'ROOTCERT_COUNTRY'}); } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'upload p12 file'}) { &General::log("ipsec", "Importing from p12..."); if (ref ($cgiparams{'FH'}) ne 'Fh') { $errormessage = $Lang::tr{'there was no file upload'}; goto ROOTCERT_ERROR; } # Move uploaded certificate request to a temporary file (my $fh, my $filename) = tempfile( ); if (copy ($cgiparams{'FH'}, $fh) != 1) { $errormessage = $!; goto ROOTCERT_ERROR; } # Extract the CA certificate from the file &General::log("ipsec", "Extracting caroot from p12..."); if (open(STDIN, "-|")) { my $opt = " pkcs12 -cacerts -nokeys"; $opt .= " -in $filename"; $opt .= " -out /tmp/newcacert"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'P12_PASS'}\n"; exit (0); } # Extract the Host certificate from the file if (!$errormessage) { &General::log("ipsec", "Extracting host cert from p12..."); if (open(STDIN, "-|")) { my $opt = " pkcs12 -clcerts -nokeys"; $opt .= " -in $filename"; $opt .= " -out /tmp/newhostcert"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'P12_PASS'}\n"; exit (0); } } # Extract the Host key from the file if (!$errormessage) { &General::log("ipsec", "Extracting private key from p12..."); if (open(STDIN, "-|")) { my $opt = " pkcs12 -nocerts -nodes"; $opt .= " -in $filename"; $opt .= " -out /tmp/newhostkey"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'P12_PASS'}\n"; exit (0); } } if (!$errormessage) { &General::log("ipsec", "Moving cacert..."); move("/tmp/newcacert", "${General::swroot}/ca/cacert.pem"); $errormessage = "$Lang::tr{'certificate file move failed'}: $!" if ($? ne 0); } if (!$errormessage) { &General::log("ipsec", "Moving host cert..."); move("/tmp/newhostcert", "${General::swroot}/certs/hostcert.pem"); $errormessage = "$Lang::tr{'certificate file move failed'}: $!" if ($? ne 0); } if (!$errormessage) { &General::log("ipsec", "Moving private key..."); move("/tmp/newhostkey", "${General::swroot}/certs/hostkey.pem"); $errormessage = "$Lang::tr{'certificate file move failed'}: $!" if ($? ne 0); } #cleanup temp files unlink ($filename); unlink ('/tmp/newcacert'); unlink ('/tmp/newhostcert'); unlink ('/tmp/newhostkey'); if ($errormessage) { unlink ("${General::swroot}/ca/cacert.pem"); unlink ("${General::swroot}/certs/hostcert.pem"); unlink ("${General::swroot}/certs/hostkey.pem"); goto ROOTCERT_ERROR; } # Create empty CRL cannot be done because we don't have # the private key for this CAROOT # IPFire can only import certificates &General::log("ipsec", "p12 import completed!"); &cleanssldatabase(); goto ROOTCERT_SUCCESS; } elsif ($cgiparams{'ROOTCERT_COUNTRY'} ne '') { # Validate input since the form was submitted if ($cgiparams{'ROOTCERT_ORGANIZATION'} eq ''){ $errormessage = $Lang::tr{'organization cant be empty'}; goto ROOTCERT_ERROR; } if (length($cgiparams{'ROOTCERT_ORGANIZATION'}) >60) { $errormessage = $Lang::tr{'organization too long'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_ORGANIZATION'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for organization'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_HOSTNAME'} eq ''){ $errormessage = $Lang::tr{'hostname cant be empty'}; goto ROOTCERT_ERROR; } unless (&General::validfqdn($cgiparams{'ROOTCERT_HOSTNAME'}) || &General::validip($cgiparams{'ROOTCERT_HOSTNAME'})) { $errormessage = $Lang::tr{'invalid input for hostname'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_EMAIL'} ne '' && (! &General::validemail($cgiparams{'ROOTCERT_EMAIL'}))) { $errormessage = $Lang::tr{'invalid input for e-mail address'}; goto ROOTCERT_ERROR; } if (length($cgiparams{'ROOTCERT_EMAIL'}) > 40) { $errormessage = $Lang::tr{'e-mail address too long'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_OU'} ne '' && $cgiparams{'ROOTCERT_OU'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for department'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_CITY'} ne '' && $cgiparams{'ROOTCERT_CITY'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for city'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_STATE'} ne '' && $cgiparams{'ROOTCERT_STATE'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for state or province'}; goto ROOTCERT_ERROR; } if ($cgiparams{'ROOTCERT_COUNTRY'} !~ /^[A-Z]*$/) { $errormessage = $Lang::tr{'invalid input for country'}; goto ROOTCERT_ERROR; } #the exact syntax is a list comma separated of # email:any-validemail # URI: a uniform resource indicator # DNS: a DNS domain name # RID: a registered OBJECT IDENTIFIER # IP: an IP address # example: email:franck@foo.com,IP:10.0.0.10,DNS:franck.foo.com if ($cgiparams{'SUBJECTALTNAME'} ne '' && $cgiparams{'SUBJECTALTNAME'} !~ /^(email|URI|DNS|RID|IP):[a-zA-Z0-9 :\/,\.\-_@]*$/) { $errormessage = $Lang::tr{'vpn altname syntax'}; goto VPNCONF_ERROR; } # Copy the cgisettings to vpnsettings and save the configfile $vpnsettings{'ROOTCERT_ORGANIZATION'} = $cgiparams{'ROOTCERT_ORGANIZATION'}; $vpnsettings{'ROOTCERT_HOSTNAME'} = $cgiparams{'ROOTCERT_HOSTNAME'}; $vpnsettings{'ROOTCERT_EMAIL'} = $cgiparams{'ROOTCERT_EMAIL'}; $vpnsettings{'ROOTCERT_OU'} = $cgiparams{'ROOTCERT_OU'}; $vpnsettings{'ROOTCERT_CITY'} = $cgiparams{'ROOTCERT_CITY'}; $vpnsettings{'ROOTCERT_STATE'} = $cgiparams{'ROOTCERT_STATE'}; $vpnsettings{'ROOTCERT_COUNTRY'} = $cgiparams{'ROOTCERT_COUNTRY'}; &General::writehash("${General::swroot}/vpn/settings", \%vpnsettings); # Replace empty strings with a . (my $ou = $cgiparams{'ROOTCERT_OU'}) =~ s/^\s*$/\./; (my $city = $cgiparams{'ROOTCERT_CITY'}) =~ s/^\s*$/\./; (my $state = $cgiparams{'ROOTCERT_STATE'}) =~ s/^\s*$/\./; # Create the CA certificate if (!$errormessage) { &General::log("ipsec", "Creating cacert..."); if (open(STDIN, "-|")) { my $opt = " req -x509 -nodes -rand /proc/interrupts:/proc/net/rt_cache"; $opt .= " -days 999999"; $opt .= " -newkey rsa:2048"; $opt .= " -keyout ${General::swroot}/private/cakey.pem"; $opt .= " -out ${General::swroot}/ca/cacert.pem"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'ROOTCERT_COUNTRY'}\n"; print "$state\n"; print "$city\n"; print "$cgiparams{'ROOTCERT_ORGANIZATION'}\n"; print "$ou\n"; print "$cgiparams{'ROOTCERT_ORGANIZATION'} CA\n"; print "$cgiparams{'ROOTCERT_EMAIL'}\n"; exit (0); } } # Create the Host certificate request if (!$errormessage) { &General::log("ipsec", "Creating host cert..."); if (open(STDIN, "-|")) { my $opt = " req -nodes -rand /proc/interrupts:/proc/net/rt_cache"; $opt .= " -newkey rsa:1024"; $opt .= " -keyout ${General::swroot}/certs/hostkey.pem"; $opt .= " -out ${General::swroot}/certs/hostreq.pem"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'ROOTCERT_COUNTRY'}\n"; print "$state\n"; print "$city\n"; print "$cgiparams{'ROOTCERT_ORGANIZATION'}\n"; print "$ou\n"; print "$cgiparams{'ROOTCERT_HOSTNAME'}\n"; print "$cgiparams{'ROOTCERT_EMAIL'}\n"; print ".\n"; print ".\n"; exit (0); } } # Sign the host certificate request if (!$errormessage) { &General::log("ipsec", "Self signing host cert..."); #No easy way for specifying the contain of subjectAltName without writing a config file... my ($fh, $v3extname) = tempfile ('/tmp/XXXXXXXX'); print $fh <$errormessage"; print " "; &Header::closebox(); } &Header::openbox('100%', 'left', "$Lang::tr{'generate root/host certificates'}:"); print <
$Lang::tr{'organization name'}:
$Lang::tr{'ipfires hostname'}:
$Lang::tr{'your e-mail'}: *
$Lang::tr{'your department'}: *
$Lang::tr{'city'}: *
$Lang::tr{'state or province'}: *
$Lang::tr{'country'}:
$Lang::tr{'vpn subjectaltname'} (subjectAltName=email:*,URI:*,DNS:*,RID:*) *
 


$Lang::tr{'capswarning'}: $Lang::tr{'generating the root and host certificates may take a long time. it can take up to several minutes on older hardware. please be patient'}

$Lang::tr{'upload p12 file'}:
$Lang::tr{'pkcs12 file password'}: *
 
* $Lang::tr{'this field may be blank'}
END ; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit(0); ROOTCERT_SUCCESS: if (&vpnenabled) { system('/usr/local/bin/ipsecctrl', 'S'); sleep $sleepDelay; } ROOTCERT_SKIP: ### ### Export PKCS12 file to browser ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download pkcs12 file'}) { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); print "Content-Type: application/force-download\n"; print "Content-Disposition: attachment; filename=" . $confighash{$cgiparams{'KEY'}}[1] . ".p12\r\n"; print "Content-Type: application/octet-stream\r\n\r\n"; print `/bin/cat ${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1].p12`; exit (0); ### ### Display certificate ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'show certificate'}) { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ( -f "${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem") { &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); &Header::openbox('100%', 'left', "$Lang::tr{'cert'}:"); my $output = `/usr/bin/openssl x509 -text -in ${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem`; $output = &Header::cleanhtml($output,"y"); print "
$output
\n"; &Header::closebox(); print ""; &Header::closebigbox(); &Header::closepage(); exit(0); } ### ### Export Certificate to browser ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download certificate'}) { &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ( -f "${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem") { print "Content-Type: application/force-download\n"; print "Content-Disposition: attachment; filename=" . $confighash{$cgiparams{'KEY'}}[1] . "cert.pem\n\n"; print `/bin/cat ${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem`; exit (0); } ### ### Enable/Disable connection ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'toggle enable disable'}) { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ($confighash{$cgiparams{'KEY'}}) { if ($confighash{$cgiparams{'KEY'}}[0] eq 'off') { $confighash{$cgiparams{'KEY'}}[0] = 'on'; &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'}) if (&vpnenabled); } else { system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled); $confighash{$cgiparams{'KEY'}}[0] = 'off'; &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); } sleep $sleepDelay; } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Restart connection ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'restart'}) { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ($confighash{$cgiparams{'KEY'}}) { if (&vpnenabled) { system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'}); sleep $sleepDelay; } } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Remove connection ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'remove'}) { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ($confighash{$cgiparams{'KEY'}}) { system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled); unlink ("${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem"); unlink ("${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1].p12"); delete $confighash{$cgiparams{'KEY'}}; &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); } else { $errormessage = $Lang::tr{'invalid key'}; } ### ### Choose between adding a host-net or net-net connection ### } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'add'} && $cgiparams{'TYPE'} eq '') { &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', ''); &Header::openbox('100%', 'left', $Lang::tr{'connection type'}); print < $Lang::tr{'connection type'}:
$Lang::tr{'host to net vpn'}
$Lang::tr{'net to net vpn'}
END ; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit (0); ### ### Adding/Editing/Saving a connection ### } elsif (($cgiparams{'ACTION'} eq $Lang::tr{'add'}) || ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}) || ($cgiparams{'ACTION'} eq $Lang::tr{'save'} && $cgiparams{'ADVANCED'} eq '')) { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if ($cgiparams{'ACTION'} eq $Lang::tr{'edit'}) { if (! $confighash{$cgiparams{'KEY'}}[0]) { $errormessage = $Lang::tr{'invalid key'}; goto VPNCONF_END; } $cgiparams{'ENABLED'} = $confighash{$cgiparams{'KEY'}}[0]; $cgiparams{'NAME'} = $confighash{$cgiparams{'KEY'}}[1]; $cgiparams{'TYPE'} = $confighash{$cgiparams{'KEY'}}[3]; $cgiparams{'AUTH'} = $confighash{$cgiparams{'KEY'}}[4]; $cgiparams{'PSK'} = $confighash{$cgiparams{'KEY'}}[5]; #$cgiparams{'free'} = $confighash{$cgiparams{'KEY'}}[6]; $cgiparams{'LOCAL_ID'} = $confighash{$cgiparams{'KEY'}}[7]; $cgiparams{'LOCAL_SUBNET'} = $confighash{$cgiparams{'KEY'}}[8]; $cgiparams{'REMOTE_ID'} = $confighash{$cgiparams{'KEY'}}[9]; $cgiparams{'REMOTE'} = $confighash{$cgiparams{'KEY'}}[10]; $cgiparams{'REMOTE_SUBNET'} = $confighash{$cgiparams{'KEY'}}[11]; $cgiparams{'REMARK'} = $confighash{$cgiparams{'KEY'}}[25]; $cgiparams{'INTERFACE'} = $confighash{$cgiparams{'KEY'}}[26]; $cgiparams{'DPD_ACTION'} = $confighash{$cgiparams{'KEY'}}[27]; $cgiparams{'IKE_ENCRYPTION'} = $confighash{$cgiparams{'KEY'}}[18]; $cgiparams{'IKE_INTEGRITY'} = $confighash{$cgiparams{'KEY'}}[19]; $cgiparams{'IKE_GROUPTYPE'} = $confighash{$cgiparams{'KEY'}}[20]; $cgiparams{'IKE_LIFETIME'} = $confighash{$cgiparams{'KEY'}}[16]; $cgiparams{'ESP_ENCRYPTION'} = $confighash{$cgiparams{'KEY'}}[21]; $cgiparams{'ESP_INTEGRITY'} = $confighash{$cgiparams{'KEY'}}[22]; $cgiparams{'ESP_GROUPTYPE'} = $confighash{$cgiparams{'KEY'}}[23]; $cgiparams{'ESP_KEYLIFE'} = $confighash{$cgiparams{'KEY'}}[17]; $cgiparams{'AGGRMODE'} = $confighash{$cgiparams{'KEY'}}[12]; $cgiparams{'COMPRESSION'} = $confighash{$cgiparams{'KEY'}}[13]; $cgiparams{'ONLY_PROPOSED'} = $confighash{$cgiparams{'KEY'}}[24]; $cgiparams{'PFS'} = $confighash{$cgiparams{'KEY'}}[28]; $cgiparams{'VHOST'} = $confighash{$cgiparams{'KEY'}}[14]; } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'save'}) { $cgiparams{'REMARK'} = &Header::cleanhtml($cgiparams{'REMARK'}); if ($cgiparams{'TYPE'} !~ /^(host|net)$/) { $errormessage = $Lang::tr{'connection type is invalid'}; goto VPNCONF_ERROR; } if ($cgiparams{'NAME'} !~ /^[a-zA-Z0-9]+$/) { $errormessage = $Lang::tr{'name must only contain characters'}; goto VPNCONF_ERROR; } if ($cgiparams{'NAME'} =~ /^(host|01|block|private|clear|packetdefault)$/) { $errormessage = $Lang::tr{'name is invalid'}; goto VPNCONF_ERROR; } if (length($cgiparams{'NAME'}) >60) { $errormessage = $Lang::tr{'name too long'}; goto VPNCONF_ERROR; } # Check if there is no other entry with this name if (! $cgiparams{'KEY'}) { #only for add foreach my $key (keys %confighash) { if ($confighash{$key}[1] eq $cgiparams{'NAME'}) { $errormessage = $Lang::tr{'a connection with this name already exists'}; goto VPNCONF_ERROR; } } } if (($cgiparams{'TYPE'} eq 'net') && (! $cgiparams{'REMOTE'})) { $errormessage = $Lang::tr{'invalid input for remote host/ip'}; goto VPNCONF_ERROR; } if ($cgiparams{'REMOTE'}) { if (! &General::validip($cgiparams{'REMOTE'})) { if (! &General::validfqdn ($cgiparams{'REMOTE'})) { $errormessage = $Lang::tr{'invalid input for remote host/ip'}; goto VPNCONF_ERROR; } else { if (&valid_dns_host($cgiparams{'REMOTE'})) { $warnmessage = "$Lang::tr{'check vpn lr'} $cgiparams{'REMOTE'}. $Lang::tr{'dns check failed'}"; } } } } unless (&General::validipandmask($cgiparams{'LOCAL_SUBNET'})) { $errormessage = $Lang::tr{'local subnet is invalid'}; goto VPNCONF_ERROR; } # Allow only one roadwarrior/psk without remote IP-address if ($cgiparams{'REMOTE'} eq '' && $cgiparams{'AUTH'} eq 'psk') { foreach my $key (keys %confighash) { if ( ($cgiparams{'KEY'} ne $key) && ($confighash{$key}[4] eq 'psk') && ($confighash{$key}[10] eq '') ) { $errormessage = $Lang::tr{'you can only define one roadwarrior connection when using pre-shared key authentication'}; goto VPNCONF_ERROR; } } } if (($cgiparams{'TYPE'} eq 'net') && (! &General::validipandmask($cgiparams{'REMOTE_SUBNET'}))) { $errormessage = $Lang::tr{'remote subnet is invalid'}; goto VPNCONF_ERROR; } if ($cgiparams{'ENABLED'} !~ /^(on|off)$/) { $errormessage = $Lang::tr{'invalid input'}; goto VPNCONF_ERROR; } if ($cgiparams{'EDIT_ADVANCED'} !~ /^(on|off)$/) { $errormessage = $Lang::tr{'invalid input'}; goto VPNCONF_ERROR; } # Allow nothing or a string (DN,FDQN,) beginning with @ # with no comma but slashes between RID eg @O=FR/C=Paris/OU=myhome/CN=franck if ( ($cgiparams{'LOCAL_ID'} !~ /^(|[\w.-]*@[\w. =*\/-]+|\d\.\d\.\d\.\d)$/) || ($cgiparams{'REMOTE_ID'} !~ /^(|[\w.-]*@[\w. =*\/-]+|\d\.\d\.\d\.\d)$/) || (($cgiparams{'REMOTE_ID'} eq $cgiparams{'LOCAL_ID'}) && ($cgiparams{'LOCAL_ID'} ne '')) ) { $errormessage = $Lang::tr{'invalid local-remote id'} . '
' . 'DER_ASN1_DN: @c=FR/ou=Paris/ou=Home/cn=*
' . 'FQDN: @ipfire.org
' . 'USER_FQDN: info@ipfire.org
' . 'IPV4_ADDR: @123.123.123.123'; goto VPNCONF_ERROR; } # If Auth is DN, verify existance of Remote ID. if ( $cgiparams{'REMOTE_ID'} eq '' && ( $cgiparams{'AUTH'} eq 'auth-dn'|| # while creation $confighash{$cgiparams{'KEY'}}[2] eq '%auth-dn')){ # while editing $errormessage = $Lang::tr{'vpn missing remote id'}; goto VPNCONF_ERROR; } if ($cgiparams{'AUTH'} eq 'psk') { if (! length($cgiparams{'PSK'}) ) { $errormessage = $Lang::tr{'pre-shared key is too short'}; goto VPNCONF_ERROR; } if ($cgiparams{'PSK'} =~ /'/) { $cgiparams{'PSK'} =~ tr/'/ /; $errormessage = $Lang::tr{'invalid characters found in pre-shared key'}; goto VPNCONF_ERROR; } } elsif ($cgiparams{'AUTH'} eq 'certreq') { if ($cgiparams{'KEY'}) { $errormessage = $Lang::tr{'cant change certificates'}; goto VPNCONF_ERROR; } if (ref ($cgiparams{'FH'}) ne 'Fh') { $errormessage = $Lang::tr{'there was no file upload'}; goto VPNCONF_ERROR; } # Move uploaded certificate request to a temporary file (my $fh, my $filename) = tempfile( ); if (copy ($cgiparams{'FH'}, $fh) != 1) { $errormessage = $!; goto VPNCONF_ERROR; } # Sign the certificate request &General::log("ipsec", "Signing your cert $cgiparams{'NAME'}..."); my $opt = " ca -days 999999"; $opt .= " -batch -notext"; $opt .= " -in $filename"; $opt .= " -out ${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"; if ( $errormessage = &callssl ($opt) ) { unlink ($filename); unlink ("${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); &cleanssldatabase(); goto VPNCONF_ERROR; } else { unlink ($filename); &cleanssldatabase(); } $cgiparams{'CERT_NAME'} = getCNfromcert ("${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); if ($cgiparams{'CERT_NAME'} eq '') { $errormessage = $Lang::tr{'could not retrieve common name from certificate'}; goto VPNCONF_ERROR; } } elsif ($cgiparams{'AUTH'} eq 'pkcs12') { &General::log("ipsec", "Importing from p12..."); if (ref ($cgiparams{'FH'}) ne 'Fh') { $errormessage = $Lang::tr{'there was no file upload'}; goto ROOTCERT_ERROR; } # Move uploaded certificate request to a temporary file (my $fh, my $filename) = tempfile( ); if (copy ($cgiparams{'FH'}, $fh) != 1) { $errormessage = $!; goto ROOTCERT_ERROR; } # Extract the CA certificate from the file &General::log("ipsec", "Extracting caroot from p12..."); if (open(STDIN, "-|")) { my $opt = " pkcs12 -cacerts -nokeys"; $opt .= " -in $filename"; $opt .= " -out /tmp/newcacert"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'P12_PASS'}\n"; exit (0); } # Extract the Host certificate from the file if (!$errormessage) { &General::log("ipsec", "Extracting host cert from p12..."); if (open(STDIN, "-|")) { my $opt = " pkcs12 -clcerts -nokeys"; $opt .= " -in $filename"; $opt .= " -out /tmp/newhostcert"; $errormessage = &callssl ($opt); } else { #child print "$cgiparams{'P12_PASS'}\n"; exit (0); } } if (!$errormessage) { &General::log("ipsec", "Moving cacert..."); #If CA have new subject, add it to our list of CA my $casubject = &Header::cleanhtml(getsubjectfromcert ('/tmp/newcacert')); my @names; foreach my $x (keys %cahash) { $casubject='' if ($cahash{$x}[1] eq $casubject); unshift (@names,$cahash{$x}[0]); } if ($casubject) { # a new one! my $temp = `/usr/bin/openssl x509 -text -in /tmp/newcacert`; if ($temp !~ /CA:TRUE/i) { $errormessage = $Lang::tr{'not a valid ca certificate'}; } else { #compute a name for it my $idx=0; while (grep(/Imported-$idx/, @names) ) {$idx++}; $cgiparams{'CA_NAME'}="Imported-$idx"; $cgiparams{'CERT_NAME'}=&Header::cleanhtml(getCNfromcert ('/tmp/newhostcert')); move("/tmp/newcacert", "${General::swroot}/ca/$cgiparams{'CA_NAME'}cert.pem"); $errormessage = "$Lang::tr{'certificate file move failed'}: $!" if ($? ne 0); if (!$errormessage) { my $key = &General::findhasharraykey (\%cahash); $cahash{$key}[0] = $cgiparams{'CA_NAME'}; $cahash{$key}[1] = $casubject; &General::writehasharray("${General::swroot}/vpn/caconfig", \%cahash); system('/usr/local/bin/ipsecctrl', 'R'); } } } } if (!$errormessage) { &General::log("ipsec", "Moving host cert..."); move("/tmp/newhostcert", "${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); $errormessage = "$Lang::tr{'certificate file move failed'}: $!" if ($? ne 0); } #cleanup temp files unlink ($filename); unlink ('/tmp/newcacert'); unlink ('/tmp/newhostcert'); if ($errormessage) { unlink ("${General::swroot}/ca/$cgiparams{'CA_NAME'}cert.pem"); unlink ("${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); goto VPNCONF_ERROR; } &General::log("ipsec", "p12 import completed!"); } elsif ($cgiparams{'AUTH'} eq 'certfile') { if ($cgiparams{'KEY'}) { $errormessage = $Lang::tr{'cant change certificates'}; goto VPNCONF_ERROR; } if (ref ($cgiparams{'FH'}) ne 'Fh') { $errormessage = $Lang::tr{'there was no file upload'}; goto VPNCONF_ERROR; } # Move uploaded certificate to a temporary file (my $fh, my $filename) = tempfile( ); if (copy ($cgiparams{'FH'}, $fh) != 1) { $errormessage = $!; goto VPNCONF_ERROR; } # Verify the certificate has a valid CA and move it &General::log("ipsec", "Validating imported cert against our known CA..."); my $validca = 1; #assume ok my $test = `/usr/bin/openssl verify -CAfile ${General::swroot}/ca/cacert.pem $filename`; if ($test !~ /: OK/) { my $validca = 0; foreach my $key (keys %cahash) { $test = `/usr/bin/openssl verify -CAfile ${General::swroot}/ca/$cahash{$key}[0]cert.pem $filename`; if ($test =~ /: OK/) { $validca = 1; last; } } } if (! $validca) { $errormessage = $Lang::tr{'certificate does not have a valid ca associated with it'}; unlink ($filename); goto VPNCONF_ERROR; } else { move($filename, "${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); if ($? ne 0) { $errormessage = "$Lang::tr{'certificate file move failed'}: $!"; unlink ($filename); goto VPNCONF_ERROR; } } $cgiparams{'CERT_NAME'} = getCNfromcert ("${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); if ($cgiparams{'CERT_NAME'} eq '') { unlink ("${General::swroot}/certs/$cgiparams{'NAME'}cert.pem"); $errormessage = $Lang::tr{'could not retrieve common name from certificate'}; goto VPNCONF_ERROR; } } elsif ($cgiparams{'AUTH'} eq 'certgen') { if ($cgiparams{'KEY'}) { $errormessage = $Lang::tr{'cant change certificates'}; goto VPNCONF_ERROR; } # Validate input since the form was submitted if (length($cgiparams{'CERT_NAME'}) >60) { $errormessage = $Lang::tr{'name too long'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_NAME'} !~ /^[a-zA-Z0-9 ,\.\-_]+$/) { $errormessage = $Lang::tr{'invalid input for name'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_EMAIL'} ne '' && (! &General::validemail($cgiparams{'CERT_EMAIL'}))) { $errormessage = $Lang::tr{'invalid input for e-mail address'}; goto VPNCONF_ERROR; } if (length($cgiparams{'CERT_EMAIL'}) > 40) { $errormessage = $Lang::tr{'e-mail address too long'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_OU'} ne '' && $cgiparams{'CERT_OU'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for department'}; goto VPNCONF_ERROR; } if (length($cgiparams{'CERT_ORGANIZATION'}) >60) { $errormessage = $Lang::tr{'organization too long'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_ORGANIZATION'} !~ /^[a-zA-Z0-9 ,\.\-_]+$/) { $errormessage = $Lang::tr{'invalid input for organization'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_CITY'} ne '' && $cgiparams{'CERT_CITY'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for city'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_STATE'} ne '' && $cgiparams{'CERT_STATE'} !~ /^[a-zA-Z0-9 ,\.\-_]*$/) { $errormessage = $Lang::tr{'invalid input for state or province'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_COUNTRY'} !~ /^[A-Z]*$/) { $errormessage = $Lang::tr{'invalid input for country'}; goto VPNCONF_ERROR; } #the exact syntax is a list comma separated of # email:any-validemail # URI: a uniform resource indicator # DNS: a DNS domain name # RID: a registered OBJECT IDENTIFIER # IP: an IP address # example: email:franck@foo.com,IP:10.0.0.10,DNS:franck.foo.com if ($cgiparams{'SUBJECTALTNAME'} ne '' && $cgiparams{'SUBJECTALTNAME'} !~ /^(email|URI|DNS|RID|IP):[a-zA-Z0-9 :\/,\.\-_@]*$/) { $errormessage = $Lang::tr{'vpn altname syntax'}; goto VPNCONF_ERROR; } if (length($cgiparams{'CERT_PASS1'}) < 5) { $errormessage = $Lang::tr{'password too short'}; goto VPNCONF_ERROR; } if ($cgiparams{'CERT_PASS1'} ne $cgiparams{'CERT_PASS2'}) { $errormessage = $Lang::tr{'passwords do not match'}; goto VPNCONF_ERROR; } # Replace empty strings with a . (my $ou = $cgiparams{'CERT_OU'}) =~ s/^\s*$/\./; (my $city = $cgiparams{'CERT_CITY'}) =~ s/^\s*$/\./; (my $state = $cgiparams{'CERT_STATE'}) =~ s/^\s*$/\./; # Create the Host certificate request &General::log("ipsec", "Creating a cert..."); if (open(STDIN, "-|")) { my $opt = " req -nodes -rand /proc/interrupts:/proc/net/rt_cache"; $opt .= " -newkey rsa:1024"; $opt .= " -keyout ${General::swroot}/certs/$cgiparams{'NAME'}key.pem"; $opt .= " -out ${General::swroot}/certs/$cgiparams{'NAME'}req.pem"; if ( $errormessage = &callssl ($opt) ) { unlink ("${General::swroot}/certs/$cgiparams{'NAME'}key.pem"); unlink ("${General::swroot}/certs/$cgiparams{'NAME'}req.pem"); goto VPNCONF_ERROR; } } else { #child print "$cgiparams{'CERT_COUNTRY'}\n"; print "$state\n"; print "$city\n"; print "$cgiparams{'CERT_ORGANIZATION'}\n"; print "$ou\n"; print "$cgiparams{'CERT_NAME'}\n"; print "$cgiparams{'CERT_EMAIL'}\n"; print ".\n"; print ".\n"; exit (0); } # Sign the host certificate request &General::log("ipsec", "Signing the cert $cgiparams{'NAME'}..."); #No easy way for specifying the contain of subjectAltName without writing a config file... my ($fh, $v3extname) = tempfile ('/tmp/XXXXXXXX'); print $fh <$errormessage"; print " "; &Header::closebox(); } if ($warnmessage) { &Header::openbox('100%', 'left', "$Lang::tr{'warning messages'}:"); print "$warnmessage"; print " "; &Header::closebox(); } print "
"; print< END ; if ($cgiparams{'KEY'}) { print ""; print ""; } &Header::openbox('100%', 'left', "$Lang::tr{'connection'}:"); print ""; print ""; if ($cgiparams{'KEY'}) { print ""; } else { print ""; } print ""; print ''; my $disabled; my $blob; if ($cgiparams{'TYPE'} eq 'host') { $disabled = "disabled='disabled'"; $blob = "*"; }; print ""; print ""; print <$Lang::tr{'remote host/ip'}: $blob END ; if (!$cgiparams{'KEY'}) { print ""; } print "
$Lang::tr{'name'}:$cgiparams{'NAME'}$Lang::tr{'enabled'}

$Lang::tr{'host ip'}:
$Lang::tr{'local subnet'} $Lang::tr{'remote subnet'}
$Lang::tr{'vpn local id'}: *
($Lang::tr{'eg'} @xy.example.com)
$Lang::tr{'vpn remote id'}: *

$Lang::tr{'dpd action'}:   ?
$Lang::tr{'remark title'} *
$Lang::tr{'edit advanced settings when done'}
"; &Header::closebox(); if ($cgiparams{'KEY'} && $cgiparams{'AUTH'} eq 'psk') { &Header::openbox('100%', 'left', $Lang::tr{'authentication'}); print < $Lang::tr{'use a pre-shared key'} END ; &Header::closebox(); } elsif (! $cgiparams{'KEY'}) { my $pskdisabled = ($vpnsettings{'VPN_IP'} eq '%defaultroute') ? "disabled='disabled'" : '' ; $cgiparams{'PSK'} = $Lang::tr{'vpn incompatible use of defaultroute'} if ($pskdisabled); my $cakeydisabled = ( ! -f "${General::swroot}/private/cakey.pem" ) ? "disabled='disabled'" : ''; $cgiparams{'CERT_NAME'} = $Lang::tr{'vpn no full pki'} if ($cakeydisabled); my $cacrtdisabled = ( ! -f "${General::swroot}/ca/cacert.pem" ) ? "disabled='disabled'" : ''; &Header::openbox('100%', 'left', $Lang::tr{'authentication'}); print < $Lang::tr{'use a pre-shared key'}
$Lang::tr{'upload a certificate request'} $Lang::tr{'upload a certificate'} $Lang::tr{'upload p12 file'} $Lang::tr{'pkcs12 file password'}:
$Lang::tr{'vpn auth-dn'}
$Lang::tr{'generate a certificate'}    $Lang::tr{'users fullname or system hostname'}:   $Lang::tr{'users email'}: *   $Lang::tr{'users department'}: *   $Lang::tr{'organization name'}: *   $Lang::tr{'city'}: *   $Lang::tr{'state or province'}: *   $Lang::tr{'country'}:   $Lang::tr{'pkcs12 file password'}:  $Lang::tr{'pkcs12 file password'}:($Lang::tr{'confirmation'}) END ; &Header::closebox(); } print "
"; if ($cgiparams{'KEY'}) { print ""; } print "
"; &Header::closebigbox(); &Header::closepage(); exit (0); VPNCONF_END: } ### ### Advanced settings ### if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) || ($cgiparams{'ACTION'} eq $Lang::tr{'save'} && $cgiparams{'ADVANCED'} eq 'yes')) { &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); if (! $confighash{$cgiparams{'KEY'}}) { $errormessage = $Lang::tr{'invalid key'}; goto ADVANCED_END; } if ($cgiparams{'ACTION'} eq $Lang::tr{'save'}) { # I didn't read any incompatibilities here.... #if ($cgiparams{'VHOST'} eq 'on' && $cgiparams{'COMPRESSION'} eq 'on') { # $errormessage = $Lang::tr{'cannot enable both nat traversal and compression'}; # goto ADVANCED_ERROR; #} my @temp = split('\|', $cgiparams{'IKE_ENCRYPTION'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(aes256|aes128|3des|twofish256|twofish128|serpent256|serpent128|blowfish256|blowfish128|cast128)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } @temp = split('\|', $cgiparams{'IKE_INTEGRITY'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(sha2_512|sha2_256|sha|md5)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } @temp = split('\|', $cgiparams{'IKE_GROUPTYPE'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(768|1024|1536|2048|3072|4096|6144|8192)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } if ($cgiparams{'IKE_LIFETIME'} !~ /^\d+$/) { $errormessage = $Lang::tr{'invalid input for ike lifetime'}; goto ADVANCED_ERROR; } if ($cgiparams{'IKE_LIFETIME'} < 1 || $cgiparams{'IKE_LIFETIME'} > 8) { $errormessage = $Lang::tr{'ike lifetime should be between 1 and 8 hours'}; goto ADVANCED_ERROR; } @temp = split('\|', $cgiparams{'ESP_ENCRYPTION'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(aes256|aes128|3des|twofish256|twofish128|serpent256|serpent128|blowfish256|blowfish128)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } @temp = split('\|', $cgiparams{'ESP_INTEGRITY'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(sha2_512|sha2_256|sha1|md5)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } if ($cgiparams{'ESP_GROUPTYPE'} ne '' && $cgiparams{'ESP_GROUPTYPE'} !~ /^modp(768|1024|1536|2048|3072|4096)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } if ($cgiparams{'ESP_KEYLIFE'} !~ /^\d+$/) { $errormessage = $Lang::tr{'invalid input for esp keylife'}; goto ADVANCED_ERROR; } if ($cgiparams{'ESP_KEYLIFE'} < 1 || $cgiparams{'ESP_KEYLIFE'} > 24) { $errormessage = $Lang::tr{'esp keylife should be between 1 and 24 hours'}; goto ADVANCED_ERROR; } if ( ($cgiparams{'AGGRMODE'} !~ /^(|on|off)$/) || ($cgiparams{'COMPRESSION'} !~ /^(|on|off)$/) || ($cgiparams{'ONLY_PROPOSED'} !~ /^(|on|off)$/) || ($cgiparams{'PFS'} !~ /^(|on|off)$/) || ($cgiparams{'VHOST'} !~ /^(|on|off)$/) ){ $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } $confighash{$cgiparams{'KEY'}}[18] = $cgiparams{'IKE_ENCRYPTION'}; $confighash{$cgiparams{'KEY'}}[19] = $cgiparams{'IKE_INTEGRITY'}; $confighash{$cgiparams{'KEY'}}[20] = $cgiparams{'IKE_GROUPTYPE'}; $confighash{$cgiparams{'KEY'}}[16] = $cgiparams{'IKE_LIFETIME'}; $confighash{$cgiparams{'KEY'}}[21] = $cgiparams{'ESP_ENCRYPTION'}; $confighash{$cgiparams{'KEY'}}[22] = $cgiparams{'ESP_INTEGRITY'}; $confighash{$cgiparams{'KEY'}}[23] = $cgiparams{'ESP_GROUPTYPE'}; $confighash{$cgiparams{'KEY'}}[17] = $cgiparams{'ESP_KEYLIFE'}; $confighash{$cgiparams{'KEY'}}[12] = $cgiparams{'AGGRMODE'}; $confighash{$cgiparams{'KEY'}}[13] = $cgiparams{'COMPRESSION'}; $confighash{$cgiparams{'KEY'}}[24] = $cgiparams{'ONLY_PROPOSED'}; $confighash{$cgiparams{'KEY'}}[28] = $cgiparams{'PFS'}; $confighash{$cgiparams{'KEY'}}[14] = $cgiparams{'VHOST'}; &General::writehasharray("${General::swroot}/vpn/config", \%confighash); &writeipsecfiles(); if (&vpnenabled) { system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'}); sleep $sleepDelay; } goto ADVANCED_END; } else { $cgiparams{'IKE_ENCRYPTION'} = $confighash{$cgiparams{'KEY'}}[18]; $cgiparams{'IKE_INTEGRITY'} = $confighash{$cgiparams{'KEY'}}[19]; $cgiparams{'IKE_GROUPTYPE'} = $confighash{$cgiparams{'KEY'}}[20]; $cgiparams{'IKE_LIFETIME'} = $confighash{$cgiparams{'KEY'}}[16]; $cgiparams{'ESP_ENCRYPTION'} = $confighash{$cgiparams{'KEY'}}[21]; $cgiparams{'ESP_INTEGRITY'} = $confighash{$cgiparams{'KEY'}}[22]; $cgiparams{'ESP_GROUPTYPE'} = $confighash{$cgiparams{'KEY'}}[23]; $cgiparams{'ESP_KEYLIFE'} = $confighash{$cgiparams{'KEY'}}[17]; $cgiparams{'AGGRMODE'} = $confighash{$cgiparams{'KEY'}}[12]; $cgiparams{'COMPRESSION'} = $confighash{$cgiparams{'KEY'}}[13]; $cgiparams{'ONLY_PROPOSED'} = $confighash{$cgiparams{'KEY'}}[24]; $cgiparams{'PFS'} = $confighash{$cgiparams{'KEY'}}[28]; $cgiparams{'VHOST'} = $confighash{$cgiparams{'KEY'}}[14]; if ($confighash{$cgiparams{'KEY'}}[3] eq 'net' || $confighash{$cgiparams{'KEY'}}[10]) { $cgiparams{'VHOST'} = 'off'; } } ADVANCED_ERROR: $checked{'IKE_ENCRYPTION'}{'aes256'} = ''; $checked{'IKE_ENCRYPTION'}{'aes128'} = ''; $checked{'IKE_ENCRYPTION'}{'3des'} = ''; $checked{'IKE_ENCRYPTION'}{'twofish256'} = ''; $checked{'IKE_ENCRYPTION'}{'twofish128'} = ''; $checked{'IKE_ENCRYPTION'}{'serpent256'} = ''; $checked{'IKE_ENCRYPTION'}{'serpent128'} = ''; $checked{'IKE_ENCRYPTION'}{'blowfish256'} = ''; $checked{'IKE_ENCRYPTION'}{'blowfish128'} = ''; $checked{'IKE_ENCRYPTION'}{'cast128'} = ''; my @temp = split('\|', $cgiparams{'IKE_ENCRYPTION'}); foreach my $key (@temp) {$checked{'IKE_ENCRYPTION'}{$key} = "selected='selected'"; } $checked{'IKE_INTEGRITY'}{'sha2_512'} = ''; $checked{'IKE_INTEGRITY'}{'sha2_256'} = ''; $checked{'IKE_INTEGRITY'}{'sha'} = ''; $checked{'IKE_INTEGRITY'}{'md5'} = ''; @temp = split('\|', $cgiparams{'IKE_INTEGRITY'}); foreach my $key (@temp) {$checked{'IKE_INTEGRITY'}{$key} = "selected='selected'"; } $checked{'IKE_GROUPTYPE'}{'768'} = ''; $checked{'IKE_GROUPTYPE'}{'1024'} = ''; $checked{'IKE_GROUPTYPE'}{'1536'} = ''; $checked{'IKE_GROUPTYPE'}{'2048'} = ''; $checked{'IKE_GROUPTYPE'}{'3072'} = ''; $checked{'IKE_GROUPTYPE'}{'4096'} = ''; $checked{'IKE_GROUPTYPE'}{'6144'} = ''; $checked{'IKE_GROUPTYPE'}{'8192'} = ''; @temp = split('\|', $cgiparams{'IKE_GROUPTYPE'}); foreach my $key (@temp) {$checked{'IKE_GROUPTYPE'}{$key} = "selected='selected'"; } $checked{'ESP_ENCRYPTION'}{'aes256'} = ''; $checked{'ESP_ENCRYPTION'}{'aes128'} = ''; $checked{'ESP_ENCRYPTION'}{'3des'} = ''; $checked{'ESP_ENCRYPTION'}{'twofish256'} = ''; $checked{'ESP_ENCRYPTION'}{'twofish128'} = ''; $checked{'ESP_ENCRYPTION'}{'serpent256'} = ''; $checked{'ESP_ENCRYPTION'}{'serpent128'} = ''; $checked{'ESP_ENCRYPTION'}{'blowfish256'} = ''; $checked{'ESP_ENCRYPTION'}{'blowfish128'} = ''; @temp = split('\|', $cgiparams{'ESP_ENCRYPTION'}); foreach my $key (@temp) {$checked{'ESP_ENCRYPTION'}{$key} = "selected='selected'"; } $checked{'ESP_INTEGRITY'}{'sha2_512'} = ''; $checked{'ESP_INTEGRITY'}{'sha2_256'} = ''; $checked{'ESP_INTEGRITY'}{'sha1'} = ''; $checked{'ESP_INTEGRITY'}{'md5'} = ''; @temp = split('\|', $cgiparams{'ESP_INTEGRITY'}); foreach my $key (@temp) {$checked{'ESP_INTEGRITY'}{$key} = "selected='selected'"; } $checked{'ESP_GROUPTYPE'}{$cgiparams{'ESP_GROUPTYPE'}} = "selected='selected'"; $checked{'AGGRMODE'} = $cgiparams{'AGGRMODE'} eq 'on' ? "checked='checked'" : '' ; $checked{'COMPRESSION'} = $cgiparams{'COMPRESSION'} eq 'on' ? "checked='checked'" : '' ; $checked{'ONLY_PROPOSED'} = $cgiparams{'ONLY_PROPOSED'} eq 'on' ? "checked='checked'" : '' ; $checked{'PFS'} = $cgiparams{'PFS'} eq 'on' ? "checked='checked'" : '' ; $checked{'VHOST'} = $cgiparams{'VHOST'} eq 'on' ? "checked='checked'" : '' ; &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', $errormessage); if ($errormessage) { &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); print "$errormessage"; print " "; &Header::closebox(); } if ($warnmessage) { &Header::openbox('100%', 'left', $Lang::tr{'warning messages'}); print "$warnmessage"; print " "; &Header::closebox(); } &Header::openbox('100%', 'left', "$Lang::tr{'advanced'}:"); print < EOF ; if ($confighash{$cgiparams{'KEY'}}[3] eq 'net') { print ""; } elsif ($confighash{$cgiparams{'KEY'}}[10]) { print ""; } else { print ""; } print "
$Lang::tr{'ike encryption'} $Lang::tr{'ike integrity'} $Lang::tr{'ike grouptype'}
$Lang::tr{'ike lifetime'} $Lang::tr{'hours'}

$Lang::tr{'esp encryption'} $Lang::tr{'esp integrity'} $Lang::tr{'esp grouptype'}
$Lang::tr{'esp keylife'} $Lang::tr{'hours'}

IKE+ESP: $Lang::tr{'use only proposed settings'}
$Lang::tr{'vpn aggrmode'}
$Lang::tr{'pfs yes no'}
$Lang::tr{'vpn payload compression'}
"; print " $Lang::tr{'vpn vhost'}
"; print " $Lang::tr{'vpn vhost'}
"; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit(0); ADVANCED_END: } ### ### Default status page ### %cgiparams = (); %cahash = (); %confighash = (); &General::readhash("${General::swroot}/vpn/settings", \%cgiparams); &General::readhasharray("${General::swroot}/vpn/caconfig", \%cahash); &General::readhasharray("${General::swroot}/vpn/config", \%confighash); $cgiparams{'CA_NAME'} = ''; my @status = `/usr/sbin/ipsec auto --status`; # suggest a default name for this side if ($cgiparams{'VPN_IP'} eq '' && -e "${General::swroot}/red/active") { if (open(IPADDR, "${General::swroot}/red/local-ipaddress")) { my $ipaddr = ; close IPADDR; chomp ($ipaddr); $cgiparams{'VPN_IP'} = (gethostbyaddr(pack("C4", split(/\./, $ipaddr)), 2))[0]; if ($cgiparams{'VPN_IP'} eq '') { $cgiparams{'VPN_IP'} = $ipaddr; } } } # no IP found, use %defaultroute $cgiparams{'VPN_IP'} ='%defaultroute' if ($cgiparams{'VPN_IP'} eq ''); $cgiparams{'VPN_DELAYED_START'} = 0 if (! defined ($cgiparams{'VPN_DELAYED_START'})); $checked{'VPN_WATCH'} = $cgiparams{'VPN_WATCH'} eq 'on' ? "checked='checked'" : '' ; map ($checked{$_} = $cgiparams{$_} eq 'on' ? "checked='checked'" : '', ('ENABLED','DBG_CRYPT','DBG_PARSING','DBG_EMITTING','DBG_CONTROL', 'DBG_KLIPS','DBG_DNS','DBG_NAT_T')); &Header::showhttpheaders(); &Header::openpage($Lang::tr{'vpn configuration main'}, 1, ''); &Header::openbigbox('100%', 'left', '', $errormessage); if ($errormessage) { &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); print "$errormessage\n"; print " \n"; &Header::closebox(); } &Header::openbox('100%', 'left', $Lang::tr{'global settings'}); print < END ; print < END ; print <
$Lang::tr{'vpn red name'}: $Lang::tr{'enabled'}
$Lang::tr{'override mtu'}: *
$Lang::tr{'vpn delayed start'}: **

$Lang::tr{'vpn watch'}:

PLUTO DEBUG = crypt:,  parsing:,  emitting:,  control:,  klips:,  dns:,  nat_t:


* $Lang::tr{'this field may be blank'}
**  $Lang::tr{'vpn delayed start help'}
END ; print ""; &Header::closebox(); &Header::openbox('100%', 'left', $Lang::tr{'connection status and controlc'}); print < $Lang::tr{'name'} $Lang::tr{'type'} $Lang::tr{'common name'} $Lang::tr{'remark'} $Lang::tr{'status'} $Lang::tr{'action'} END ; my $id = 0; my $gif; foreach my $key (keys %confighash) { if ($confighash{$key}[0] eq 'on') { $gif = 'on.gif'; } else { $gif = 'off.gif'; } if ($id % 2) { print "\n"; } else { print "\n"; } print "$confighash{$key}[1]"; print "" . $Lang::tr{"$confighash{$key}[3]"} . " (" . $Lang::tr{"$confighash{$key}[4]"} . ")"; if ($confighash{$key}[2] eq '%auth-dn') { print "$confighash{$key}[9]"; } elsif ($confighash{$key}[4] eq 'cert') { print "$confighash{$key}[2]"; } else { print " "; } print "$confighash{$key}[25]"; # get real state my $active = "
$Lang::tr{'capsclosed'}
"; foreach my $line (@status) { if ($line =~ /\"$confighash{$key}[1]\".*IPsec SA established/) { $active = "
$Lang::tr{'capsopen'}
"; } } # move to blueif really down if ($confighash{$key}[0] eq 'off' && $active =~ /${Header::colourred}/ ) { $active = "
$Lang::tr{'capsclosed'}
"; } print <$active
END ; if (($confighash{$key}[4] eq 'cert') && ($confighash{$key}[2] ne '%auth-dn')) { print <
END ; } else { print " "; } if ($confighash{$key}[4] eq 'cert' && -f "${General::swroot}/certs/$confighash{$key}[1].p12") { print <
END ; } elsif (($confighash{$key}[4] eq 'cert') && ($confighash{$key}[2] ne '%auth-dn')) { print <
END ; } else { print " "; } print <
END ; $id++; } print ""; # If the config file contains entries, print Key to action icons if ( $id ) { print <   $Lang::tr{'legend'}:   $Lang::tr{ $Lang::tr{'click to disable'}     $Lang::tr{ $Lang::tr{'show certificate'}     $Lang::tr{ $Lang::tr{'edit'}     $Lang::tr{ $Lang::tr{'remove'}     ?OFF $Lang::tr{'click to enable'}     ?FLOPPY $Lang::tr{'download certificate'}     ?RELOAD $Lang::tr{'restart'} END ; } print <
END ; &Header::closebox(); &Header::openbox('100%', 'left', "$Lang::tr{'certificate authorities'}:"); print < $Lang::tr{'name'} $Lang::tr{'subject'} $Lang::tr{'action'} EOF ; if (-f "${General::swroot}/ca/cacert.pem") { my $casubject = &Header::cleanhtml(getsubjectfromcert ("${General::swroot}/ca/cacert.pem")); print < $Lang::tr{'root certificate'} $casubject
  END ; } else { # display rootcert generation buttons print < $Lang::tr{'root certificate'}: $Lang::tr{'not present'}   END ; } if (-f "${General::swroot}/certs/hostcert.pem") { my $hostsubject = &Header::cleanhtml(getsubjectfromcert ("${General::swroot}/certs/hostcert.pem")); print < $Lang::tr{'host certificate'} $hostsubject
  END ; } else { # Nothing print < $Lang::tr{'host certificate'}: $Lang::tr{'not present'}   END ; } my $rowcolor = 0; if (keys %cahash > 0) { foreach my $key (keys %cahash) { if ($rowcolor++ % 2) { print "\n"; } else { print "\n"; } print "$cahash{$key}[0]\n"; print "$cahash{$key}[1]\n"; print <
END ; } } print ""; # If the file contains entries, print Key to action icons if ( -f "${General::swroot}/ca/cacert.pem") { print <   $Lang::tr{'legend'}:     $Lang::tr{ $Lang::tr{'show certificate'}     $Lang::tr{ $Lang::tr{'download certificate'} END ; } my $createCA = -f "${General::swroot}/ca/cacert.pem" ? '' : ""; print <
$createCA
$Lang::tr{'ca name'}:
$Lang::tr{'resetting the vpn configuration will remove the root ca, the host certificate and all certificate based connections'}:
END ; &Header::closebox(); &Header::closebigbox(); &Header::closepage();