initial commit of file from CVS for smeserver-openvpn-s2s on Sat Sep 7 19:57:57 AEST 2024

This commit is contained in:
Trevor Batley
2024-09-07 19:57:57 +10:00
parent 8855fbff54
commit 59fbb967a2
61 changed files with 10315 additions and 2 deletions

View File

@@ -0,0 +1,16 @@
[Unit]
Description=OpenVPN Server to Server
After=network.service
[Service]
Type=oneshot
RemainAfterExit=yes
GuessMainPID=no
ExecStart=/usr/sbin/systemd/openvpn-s2s start
ExecStop=/usr/sbin/systemd/openvpn-s2s stop
ExecReload=/usr/sbin/systemd/openvpn-s2s reopen
[Install]
WantedBy=sme-server.target

View File

@@ -0,0 +1,269 @@
#!/bin/sh
#
# openvpn This shell script takes care of starting and stopping
# openvpn on RedHat or other chkconfig-based system.
#
# chkconfig: - 24 76
#
# processname: openvpn
# description: OpenVPN is a robust and highly flexible tunneling \
# application that uses all of the encryption, \
# authentication, and certification features of the OpenSSL \
# library to securely tunnel IP networks over a single UDP \
# port.
#
### BEGIN INIT INFO
# Provides: openvpn
# Required-Start: $network
# Required-Stop: $network
# Short-Description: start and stop openvpn
# Description: OpenVPN is a robust and highly flexible tunneling \
# application that uses all of the encryption, \
# authentication, and certification features of the OpenSSL \
# library to securely tunnel IP networks over a single UDP \
# port.
### END INIT INFO
# Contributed to the OpenVPN project by
# Douglas Keller <doug@voidstar.dyndns.org>
# 2002.05.15
# To install:
# copy this file to /etc/rc.d/init.d/openvpn
# shell> chkconfig --add openvpn
# shell> mkdir /etc/openvpn
# make .conf or .sh files in /etc/openvpn (see below)
# To uninstall:
# run: chkconfig --del openvpn
# Author's Notes:
#
# I have created an /etc/init.d init script and enhanced openvpn.spec to
# automatically register the init script. Once the RPM is installed you
# can start and stop OpenVPN with "service openvpn start" and "service
# openvpn stop".
#
# The init script does the following:
#
# - Starts an openvpn process for each .conf file it finds in
# /etc/openvpn.
#
# - If /etc/openvpn/xxx.sh exists for a xxx.conf file then it executes
# it before starting openvpn (useful for doing openvpn --mktun...).
#
# - In addition to start/stop you can do:
#
# service openvpn reload - SIGHUP
# service openvpn reopen - SIGUSR1
# service openvpn status - SIGUSR2
#
# Modifications:
#
# 2003.05.02
# * Changed == to = for sh compliance (Bishop Clark).
# * If condrestart|reload|reopen|status, check that we were
# actually started (James Yonan).
# * Added lock, piddir, and work variables (James Yonan).
# * If start is attempted twice, without an intervening stop, or
# if start is attempted when previous start was not properly
# shut down, then kill any previously started processes, before
# commencing new start operation (James Yonan).
# * Do a better job of flagging errors on start, and properly
# returning success or failure status to caller (James Yonan).
#
# 2005.04.04
# * Added openvpn-startup and openvpn-shutdown script calls
# (James Yonan).
#
# Location of openvpn binary
openvpn=""
openvpn_locations="/usr/sbin/openvpn /usr/local/sbin/openvpn"
for location in $openvpn_locations
do
if [ -f "$location" ]
then
openvpn=$location
fi
done
#ncp
ncp=" --ncp-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC:AES-128-CBC:BF-CBC "
# Lockfile
lock="/var/lock/subsys/openvpn-s2s"
# PID directory
piddir="/var/run/openvpn-s2s"
# Our working directory
work=/etc/openvpn/s2s
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
if [ ${NETWORKING} = "no" ]
then
echo "Networking is down"
exit 0
fi
# Check that binary exists
if ! [ -f $openvpn ]
then
echo "openvpn binary not found"
exit 0
fi
# See how we were called.
case "$1" in
start)
echo -n $"Starting openvpn-s2s: "
/sbin/modprobe tun >/dev/null 2>&1
# From a security perspective, I think it makes
# sense to remove this, and have users who need
# it explictly enable in their --up scripts or
# firewall setups.
echo 1 > /proc/sys/net/ipv4/ip_forward
# Run startup script, if defined
if [ -f $work/openvpn-startup ]; then
$work/openvpn-startup
fi
if [ ! -d $piddir ]; then
mkdir $piddir
fi
if [ -f $lock ]; then
# we were not shut down correctly
for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
if [ -s $pidf ]; then
kill `cat $pidf` >/dev/null 2>&1
fi
rm -f $pidf
done
rm -f $lock
sleep 2
fi
rm -f $piddir/*.pid
cd $work
# Start every .conf in $work and run .sh if exists
errors=0
successes=0
for c in `/bin/ls *.conf 2>/dev/null`; do
bn=${c%%.conf}
if [ -f "$bn.sh" ]; then
. ./$bn.sh
fi
rm -f $piddir/$bn.pid
# Handle backward compatibility, see Red Hat Bugzilla ID #458594
script_security=''
if [ -z "$( grep '^[[:space:]]*script-security[[:space:]]' $c )" ]; then
script_security="--script-security 2"
fi
$openvpn --daemon --writepid $piddir/$bn.pid --config $c --cd $work $script_security
if [ $? = 0 ]; then
successes=1
else
errors=1
fi
done
if [ $errors = 1 ]; then
failure; echo
else
success; echo
fi
if [ $successes = 1 ]; then
touch $lock
fi
;;
stop)
echo -n $"Shutting down openvpn: "
for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
if [ -s $pidf ]; then
kill `cat $pidf` >/dev/null 2>&1
fi
rm -f $pidf
done
# Run shutdown script, if defined
if [ -f $work/openvpn-shutdown ]; then
$work/openvpn-shutdown
fi
success; echo
rm -f $lock
;;
restart)
$0 stop
sleep 2
$0 start
;;
reload)
if [ -f $lock ]; then
for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
if [ -s $pidf ]; then
kill -HUP `cat $pidf` >/dev/null 2>&1
fi
done
exit 0
else
echo "openvpn: service not started"
exit 1
fi
;;
reopen)
if [ -f $lock ]; then
for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
if [ -s $pidf ]; then
kill -USR1 `cat $pidf` >/dev/null 2>&1
fi
done
exit 0
else
echo "openvpn: service not started"
exit 1
fi
;;
condrestart)
if [ -f $lock ]; then
$0 stop
# avoid race
sleep 2
$0 start
fi
;;
status)
if [ -f $lock ]; then
for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
if [ -s $pidf ]; then
kill -USR2 `cat $pidf` >/dev/null 2>&1
fi
done
echo "Status written to /var/log/messages"
else
echo "openvpn: service not started"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|condrestart|reload|reopen|status}"
exit 1
;;
esac
exit 0

View File

@@ -0,0 +1,783 @@
#!/usr/bin/perl
package esmith::FormMagick::Panel::openvpns2s;
# Imports
use strict;
use warnings;
use esmith::ConfigDB;
use esmith::NetworksDB;
use esmith::FormMagick;
use esmith::cgi;
use esmith::util;
use Net::IP;
use CGI::Carp qw ( fatalsToBrowser );
use File::Basename;
our @ISA = qw(esmith::FormMagick Exporter);
# TODO: update sub list
our @EXPORT = qw(
print_custom_button
print_section_bar
write_db_conf
update_ports
print_conf_table
print_conf_name_field
remove_conf
print_conf_to_remove
read_file
reload
);
our $pubdir = '/etc/openvpn/s2s/pub';
our $privdir = '/etc/openvpn/s2s/priv';
our $ovpn_db = esmith::ConfigDB->open('openvpn-s2s') || esmith::ConfigDB->create('openvpn-s2s');
our $conf_db = esmith::ConfigDB->open || die "Error opening configuration DB\n";
our $net_db = esmith::NetworksDB->open_ro || die "Error opening netwoks DB\n";
our $base_url = "?page=0&page_stack=&Next=Next&wherenext=";
*wherenext = \&CGI::FormMagick::wherenext;
sub new {
shift;
my $fm = esmith::FormMagick->new();
$fm->{calling_package} = (caller)[0];
bless $fm;
return $fm;
}
sub print_custom_button{
my ($fm,$desc,$url) = @_;
my $q = $fm->{cgi};
$url="openvpns2s?page=0&page_stack=&Next=Next&wherenext=".$url;
print " <tr>\n <td colspan='2'>\n";
print $q->p($q->a({href => $url, -class => "button-like"},$fm->localise($desc)));
print qq(</tr>\n);
return undef;
}
sub print_section_bar{
my ($fm) = @_;
print " <tr>\n <td colspan='2'>\n";
print "<hr class=\"sectionbar\"/>\n";
return undef;
}
sub write_db_conf{
my ($fm,$type) = @_;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
if ($q->param('action') ne 'modify'){
if ($ovpn_db->get($conf)){
return $fm->error('CONF_CONFLICT','FIRST_PAGE');
}
my $msg = $fm->validate_conf_name($conf);
unless ($msg eq "OK"){
return $fm->error($msg,'FIRST_PAGE');
}
}
# Write DB values
$ovpn_db->new_record($conf, {type => $type});
if ($type eq 'client'){
$ovpn_db->set_prop($conf, 'RemoteHost', $q->param("remote_host"));
}
$ovpn_db->set_prop($conf, 'status', $q->param("status"));
$ovpn_db->set_prop($conf, 'Authentication', $q->param("auth"));
$ovpn_db->set_prop($conf, 'LocalIP', $q->param("local_ip"));
$ovpn_db->set_prop($conf, 'RemoteIP', $q->param("remote_ip"));
$ovpn_db->set_prop($conf, 'Port', $q->param("port"));
$ovpn_db->set_prop($conf, 'status', $q->param("status"));
$ovpn_db->set_prop($conf, 'Comment', $q->param("comment"));
$ovpn_db->set_prop($conf, 'RemoteNetworks', $q->param("remote_net"));
$ovpn_db->set_prop($conf, 'SnatOutbound', $q->param("SnatOutbound"));
if ($q->param("hmac") eq 'SHA1') {
my $tmpk = $ovpn_db->get($conf);
$tmpk->delete_prop('HMAC');
}
else {
$ovpn_db->set_prop($conf, 'HMAC' , $q->param("hmac"));
}
if ($q->param("cipher") eq 'BF-CBC') {
my $tmpk = $ovpn_db->get($conf);
$tmpk->delete_prop('Cipher');
}
else {
$ovpn_db->set_prop($conf, 'Cipher', $q->param("cipher"));
}
# Now, update the main configuration entry
update_ports();
$fm->success('SUCCESS','CONFIG_CERT_PAGE');
return undef;
}
sub write_pem{
my ($fm) = @_;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
my $type = $ovpn_db->get_prop($conf, 'type') || 'server';
my $auth = $ovpn_db->get_prop($conf, 'Authentication') || 'TLS';
# Run validation routines
my $msg = $fm->is_url_or_empty( $q->param("crl_url"));
unless ($msg eq "OK"){
return $fm->error($msg,'CONFIG_CERT_PAGE');
}
my @pems = ();
if ($auth eq 'TLS'){
push @pems, qw/cacert_pem cert_pem key_pem/;
push @pems, 'dh_pem' if $type eq 'server';
}
else{
push @pems, 'shared_key' if $auth eq 'SharedKey';
}
foreach my $pem (@pems){
$msg = $fm->is_valid_key( $q->param("$pem") );
unless ($msg eq "OK"){
return $fm->error($msg,'CONFIG_CERT_PAGE');
}
}
# Untaint $conf
$conf =~ m/(.*)/;
$conf = $1;
if (! open (CA, ">$pubdir/$conf". "_cacert.pem")){
$fm->error('ERROR_OPEN_CA','FIRST_PAGE');
return;
}
print CA $q->param('cacert_pem');
close CA;
if (! open (CRT, ">$pubdir/$conf" . "_cert.pem")){
$fm->error('ERROR_OPEN_CRT','FIRST_PAGE');
return;
}
print CRT $q->param('cert_pem');
close CRT;
if (! open (KEY, ">$privdir/$conf" . "_key.pem")){
$fm->error('ERROR_OPEN_KEY','FIRST_PAGE');
return;
}
print KEY $q->param('key_pem');
close KEY;
chmod(0600, "$privdir/${conf}_key.pem" );
esmith::util::chownFile("root", "root","$privdir/${conf}_key.pem" );
if (! open (DH, ">$pubdir/$conf" . "_dh.pem")){
$fm->error('ERROR_OPEN_DH','FIRST_PAGE');
return;
}
print DH $q->param('dh_pem');
close DH;
if (! open (TA, ">$privdir/$conf" . "_sharedkey.pem")){
$fm->error('ERROR_OPEN_TA','FIRST_PAGE');
return;
}
print TA $q->param('shared_key');
close TA;
chmod(0600, "$privdir/${conf}_sharedkey.pem" );
esmith::util::chownFile("root", "root","$privdir/${conf}_sharedkey.pem" );
esmith::util::chownFile("root", "root", "$privdir");
esmith::util::chownFile("root", "root", "$pubdir");
chmod 0600, "$privdir";
chmod 0644, "$pubdir";
$ovpn_db->set_prop($conf, 'CrlUrl', $q->param("crl_url"));
unless ( system ("/sbin/e-smith/signal-event", "openvpn-s2s-update") == 0 ){
return $fm->error("ERROR_OCCURED", 'FIRST_PAGE');
}
$fm->success('SUCCESS','FIRST_PAGE');
return undef;
}
# Update ports used in the configuration DB
sub update_ports{
my @tcp_ports = ();
my @udp_ports = ();
foreach my $vpn ($ovpn_db->get_all_by_prop(type=>'server')){
my $port = $vpn->prop('Port') || next;
my $proto = ($vpn->prop('Protocol') || 'udp');
my $status = ($vpn->prop('status') || 'disabled');
if ( $proto eq 'udp' && $status eq 'enabled' ){
push @udp_ports, $port;
}
elsif ( $proto eq 'tcp' && $status eq 'enabled' ){
push @tcp_ports, $port;
}
}
$conf_db->set_prop('openvpn-s2s', 'UDPPorts', join(',',@udp_ports));
$conf_db->set_prop('openvpn-s2s', 'TCPPorts', join(',',@tcp_ports));
}
sub print_cert_fields{
my $fm = shift;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
my $rec = $ovpn_db->get("$conf");
my $type = $rec->prop('type') || 'server';
my $auth = $rec->prop('Authentication') || 'TLS';
my $crlurl = $rec->prop('CrlUrl') || '';
# Untaint $conf
$conf =~ m/(.*)/;
$conf = $1;
if ($auth eq 'TLS'){
print esmith::cgi::genTextRow($q,$fm->localise('DESC_CRL_URL'));
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_CRL_URL')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textfield (
-name => 'crl_url',
-override => 1,
-default => $crlurl,
-size => 62))),"\n";
print esmith::cgi::genTextRow($q,$fm->localise('DESC_CA_PEM'));
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_CA_PEM')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textarea (
-name => 'cacert_pem',
-override => 1,
-default => read_file("$pubdir/$conf"."_cacert.pem"),
-rows => 15,
-columns => 70))),"\n";
print esmith::cgi::genTextRow($q,$fm->localise('DESC_CRT_PEM'));
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_CRT_PEM')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textarea (
-name => 'cert_pem',
-override => 1,
-default => read_file("$pubdir/$conf"."_cert.pem"),
-rows => 15,
-columns => 70))),"\n";
print esmith::cgi::genTextRow($q,$fm->localise('DESC_KEY_PEM'));
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_KEY_PEM')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textarea (
-name => 'key_pem',
-override => 1,
-default => read_file("$privdir/$conf"."_key.pem"),
-rows => 15,
-columns => 70))),"\n";
if ($type eq 'server'){
print esmith::cgi::genTextRow($q,$fm->localise('DESC_DH_PEM'));
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_DH_PEM')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textarea (
-name => 'dh_pem',
-override => 1,
-default => read_file("$pubdir/$conf"."_dh.pem"),
-rows => 8,
-columns => 70))),"\n";
}
print esmith::cgi::genTextRow($q,$fm->localise('DESC_SHARED_KEY_TLS'));
}
else{
print esmith::cgi::genTextRow($q,$fm->localise('DESC_SHARED_KEY'));
}
print $q->Tr (
$q->td ({-class => "sme-noborders-label"},
$fm->localise('LABEL_SHARED_KEY')),"\n",
$q->td ({-class => "sme-noborders-content"},
$q->textarea (
-name => 'shared_key',
-override => 1,
-default => read_file("$privdir/$conf"."_sharedkey.pem"),
-rows => 5,
-columns => 70))),"\n";
return undef;
}
# Print clients or servers table
sub reload{
my ($fm) = @_;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
#$conf = $conf=~ m/^([a-zA-Z\-\_0-9]+)$/;
unless (system ("/sbin/e-smith/signal-event", "openvpn-s2s-update-one", $conf) == 0 ){
$fm->error('ERROR_OCCURED1','FIRST_PAGE');
return undef;
}
$fm->success( $fm->localise('SUCCESS_RELOAD') . " $conf" ,'FIRST_PAGE');
return undef;
}
# Print clients or servers table
sub print_conf_table{
my $fm = shift;
my $type = shift;
my $q = $fm->{cgi};
my $conf_name = $fm->localise('CONF_NAME');
my $modify = $fm->localise('MODIFY');
my @conf = $ovpn_db->get_all_by_prop(type=>$type);
unless ( scalar @conf ){
print $q->Tr($q->td($fm->localise('NO_CONF')));
return "";
}
print $q->start_table({-CLASS => "sme-border"}),"\n";
print $q->Tr (
esmith::cgi::genSmallCell($q, $fm->localise('CONF_NAME'),"header"),
esmith::cgi::genSmallCell($q, $fm->localise('STATUS'),"header"),
esmith::cgi::genSmallCell($q, $fm->localise('CIPHER'),"header"),
esmith::cgi::genSmallCell($q, 'HMAC',"header"),
esmith::cgi::genSmallCell($q, $fm->localise('LABEL_AUTH'),"header"),
esmith::cgi::genSmallCell($q, $fm->localise('LINK'),"header"),
esmith::cgi::genSmallCell($q, $fm->localise('ACTION'),"header", 3),
),
"\n";
foreach my $config (@conf){
my $key = $config->key;
my $status = $config->prop('status') || 'enabled';
my $linkup = "<span style='color:red'>". $fm->localise('DOWN')."</span>" ;
use Net::Ping;
my $p = Net::Ping->new('icmp');
$linkup = "<span style='color:green'>". $fm->localise('UP') . "</span>" if (($status eq "enabled" ) && ( $p->ping($config->prop('RemoteIP'),1) ) );
$linkup = " " if ($status eq "disabled" );
if ($status eq 'enabled'){
$status = $fm->localise('ENABLED');
}
elsif ($status eq 'disabled'){
$status = $fm->localise('DISABLED');
}
my $cipher = $config->prop('Cipher') || 'BF-CBC';
$cipher = "<span style='color:red'>". $fm->localise('INSECURE'). " $cipher</span> " unless ($cipher =~ /(128|192|256|512|SEED)/ );
my $hmac = $config->prop('HMAC') || 'SHA1';
$hmac= "<span style='color:red'>". $fm->localise('INSECURE'). " $hmac</span> " unless ($hmac eq "whirlpool" || $hmac =~ /(512|256|384|224)$/);
my $authe = $config->prop('Authentication') || '';
print $q->Tr (esmith::cgi::genSmallCell($q,"$key"),
esmith::cgi::genSmallCell($q,"$status"),
esmith::cgi::genSmallCell($q,"$cipher"),
esmith::cgi::genSmallCell($q,"$hmac"),
esmith::cgi::genSmallCell($q,"$authe"),
esmith::cgi::genSmallCell($q,"$linkup"),
esmith::cgi::genSmallCell ($q, $q->a ({href => $q->url (-absolute => 1).
$base_url."RELOAD_PAGE&action=reload&conf_name=".
$key}, $fm->localise('RELOAD'))),
esmith::cgi::genSmallCell ($q, $q->a ({href => $q->url (-absolute => 1).
$base_url."CREATE_OR_MODIFY_".uc($type)."_CONF_PAGE&action=modify&conf_name=".
$key}, $fm->localise('MODIFY'))),
esmith::cgi::genSmallCell ($q, $q->a ({href => $q->url (-absolute => 1).
$base_url."REMOVE_CONF_PAGE&conf_name=".
$key}, $fm->localise('REMOVE'))));
}
print $q->end_table,"\n";
return "";
}
# Print conf_name field
sub print_conf_name_field{
my $fm = shift;
my $q = $fm->{cgi};
my $name = $q->param('conf_name') || '';
my $action = $q->param('action') || '';
print qq(<tr><td colspan="2">) . $fm->localise('DESC_CONF_NAME').qq(</td></tr>);
print qq(<tr><td class="sme-noborders-label">) .
$fm->localise('CONF_NAME') . qq(</td>\n);
if ($action eq 'modify' and $name) {
print qq(
<td class="sme-noborders-content">$name
<input type="hidden" name="name" value="$name">
<input type="hidden" name="action" value="modify">
</td>
);
# If action is modify, we need to read the DB
# And set CGI parameters
my $rec = $ovpn_db->get($name);
if ($rec){
if ($rec->prop('type') eq 'client'){
$q->param(-name=>'remote_host',-value=>
$rec->prop('RemoteHost'));
}
$q->param(-name=>'auth',-value=>
$rec->prop('Authentication'));
$q->param(-name=>'local_ip',-value=>
$rec->prop('LocalIP'));
$q->param(-name=>'remote_ip',-value=>
$rec->prop('RemoteIP'));
$q->param(-name=>'port',-value=>
$rec->prop('Port'));
$q->param(-name=>'comment',-value=>
$rec->prop('Comment'));
$q->param(-name=>'status',-value=>
$rec->prop('status'));
$q->param(-name=>'remote_net',-value=>
$rec->prop('RemoteNetworks'));
$q->param(-name=>'hmac',-value=>
get_current_hmac($fm));
$q->param(-name=>'cipher',-value=>
get_current_cipher($fm));
$q->param(-name=>'SnatOutbound',-value=>
$rec->prop('SnatOutbound'));
}
}
else {
print qq(
<td><input type="text" name="conf_name" value="$name">
<input type="hidden" name="action" value="create">
</td>
);
$q->param(-name=>'status',-value=>
'enabled')
}
print qq(</tr>\n);
return undef;
}
sub remove_conf{
my ($fm) = @_;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
unless($q->param("cancel")){
unless ($ovpn_db->get($conf)->delete()){
$fm->error('ERROR_OCCURED','FIRST_PAGE');
return undef;
}
unless (system ("/sbin/e-smith/signal-event", "openvpn-s2s-update") == 0 ){
$fm->error('ERROR_OCCURED','FIRST_PAGE');
return undef;
}
update_ports();
$fm->success('SUCCESS','FIRST_PAGE');
return undef;
}
$fm->error('CANCELED','FIRST_PAGE');
return undef;
}
sub print_conf_to_remove{
my ($fm) = @_;
my $q = $fm->{cgi};
my $conf = $q->param('conf_name');
my $rec = $ovpn_db->get($conf);
my $comment = $rec->prop('Comment') || '';
print $q->Tr(
$q->td(
{ -class => 'sme-noborders-label' },
$fm->localise('CONF_NAME')
),
$q->td( { -class => 'sme-noborders-content' }, $conf )
),
"\n";
print $q->Tr(
$q->td(
{ -class => 'sme-noborders-label' },
$fm->localise('COMMENT')
),
$q->td( { -class => 'sme-noborders-content' }, $comment )
),
"\n";
print $q->table(
{ -width => '100%' },
$q->Tr(
$q->th(
{ -class => 'sme-layout' },
$q->submit(
-name => 'cancel',
-value => $fm->localise('CANCEL')
),
' ',
$q->submit(
-name => 'remove',
-value => $fm->localise('REMOVE')
)
)
)
),
"\n";
# Clear these values to prevent collisions when the page reloads.
$q->delete("cancel");
$q->delete("remove");
return undef;
}
sub read_file{
my $file = shift;
my $ret = '';
if (open (PEM, "<$file")){
$ret .= $_ while (<PEM>);
close PEM;
}
return $ret;
}
# Validation routines
sub is_valid_key{
my ($fm, $string) = @_;
my $ret = 'OK';
# Just check if the string is empty
$ret = $fm->localise('INVALID_SHARED_KEY') if ($string eq '');
return $ret;
}
sub is_url_or_empty{
my ($fm, $url) = @_;
my $ret = 'OK';
if (defined $url && ($url !~ /^(http:\/\/)|(https:\/\/)/) && ($url ne '')){
$ret = $fm->localise('NOT_A_VALID_URL',{string => $url});
}
return $ret;
}
sub is_valid_net_or_empty{
my ($fm, $nets) = @_;
my $ret = 'OK';
my $conf = $fm->{cgi}->param('conf_name') || '';
foreach my $net (split (/[;,]/, $nets)){
$ret = $fm->localise('INVALID_NET') unless $net =~ m/([\d\.]+)\/([\d\.]+)/;
my $netaddr = $1;
my $mask = $2;
$ret = $fm->localise('NET_IS_LOCAL') if
($net_db->get("$netaddr") &&
($net_db->get("$netaddr")->prop('VPN') || '') ne $conf);
$ret = $fm->localise('INVALID_NET') unless
(CGI::FormMagick::Validator::ip_number($fm,$netaddr) eq 'OK' &&
CGI::FormMagick::Validator::ip_number($fm,$mask) eq 'OK');
}
return $ret;
}
sub is_hostname_or_ip{
my ($fm,$host) = @_;
return "OK" if ( CGI::FormMagick::Validator::ip_number( $fm, $host ) eq "OK" );
return "OK" if ( $host =~ m/^([a-zA-Z0-9][\_\.\-a-zA-Z0-9]*)$/);
return $fm->localise('NOT_A_VALID_HOSTNAME_OR_IP');
}
sub validate_conf_name{
my ($fm, $conf_name) = @_;
unless ($conf_name =~ /^([a-zA-Z0-9][\_\.\-a-zA-Z0-9]{0,9})$/){
return $fm->localise('INVALID_CHARS',{string => $conf_name});
}
return "OK";
}
sub is_valid_port{
my ($fm, $port) = @_;
my $ret = $fm->localise('NOT_A_VALID_PORT',{string => $port});
if (($port =~ /^\d+$/) &&
($port > 0) &&
($port < 65536)){
$ret = "OK";
}
return $ret;
}
sub is_valid_vpn_ip{
my ($fm,$ip) = @_;
my $ret = 'OK';
my $conf = $fm->{cgi}->param('conf_name') || '';
# Check it's a valid IP number
return $fm->localise('NOT_A_VALID_IP_NUMBER') unless
( CGI::FormMagick::Validator::ip_number( $fm, $ip ) eq "OK" );
# Check it's not already used by another daemon
foreach my $vpn ($ovpn_db->get_all_by_prop(type=>'client'),
$ovpn_db->get_all_by_prop(type=>'server')){
next if ($vpn->key eq $conf);
$ret = $fm->localise('IP_ALREADY_IN_USED')
if ((($vpn->prop('LocalIP') || '') eq $ip) ||
(($vpn->prop('RemoteIP') || '') eq $ip));
}
# 127 is a reserved network
return $fm->localise('RESERVED_NET') if ($ip =~ m/^127\./);
# Check if it's not part of a local network
foreach my $net ($net_db->networks){
# Skip VPN networks
next if (($net->prop('VPN') || '') ne '');
my $mask = $net->prop('Mask');
$net = $net->key . '/' . $mask;
$ret = $fm->localise('IP_IN_LOCAL_NET') if (eval{Net::IPv4Addr::ipv4_in_network($net,$ip)});
}
return $ret;
}
sub is_valid_and_available_port{
my ($fm, $port) = @_;
my $ret = is_valid_port($fm,$port);
my $conf = $fm->{cgi}->param('conf_name');
my $oldport = $ovpn_db->get_prop($conf,'Port') || '';
# If the port is valid
# Check if it's not used by another service
if ($ret eq 'OK'){
my @used = ();
foreach my $srv ($conf_db->get_all_by_prop(type=>'service')){
push @used, $srv->prop('UDPPort') if $srv->prop('UDPPort');
push @used, $srv->prop('TCPPort') if $srv->prop('TCPPort');
push @used, split( /[;,]/, $srv->prop('UDPPorts')) if $srv->prop('UDPPorts');
push @used, split( /[;,]/, $srv->prop('TCPPorts')) if $srv->prop('TCPPorts');
}
# Remove the old port number from the list
# So it won't be marked as already used
# if we just modify an existing VPN server
foreach my $i (0..$#used){
splice(@used,$i,1) if ($used[$i] eq $oldport);
}
$ret = $fm->localise('PORT_ALREAY_USED',{string => $port})
if (grep { $_ eq $port } @used);
}
return $ret;
}
###### those are copy paste for bridge and s2s
#
=head2 get_current_hmac
=cut
sub get_current_hmac{
my ($self) = @_;
my $name = $self->cgi->param('conf_name') or return "SHA256";
my $cvpn= $ovpn_db->get($name);
return "SHA1" unless defined $cvpn->prop('HMAC');
return $cvpn->prop('HMAC') ;
}
=head2 get_digests_options
=cut
sub get_digests_options{
my ($self) = @_;
my $translate = $self->localise('DEFAULT');
my $suggested = $self->localise('SUGGESTED');
my %options= (
'whirlpool' => 'whirlpool (512)',
'SHA512' => 'SHA512',
'SHA384' => 'SHA384',
'SHA256' => 'SHA256' . ": $suggested",
'SHA224' => 'SHA224',
'SHA1' => 'SHA1 (160)' . ": $translate",
'SHA' => 'SHA (160)',
'ecdsa-with-SHA1' => 'ecdsa-with-SHA1 (160)',
'RIPEMD160' => 'RIPEMD160',
'MD5' => 'MD5 (128)',
'MD4' => 'MD4 (128)',
);
return \%options;
}
=head2 get_current_cipher
list obtained using
openvpn --show-digests | egrep 'digest size' | awk {'print "'\''" $1 "'\'' => '\''" $1 "'\''," '}
=cut
sub get_current_cipher{
my ($self) = @_;
my $name = $self->cgi->param('conf_name') or return "AES-128-CBC";
my $cvpn= $ovpn_db->get($name);
return "BF-CBC" unless defined $cvpn->prop('Cipher');
return $cvpn->prop('Cipher') ;
}
=head2 get_ciphers_options
list obtained using
openvpn --show-ciphers | egrep '^[A-Z]{2}' | sed 's/ by//; s/ default//; s/block,/block/; s/)// ' | awk {'print " '\''" $1 "'\'' => '\''" $1 $2 " " $4 " " $5 " " $7")'\''," '}
then reduced to remove most of insecure ciphers
Using a CBC or GCM mode is recommended.
In static key mode only CBC mode is allowed.
=cut
sub get_ciphers_options{
my ($self) = @_;
my $translate = $self->localise('DEFAULT');
my $suggested = $self->localise('SUGGESTED');
my %options= (
'AES-128-CBC' => 'AES-128-CBC (128 key, 128 block)'.": $suggested",
'AES-128-CFB' => 'AES-128-CFB (128 key, 128 block)',
'AES-128-CFB1' => 'AES-128-CFB1 (128 key, 128 block)',
'AES-128-CFB8' => 'AES-128-CFB8 (128 key, 128 block)',
'AES-128-GCM' => 'AES-128-GCM (128 key, 128 block)',
'AES-128-OFB' => 'AES-128-OFB (128 key, 128 block)',
'AES-192-CBC' => 'AES-192-CBC (192 key, 128 block)',
'AES-192-CFB' => 'AES-192-CFB (192 key, 128 block)',
'AES-192-CFB1' => 'AES-192-CFB1 (192 key, 128 block)',
'AES-192-CFB8' => 'AES-192-CFB8 (192 key, 128 block)',
'AES-192-GCM' => 'AES-192-GCM (192 key, 128 block)',
'AES-192-OFB' => 'AES-192-OFB (192 key, 128 block)',
'AES-256-CBC' => 'AES-256-CBC (256 key, 128 block)',
'AES-256-CFB' => 'AES-256-CFB (256 key, 128 block)',
'AES-256-CFB1' => 'AES-256-CFB1 (256 key, 128 block)',
'AES-256-CFB8' => 'AES-256-CFB8 (256 key, 128 block)',
'AES-256-GCM' => 'AES-256-GCM (256 key, 128 block)',
'AES-256-OFB' => 'AES-256-OFB (256 key, 128 block)',
'CAMELLIA-128-CBC' => 'CAMELLIA-128-CBC (128 key, 128 block)',
'CAMELLIA-128-CFB' => 'CAMELLIA-128-CFB (128 key, 128 block)',
'CAMELLIA-128-CFB1' => 'CAMELLIA-128-CFB1 (128 key, 128 block)',
'CAMELLIA-128-CFB8' => 'CAMELLIA-128-CFB8 (128 key, 128 block)',
'CAMELLIA-128-OFB' => 'CAMELLIA-128-OFB (128 key, 128 block)',
'CAMELLIA-192-CBC' => 'CAMELLIA-192-CBC (192 key, 128 block)',
'CAMELLIA-192-CFB' => 'CAMELLIA-192-CFB (192 key, 128 block)',
'CAMELLIA-192-CFB1' => 'CAMELLIA-192-CFB1 (192 key, 128 block)',
'CAMELLIA-192-CFB8' => 'CAMELLIA-192-CFB8 (192 key, 128 block)',
'CAMELLIA-192-OFB' => 'CAMELLIA-192-OFB (192 key, 128 block)',
'CAMELLIA-256-CBC' => 'CAMELLIA-256-CBC (256 key, 128 block)',
'CAMELLIA-256-CFB' => 'CAMELLIA-256-CFB (256 key, 128 block)',
'CAMELLIA-256-CFB1' => 'CAMELLIA-256-CFB1 (256 key, 128 block)',
'CAMELLIA-256-CFB8' => 'CAMELLIA-256-CFB8 (256 key, 128 block)',
'CAMELLIA-256-OFB' => 'CAMELLIA-256-OFB (256 key, 128 block)',
'SEED-CBC' => 'SEED-CBC (128 key, 128 block)',
'SEED-CFB' => 'SEED-CFB (128 key, 128 block)',
'SEED-OFB' => 'SEED-OFB (128 key, 128 block)',
'BF-CBC' => 'BF-CBC(128 key, 64 block)'. ": $translate ",
);
return \%options;
}
1;