#!/usr/bin/perl -wT

#----------------------------------------------------------------------
# heading     : Security
# description : IPSEC VPN
# navigation  : 1000 1400
# 
#----------------------------------------------------------------------

package esmith;

use strict;
use CGI ':all';
use CGI::Carp qw(fatalsToBrowser);

use esmith::cgi;
use esmith::config;
use esmith::util;
use esmith::db;

sub showInitial ($$);
sub showPubKey ($);
sub createNetwork ($);
sub performCreateNetwork ($);
sub modifyNetwork($);
sub performModifyNetwork($);
sub deleteNetwork ($);
sub performDeleteNetwork ($);
sub showWINS ($);
sub performWINS ($);
sub mailKey ($);
sub changePubID ($);
sub performsetPubIDdomain ($);
sub performsetPubIDIP ($);
BEGIN
{
    # Clear PATH and related environment variables so that calls to
    # external programs do not cause results to be tainted. See
    # "perlsec" manual page for details.

    $ENV {'PATH'} = '';
    $ENV {'SHELL'} = '/bin/bash';
    delete $ENV {'ENV'};
}

esmith::util::setRealToEffective ();

$CGI::POST_MAX=1024 * 600;  # max 100K posts
$CGI::DISABLE_UPLOADS = 1;  # no uploads

my %conf;
tie %conf, 'esmith::config';

my %vpns;
tie %vpns, 'esmith::config', '/home/e-smith/vpns';


    my ($localIP,$localNetmask,$externalIP,$domainName) = ($conf {'LocalIP'}, $conf {'LocalNetmask'}, $conf {'ExternalIP'}, $conf {'DomainName'});
    my $pubid = db_get_prop(\%conf, "ipsec", "pubid") || "$domainName";

#------------------------------------------------------------
# examine state parameter and display the appropriate form
#------------------------------------------------------------

my $q = new CGI;

if (! grep (/^state$/, $q->param))
{
    showInitial ($q, '');
}

elsif ($q->param ('state') eq "showPubKey")
{
    showPubKey ($q);
}
elsif ($q->param ('state') eq "create")
{
    createNetwork ($q);
}

elsif ($q->param ('state') eq "performCreate")
{
    performCreateNetwork ($q);
}

elsif ($q->param ('state') eq "modify")
{
    modifyNetwork ($q);
}

elsif ($q->param ('state') eq "performModify")
{
    performModifyNetwork ($q);
}

elsif ($q->param ('state') eq "delete")
{
    deleteNetwork ($q);
}

elsif ($q->param ('state') eq "performDelete")
{
    performDeleteNetwork ($q);
}

elsif ($q->param ('state') eq "showWINS")
{
    showWINS ($q);
}

elsif ($q->param ('state') eq "performWINS")
{
    performWINS ($q);
}

elsif ($q->param ('state') eq "mailKey")
{
    mailKey ($q);
}
elsif ($q->param ('state') eq "showchangePubID")
{
	changePubID ($q);
}
elsif ($q->param ('state') eq "chpubdomain")
{
	performsetPubIDdomain ($q);
}
elsif ($q->param ('state') eq "chpubip")
{
	performsetPubIDIP ($q);
}

else
{
    esmith::cgi::genStateError ($q, \%conf);
}

exit (0);

#------------------------------------------------------------
# subroutine to display initial form
#------------------------------------------------------------

sub showInitial ($$)
{
    my ($q, $msg) = @_;

    #------------------------------------------------------------
    # If there's a message, we just finished an operation so show the
    # status report. If no message, this is a new list of networks.
    #------------------------------------------------------------

    if ($msg eq '')
    {
	esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'Add, modify or remove IPSEC VPNs');
    }
    else
    {
	esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'Operation status report');
        print $q->p ($msg);
        esmith::cgi::genFooter ($q);
        return;

    }

    #------------------------------------------------------------
    # Look up networks and network descriptions
    #------------------------------------------------------------

    my @vpnList = ();

    foreach (sort keys %vpns)
    {
	push (@vpnList, $_)
		if (db_get_type(\%vpns, $_) eq "remotenetwork");
    }

    my $description = <<END_TEXT;
An IPSEC VPN allows traffic between two locations to travel across the Internet securely.  
For an IPSEC VPN to function, a server must be setup at each location to be involved in the network. 
END_TEXT

    print $q->p($description);

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=showchangePubID"},
			'Click here'),
		 'to change the Public ID for this server. Current value: ',$pubid);

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=showPubKey"},
			'Click here'),
		 'to view the public encryption key for this server.');

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=create"},
			'Click here'),
		 'to add an IPSEC VPN.');

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=showWINS"},
                        'Click here'),
                 'to add an IPSEC VPN WINS server.');

    unless (scalar @vpnList)
    {
	print $q->h4 ('No IPSEC VPNs are on file.');
    }
    else
    {
	print $q->h4 ('IPSEC VPNs setup:');

	print $q->table ({border => 1, cellspacing => 1, cellpadding => 4});

	print $q->Tr (
                      esmith::cgi::genSmallCell ($q, $q->b ('Remote ID')),
		      esmith::cgi::genSmallCell ($q, $q->b ('Remote Host')),
		      esmith::cgi::genSmallCell ($q, $q->b ('Remote Internal IP')),
		      esmith::cgi::genSmallCell ($q, $q->b ('Remote Internal Subnet Mask')),
		      $q->td ('&nbsp;'),
		      $q->td ('&nbsp;'));

	my $remotenetwork;

	foreach $remotenetwork (sort @vpnList)
	{
            my $remoteID = db_get_prop(\%vpns, $remotenetwork, "RemoteID");
	    my $remoteHost = db_get_prop(\%vpns, $remotenetwork, "RemoteHost");
	    my $remoteInternalIP = db_get_prop(\%vpns, $remotenetwork, "InternalIP");
	    my $remoteInternalSubnetMask = db_get_prop(\%vpns, $remotenetwork, "InternalMask");

	    print $q->Tr (esmith::cgi::genSmallCell ($q, $remoteID),
			  esmith::cgi::genSmallCell ($q, $remoteHost),
			  esmith::cgi::genSmallCell ($q, $remoteInternalIP),
			  esmith::cgi::genSmallCell ($q, $remoteInternalSubnetMask),
			  esmith::cgi::genSmallCell ($q,
						     $q->a ({href => $q->url (-absolute => 1)
								 . "?state=modify&remotenetwork="
								     . $remotenetwork}, 'Modify...')),
			  esmith::cgi::genSmallCell ($q,
						     $q->a ({href => $q->url (-absolute => 1)
								 . "?state=delete&remotenetwork="
								     . $remotenetwork}, 'Remove...')));
	}

	print '</table>';
    }
    print $q->endform;
    esmith::cgi::genFooter ($q);
}

#------------------------------------------------------------
# subroutine to show the public key of this server
#------------------------------------------------------------

sub showPubKey ($)
{
    my ($q) = @_;

    esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'Public encryption key for this server');

    my $description = <<END_TEXT;
The public encryption key for your server is shown below.  You use this key 
in the setup of the gateway server at the other end of the IPSEC VPN. 
For your convenience you may e-mail the key along with the other important
information needed to setup a IPSEC VPN to the admin mailbox for easy forwarding.
END_TEXT

    print $q->p($description);

    my $localpubenckey = db_get_prop(\%conf, "ipsec", "pubenckey");

    print $q->p("Public encryption key:");

    print $q->p($localpubenckey);

    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=mailKey"},
                        'Click here'),
                 'to e-mail the key and info to admin.');

    print $q->p ($q->a ({href => $q->url (-absolute => 1)},
			'Click here'),
		 'to return to the main IPSEC VPN screen.');

    print $q->endform;
    esmith::cgi::genFooter ($q);
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub createNetwork ($)
{
    my ($q) = @_;
 
    my $description = <<END_TEXT;
The Remote router's ID is generally the Domain Name or IP Address of the router.  Internal IP address and subnet
mask must be in the form #.#.#.# (each # is a number from 0 to 255).  The server will zero out the ending 
(host identifier) part of the internal IP address according to the subnet mask, to determine the 
network address.
END_TEXT

    esmith::cgi::genHeaderNonCacheable
	($q, \%conf, 'Add a IPSEC VPN');

    print $q->startform (-method => 'POST',
			 -action => $q->url (-absolute => 1));

    print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

	esmith::cgi::genTextRow ($q,
	    $q->p ($description)),

        esmith::cgi::genNameValueRow ($q,
                                      "Remote router's ID",
                                      "remoteID",
                                      ""),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's external IP address or hostname",
				      "remoteHost",
				      ""),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's internal IP address",
				      "remoteInternalIP",
				      ""),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's internal subnet mask",
				      "remoteInternalSubnetMask",
				      ""),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's public encryption key",
				      "remotePubKey",
				      ""),

	esmith::cgi::genWidgetRow ($q,
                               "Remote network NAT'ed",
                               $q->popup_menu (-name    => 'remoteNAT',
                                               -values  => ['yes', 'no'],
                                               -default => 'yes')),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Network to Network traffic",
                               $q->popup_menu (-name    => 'protectNetToNet',
                                               -values  => ['yes', 'no'],
                                               -default => 'yes')),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Gateway to Gateway traffic",
                               $q->popup_menu (-name    => 'protectGateToGate',
                                               -values  => ['yes', 'no'],
                                               -default => 'yes')),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Gateway to Network traffic",
                               $q->popup_menu (-name    => 'protectGateToNet',
                                               -values  => ['yes', 'no'],
                                               -default => 'yes')),

	esmith::cgi::genWidgetRow ($q,
                               "Local machine acts as a",
                               $q->popup_menu (-name    => 'localFunction',
                                               -values  => ['server', 'client'],
                                               -default => 'server')),

	esmith::cgi::genButtonRow ($q,
				   $q->submit (-name => 'action',
					       -value => 'Add')));

    print $q->hidden (-name => 'state',
		      -override => 1,
		      -default => 'performCreate');

    print $q->endform;
    esmith::cgi::genFooter ($q);
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub performCreateNetwork ($)
{
    my ($q) = @_;

    #------------------------------------------------------------
    # Validate parameters and untaint them
    #------------------------------------------------------------

    my $remoteID = $q->param ('remoteID') || "";
    if ($remoteID =~ /^([0-9a-zA-Z-_.]*)$/)
    {
        $remoteID = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote ID $remoteID. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteHost = $q->param ('remoteHost') || "";
    if ($remoteHost =~ /^([0-9a-zA-Z-_.]*)$/)
    {
        $remoteHost = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote host/ip $remoteHost. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteInternalIP = $q->param ('remoteInternalIP');
    if ($remoteInternalIP =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remoteInternalIP = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote router internal IP address $remoteInternalIP. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteInternalSubnetMask = $q->param ('remoteInternalSubnetMask');
    if ($remoteInternalSubnetMask =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remoteInternalSubnetMask = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid subnet mask $remoteInternalSubnetMask. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remotePubKey = $q->param ('remotePubKey');
    if ($remotePubKey =~ /^(.*)$/)
    {
        $remotePubKey = $1;
    }
    else
    {
        $remotePubKey = "";
    }

    my $remoteNAT = $q->param ('remoteNAT');
    if ($remoteNAT =~ /^(.*)$/)
    {
        $remoteNAT = $1;
    }
    else
    {
        $remotePubKey = "";
    }

    my $protectNetToNet = $q->param ('protectNetToNet');
    if ($protectNetToNet =~ /^(.*)$/)
    {
        $protectNetToNet = $1;
    }
    else
    {
        $protectNetToNet = "yes";
    }

    my $protectGateToGate = $q->param ('protectGateToGate');
    if ($protectGateToGate =~ /^(.*)$/)
    {
        $protectGateToGate = $1;
    }
    else
    {
        $protectGateToGate = "yes";
    }

    my $protectGateToNet = $q->param ('protectGateToNet');
    if ($protectGateToNet =~ /^(.*)$/)
    {
        $protectGateToNet = $1;
    }
    else
    {
        $protectGateToNet = "yes";
    }

    my $localFunction = $q->param ('localFunction');
    if ($localFunction =~ /^(.*)$/)
    {
        $localFunction = $1;
    }
    else
    {
        $localFunction = "server";
    }

    #------------------------------------------------------------
    # Calculate both the localNetwork and localBroadcast.
    #------------------------------------------------------------

    my ($localNetwork, $localBroadcast) =
        esmith::util::computeNetworkAndBroadcast ($conf {'LocalIP'}, $conf {'LocalNetmask'});
    
    #------------------------------------------------------------
    # Mask off the netmask bits
    #------------------------------------------------------------

    my ($remotenetwork, $remotebroadcast) = esmith::util::computeNetworkAndBroadcast ($remoteInternalIP, $remoteInternalSubnetMask);

    #------------------------------------------------------------
    # Looks good. Find out if this network has been taken
    #------------------------------------------------------------

    if ($remotenetwork eq $localNetwork)
    {
	showInitial ($q, "Error: network $remotenetwork (derived from IP address $remoteInternalIP and subnet mask $remoteInternalSubnetMask) " .
		     "is our local network. Did not add new network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    if (db_get(\%vpns, $remotenetwork))
    {
	showInitial ($q, "Error: network $remotenetwork (derived from IP address $remoteInternalIP and subnet mask $remoteInternalSubnetMask) " .
		         "has already been added. Did not add new network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    #------------------------------------------------------------
    # Network is available! Update networks database and signal the
    # network-create event.
    #------------------------------------------------------------

    db_set(\%vpns, $remotenetwork, "remotenetwork");
    db_set_prop(\%vpns, $remotenetwork, 'RemoteID', $remoteID);
    db_set_prop(\%vpns, $remotenetwork, 'RemoteHost', $remoteHost);
    db_set_prop(\%vpns, $remotenetwork, 'InternalIP', $remoteInternalIP);
    db_set_prop(\%vpns, $remotenetwork, 'InternalMask', $remoteInternalSubnetMask);
    db_set_prop(\%vpns, $remotenetwork, 'NAT', $remoteNAT);
    db_set_prop(\%vpns, $remotenetwork, 'NetToNet', $protectNetToNet);
    db_set_prop(\%vpns, $remotenetwork, 'GateToGate', $protectGateToGate);
    db_set_prop(\%vpns, $remotenetwork, 'GateToNet', $protectGateToNet);
    db_set_prop(\%vpns, $remotenetwork, 'Function', $localFunction);
    db_set_prop(\%vpns, $remotenetwork, 'PubKey', $remotePubKey);

    system ("/sbin/e-smith/signal-event", "vpn-create", "$remotenetwork") == 0
	or die ("Error occurred while creating network.\n");

    my ($totalHosts, $firstAddr, $lastAddr) = esmith::util::computeHostRange ($remotenetwork, $remoteInternalSubnetMask);

    my $msg = "Successfully added IPSEC VPN $remotenetwork/$remoteInternalSubnetMask via router $remoteHost.";

    if ($totalHosts == 1)
    {
	$msg .= "<P>Your server will grant local access privileges to the single IP address $remotenetwork.";
    }
    elsif (($totalHosts == 256) || ($totalHosts == 65536) || ($totalHosts == 16777216))
    {
	$msg .= "<P>Your server will grant local access privileges to $totalHosts IP addresses in the range $firstAddr to $lastAddr.";
    }
    else
    {
	$msg .= "<P>Your server will grant local access privileges to $totalHosts IP addresses in the range $firstAddr to $lastAddr.";
	$msg .= "<P>Warning: the ProFTPd FTP server cannot handle this nonstandard subnet mask. The simpler specification";
	$msg .= " <B>" . esmith::util::computeLocalNetworkPrefix ($remotenetwork, $remoteInternalSubnetMask) . "</B> will be used instead.";
    }

    showInitial ($q, "$msg [ <a href=virtualprivatenetworks>refresh</a> ]");
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub modifyNetwork ($)
{
    my ($q) = @_;

    my $description = <<END_TEXT;
The Remote router's ID is generally the Domain Name or IP Address of the router.  Internal IP address and subnet
mask must be in the form #.#.#.# (each # is a number from 0 to 255).  The server will zero out the ending 
(host identifier) part of the internal IP address according to the subnet mask, to determine the 
network address.
END_TEXT

    esmith::cgi::genHeaderNonCacheable
	($q, \%conf, 'Modify IPSEC VPN');

    print $q->startform (-method => 'POST',
			 -action => $q->url (-absolute => 1));


    my $remotenetwork = $q->param ('remotenetwork');

    if (db_get(\%vpns, $remotenetwork))
    {
        print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

	esmith::cgi::genTextRow ($q,
	    $q->p ($description)),

        esmith::cgi::genNameValueRow ($q,
                                      "Remote router's ID",
                                      "remoteID",
                                      db_get_prop(\%vpns, $remotenetwork, "RemoteID")),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's external IP address or hostname",
				      "remoteHost",
				      db_get_prop(\%vpns, $remotenetwork, "RemoteHost")),

	esmith::cgi::genNameValueRow ($q,
				      "Remote router's internal IP address",
				      "remoteInternalIP",
				      db_get_prop(\%vpns, $remotenetwork, "InternalIP")),

        esmith::cgi::genNameValueRow ($q,
                                      "Remote router's internal subnet mask",
                                      "remoteInternalSubnetMask",
                                      db_get_prop(\%vpns, $remotenetwork, "InternalMask")),

	esmith::cgi::genNameValueRow ($q,
				      "Remote gateway's public encryption key",
				      "remotePubKey",
				      db_get_prop(\%vpns, $remotenetwork, "PubKey")),

	esmith::cgi::genWidgetRow ($q,
                               "Remote network NAT'ed",
                               $q->popup_menu (-name    => 'remoteNAT',
                                               -values  => ['yes', 'no'],
                                               -default => db_get_prop(\%vpns, $remotenetwork, "NAT"))),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Network to Network traffic",
                               $q->popup_menu (-name    => 'protectNetToNet',
                                               -values  => ['yes', 'no'],
                                               -default => db_get_prop(\%vpns, $remotenetwork, "NetToNet"))),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Gateway to Gateway traffic",
                               $q->popup_menu (-name    => 'protectGateToGate',
                                               -values  => ['yes', 'no'],
                                               -default => db_get_prop(\%vpns, $remotenetwork, "GateToGate"))),

	esmith::cgi::genWidgetRow ($q,
                               "Encrypt Gateway to Network traffic",
                               $q->popup_menu (-name    => 'protectGateToNet',
                                               -values  => ['yes', 'no'],
                                               -default => db_get_prop(\%vpns, $remotenetwork, "GateToNet"))),

	esmith::cgi::genWidgetRow ($q,
                               "Local machine acts as a",
                               $q->popup_menu (-name    => 'localFunction',
                                               -values  => ['server', 'client'],
                                               -default => db_get_prop(\%vpns, $remotenetwork, "Function"))),

	esmith::cgi::genButtonRow ($q,
				   $q->submit (-name => 'action',
					       -value => 'Modify')));

        print $q->hidden (-name => 'state',
		      -override => 1,
		      -default => 'performModify');

        print $q->hidden (-name => 'remoteNetworkAddress',
		      -override => 1,
		      -default => $remotenetwork);

    }

    print $q->endform;
    esmith::cgi::genFooter ($q);
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub performModifyNetwork ($)
{
    my ($q) = @_;

    #------------------------------------------------------------
    # Validate parameters and untaint them
    #------------------------------------------------------------

    my $remoteID = $q->param ('remoteID') || "";
    if ($remoteID =~ /^([0-9a-zA-Z-_.]*)$/)
    {
        $remoteID = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote ID $remoteID. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteHost = $q->param ('remoteHost') || "";
    if ($remoteHost =~ /^([0-9a-zA-Z-_.]*)$/)
    {
        $remoteHost = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote host/ip $remoteHost. Did not add network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteNetworkAddress = $q->param ('remoteNetworkAddress');
    if ($remoteNetworkAddress =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remoteNetworkAddress = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote network address $remoteNetworkAddress. Did not modify network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    unless (db_get_type(\%vpns, $remoteNetworkAddress) eq "remotenetwork")
    {
	showInitial ($q, "Error: remote network \"$remoteNetworkAddress\" is not an existing virtual priavte network. [ <a href=virtualprivatenetworks>refresh</a> ]");
        return;
    }

    my $remoteInternalIP = $q->param ('remoteInternalIP');
    if ($remoteInternalIP =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remoteInternalIP = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid remote router internal IP address $remoteInternalIP. Did not modify network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remoteInternalSubnetMask = $q->param ('remoteInternalSubnetMask');
    if ($remoteInternalSubnetMask =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remoteInternalSubnetMask = $1;
    }
    else
    {
	showInitial ($q, "Error: invalid subnet mask $remoteInternalSubnetMask. Did not modify network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    #------------------------------------------------------------
    # Make sure that if the user inputed a new local IP that
    # it is a valid one for the network/mask specified
    #------------------------------------------------------------

    my ($totalHosts, $firstAddr, $lastAddr) = esmith::util::computeHostRange ($remoteNetworkAddress, $remoteInternalSubnetMask);
    my $firstAddr_int = esmith::util::IPquadToAddr($firstAddr);
    my $lastAddr_int = esmith::util::IPquadToAddr($lastAddr);
    my $remoteInternalIP_int = esmith::util::IPquadToAddr($remoteInternalIP);
    if (($remoteInternalIP_int < $firstAddr_int) || ($remoteInternalIP_int > $lastAddr_int))
    {
	showInitial ($q, "Error: invalid remote router internal IP address $remoteInternalIP. It must be between $firstAddr and $lastAddr. Did not modify network. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    my $remotePubKey = $q->param ('remotePubKey');
    if ($remotePubKey =~ /^(.*)$/)
    {
        $remotePubKey = $1;
    }
    else
    {
        $remotePubKey = "";
    }

    my $remoteNAT = $q->param ('remoteNAT');
    if ($remoteNAT =~ /^(.*)$/)
    {
        $remoteNAT = $1;
    }
    else
    {
        $remotePubKey = "";
    }

    my $protectNetToNet = $q->param ('protectNetToNet');
    if ($protectNetToNet =~ /^(.*)$/)
    {
        $protectNetToNet = $1;
    }
    else
    {
        $protectNetToNet = "yes";
    }

    my $protectGateToGate = $q->param ('protectGateToGate');
    if ($protectGateToGate =~ /^(.*)$/)
    {
        $protectGateToGate = $1;
    }
    else
    {
        $protectGateToGate = "yes";
    }

    my $protectGateToNet = $q->param ('protectGateToNet');
    if ($protectGateToNet =~ /^(.*)$/)
    {
        $protectGateToNet = $1;
    }
    else
    {
        $protectGateToNet = "yes";
    }

    my $localFunction = $q->param ('localFunction');
    if ($localFunction =~ /^(.*)$/)
    {
        $localFunction = $1;
    }
    else
    {
        $localFunction = "server";
    }

    #------------------------------------------------------------
    # Calculate both the localNetwork and localBroadcast.
    #------------------------------------------------------------

    my ($localNetwork, $localBroadcast) =
        esmith::util::computeNetworkAndBroadcast ($conf {'LocalIP'}, $conf {'LocalNetmask'});
    
    #------------------------------------------------------------
    # Mask off the netmask bits
    #------------------------------------------------------------

    my $remotenetwork = $remoteNetworkAddress;
 
    #------------------------------------------------------------
    # Update networks database and signal the
    # network-modify event.
    #------------------------------------------------------------

    db_set_prop(\%vpns, $remotenetwork, 'RemoteID', $remoteID);
    db_set_prop(\%vpns, $remotenetwork, 'RemoteHost', $remoteHost);
    db_set_prop(\%vpns, $remotenetwork, 'InternalIP', $remoteInternalIP);
    db_set_prop(\%vpns, $remotenetwork, 'InternalMask', $remoteInternalSubnetMask);
    db_set_prop(\%vpns, $remotenetwork, 'NAT', $remoteNAT);
    db_set_prop(\%vpns, $remotenetwork, 'NetToNet', $protectNetToNet);
    db_set_prop(\%vpns, $remotenetwork, 'GateToGate', $protectGateToGate);
    db_set_prop(\%vpns, $remotenetwork, 'GateToNet', $protectGateToNet);
    db_set_prop(\%vpns, $remotenetwork, 'Function', $localFunction);
    db_set_prop(\%vpns, $remotenetwork, 'PubKey', $remotePubKey);

    system ("/sbin/e-smith/signal-event", "vpn-modify", "$remotenetwork") == 0
	or die ("Error occurred while modifying network.\n");

    ($totalHosts, $firstAddr, $lastAddr) = esmith::util::computeHostRange ($remotenetwork, $remoteInternalSubnetMask);

    my $msg = "Successfully modified IPSEC VPN $remotenetwork/$remoteInternalSubnetMask via router $remoteHost.";

    showInitial ($q, "$msg [ <a href=virtualprivatenetworks>refresh</a> ]");
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub deleteNetwork ($)
{
    my ($q) = @_;

    esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'Remove local network');

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    my $remotenetwork = $q->param ('remotenetwork');

    if (db_get(\%vpns, $remotenetwork))
    {

	print $q->p ('You are about to remove the IPSEC VPN '
		     . $remotenetwork
		     . ' (subnet mask ' . db_get_prop(\%vpns, $remotenetwork, "InternalMask") . ').');

	print $q->p ($q->b ('Are you sure you wish to remove this network?'));
	
	print $q->submit (-name => 'action', -value => 'Remove');
	print $q->hidden (-name => 'remotenetwork', -override => 1, -default => $remotenetwork);

	print $q->hidden (-name	    => 'state',
			  -override => 1,
			  -default  => 'performDelete');
    }

    print $q->endform;
    print $q->p ('<HR>');
    return;
}

#------------------------------------------------------------
# 
#------------------------------------------------------------

sub performDeleteNetwork ($)
{
    my ($q) = @_;

    #------------------------------------------------------------
    # Attempt to delete network
    #------------------------------------------------------------

    my $remotenetwork = $q->param ('remotenetwork');

    if ($remotenetwork =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/)
    {
	$remotenetwork = $1;
    }
    else
    {
	showInitial ($q, "Error: internal failure while removing IPSEC VPN $remotenetwork. [ <a href=virtualprivatenetworks>refresh</a> ]");
	return;
    }

    unless (db_get_type(\%vpns, $remotenetwork) eq "remotenetwork")
    {
        showInitial ($q, "Error: \"$remotenetwork\" is not an existing IPSEC VPN. [ <a href=virtualprivatenetworks>refresh</a> ]");
        return;
    }

    db_set_type(\%vpns, $remotenetwork, "remotenetwork-deleted");

    system ("/sbin/e-smith/signal-event", "vpn-delete", "$remotenetwork") == 0
	or die ("Error occurred while deleting network.\n");

    db_delete(\%vpns, $remotenetwork);

    showInitial ($q, "Successfully deleted IPSEC VPN $remotenetwork. [ <a href=virtualprivatenetworks>refresh</a> ]");
}

#------------------------------------------------------------
# subroutine to showWINS
#------------------------------------------------------------

sub showWINS ($)
{
    my ($q) = @_;

    my $WINSServer = db_get_prop(\%conf, 'smb', "WINSServer") || "";

    esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'IPSEC VPN WINS server');

    print $q->start_multipart_form (
                    -method => 'POST',
                    -action => $q->url (-absolute => 1));

    print $q->table ({border => 0, cellspacing => 0, cellpadding => 4},

    esmith::cgi::genTextRow ($q,
           $q->p ('Windows Internet Name Server (WINS) is a service that keeps a database',
                  'of computer name to IP address mappings. This permits accessing a computer',
                  'using the NetBIOS computer name. This name is handed to the nearest WINS server',
                  'which then returns an IP address.')), 

    esmith::cgi::genTextRow ($q,
           $q->p ('By default, this server is set to act as the WINS server.',
                  'However, if this server is a remote IPSEC VPN client, you must enter the internal',
                  'IP address of the IPSEC VPN server below.')),

    esmith::cgi::genNameValueRow ($q,
                                 "IPSEC VPN WINS server, internal IP address",
                                 "winsServer",
                                 $WINSServer),

    esmith::cgi::genButtonRow ($q,
                               $q->submit (-name => 'action', -value => 'Save')));

    print $q->hidden (-name => 'state', -override => 1, -default => 'performWINS');
    print $q->endform;
    esmith::cgi::genFooter ($q);

}

#------------------------------------------------------------
# subroutine to performWINS and display result
#------------------------------------------------------------

sub performWINS ($)
{
    my ($q) = @_;

    my $winsServer = $q->param ('winsServer');
    db_set_prop(\%conf, 'smb', "WINSServer", "$winsServer");

    system ("/sbin/e-smith/expand-template", "/etc/smb.conf") == 0
        or die ("Error occurred expanding smb.conf template.\n");

    system ("/etc/rc.d/init.d/smb",  "restart") == 0
        or die ("Error occurred restarting smb.\n");

    showInitial ($q, "Successfully updated IPSEC VPN WINS server. [ <a href=virtualprivatenetworks>refresh</a> ]");
    return;
}

#------------------------------------------------------------
# subroutine to mailKey and display result
#------------------------------------------------------------

sub mailKey ($)
{
    my ($q) = @_;
    my $localpubenckey = db_get_prop(\%conf, "ipsec", "pubenckey") || "no key found";
    my ($localIP,$localNetmask,$externalIP,$domainName) = ($conf {'LocalIP'}, $conf {'LocalNetmask'}, $conf {'ExternalIP'}, $conf {'DomainName'});
    my $pubid = db_get_prop(\%conf, "ipsec", "pubid") || "$domainName";

    system ("echo Encryption Key: $localpubenckey > /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");
    system ("echo >> /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");
    system ("echo Router ID: $pubid >> /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");
    system ("echo Router IP: $externalIP >> /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");
    system ("echo Router Internal IP: $localIP >> /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");
    system ("echo Router Internal Subnet Mask: $localNetmask >> /root/pubenckey") == 0
        or die ("Error occurred uploading key.\n");

    system ("/etc/e-smith/events/actions/mail-ipsec-key") == 0
        or die ("Error occurred mailing key.\n");

    showInitial ($q, "Successfully sent key via e-mail to admin. [ <a href=virtualprivatenetworks>refresh</a> ]");
    return;
}
sub changePubID ($)
{
    my ($q) = @_;

    my ($localIP,$localNetmask,$externalIP,$domainName) = ($conf {'LocalIP'}, $conf {'LocalNetmask'}, $conf {'ExternalIP'}, $conf {'DomainName'});
    my $pubid = db_get_prop(\%conf, "ipsec", "pubid") || "$domainName";

    esmith::cgi::genHeaderNonCacheable ($q, \%conf, 'Change the Public ID for this Server');

    print $q->startform (-method => 'POST', -action => $q->url (-absolute => 1));

    print $q->p ('The current Public ID of this Server is: ',$pubid);

    print $q->p ($q->b ('If you want to change the Public ID klick below'));


    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=chpubdomain"},
			'Click here'),
		 'to to set the Public ID to the Domainname.');


    print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=chpubip"},
			'Click here'),
		 'to set the Public ID to the external IP.');
	

    print $q->endform;
    return;
}
sub performsetPubIDdomain ($)
{

    my ($q) = @_;

    db_set_prop(\%conf, 'ipsec', 'pubid', "$domainName");

    system ("/sbin/e-smith/expand-template", "/etc/ipsec.conf") == 0
        or die ("Error occurred expanding ipsec.conf template.\n");

    system ("/etc/rc.d/init.d/ipsec",  "restart") == 0
        or die ("Error occurred restarting ipsec.\n");

    showInitial ($q, "Successfully change Public ID to local Domainname. [ <a href=virtualprivatenetworks>refresh</a> ]");
    return;
}
sub performsetPubIDIP ($)
{

    my ($q) = @_;
    db_set_prop(\%conf, 'ipsec', 'pubid', "$externalIP");

    system ("/sbin/e-smith/expand-template", "/etc/ipsec.conf") == 0
        or die ("Error occurred expanding ipsec.conf template.\n");

    system ("/etc/rc.d/init.d/ipsec",  "restart") == 0
        or die ("Error occurred restarting ipsec.\n");


    showInitial ($q, "Successfully change Public ID to local external IP. [ <a href=virtualprivatenetworks>refresh</a> ]");
    return;
}
