]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - html/cgi-bin/vpnmain.cgi
vpnmain.cgi: Fix typo.
[people/pmueller/ipfire-2.x.git] / html / cgi-bin / vpnmain.cgi
index 601fc74927b535774912dc7b73d229780d8d36e9..8f13cf51fa294a3d8d871ccabe51b719514be1b3 100644 (file)
@@ -26,6 +26,7 @@ use File::Copy;
 use File::Temp qw/ tempfile tempdir /;
 use strict;
 use Sort::Naturally;
+use Sys::Hostname;
 # enable only the following on debugging purpose
 #use warnings;
 #use CGI::Carp 'fatalsToBrowser';
@@ -112,6 +113,7 @@ $cgiparams{'ROOTCERT_EMAIL'} = '';
 $cgiparams{'ROOTCERT_OU'} = '';
 $cgiparams{'ROOTCERT_CITY'} = '';
 $cgiparams{'ROOTCERT_STATE'} = '';
+$cgiparams{'RW_ENDPOINT'} = '';
 $cgiparams{'RW_NET'} = '';
 $cgiparams{'DPD_DELAY'} = '30';
 $cgiparams{'DPD_TIMEOUT'} = '120';
@@ -122,8 +124,38 @@ $cgiparams{'MODE'} = "tunnel";
 $cgiparams{'INTERFACE_MODE'} = "";
 $cgiparams{'INTERFACE_ADDRESS'} = "";
 $cgiparams{'INTERFACE_MTU'} = 1500;
+$cgiparams{'DNS_SERVERS'} = "";
 &Header::getcgihash(\%cgiparams, {'wantfile' => 1, 'filevar' => 'FH'});
 
+my %APPLE_CIPHERS = (
+       "aes256gcm128" => "AES-256-GCM",
+       "aes128gcm128" => "AES-128-GCM",
+       "aes256"       => "AES-256",
+       "aes128"       => "AES-128",
+       "3des"         => "3DES",
+);
+
+my %APPLE_INTEGRITIES = (
+       "sha2_512" => "SHA2-512",
+       "sha2_384" => "SHA2-384",
+       "sha2_256" => "SHA2-256",
+       "sha1"     => "SHA1-160",
+);
+
+my %APPLE_DH_GROUPS = (
+       "768" => 1,
+       "1024" => 2,
+       "1536" => 5,
+       "2048" => 14,
+       "3072" => 15,
+       "4096" => 16,
+       "6144" => 17,
+       "8192" => 18,
+       "e256" => 19,
+       "e384" => 20,
+       "e521" => 21,
+);
+
 ###
 ### Useful functions
 ###
@@ -176,10 +208,12 @@ sub newcleanssldatabase {
                close FILE;
        }
        if (! -s ">${General::swroot}/certs/index.txt") {
-               system ("touch ${General::swroot}/certs/index.txt");
+               open(FILE, ">${General::swroot}/certs/index.txt");
+               close(FILE);
        }
        if (! -s ">${General::swroot}/certs/index.txt.attr") {
-               system ("touch ${General::swroot}/certs/index.txt.attr");
+               open(FILE, ">${General::swroot}/certs/index.txt.attr");
+               close(FILE);
        }
        unlink ("${General::swroot}/certs/index.txt.old");
        unlink ("${General::swroot}/certs/index.txt.attr.old");
@@ -192,9 +226,13 @@ sub newcleanssldatabase {
 ###
 sub callssl ($) {
        my $opt = shift;
-       my $retssl = `/usr/bin/openssl $opt 2>&1`; #redirect stderr
+
+       # Split the given argument string into single pieces and assign them to an array.
+       my @opts = split(/ /, $opt);
+
+       my @retssl = &General::system_output("/usr/bin/openssl", @opts); #redirect stderr
        my $ret = '';
-       foreach my $line (split (/\n/, $retssl)) {
+       foreach my $line (split (/\n/, @retssl)) {
                &General::log("ipsec", "$line") if (0); # 1 for verbose logging
                $ret .= '<br>'.$line if ( $line =~ /error|unknown/ );
        }
@@ -208,13 +246,21 @@ sub callssl ($) {
 ###
 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;
+       my @temp = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "$_[0]");
+       my $temp;
+
+       foreach my $line (@temp) {
+               if ($line =~ /Subject:.*CN = (.*)[\n]/) {
+                       $temp = $1;
+                       $temp =~ s+/Email+, E+;
+                       $temp =~ s/ ST = / S = /;
+                       $temp =~ s/,//g;
+                       $temp =~ s/\'//g;
+
+                       last;
+               }
+       }
+
        return $temp;
 }
 ###
@@ -222,11 +268,19 @@ sub getCNfromcert ($) {
 ###
 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 = /;
+       my @temp = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "$_[0]");
+       my $temp;
+
+       foreach my $line (@temp) {
+               if($line =~ /Subject: (.*)[\n]/) {
+                       $temp = $1;
+                       $temp =~ s+/Email+, E+;
+                       $temp =~ s/ ST = / S = /;
+
+                       last;
+               }
+       }
+
        return $temp;
 }
 ###
@@ -318,6 +372,12 @@ sub writeipsecfiles {
 
                print CONF "\tleftfirewall=yes\n";
                print CONF "\tlefthostaccess=yes\n";
+
+               # Always send the host certificate
+               if ($lconfighash{$key}[3] eq 'host') {
+                       print CONF "\tleftsendcert=always\n";
+               }
+
                print CONF "\tright=$lconfighash{$key}[10]\n";
 
                if ($lconfighash{$key}[3] eq 'net') {
@@ -480,6 +540,13 @@ sub writeipsecfiles {
                # Fragmentation
                print CONF "\tfragmentation=yes\n";
 
+               # DNS Servers for RW
+               if ($lconfighash{$key}[3] eq 'host') {
+                       my @servers = split(/\|/, $lconfighash{$key}[39]);
+
+                       print CONF "\trightdns=" . join(",", @servers) . "\n";
+               }
+
                print CONF "\n";
        } #foreach key
 
@@ -507,19 +574,25 @@ if ($ENV{"REMOTE_ADDR"} eq "") {
 if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} && $cgiparams{'TYPE'} eq '' && $cgiparams{'KEY'} eq '') {
        &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings);
 
+       if ($cgiparams{'RW_ENDPOINT'} ne '' && !&General::validip($cgiparams{'RW_ENDPOINT'}) && !&General::validfqdn($cgiparams{'RW_ENDPOINT'})) {
+               $errormessage = $Lang::tr{'ipsec invalid ip address or fqdn for rw endpoint'};
+               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{'RW_ENDPOINT'} = $cgiparams{'RW_ENDPOINT'};
        $vpnsettings{'RW_NET'} = $cgiparams{'RW_NET'};
        &General::writehash("${General::swroot}/vpn/settings", \%vpnsettings);
        &writeipsecfiles();
        if (&vpnenabled) {
-               system('/usr/local/bin/ipsecctrl', 'S');
+               &General::system('/usr/local/bin/ipsecctrl', 'S');
        } else {
-               system('/usr/local/bin/ipsecctrl', 'D');
+               &General::system('/usr/local/bin/ipsecctrl', 'D');
        }
        sleep $sleepDelay;
        SAVE_ERROR:
@@ -544,7 +617,7 @@ if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} && $cgiparams{'TYPE'} eq '' && $cg
        }
        &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
        &writeipsecfiles();
-       system('/usr/local/bin/ipsecctrl', 'R');
+       &General::system('/usr/local/bin/ipsecctrl', 'R');
        sleep $sleepDelay;
 
 ###
@@ -616,8 +689,8 @@ END
                $errormessage = $!;
                goto UPLOADCA_ERROR;
        }
-       my $temp = `/usr/bin/openssl x509 -text -in $filename`;
-       if ($temp !~ /CA:TRUE/i) {
+       my @temp = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "$filename");
+       if (! grep(/CA:TRUE/, @temp)) {
                $errormessage = $Lang::tr{'not a valid ca certificate'};
                unlink ($filename);
                goto UPLOADCA_ERROR;
@@ -635,7 +708,7 @@ END
        $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');
+       &General::system('/usr/local/bin/ipsecctrl', 'R');
        sleep $sleepDelay;
 
        UPLOADCA_ERROR:
@@ -651,9 +724,9 @@ END
                &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 "<pre>$output</pre>\n";
+               my @output = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem");
+               @output = &Header::cleanhtml(@output,"y");
+               print "<pre>@output</pre>\n";
                &Header::closebox();
                print "<div align='center'><a href='/cgi-bin/vpnmain.cgi'>$Lang::tr{'back'}</a></div>";
                &Header::closebigbox();
@@ -673,7 +746,9 @@ END
                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`;
+
+               my @cert = &General::system_output("/usr/bin/openssl", "x509", "-in", "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem");
+               print "@cert";
                exit(0);
        } else {
                $errormessage = $Lang::tr{'invalid key'};
@@ -688,21 +763,21 @@ END
 
        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/) {
+                       my @test = &General::system_output("/usr/bin/openssl", "verify", "-CAfile", "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem", "${General::swroot}/certs/$confighash{$key}[1]cert.pem");
+                       if (grep(/: OK/, @test)) {
                                # Delete connection
                                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();
-                               system('/usr/local/bin/ipsecctrl', 'D', $key) if (&vpnenabled);
+                               &General::system('/usr/local/bin/ipsecctrl', 'D', $key) if (&vpnenabled);
                        }
                }
                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');
+               &General::system('/usr/local/bin/ipsecctrl', 'R');
                sleep $sleepDelay;
        } else {
                $errormessage = $Lang::tr{'invalid key'};
@@ -717,8 +792,8 @@ END
        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/) {
+                       my @test = &General::system_output("/usr/bin/openssl", "verify", "-CAfile", "${General::swroot}/ca/$cahash{$cgiparams{'KEY'}}[0]cert.pem", "${General::swroot}/certs/$confighash{$key}[1]cert.pem");
+                       if (grep(/: OK/, @test)) {
                                $assignedcerts++;
                        }
                }
@@ -754,7 +829,7 @@ END
                        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');
+                       &General::system('/usr/local/bin/ipsecctrl', 'R');
                        sleep $sleepDelay;
                }
        } else {
@@ -766,19 +841,19 @@ END
 ###
 } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'show root certificate'} ||
        $cgiparams{'ACTION'} eq $Lang::tr{'show host certificate'}) {
-       my $output;
+       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`;
+               @output = &General::system_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 = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "${General::swroot}/certs/hostcert.pem");
        }
-       $output = &Header::cleanhtml($output,"y");
-       print "<pre>$output</pre>\n";
+       @output = &Header::cleanhtml(@output,"y");
+       print "<pre>@output</pre>\n";
        &Header::closebox();
        print "<div align='center'><a href='/cgi-bin/vpnmain.cgi'>$Lang::tr{'back'}</a></div>";
        &Header::closebigbox();
@@ -792,7 +867,9 @@ END
        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`;
+
+               my @cert = &General::system_output("/usr/bin/openssl", "x509", "-in", "${General::swroot}/ca/cacert.pem");
+               print "@cert";
                exit(0);
        }
 ###
@@ -802,7 +879,9 @@ END
        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`;
+
+               my @cert = &General::system_output("/usr/bin/openssl", "x509", "-in", "${General::swroot}/certs/hostcert.pem");
+               print "@cert";
                exit(0);
        }
 ###
@@ -1165,7 +1244,7 @@ END
 
        ROOTCERT_SUCCESS:
        if (&vpnenabled) {
-               system('/usr/local/bin/ipsecctrl', 'S');
+               &General::system('/usr/local/bin/ipsecctrl', 'S');
                sleep $sleepDelay;
        }
        ROOTCERT_SKIP:
@@ -1177,11 +1256,20 @@ END
        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`;
+
+       open(FILE, "${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1].p12");
+       my @p12 = <FILE>;
+       close(FILE);
+       print "@p12";
+
        exit (0);
 
 # Export Apple profile to browser
 } elsif ($cgiparams{'ACTION'} eq $Lang::tr{'download apple profile'}) {
+       # Read global configuration
+       &General::readhash("${General::swroot}/vpn/settings", \%vpnsettings);
+
+       # Read connections
        &General::readhasharray("${General::swroot}/vpn/config", \%confighash);
        my $key = $cgiparams{'KEY'};
 
@@ -1191,13 +1279,23 @@ END
        my $uuid1 = $uuid->create_str();
        my $uuid2 = $uuid->create_str();
 
+       my $ca = "";
+       my $ca_uuid = $uuid->create_str();
+
        my $cert = "";
        my $cert_uuid = $uuid->create_str();
 
-       # Read and encode certificate
+       # Read and encode the CA & certificate
        if ($confighash{$key}[4] eq "cert") {
+               my $ca_path = "${General::swroot}/ca/cacert.pem";
                my $cert_path = "${General::swroot}/certs/$confighash{$key}[1].p12";
 
+               # Read the CA and encode it into Base64
+               open(CA, "<${ca_path}");
+               local($/) = undef; # slurp
+               $ca = MIME::Base64::encode_base64(<CA>);
+               close(CA);
+
                # Read certificate and encode it into Base64
                open(CERT, "<${cert_path}");
                local($/) = undef; # slurp
@@ -1209,6 +1307,9 @@ END
        print "Content-Disposition: attachment; filename=" . $confighash{$key}[1] . ".mobileconfig\n";
        print "\n"; # end headers
 
+       # Use our own FQDN if nothing else is configured
+       my $endpoint = ($vpnsettings{'RW_ENDPOINT'} ne "") ? $vpnsettings{'RW_ENDPOINT'} : &hostname();
+
        print "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
        print "<plist version=\"1.0\">\n";
        print " <dict>\n";
@@ -1240,35 +1341,143 @@ END
        print "                         <key>IKEv2</key>\n";
        print "                         <dict>\n";
        print "                                 <key>RemoteAddress</key>\n";
-       print "                                 <string>18.206.152.26</string>\n";
+       print "                                 <string>$endpoint</string>\n";
+
+       # PFS
+       my $pfs = $confighash{$key}[28];
+       if ($pfs eq "on") {
+               print "                                 <key>EnablePFS</key>\n";
+               print "                                 <true/>\n";
+       }
+
+       # IKE Cipher Suite
+       print "                                 <key>IKESecurityAssociationParameters</key>\n";
+       print "                                 <dict>\n";
+
+       # Encryption
+       foreach my $cipher (split(/\|/,$confighash{$key}[18])) {
+               # Skip all unsupported ciphers
+               next unless (exists $APPLE_CIPHERS{$cipher});
+
+               print "                                         <key>EncryptionAlgorithm</key>\n";
+               print "                                         <string>$APPLE_CIPHERS{$cipher}</string>\n";
+               last;
+       }
+
+       # Integrity
+       foreach my $integrity (split(/\|/,$confighash{$key}[19])) {
+               # Skip all unsupported algorithms
+               next unless (exists $APPLE_INTEGRITIES{$integrity});
+
+               print "                                         <key>IntegrityAlgorithm</key>\n";
+               print "                                         <string>$APPLE_INTEGRITIES{$integrity}</string>\n";
+               last;
+       }
+
+       # Diffie Hellman Groups
+       foreach my $group (split(/\|/,$confighash{$key}[20])) {
+               # Skip all unsupported algorithms
+               next unless (exists $APPLE_DH_GROUPS{$group});
+
+               print "                                         <key>DiffieHellmanGroup</key>\n";
+               print "                                         <string>$APPLE_DH_GROUPS{$group}</string>\n";
+               last;
+       }
+
+       # Lifetime
+       my $lifetime = $confighash{$key}[16] * 60;
+       print "                                         <key>LifeTimeInMinutes</key>\n";
+       print "                                         <integer>$lifetime</integer>\n";
+       print "                                 </dict>\n";
+
+       # ESP Cipher Suite
+       print "                                 <key>ChildSecurityAssociationParameters</key>\n";
+       print "                                 <dict>\n";
+
+       # Encryption
+       foreach my $cipher (split(/\|/,$confighash{$key}[21])) {
+               # Skip all unsupported ciphers
+               next unless (exists $APPLE_CIPHERS{$cipher});
+
+               print "                                         <key>EncryptionAlgorithm</key>\n";
+               print "                                         <string>$APPLE_CIPHERS{$cipher}</string>\n";
+               last;
+       }
+
+       # Integrity
+       foreach my $integrity (split(/\|/,$confighash{$key}[22])) {
+               # Skip all unsupported algorithms
+               next unless (exists $APPLE_INTEGRITIES{$integrity});
+
+               print "                                         <key>IntegrityAlgorithm</key>\n";
+               print "                                         <string>$APPLE_INTEGRITIES{$integrity}</string>\n";
+               last;
+       }
+
+       # Diffie Hellman Groups
+       foreach my $group (split(/\|/,$confighash{$key}[23])) {
+               # Skip all unsupported algorithms
+               next unless (exists $APPLE_DH_GROUPS{$group});
+
+               print "                                         <key>DiffieHellmanGroup</key>\n";
+               print "                                         <string>$APPLE_DH_GROUPS{$group}</string>\n";
+               last;
+       }
+
+       # Lifetime
+       my $lifetime = $confighash{$key}[17] * 60;
+       print "                                         <key>LifeTimeInMinutes</key>\n";
+       print "                                         <integer>$lifetime</integer>\n";
+       print "                                 </dict>\n";
+
 
        # Left ID
        if ($confighash{$key}[9]) {
-               print "                         <key>LocalIdentifier</key>\n";
-               print "                         <string>$confighash{$key}[9]</string>\n";
+               my $leftid = $confighash{$key}[9];
+
+               # Strip leading @ from FQDNs
+               if ($leftid =~ m/^@(.*)$/) {
+                       $leftid = $1;
+               }
+
+               print "                                 <key>LocalIdentifier</key>\n";
+               print "                                 <string>$leftid</string>\n";
        }
 
        # Right ID
        if ($confighash{$key}[7]) {
-               print "                         <key>RemoteIdentifier</key>\n";
-               print "                         <string>$confighash{$key}[7]</string>\n";
+               my $rightid = $confighash{$key}[7];
+
+               # Strip leading @ from FQDNs
+               if ($rightid =~ m/^@(.*)$/) {
+                       $rightid = $1;
+               }
+
+               print "                                 <key>RemoteIdentifier</key>\n";
+               print "                                 <string>$rightid</string>\n";
        }
 
        if ($confighash{$key}[4] eq "cert") {
-               print "                         <key>AuthenticationMethod</key>\n";
-               print "                         <string>Certificate</string>\n";
+               print "                                 <key>AuthenticationMethod</key>\n";
+               print "                                 <string>Certificate</string>\n";
 
-               print "                         <key>PayloadCertificateUUID</key>\n";
-               print "                         <string>${cert_uuid}</string>\n";
+               print "                                 <key>PayloadCertificateUUID</key>\n";
+               print "                                 <string>${cert_uuid}</string>\n";
        } else {
-               print "                         <key>AuthenticationMethod</key>\n";
-               print "                         <string>SharedSecret</string>\n";
-               print "                         <key>SharedSecret</key>\n";
-               print "                         <string>$confighash{$key}[5]</string>\n";
+               print "                                 <key>AuthenticationMethod</key>\n";
+               print "                                 <string>SharedSecret</string>\n";
+               print "                                 <key>SharedSecret</key>\n";
+               print "                                 <string>$confighash{$key}[5]</string>\n";
        }
 
        print "                                 <key>ExtendedAuthEnabled</key>\n";
        print "                                 <integer>0</integer>\n";
+
+       # These are not needed, but we provide some default to stop iPhone asking for credentials
+       print "                                 <key>AuthName</key>\n";
+       print "                                 <string>$confighash{$key}[1]</string>\n";
+       print "                                 <key>AuthPassword</key>\n";
+       print "                                 <string></string>\n";
        print "                         </dict>\n";
        print "                 </dict>\n";
 
@@ -1276,6 +1485,8 @@ END
                print "                 <dict>\n";
                print "                         <key>PayloadIdentifier</key>\n";
                print "                         <string>org.example.vpn1.client</string>\n";
+               print "                         <key>PayloadDisplayName</key>\n";
+               print "                         <string>$confighash{$key}[1]</string>\n";
                print "                         <key>PayloadUUID</key>\n";
                print "                         <string>${cert_uuid}</string>\n";
                print "                         <key>PayloadType</key>\n";
@@ -1291,6 +1502,25 @@ END
 
                print "                         </data>\n";
                print "                 </dict>\n";
+
+               print "                 <dict>\n";
+               print "                         <key>PayloadIdentifier</key>\n";
+               print "                         <string>org.example.ca</string>\n";
+               print "                         <key>PayloadUUID</key>\n";
+               print "                         <string>${ca_uuid}</string>\n";
+               print "                         <key>PayloadType</key>\n";
+               print "                         <string>com.apple.security.root</string>\n";
+               print "                         <key>PayloadVersion</key>\n";
+               print "                         <integer>1</integer>\n";
+               print "                         <key>PayloadContent</key>\n";
+               print "                         <data>\n";
+
+               foreach (split /\n/,${ca}) {
+                       print "                                 $_\n";
+               }
+
+               print "                         </data>\n";
+               print "                 </dict>\n";
        }
 
        print "         </array>\n";
@@ -1310,9 +1540,9 @@ END
                &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 "<pre>$output</pre>\n";
+               my @output = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem");
+               @output = &Header::cleanhtml(@output,"y");
+               print "<pre>@output</pre>\n";
                &Header::closebox();
                print "<div align='center'><a href='/cgi-bin/vpnmain.cgi'>$Lang::tr{'back'}</a></div>";
                &Header::closebigbox();
@@ -1329,7 +1559,12 @@ END
        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`;
+
+               open(FILE, "${General::swroot}/certs/$confighash{$cgiparams{'KEY'}}[1]cert.pem");
+               my @pem = <FILE>;
+               close(FILE);
+               print "@pem";
+
                exit (0);
        }
 
@@ -1346,12 +1581,12 @@ END
                        $confighash{$cgiparams{'KEY'}}[0] = 'on';
                        &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
                        &writeipsecfiles();
-                       system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'}) if (&vpnenabled);
+                       &General::system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'}) if (&vpnenabled);
                } else {
                        $confighash{$cgiparams{'KEY'}}[0] = 'off';
                        &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
                        &writeipsecfiles();
-                       system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled);
+                       &General::system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled);
                }
                sleep $sleepDelay;
        } else {
@@ -1367,7 +1602,7 @@ END
 
        if ($confighash{$cgiparams{'KEY'}}) {
                if (&vpnenabled) {
-                       system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'});
+                       &General::system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'});
                        sleep $sleepDelay;
                }
        } else {
@@ -1387,7 +1622,7 @@ END
                delete $confighash{$cgiparams{'KEY'}};
                &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
                &writeipsecfiles();
-               system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled);
+               &General::system('/usr/local/bin/ipsecctrl', 'D', $cgiparams{'KEY'}) if (&vpnenabled);
        } else {
                $errormessage = $Lang::tr{'invalid key'};
        }
@@ -1474,6 +1709,7 @@ END
                $cgiparams{'INTERFACE_MODE'}            = $confighash{$cgiparams{'KEY'}}[36];
                $cgiparams{'INTERFACE_ADDRESS'}         = $confighash{$cgiparams{'KEY'}}[37];
                $cgiparams{'INTERFACE_MTU'}             = $confighash{$cgiparams{'KEY'}}[38];
+               $cgiparams{'DNS_SERVERS'}               = $confighash{$cgiparams{'KEY'}}[39];
 
                if (!$cgiparams{'DPD_DELAY'}) {
                        $cgiparams{'DPD_DELAY'} = 30;
@@ -1607,6 +1843,16 @@ END
                        }
                }
 
+               if ($cgiparams{'TYPE'} eq 'host') {
+                       my @servers = split(",", $cgiparams{'DNS_SERVERS'});
+                       foreach my $server (@servers) {
+                               unless (&Network::check_ip_address($server)) {
+                                       $errormessage = $Lang::tr{'ipsec dns server address is invalid'};
+                                       goto VPNCONF_ERROR;
+                               }
+                       }
+               }
+
                if ($cgiparams{'ENABLED'} !~ /^(on|off)$/) {
                        $errormessage = $Lang::tr{'invalid input'};
                        goto VPNCONF_ERROR;
@@ -1744,8 +1990,8 @@ END
                                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) {
+                               my @temp = &General::system_output("/usr/bin/openssl", "x509", "-text", "-in", "/tmp/newcacert");
+                               if (! grep(/CA:TRUE/, @temp)) {
                                        $errormessage = $Lang::tr{'not a valid ca certificate'};
                                } else {
                                        #compute a name for it
@@ -1760,7 +2006,7 @@ END
                                                $cahash{$key}[0] = $cgiparams{'CA_NAME'};
                                                $cahash{$key}[1] = $casubject;
                                                &General::writehasharray("${General::swroot}/vpn/caconfig", \%cahash);
-                                               system('/usr/local/bin/ipsecctrl', 'R');
+                                               &General::system('/usr/local/bin/ipsecctrl', 'R');
                                        }
                                }
                        }
@@ -1800,12 +2046,12 @@ END
                # 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 @test = &General::system_output("/usr/bin/openssl", "verify", "-CAfile", "${General::swroot}/ca/cacert.pem", "$filename");
+               if (! grep(/: OK/, @test)) {
                        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/) {
+                               @test = &General::system_output("/usr/bin/openssl", "verify", "-CAfile", "${General::swroot}/ca/$cahash{$key}[0]cert.pem", "$filename");
+                               if (grep(/: OK/, @test)) {
                                        $validca = 1;
                                        last;
                                }
@@ -2009,7 +2255,7 @@ END
        my $key = $cgiparams{'KEY'};
        if (! $key) {
                $key = &General::findhasharraykey (\%confighash);
-               foreach my $i (0 .. 38) { $confighash{$key}[$i] = "";}
+               foreach my $i (0 .. 39) { $confighash{$key}[$i] = "";}
        }
        $confighash{$key}[0] = $cgiparams{'ENABLED'};
        $confighash{$key}[1] = $cgiparams{'NAME'};
@@ -2060,6 +2306,7 @@ END
        $confighash{$key}[36] = $cgiparams{'INTERFACE_MODE'};
        $confighash{$key}[37] = $cgiparams{'INTERFACE_ADDRESS'};
        $confighash{$key}[38] = $cgiparams{'INTERFACE_MTU'};
+       $confighash{$key}[39] = join("|", split(",", $cgiparams{'DNS_SERVERS'}));
 
        # free unused fields!
        $confighash{$key}[15] = 'off';
@@ -2067,7 +2314,7 @@ END
        &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
        &writeipsecfiles();
        if (&vpnenabled) {
-               system('/usr/local/bin/ipsecctrl', 'S', $key);
+               &General::system('/usr/local/bin/ipsecctrl', 'S', $key);
                sleep $sleepDelay;
        }
        if ($cgiparams{'EDIT_ADVANCED'} eq 'on') {
@@ -2128,11 +2375,11 @@ END
        #use default advanced value
        $cgiparams{'IKE_ENCRYPTION'}    = 'chacha20poly1305|aes256gcm128|aes256gcm96|aes256gcm64|aes256|aes192gcm128|aes192gcm96|aes192gcm64|aes192|aes128gcm128|aes128gcm96|aes128gcm64|aes128'; #[18];
        $cgiparams{'IKE_INTEGRITY'}             = 'sha2_512|sha2_256'; #[19];
-       $cgiparams{'IKE_GROUPTYPE'}             = 'curve25519|curve448|4096|3072|2048'; #[20];
+       $cgiparams{'IKE_GROUPTYPE'}             = 'curve448|curve25519|4096|3072|2048'; #[20];
        $cgiparams{'IKE_LIFETIME'}              = '3'; #[16];
        $cgiparams{'ESP_ENCRYPTION'}    = 'chacha20poly1305|aes256gcm128|aes256gcm96|aes256gcm64|aes256|aes192gcm128|aes192gcm96|aes192gcm64|aes192|aes128gcm128|aes128gcm96|aes128gcm64|aes128'; #[21];
        $cgiparams{'ESP_INTEGRITY'}             = 'sha2_512|sha2_256'; #[22];
-       $cgiparams{'ESP_GROUPTYPE'}             = 'curve25519|curve448|4096|3072|2048'; #[23];
+       $cgiparams{'ESP_GROUPTYPE'}             = 'curve448|curve25519|4096|3072|2048'; #[23];
        $cgiparams{'ESP_KEYLIFE'}               = '1'; #[17];
        $cgiparams{'COMPRESSION'}               = 'off'; #[13];
        $cgiparams{'ONLY_PROPOSED'}             = 'on'; #[24];
@@ -2142,6 +2389,7 @@ END
        $cgiparams{'INTERFACE_MODE'}            = "";
        $cgiparams{'INTERFACE_ADDRESS'}         = "";
        $cgiparams{'INTERFACE_MTU'}             = 1500;
+       $cgiparams{'DNS_SERVERS'}               = "";
 }
 
 VPNCONF_ERROR:
@@ -2238,11 +2486,8 @@ END
 EOF
        }
 
-       my $disabled;
-       my $blob;
-       if ($cgiparams{'TYPE'} eq 'host') {
-               $disabled = "disabled='disabled'";
-       } elsif ($cgiparams{'TYPE'} eq 'net') {
+       my $blob = "";
+       if ($cgiparams{'TYPE'} eq 'net') {
                $blob = "<img src='/blob.gif' alt='*' />";
        };
 
@@ -2252,6 +2497,9 @@ EOF
        my @remote_subnets = split(/\|/, $cgiparams{'REMOTE_SUBNET'});
        my $remote_subnets = join(",", @remote_subnets);
 
+       my @dns_servers = split(/\|/, $cgiparams{'DNS_SERVERS'});
+       my $dns_servers = join(",", @dns_servers);
+
        print <<END;
        <tr>
                <td width='20%'>$Lang::tr{'enabled'}</td>
@@ -2287,10 +2535,26 @@ END
                <td width='30%'>
                        <input type='text' name='LOCAL_SUBNET' value='$local_subnets' size="25" />
                </td>
-               <td class='boldbase' nowrap='nowrap' width='20%'>$Lang::tr{'remote subnet'}&nbsp;$blob</td>
+END
+
+       if ($cgiparams{'TYPE'} eq "net") {
+               print <<END;
+               <td class='boldbase' nowrap='nowrap' width='20%'>$Lang::tr{'remote subnet'}&nbsp;<img src='/blob.gif' alt='*' /></td>
                <td width='30%'>
-                       <input $disabled type='text' name='REMOTE_SUBNET' value='$remote_subnets' size="25" />
+                       <input type='text' name='REMOTE_SUBNET' value='$remote_subnets' size="25" />
                </td>
+END
+
+       } elsif ($cgiparams{'TYPE'} eq "host") {
+               print <<END;
+               <td class='boldbase' nowrap='nowrap' width='20%'>$Lang::tr{'dns servers'}:</td>
+               <td width='30%'>
+                       <input type='text' name='DNS_SERVERS' value='$dns_servers' size="25" />
+               </td>
+END
+       }
+
+       print <<END;
        </tr>
        <tr>
                <td class='boldbase' width='20%'>$Lang::tr{'vpn local id'}:</td>
@@ -2496,7 +2760,7 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                        goto ADVANCED_ERROR;
                }
                foreach my $val (@temp) {
-                       if ($val !~ /^(curve25519|curve448|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|3072|4096|6144|8192)$/) {
+                       if ($val !~ /^(curve448|curve25519|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|3072|4096|6144|8192)$/) {
                                $errormessage = $Lang::tr{'invalid input'};
                                goto ADVANCED_ERROR;
                        }
@@ -2537,7 +2801,7 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                        goto ADVANCED_ERROR;
                }
                foreach my $val (@temp) {
-                       if ($val !~ /^(curve25519|curve448|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|3072|4096|6144|8192|none)$/) {
+                       if ($val !~ /^(curve448|curve25519|e521|e384|e256|e224|e192|e512bp|e384bp|e256bp|e224bp|768|1024|1536|2048|3072|4096|6144|8192|none)$/) {
                                $errormessage = $Lang::tr{'invalid input'};
                                goto ADVANCED_ERROR;
                        }
@@ -2596,7 +2860,7 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                &General::writehasharray("${General::swroot}/vpn/config", \%confighash);
                &writeipsecfiles();
                if (&vpnenabled) {
-                       system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'});
+                       &General::system('/usr/local/bin/ipsecctrl', 'S', $cgiparams{'KEY'});
                        sleep $sleepDelay;
                }
                goto ADVANCED_END;
@@ -2626,6 +2890,7 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                $cgiparams{'INTERFACE_MODE'}            = $confighash{$cgiparams{'KEY'}}[36];
                $cgiparams{'INTERFACE_ADDRESS'}         = $confighash{$cgiparams{'KEY'}}[37];
                $cgiparams{'INTERFACE_MTU'}             = $confighash{$cgiparams{'KEY'}}[38];
+               $cgiparams{'DNS_SERVERS'}               = $confighash{$cgiparams{'KEY'}}[39];
 
                if (!$cgiparams{'DPD_DELAY'}) {
                        $cgiparams{'DPD_DELAY'} = 30;
@@ -2676,8 +2941,8 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
        $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'}{'curve448'} = '';
+       $checked{'IKE_GROUPTYPE'}{'curve25519'} = '';
        $checked{'IKE_GROUPTYPE'}{'768'} = '';
        $checked{'IKE_GROUPTYPE'}{'1024'} = '';
        $checked{'IKE_GROUPTYPE'}{'1536'} = '';
@@ -2716,8 +2981,8 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
        $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'}{'curve448'} = '';
+       $checked{'ESP_GROUPTYPE'}{'curve25519'} = '';
        $checked{'ESP_GROUPTYPE'}{'768'} = '';
        $checked{'ESP_GROUPTYPE'}{'1024'} = '';
        $checked{'ESP_GROUPTYPE'}{'1536'} = '';
@@ -2881,8 +3146,8 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                        <td class='boldbase' width="15%">$Lang::tr{'grouptype'}</td>
                        <td class='boldbase'>
                                <select name='IKE_GROUPTYPE' multiple='multiple' size='6' style='width: 100%'>
-                                       <option value='curve25519' $checked{'IKE_GROUPTYPE'}{'curve25519'}>Curve 25519 (256 bit)</option>
                                        <option value='curve448' $checked{'IKE_GROUPTYPE'}{'curve448'}>Curve 448 (224 bit)</option>
+                                       <option value='curve25519' $checked{'IKE_GROUPTYPE'}{'curve25519'}>Curve 25519 (128 bit)</option>
                                        <option value='e521' $checked{'IKE_GROUPTYPE'}{'e521'}>ECP-521 (NIST)</option>
                                        <option value='e512bp' $checked{'IKE_GROUPTYPE'}{'e512bp'}>ECP-512 (Brainpool)</option>
                                        <option value='e384' $checked{'IKE_GROUPTYPE'}{'e384'}>ECP-384 (NIST)</option>
@@ -2904,8 +3169,8 @@ if(($cgiparams{'ACTION'} eq $Lang::tr{'advanced'}) ||
                        </td>
                        <td class='boldbase'>
                                <select name='ESP_GROUPTYPE' multiple='multiple' size='6' style='width: 100%'>
-                                       <option value='curve25519' $checked{'ESP_GROUPTYPE'}{'curve25519'}>Curve 25519 (256 bit)</option>
                                        <option value='curve448' $checked{'ESP_GROUPTYPE'}{'curve448'}>Curve 448 (224 bit)</option>
+                                       <option value='curve25519' $checked{'ESP_GROUPTYPE'}{'curve25519'}>Curve 25519 (128 bit)</option>
                                        <option value='e521' $checked{'ESP_GROUPTYPE'}{'e521'}>ECP-521 (NIST)</option>
                                        <option value='e512bp' $checked{'ESP_GROUPTYPE'}{'e512bp'}>ECP-512 (Brainpool)</option>
                                        <option value='e384' $checked{'ESP_GROUPTYPE'}{'e384'}>ECP-384 (NIST)</option>
@@ -3044,7 +3309,7 @@ EOF
        &General::readhasharray("${General::swroot}/vpn/config", \%confighash);
        $cgiparams{'CA_NAME'} = '';
 
-       my @status = `/usr/local/bin/ipsecctrl I 2>/dev/null`;
+       my @status = &General::system_output("/usr/local/bin/ipsecctrl", "I");
 
        $checked{'ENABLED'} = $cgiparams{'ENABLED'} eq 'on' ? "checked='checked'" : '';
 
@@ -3081,6 +3346,10 @@ EOF
                                <input type='checkbox' name='ENABLED' $checked{'ENABLED'} />
                        </td>
                </tr>
+               <tr>
+                       <td class='base' nowrap='nowrap' width="60%">$Lang::tr{'ipsec roadwarrior endpoint'}:</td>
+                       <td width="40%"><input type='text' name='RW_ENDPOINT' value='$cgiparams{'RW_ENDPOINT'}' /></td>
+               </tr>
                <tr>
                        <td class='base' nowrap='nowrap' width="60%">$Lang::tr{'host to net vpn'}:</td>
                        <td width="40%"><input type='text' name='RW_NET' value='$cgiparams{'RW_NET'}' /></td>
@@ -3474,7 +3743,7 @@ sub make_algos($$$$$) {
 
                                        if ($grp =~ m/^e(.*)$/) {
                                                push(@algo, "ecp$1");
-                                       } elsif ($grp =~ m/curve(25519|448)/) {
+                                       } elsif ($grp =~ m/curve(448|25519)/) {
                                                push(@algo, "$grp");
                                        } else {
                                                push(@algo, "modp$grp");
@@ -3491,7 +3760,7 @@ sub make_algos($$$$$) {
                                                # noop
                                        } elsif ($grp =~ m/^e(.*)$/) {
                                                push(@algo, "ecp$1");
-                                       } elsif ($grp =~ m/curve(25519|448)/) {
+                                       } elsif ($grp =~ m/curve(448|25519)/) {
                                                push(@algo, "$grp");
                                        } else {
                                                push(@algo, "modp$grp");