#!/usr/bin/perl ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2007-2013 IPFire Team info@ipfire.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 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; use Sort::Naturally; # 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); my $green_cidr = &General::ipcidr("$netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}"); my $blue_cidr = "# Blue not defined"; if (&Header::blue_used() && $netsettings{'BLUE_DEV'}) { $blue_cidr = &General::ipcidr("$netsettings{'BLUE_NETADDRESS'}/$netsettings{'BLUE_NETMASK'}"); } my $orange_cidr = "# Orange not defined"; if (&Header::orange_used() && $netsettings{'ORANGE_DEV'}) { $orange_cidr = &General::ipcidr("$netsettings{'ORANGE_NETADDRESS'}/$netsettings{'ORANGE_NETMASK'}"); } my $col=""; $cgiparams{'ENABLED'} = 'off'; $cgiparams{'EDIT_ADVANCED'} = 'off'; $cgiparams{'ACTION'} = ''; $cgiparams{'CA_NAME'} = ''; $cgiparams{'KEY'} = ''; $cgiparams{'TYPE'} = ''; $cgiparams{'ADVANCED'} = ''; $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'} = ''; $cgiparams{'RW_NET'} = ''; $cgiparams{'DPD_DELAY'} = '30'; $cgiparams{'DPD_TIMEOUT'} = '120'; $cgiparams{'FORCE_MOBIKE'} = 'off'; $cgiparams{'START_ACTION'} = 'start'; &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'. ### 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 "conn %default\n"; print CONF "\tkeyingtries=%forever\n"; print CONF "\n"; # Add user includes to config file if (-e "/etc/ipsec.user.conf") { print CONF "include /etc/ipsec.user.conf\n"; print CONF "\n"; } print SECRETS "include /etc/ipsec.user.secrets\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 "\tleftsubnet=" . &make_subnets($lconfighash{$key}[8]) . "\n"; print CONF "\tleftfirewall=yes\n"; print CONF "\tlefthostaccess=yes\n"; print CONF "\tright=$lconfighash{$key}[10]\n"; if ($lconfighash{$key}[3] eq 'net') { print CONF "\trightsubnet=" . &make_subnets($lconfighash{$key}[11]) . "\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]); # Is PFS enabled? my $pfs = $lconfighash{$key}[28] eq 'on' ? 'on' : 'off'; # Algorithms if ($lconfighash{$key}[18] && $lconfighash{$key}[19] && $lconfighash{$key}[20]) { my @encs = split('\|', $lconfighash{$key}[18]); my @ints = split('\|', $lconfighash{$key}[19]); my @groups = split('\|', $lconfighash{$key}[20]); my @algos = &make_algos("ike", \@encs, \@ints, \@groups, 1); print CONF "\tike=" . join(",", @algos); if ($lconfighash{$key}[24] eq 'on') { #only proposed algorythms? print CONF "!\n"; } else { print CONF "\n"; } } if ($lconfighash{$key}[21] && $lconfighash{$key}[22]) { my @encs = split('\|', $lconfighash{$key}[21]); my @ints = split('\|', $lconfighash{$key}[22]); my @groups = split('\|', $lconfighash{$key}[23]); # Use IKE grouptype if no ESP group type has been selected # (for backwards compatibility) if ($lconfighash{$key}[23] eq "") { @groups = split('\|', $lconfighash{$key}[20]); } my @algos = &make_algos("esp", \@encs, \@ints, \@groups, ($pfs eq "on")); print CONF "\tesp=" . join(",", @algos); if ($lconfighash{$key}[24] eq 'on') { #only proposed algorythms? print CONF "!\n"; } else { print CONF "\n"; } } # IKE V1 or V2 if (! $lconfighash{$key}[29]) { $lconfighash{$key}[29] = "ikev1"; } print CONF "\tkeyexchange=$lconfighash{$key}[29]\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]); # Compression print CONF "\tcompress=yes\n" if ($lconfighash{$key}[13] eq 'on'); # Force MOBIKE? if (($lconfighash{$key}[29] eq "ikev2") && ($lconfighash{$key}[32] eq 'on')) { print CONF "\tmobike=yes\n"; } # Dead Peer Detection my $dpdaction = $lconfighash{$key}[27]; print CONF "\tdpdaction=$dpdaction\n"; # If the dead peer detection is disabled and IKEv2 is used, # dpddelay must be set to zero, too. if ($dpdaction eq "none") { if ($lconfighash{$key}[29] eq "ikev2") { print CONF "\tdpddelay=0\n"; } } else { my $dpddelay = $lconfighash{$key}[31]; if (!$dpddelay) { $dpddelay = 30; } print CONF "\tdpddelay=$dpddelay\n"; my $dpdtimeout = $lconfighash{$key}[30]; if (!$dpdtimeout) { $dpdtimeout = 120; } print CONF "\tdpdtimeout=$dpdtimeout\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"; } my $start_action = $lconfighash{$key}[33]; if (!$start_action) { $start_action = "start"; } # Automatically start only if a net-to-net connection if ($lconfighash{$key}[3] eq 'host') { print CONF "\tauto=add\n"; print CONF "\trightsourceip=$lvpnsettings{'RW_NET'}\n"; } else { print CONF "\tauto=$start_action\n"; # If in on-demand mode, we terminate the tunnel # after 15 min of no traffic if ($start_action eq 'route') { print CONF "\tinactivity=900\n"; } } # Fragmentation print CONF "\tfragmentation=yes\n"; print CONF "\n"; } #foreach key # Add post user includes to config file # After the GUI-connections allows to patch connections. if (-e "/etc/ipsec.user-post.conf") { print CONF "include /etc/ipsec.user-post.conf\n"; print CONF "\n"; } print SECRETS $last_secrets if ($last_secrets); close(CONF); close(SECRETS); } # Hook to regenerate the configuration files. if ($ENV{"REMOTE_ADDR"} eq "") { writeipsecfiles(); exit(0); } ### ### 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; } if ( $cgiparams{'RW_NET'} ne '' and !&General::validipandmask($cgiparams{'RW_NET'}) ) { $errormessage = $Lang::tr{'urlfilter invalid ip or mask error'}; goto SAVE_ERROR; } $vpnsettings{'ENABLED'} = $cgiparams{'ENABLED'}; $vpnsettings{'VPN_IP'} = $cgiparams{'VPN_IP'}; $vpnsettings{'VPN_DELAYED_START'} = $cgiparams{'VPN_DELAYED_START'}; $vpnsettings{'RW_NET'} = $cgiparams{'RW_NET'}; &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{'ipsec'}, 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{'ipsec'}, 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{'ipsec'}, 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{'ipsec'}, 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 -sha256 -nodes"; $opt .= " -days 999999"; $opt .= " -newkey rsa:4096"; $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 -sha256 -nodes"; $opt .= " -newkey rsa:2048"; $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{'required field'}
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{'ipsec'}, 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'}; } &General::firewall_reload(); ### ### 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{'ipsec'}, 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]; my @local_subnets = split(",", $confighash{$cgiparams{'KEY'}}[8]); $cgiparams{'LOCAL_SUBNET'} = join(/\|/, @local_subnets); $cgiparams{'REMOTE_ID'} = $confighash{$cgiparams{'KEY'}}[9]; $cgiparams{'REMOTE'} = $confighash{$cgiparams{'KEY'}}[10]; my @remote_subnets = split(",", $confighash{$cgiparams{'KEY'}}[11]); $cgiparams{'REMOTE_SUBNET'} = join(/\|/, @remote_subnets); $cgiparams{'REMARK'} = $confighash{$cgiparams{'KEY'}}[25]; $cgiparams{'DPD_ACTION'} = $confighash{$cgiparams{'KEY'}}[27]; $cgiparams{'IKE_VERSION'} = $confighash{$cgiparams{'KEY'}}[29]; $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]; if ($cgiparams{'ESP_GROUPTYPE'} eq "") { $cgiparams{'ESP_GROUPTYPE'} = $cgiparams{'IKE_GROUPTYPE'}; } $cgiparams{'ESP_KEYLIFE'} = $confighash{$cgiparams{'KEY'}}[17]; $cgiparams{'COMPRESSION'} = $confighash{$cgiparams{'KEY'}}[13]; $cgiparams{'ONLY_PROPOSED'} = $confighash{$cgiparams{'KEY'}}[24]; $cgiparams{'PFS'} = $confighash{$cgiparams{'KEY'}}[28]; $cgiparams{'DPD_TIMEOUT'} = $confighash{$cgiparams{'KEY'}}[30]; $cgiparams{'DPD_DELAY'} = $confighash{$cgiparams{'KEY'}}[31]; $cgiparams{'FORCE_MOBIKE'} = $confighash{$cgiparams{'KEY'}}[32]; if (!$cgiparams{'DPD_DELAY'}) { $cgiparams{'DPD_DELAY'} = 30; } if (!$cgiparams{'DPD_TIMEOUT'}) { $cgiparams{'DPD_TIMEOUT'} = 120; } } 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 (($cgiparams{'REMOTE'} ne '%any') && (! &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'}"; } } } } my @local_subnets = split(",", $cgiparams{'LOCAL_SUBNET'}); foreach my $subnet (@local_subnets) { unless (&Network::check_subnet($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') { my @remote_subnets = split(",", $cgiparams{'REMOTE_SUBNET'}); foreach my $subnet (@remote_subnets) { unless (&Network::check_subnet($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{'TYPE'} eq 'net'){ $warnmessage=&General::checksubnets('',$cgiparams{'REMOTE_SUBNET'},'ipsec'); if ($warnmessage ne ''){ $warnmessage=$Lang::tr{'remote subnet'}." ($cgiparams{'REMOTE_SUBNET'})
".$warnmessage; } } 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 -md sha256 -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 Client 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:2048"; $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 client 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 ""; print ""; } &Header::openbox('100%', 'left', "$Lang::tr{'connection'}: $cgiparams{'NAME'}"); print ""; if (!$cgiparams{'KEY'}) { print < EOF } my $disabled; my $blob; if ($cgiparams{'TYPE'} eq 'host') { $disabled = "disabled='disabled'"; } elsif ($cgiparams{'TYPE'} eq 'net') { $blob = "*"; }; my @local_subnets = split(/\|/, $cgiparams{'LOCAL_SUBNET'}); my $local_subnets = join(",", @local_subnets); my @remote_subnets = split(/\|/, $cgiparams{'REMOTE_SUBNET'}); my $remote_subnets = join(",", @remote_subnets); print < END ; if (!$cgiparams{'KEY'}) { print ""; } print "
$Lang::tr{'name'}: *
$Lang::tr{'enabled'} $Lang::tr{'local subnet'} *
$Lang::tr{'remote host/ip'}: $blob $Lang::tr{'remote subnet'} $blob
$Lang::tr{'vpn local id'}: $Lang::tr{'vpn remote id'}:

$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 $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'}) { my @temp = split('\|', $cgiparams{'IKE_ENCRYPTION'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(aes(256|192|128)(gcm(128|96|64))?|3des|camellia(256|192|128))$/) { $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|384|256)|sha|md5|aesxcbc)$/) { $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 !~ /^(curve25519|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|2048s256|2048s224|2048s160|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 !~ /^(aes(256|192|128)(gcm(128|96|64))?|3des|camellia(256|192|128))$/) { $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|384|256)|sha1|md5|aesxcbc)$/) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } } @temp = split('\|', $cgiparams{'ESP_GROUPTYPE'}); if ($#temp < 0) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } foreach my $val (@temp) { if ($val !~ /^(curve25519|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|2048s256|2048s224|2048s160|3072|4096|6144|8192|none)$/) { $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{'COMPRESSION'} !~ /^(|on|off)$/) || ($cgiparams{'FORCE_MOBIKE'} !~ /^(|on|off)$/) || ($cgiparams{'ONLY_PROPOSED'} !~ /^(|on|off)$/) || ($cgiparams{'PFS'} !~ /^(|on|off)$/)) { $errormessage = $Lang::tr{'invalid input'}; goto ADVANCED_ERROR; } if ($cgiparams{'DPD_DELAY'} !~ /^\d+$/) { $errormessage = $Lang::tr{'invalid input for dpd delay'}; goto ADVANCED_ERROR; } if ($cgiparams{'DPD_TIMEOUT'} !~ /^\d+$/) { $errormessage = $Lang::tr{'invalid input for dpd timeout'}; goto ADVANCED_ERROR; } $confighash{$cgiparams{'KEY'}}[29] = $cgiparams{'IKE_VERSION'}; $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] = 'off'; #$cgiparams{'AGGRMODE'}; $confighash{$cgiparams{'KEY'}}[13] = $cgiparams{'COMPRESSION'}; $confighash{$cgiparams{'KEY'}}[24] = $cgiparams{'ONLY_PROPOSED'}; $confighash{$cgiparams{'KEY'}}[28] = $cgiparams{'PFS'}; $confighash{$cgiparams{'KEY'}}[27] = $cgiparams{'DPD_ACTION'}; $confighash{$cgiparams{'KEY'}}[30] = $cgiparams{'DPD_TIMEOUT'}; $confighash{$cgiparams{'KEY'}}[31] = $cgiparams{'DPD_DELAY'}; $confighash{$cgiparams{'KEY'}}[32] = $cgiparams{'FORCE_MOBIKE'}; $confighash{$cgiparams{'KEY'}}[33] = $cgiparams{'START_ACTION'}; &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_VERSION'} = $confighash{$cgiparams{'KEY'}}[29]; $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]; if ($cgiparams{'ESP_GROUPTYPE'} eq "") { $cgiparams{'ESP_GROUPTYPE'} = $cgiparams{'IKE_GROUPTYPE'}; } $cgiparams{'ESP_KEYLIFE'} = $confighash{$cgiparams{'KEY'}}[17]; $cgiparams{'COMPRESSION'} = $confighash{$cgiparams{'KEY'}}[13]; $cgiparams{'ONLY_PROPOSED'} = $confighash{$cgiparams{'KEY'}}[24]; $cgiparams{'PFS'} = $confighash{$cgiparams{'KEY'}}[28]; $cgiparams{'DPD_ACTION'} = $confighash{$cgiparams{'KEY'}}[27]; $cgiparams{'DPD_TIMEOUT'} = $confighash{$cgiparams{'KEY'}}[30]; $cgiparams{'DPD_DELAY'} = $confighash{$cgiparams{'KEY'}}[31]; $cgiparams{'FORCE_MOBIKE'} = $confighash{$cgiparams{'KEY'}}[32]; $cgiparams{'START_ACTION'} = $confighash{$cgiparams{'KEY'}}[33]; if (!$cgiparams{'DPD_DELAY'}) { $cgiparams{'DPD_DELAY'} = 30; } if (!$cgiparams{'DPD_TIMEOUT'}) { $cgiparams{'DPD_TIMEOUT'} = 120; } if (!$cgiparams{'START_ACTION'}) { $cgiparams{'START_ACTION'} = "start"; } } ADVANCED_ERROR: $checked{'IKE_ENCRYPTION'}{'aes256'} = ''; $checked{'IKE_ENCRYPTION'}{'aes192'} = ''; $checked{'IKE_ENCRYPTION'}{'aes128'} = ''; $checked{'IKE_ENCRYPTION'}{'aes256gcm128'} = ''; $checked{'IKE_ENCRYPTION'}{'aes192gcm128'} = ''; $checked{'IKE_ENCRYPTION'}{'aes128gcm128'} = ''; $checked{'IKE_ENCRYPTION'}{'aes256gcm96'} = ''; $checked{'IKE_ENCRYPTION'}{'aes192gcm96'} = ''; $checked{'IKE_ENCRYPTION'}{'aes128gcm96'} = ''; $checked{'IKE_ENCRYPTION'}{'aes256gcm64'} = ''; $checked{'IKE_ENCRYPTION'}{'aes192gcm64'} = ''; $checked{'IKE_ENCRYPTION'}{'aes128gcm64'} = ''; $checked{'IKE_ENCRYPTION'}{'3des'} = ''; $checked{'IKE_ENCRYPTION'}{'camellia256'} = ''; $checked{'IKE_ENCRYPTION'}{'camellia192'} = ''; $checked{'IKE_ENCRYPTION'}{'camellia128'} = ''; 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_384'} = ''; $checked{'IKE_INTEGRITY'}{'sha2_256'} = ''; $checked{'IKE_INTEGRITY'}{'sha'} = ''; $checked{'IKE_INTEGRITY'}{'md5'} = ''; $checked{'IKE_INTEGRITY'}{'aesxcbc'} = ''; @temp = split('\|', $cgiparams{'IKE_INTEGRITY'}); foreach my $key (@temp) {$checked{'IKE_INTEGRITY'}{$key} = "selected='selected'"; } $checked{'IKE_GROUPTYPE'}{'curve25519'} = ''; $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'}{'aes192'} = ''; $checked{'ESP_ENCRYPTION'}{'aes128'} = ''; $checked{'ESP_ENCRYPTION'}{'aes256gcm128'} = ''; $checked{'ESP_ENCRYPTION'}{'aes192gcm128'} = ''; $checked{'ESP_ENCRYPTION'}{'aes128gcm128'} = ''; $checked{'ESP_ENCRYPTION'}{'aes256gcm96'} = ''; $checked{'ESP_ENCRYPTION'}{'aes192gcm96'} = ''; $checked{'ESP_ENCRYPTION'}{'aes128gcm96'} = ''; $checked{'ESP_ENCRYPTION'}{'aes256gcm64'} = ''; $checked{'ESP_ENCRYPTION'}{'aes192gcm64'} = ''; $checked{'ESP_ENCRYPTION'}{'aes128gcm64'} = ''; $checked{'ESP_ENCRYPTION'}{'3des'} = ''; $checked{'ESP_ENCRYPTION'}{'camellia256'} = ''; $checked{'ESP_ENCRYPTION'}{'camellia192'} = ''; $checked{'ESP_ENCRYPTION'}{'camellia128'} = ''; @temp = split('\|', $cgiparams{'ESP_ENCRYPTION'}); foreach my $key (@temp) {$checked{'ESP_ENCRYPTION'}{$key} = "selected='selected'"; } $checked{'ESP_INTEGRITY'}{'sha2_512'} = ''; $checked{'ESP_INTEGRITY'}{'sha2_384'} = ''; $checked{'ESP_INTEGRITY'}{'sha2_256'} = ''; $checked{'ESP_INTEGRITY'}{'sha1'} = ''; $checked{'ESP_INTEGRITY'}{'md5'} = ''; $checked{'ESP_INTEGRITY'}{'aesxcbc'} = ''; @temp = split('\|', $cgiparams{'ESP_INTEGRITY'}); foreach my $key (@temp) {$checked{'ESP_INTEGRITY'}{$key} = "selected='selected'"; } $checked{'ESP_GROUPTYPE'}{'curve25519'} = ''; $checked{'ESP_GROUPTYPE'}{'768'} = ''; $checked{'ESP_GROUPTYPE'}{'1024'} = ''; $checked{'ESP_GROUPTYPE'}{'1536'} = ''; $checked{'ESP_GROUPTYPE'}{'2048'} = ''; $checked{'ESP_GROUPTYPE'}{'3072'} = ''; $checked{'ESP_GROUPTYPE'}{'4096'} = ''; $checked{'ESP_GROUPTYPE'}{'6144'} = ''; $checked{'ESP_GROUPTYPE'}{'8192'} = ''; $checked{'ESP_GROUPTYPE'}{'none'} = ''; @temp = split('\|', $cgiparams{'ESP_GROUPTYPE'}); foreach my $key (@temp) {$checked{'ESP_GROUPTYPE'}{$key} = "selected='selected'"; } $checked{'COMPRESSION'} = $cgiparams{'COMPRESSION'} eq 'on' ? "checked='checked'" : '' ; $checked{'FORCE_MOBIKE'} = $cgiparams{'FORCE_MOBIKE'} eq 'on' ? "checked='checked'" : '' ; $checked{'ONLY_PROPOSED'} = $cgiparams{'ONLY_PROPOSED'} eq 'on' ? "checked='checked'" : '' ; $checked{'PFS'} = $cgiparams{'PFS'} eq 'on' ? "checked='checked'" : '' ; $selected{'IKE_VERSION'}{'ikev1'} = ''; $selected{'IKE_VERSION'}{'ikev2'} = ''; $selected{'IKE_VERSION'}{$cgiparams{'IKE_VERSION'}} = "selected='selected'"; $selected{'DPD_ACTION'}{'clear'} = ''; $selected{'DPD_ACTION'}{'hold'} = ''; $selected{'DPD_ACTION'}{'restart'} = ''; $selected{'DPD_ACTION'}{'none'} = ''; $selected{'DPD_ACTION'}{$cgiparams{'DPD_ACTION'}} = "selected='selected'"; $selected{'START_ACTION'}{'route'} = ''; $selected{'START_ACTION'}{'start'} = ''; $selected{'START_ACTION'}{$cgiparams{'START_ACTION'}} = "selected='selected'"; &Header::showhttpheaders(); &Header::openpage($Lang::tr{'ipsec'}, 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 <
IKE ESP
$Lang::tr{'vpn keyexchange'}:
$Lang::tr{'encryption'}
$Lang::tr{'integrity'}
$Lang::tr{'lifetime'} * $Lang::tr{'hours'} $Lang::tr{'hours'}
$Lang::tr{'grouptype'}


$Lang::tr{'dead peer detection'}

$Lang::tr{'dpd action'}:
$Lang::tr{'dpd timeout'}: *
$Lang::tr{'dpd delay'}: *

* $Lang::tr{'required field'}
EOF &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/local/bin/ipsecctrl I 2>/dev/null`; # 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{'ENABLED'} = $cgiparams{'ENABLED'} eq 'on' ? "checked='checked'" : ''; &Header::showhttpheaders(); &Header::openpage($Lang::tr{'ipsec'}, 1, ''); &Header::openbigbox('100%', 'left', '', $errormessage); if ($errormessage) { &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); print "$errormessage\n"; print " \n"; &Header::closebox(); } if ($warnmessage) { &Header::openbox('100%', 'left', $Lang::tr{'warning messages'}); print "$warnmessage
"; print "$Lang::tr{'fwdfw warn1'}
"; &Header::closebox(); print"
"; &Header::closepage(); exit 0; } &Header::openbox('100%', 'left', $Lang::tr{'global settings'}); print < END ; print <
$Lang::tr{'vpn red name'}: * $Lang::tr{'enabled'}
$Lang::tr{'vpn delayed start'}: **
$Lang::tr{'host to net vpn'}:


* $Lang::tr{'required field'}
**  $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 (sort { ncmp ($confighash{$a}[1],$confighash{$b}[1]) } keys %confighash) { if ($confighash{$key}[0] eq 'on') { $gif = 'on.gif'; } else { $gif = 'off.gif'; } if ($id % 2) { print ""; $col="bgcolor='$color{'color20'}'"; } else { print ""; $col="bgcolor='$color{'color22'}'"; } print "$confighash{$key}[1]"; print "" . $Lang::tr{"$confighash{$key}[3]"} . " (" . $Lang::tr{"$confighash{$key}[4]"} . ") $confighash{$key}[29]"; 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]"; my $col1="bgcolor='${Header::colourred}'"; # get real state my $active = "$Lang::tr{'capsclosed'}"; foreach my $line (@status) { if (($line =~ /\"$confighash{$key}[1]\".*IPsec SA established/) || ($line =~ /$confighash{$key}[1]\{.*INSTALLED/)) { $col1="bgcolor='${Header::colourgreen}'"; $active = "$Lang::tr{'capsopen'}"; } elsif ($line =~ /$confighash{$key}[1]\[.*CONNECTING/) { $col1="bgcolor='${Header::colourorange}'"; $active = "$Lang::tr{'vpn connecting'}"; } elsif ($line =~ /$confighash{$key}[1]\{.*ROUTED/) { $col1="bgcolor='${Header::colourorange}'"; $active = "$Lang::tr{'vpn on-demand'}"; } } # move to blue if really down if ($confighash{$key}[0] eq 'off' && $col1 =~ /${Header::colourred}/ ) { $col1="bgcolor='${Header::colourblue}'"; $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 ; my $col1="bgcolor='$color{'color22'}'"; my $col2="bgcolor='$color{'color20'}'"; 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 ""; $col="bgcolor='$color{'color20'}'"; } else { print ""; $col="bgcolor='$color{'color22'}'"; } 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(); sub array_unique($) { my $array = shift; my @unique = (); my %seen = (); foreach my $e (@$array) { next if $seen{$e}++; push(@unique, $e); } return @unique; } sub make_algos($$$$$) { my ($mode, $encs, $ints, $grps, $pfs) = @_; my @algos = (); foreach my $enc (@$encs) { foreach my $int (@$ints) { foreach my $grp (@$grps) { my @algo = ($enc); if ($mode eq "ike") { push(@algo, $int); if ($grp =~ m/^e(.*)$/) { push(@algo, "ecp$1"); } else { push(@algo, "modp$grp"); } } elsif ($mode eq "esp" && $pfs) { my $is_aead = ($enc =~ m/[cg]cm/); if (!$is_aead) { push(@algo, $int); } if ($grp eq "none") { # noop } elsif ($grp =~ m/^e(.*)$/) { push(@algo, "ecp$1"); } else { push(@algo, "modp$grp"); } } push(@algos, join("-", @algo)); } } } return &array_unique(\@algos); } sub make_subnets($) { my $subnets = shift; my @nets = split(/\|/, $subnets); my @cidr_nets = (); foreach my $net (@nets) { my $cidr_net = &General::ipcidr($net); push(@cidr_nets, $cidr_net); } return join(",", @cidr_nets); }