mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
VPN:OpenVPN: Servers - deferred authentication, work in progress for https://github.com/opnsense/core/issues/6293
This initial commit focusses on structuring the event flow around user and client registration, moving events to our new ovpn_event.py handler. By supporting both deferred and direct authentication in user_pass_verify.php, we should be able to start with a cleanup patch for OpenVPN 2.5.x and work our way up to a smaller fix for 2.6.x. In preperation for 2.6, this commit also moves --cipher to --data-ciphers-fallback as suggested by the warning "DEPRECATED OPTION: --cipher set to '' but missing in --data-ciphers". Rename the option in the gui while there and add a note in the help text.
This commit is contained in:
parent
f984b7af16
commit
1e28d5b352
4
plist
4
plist
@ -32,14 +32,10 @@
|
||||
/usr/local/etc/inc/plugins.inc.d/opendns.inc
|
||||
/usr/local/etc/inc/plugins.inc.d/openssh.inc
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn.inc
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/attributes.sh
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/auth-user.php
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/dh.rfc7919
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn-linkdown
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn-linkup
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_auth_verify
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_setup_cso.php
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/tls-verify.php
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/tunnel_endpoint.php
|
||||
/usr/local/etc/inc/plugins.inc.d/openvpn/wizard.inc
|
||||
/usr/local/etc/inc/plugins.inc.d/pf.inc
|
||||
|
||||
@ -295,7 +295,7 @@ function openvpn_port_next($prot, $interface = "wan")
|
||||
|
||||
function openvpn_get_cipherlist()
|
||||
{
|
||||
$ciphers = array();
|
||||
$ciphers = ["" => gettext("None")];
|
||||
exec('/usr/local/sbin/openvpn --show-ciphers', $lines);
|
||||
foreach ($lines as $line) {
|
||||
if (strstr($line, ' (') !== false) {
|
||||
@ -304,7 +304,6 @@ function openvpn_get_cipherlist()
|
||||
}
|
||||
}
|
||||
ksort($ciphers, SORT_STRING | SORT_FLAG_CASE);
|
||||
$ciphers['none'] = gettext('None (No Encryption)');
|
||||
return $ciphers;
|
||||
}
|
||||
|
||||
@ -528,7 +527,9 @@ function openvpn_reconfigure($mode, $settings, $device_only = false)
|
||||
$conf .= "persist-tun\n";
|
||||
$conf .= "persist-key\n";
|
||||
$conf .= "proto {$proto}\n";
|
||||
$conf .= "cipher {$cipher}\n";
|
||||
if (!empty($cipher) && $cipher != 'none') {
|
||||
$conf .= "data-ciphers-fallback {$cipher}\n";
|
||||
}
|
||||
$conf .= "auth {$digest}\n";
|
||||
$conf .= "up /usr/local/etc/inc/plugins.inc.d/openvpn/ovpn-linkup\n";
|
||||
$conf .= "down /usr/local/etc/inc/plugins.inc.d/openvpn/ovpn-linkdown\n";
|
||||
@ -549,20 +550,20 @@ function openvpn_reconfigure($mode, $settings, $device_only = false)
|
||||
switch ($settings['mode']) {
|
||||
case 'server_user':
|
||||
case 'server_tls_user':
|
||||
$conf .= "client-disconnect \"/usr/local/etc/inc/plugins.inc.d/openvpn/attributes.sh {$mode_id}\"\n";
|
||||
$conf .= "client-disconnect \"/usr/local/opnsense/scripts/openvpn/ovpn_event.py '{$vpnid}'\"\n";
|
||||
break;
|
||||
case 'server_tls':
|
||||
// For non user auth types setup client specific overrides,
|
||||
// user authenticated ones are commissioned using the auth
|
||||
// script in option auth-user-pass-verify.
|
||||
$conf .= "client-connect \"/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_setup_cso.php {$mode_id}\"\n";
|
||||
$conf .= "client-connect \"/usr/local/opnsense/scripts/openvpn/ovpn_event.py '{$vpnid}'\"\n";
|
||||
break;
|
||||
case 'p2p_tls':
|
||||
// same as server_tls, but only valid if cidr < 30, without
|
||||
// server directive client-connect is not valid.
|
||||
// XXX: IPv6 is likely flawed, see "server" directive too.
|
||||
if (!empty($ip) && !empty($mask) && ($cidr < 30)) {
|
||||
$conf .= "client-connect \"/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_setup_cso.php {$mode_id}\"\n";
|
||||
$conf .= "client-connect \"/usr/local/opnsense/scripts/openvpn/ovpn_event.py '{$vpnid}'\"\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -664,28 +665,12 @@ function openvpn_reconfigure($mode, $settings, $device_only = false)
|
||||
$conf .= "username-as-common-name\n";
|
||||
}
|
||||
if (!empty($settings['authmode'])) {
|
||||
$strictusercn = "false";
|
||||
if ($settings['strictusercn']) {
|
||||
$strictusercn = "true";
|
||||
}
|
||||
$conf .= "auth-user-pass-verify \"/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_auth_verify " .
|
||||
"user '{$settings['authmode']}' '{$strictusercn}' '{$mode_id}'\" via-env\n";
|
||||
$conf .= "auth-user-pass-verify \"/usr/local/opnsense/scripts/openvpn/ovpn_event.py '{$vpnid}'\" via-env\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!isset($settings['cert_depth']) && (strstr($settings['mode'], 'tls'))) {
|
||||
$settings['cert_depth'] = 1;
|
||||
}
|
||||
if (is_numeric($settings['cert_depth'])) {
|
||||
if (($mode == 'client') && empty($settings['certref'])) {
|
||||
$cert = "";
|
||||
} else {
|
||||
$cert = lookup_cert($settings['certref']);
|
||||
/* XXX: Seems not used at all! */
|
||||
$servercn = urlencode(cert_get_cn($cert['crt']));
|
||||
$conf .= "tls-verify \"/usr/local/etc/inc/plugins.inc.d/openvpn/ovpn_auth_verify " .
|
||||
"tls '{$servercn}' {$settings['cert_depth']}\"\n";
|
||||
}
|
||||
if ($mode == 'server' && (strstr($settings['mode'], 'tls'))) {
|
||||
$conf .= "tls-verify \"/usr/local/opnsense/scripts/openvpn/ovpn_event.py '{$vpnid}'\"\n";
|
||||
}
|
||||
|
||||
// The local port to listen on
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$script_type" = "client-disconnect" ]; then
|
||||
/sbin/pfctl -k $ifconfig_pool_remote_ip
|
||||
/sbin/pfctl -K $ifconfig_pool_remote_ip
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$1" = "tls" ]; then
|
||||
/usr/local/bin/php /usr/local/etc/inc/plugins.inc.d/openvpn/tls-verify.php -d "$2" "$3"
|
||||
else
|
||||
# Single quoting $password breaks getting the value from the variable.
|
||||
# XXX I really don't like going through openssl for this...
|
||||
password=$(echo -n "${password}" | /usr/local/bin/openssl enc -base64 | sed -e 's/=/%3D/g')
|
||||
username=$(echo -n "${username}" | /usr/local/bin/openssl enc -base64 | sed -e 's/=/%3D/g')
|
||||
|
||||
/usr/local/bin/php /usr/local/etc/inc/plugins.inc.d/openvpn/auth-user.php "$username" "$password" "$common_name" "$3" "$2" "$4"
|
||||
fi
|
||||
|
||||
exit $?
|
||||
@ -2,7 +2,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Deciso B.V.
|
||||
* Copyright (C) 2018-2023 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -34,25 +34,31 @@ require_once("plugins.inc.d/openvpn.inc");
|
||||
/* setup syslog logging */
|
||||
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
|
||||
$common_name = getenv("common_name");
|
||||
$vpnid = filter_var($argv[1], FILTER_SANITIZE_NUMBER_INT);
|
||||
$vpnid = getenv("auth_server");
|
||||
$config_file = getenv("config_file");
|
||||
if (isset($config['openvpn']['openvpn-server'])) {
|
||||
foreach ($config['openvpn']['openvpn-server'] as $server) {
|
||||
if ("{$server['vpnid']}" === "$vpnid") {
|
||||
if ($server['vpnid'] == $vpnid) {
|
||||
// XXX: Eventually we should move the responsibility to determine if we do want to write a file
|
||||
// to here instead of the configuration file (always call event, filter relevant).
|
||||
$all_cso = openvpn_fetch_csc_list();
|
||||
if (!empty($all_cso[$vpnid][$common_name])) {
|
||||
$cso = $all_cso[$vpnid][$common_name];
|
||||
} else {
|
||||
$cso = array("common_name" => $common_name);
|
||||
$cso = ["common_name" => $common_name];
|
||||
}
|
||||
// $argv[2] contains the temporary file used for the profile specified by client-connect
|
||||
$cso_filename = openvpn_csc_conf_write($cso, $server, $argv[2]);
|
||||
if (!empty($config_file)) {
|
||||
$cso_filename = openvpn_csc_conf_write($cso, $server, $config_file);
|
||||
if (!empty($cso_filename)) {
|
||||
syslog(LOG_NOTICE, "client config created @ {$cso_filename}");
|
||||
}
|
||||
} else {
|
||||
syslog(LOG_NOTICE, "unable to write client config for {$common_name}, missing target filename");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closelog();
|
||||
exit(0);
|
||||
exit(0);
|
||||
6
src/opnsense/scripts/openvpn/client_disconnect.sh
Executable file
6
src/opnsense/scripts/openvpn/client_disconnect.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
/sbin/pfctl -k $ifconfig_pool_remote_ip
|
||||
/sbin/pfctl -K $ifconfig_pool_remote_ip
|
||||
|
||||
exit 0
|
||||
99
src/opnsense/scripts/openvpn/ovpn_event.py
Executable file
99
src/opnsense/scripts/openvpn/ovpn_event.py
Executable file
@ -0,0 +1,99 @@
|
||||
#!/usr/local/bin/python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2023 Ad Schellevis <ad@opnsense.org>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def main(params):
|
||||
if params.common_name:
|
||||
os.environ['common_name'] = params.common_name
|
||||
if params.auth_control_file:
|
||||
os.environ['auth_control_file'] = params.auth_control_file
|
||||
if params.server:
|
||||
os.environ["auth_server"] = params.server
|
||||
# script action
|
||||
cmd_path = "%s/" % os.path.dirname(__file__)
|
||||
if params.script_type == 'user-pass-verify':
|
||||
os.environ["auth_method"] = params.auth_method
|
||||
if params.auth_method == 'via-file' and len(params.args) > 0:
|
||||
# user/password file to read (via-file), arg1
|
||||
os.environ["auth_file"] = params.args[0]
|
||||
if params.defer:
|
||||
os.environ["auth_defer"] = 'true'
|
||||
if os.fork() != 0:
|
||||
# When deferred, openvpn expects exit code 2 (see help --auth-user-pass-verify)
|
||||
sys.exit(2)
|
||||
|
||||
# dispatch user_pass_verify
|
||||
sys.exit(subprocess.run("%s/user_pass_verify.php" % cmd_path).returncode)
|
||||
elif params.script_type == 'tls-verify':
|
||||
if len(params.args) > 0:
|
||||
os.environ["certificate_depth"] = params.args[0]
|
||||
sys.exit(subprocess.run("%s/tls_verify.php" % cmd_path).returncode)
|
||||
elif params.script_type == 'client-connect':
|
||||
# Temporary file used for the profile specified by client-connect
|
||||
if len(params.args) > 0:
|
||||
os.environ["config_file"] = params.args[0]
|
||||
sys.exit(subprocess.run("%s/client_connect.php" % cmd_path).returncode)
|
||||
elif params.script_type == 'client-disconnect':
|
||||
sys.exit(subprocess.run("%s/client_disconnect.sh" % cmd_path).returncode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'--common_name',
|
||||
help='common name [cn], defaults to "common_name" in environment',
|
||||
default=os.environ.get('common_name', None)
|
||||
)
|
||||
parser.add_argument(
|
||||
'--script_type',
|
||||
help='script_type (event), defaults to "script_type" in environment',
|
||||
default=os.environ.get('script_type', None),
|
||||
choices=['user-pass-verify', 'tls-verify', 'client-connect', 'client-disconnect']
|
||||
)
|
||||
parser.add_argument(
|
||||
'--auth_control_file',
|
||||
help='auth control file location, defaults to "auth_control_file" in environment',
|
||||
default=os.environ.get('auth_control_file', None)
|
||||
)
|
||||
parser.add_argument(
|
||||
'--auth_method',
|
||||
help='auth method (via-env or via-file)',
|
||||
default='via-env',
|
||||
choices=['via-env', 'via-file']
|
||||
)
|
||||
parser.add_argument('--defer', help='defer action (when supported)', default=False, action="store_true")
|
||||
parser.add_argument('server', help='openvpn server id to use, authentication settings are configured per server')
|
||||
parser.add_argument('args', nargs='*', help='script arguments specified by openvpn')
|
||||
|
||||
main(parser.parse_args())
|
||||
63
src/etc/inc/plugins.inc.d/openvpn/tls-verify.php → src/opnsense/scripts/openvpn/tls_verify.php
Normal file → Executable file
63
src/etc/inc/plugins.inc.d/openvpn/tls-verify.php → src/opnsense/scripts/openvpn/tls_verify.php
Normal file → Executable file
@ -2,14 +2,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Jim Pingle <jimp@pfsense.org>
|
||||
* Copyright (C) 2018 Deciso B.V.
|
||||
* Copyright (C) 2018-2023 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
@ -28,25 +27,45 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* OpenVPN calls this script to validate a certificate
|
||||
* This script is called ONCE per DEPTH of the certificate chain
|
||||
* Normal operation would have two runs - one for the server certificate
|
||||
* and one for the client certificate. Beyond that, you're dealing with
|
||||
* intermediates.
|
||||
require_once("config.inc");
|
||||
|
||||
|
||||
/**
|
||||
* verify certificate depth
|
||||
* @param string $serverid server identifier
|
||||
* @return string|bool an error string or true when properly authenticated
|
||||
*/
|
||||
|
||||
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
|
||||
|
||||
/* read data from command line */
|
||||
$cert_depth = intval($argv[1]);
|
||||
$cert_subject = $argv[2];
|
||||
|
||||
if (isset($allowed_depth) && ($cert_depth > $allowed_depth)) {
|
||||
syslog(LOG_WARNING, "Certificate depth {$cert_depth} exceeded max allowed depth of {$allowed_depth}.\n");
|
||||
closelog();
|
||||
exit(1);
|
||||
function do_verify($serverid)
|
||||
{
|
||||
global $config;
|
||||
$a_server = null;
|
||||
if (isset($config['openvpn']['openvpn-server'])) {
|
||||
foreach ($config['openvpn']['openvpn-server'] as $server) {
|
||||
if ($server['vpnid'] == $serverid) {
|
||||
$a_server = $server;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($a_server === null) {
|
||||
return "OpenVPN '$serverid' was not found. Denying authentication for user {$username}";
|
||||
}
|
||||
$certificate_depth = getenv('certificate_depth') !== false ? getenv('certificate_depth'): 0;
|
||||
$allowed_depth = !empty($a_server['cert_depth']) ? $a_server['cert_depth'] : 1;
|
||||
if ($allowed_depth != null && ($certificate_depth > $allowed_depth)) {
|
||||
return "Certificate depth {$certificate_depth} exceeded max allowed depth of {$allowed_depth}.";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
|
||||
$response = do_verify(getenv('auth_server'));
|
||||
if ($response !== true) {
|
||||
syslog(LOG_WARNING, $response);
|
||||
closelog();
|
||||
exit(1);
|
||||
} else {
|
||||
closelog();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
closelog();
|
||||
exit(0);
|
||||
144
src/etc/inc/plugins.inc.d/openvpn/auth-user.php → src/opnsense/scripts/openvpn/user_pass_verify.php
Normal file → Executable file
144
src/etc/inc/plugins.inc.d/openvpn/auth-user.php → src/opnsense/scripts/openvpn/user_pass_verify.php
Normal file → Executable file
@ -2,9 +2,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 Shrew Soft Inc. <mgrooms@shrew.net>
|
||||
* Copyright (C) 2010 Ermal Luçi
|
||||
* Copyright (C) 2018 Deciso B.V.
|
||||
* Copyright (C) 2018-2023 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,24 +27,22 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* OpenVPN calls this script to authenticate a user
|
||||
* based on a username and password. We lookup these
|
||||
* in our config.xml file and check the credentials.
|
||||
*/
|
||||
|
||||
require_once("config.inc");
|
||||
require_once("auth.inc");
|
||||
require_once("util.inc");
|
||||
require_once("interfaces.inc");
|
||||
require_once("plugins.inc.d/openvpn.inc");
|
||||
|
||||
/**
|
||||
* @param string $serverid server identifier
|
||||
* @return array|null openvpn server properties
|
||||
*/
|
||||
function get_openvpn_server($serverid)
|
||||
{
|
||||
global $config;
|
||||
if (isset($config['openvpn']['openvpn-server'])) {
|
||||
foreach ($config['openvpn']['openvpn-server'] as $server) {
|
||||
if ("server{$server['vpnid']}" == $serverid) {
|
||||
if ($server['vpnid'] == $serverid) {
|
||||
return $server;
|
||||
}
|
||||
}
|
||||
@ -54,9 +50,14 @@ function get_openvpn_server($serverid)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse provisioning properties supplied by the authenticator
|
||||
* @param array $props key value store containing addresses and routes
|
||||
* @return array formatted like openvpn_csc_conf_write() expects
|
||||
*/
|
||||
function parse_auth_properties($props)
|
||||
{
|
||||
$result = array();
|
||||
$result = [];
|
||||
if (!empty($props['Framed-IP-Address']) && !empty($props['Framed-IP-Netmask'])) {
|
||||
$cidrmask = 32 - log((ip2long($props['Framed-IP-Netmask']) ^ ip2long('255.255.255.255')) + 1, 2);
|
||||
$result['tunnel_network'] = $props['Framed-IP-Address'] . "/" . $cidrmask;
|
||||
@ -67,62 +68,58 @@ function parse_auth_properties($props)
|
||||
return $result;
|
||||
}
|
||||
|
||||
/* setup syslog logging */
|
||||
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
|
||||
|
||||
if (count($argv) > 6) {
|
||||
$authmodes = explode(',', $argv[5]);
|
||||
$username = base64_decode(str_replace('%3D', '=', $argv[1]));
|
||||
$password = base64_decode(str_replace('%3D', '=', $argv[2]));
|
||||
$common_name = $argv[3];
|
||||
$modeid = $argv[6];
|
||||
$strictusercn = $argv[4] == 'false' ? false : true;
|
||||
|
||||
$a_server = get_openvpn_server($modeid);
|
||||
if (strpos($password, 'SCRV1:') === 0) {
|
||||
// static-challenge https://github.com/OpenVPN/openvpn/blob/v2.4.7/doc/management-notes.txt#L1146
|
||||
// validate and concat password into our default pin+password
|
||||
$tmp = explode(':', $password);
|
||||
if (count($tmp) == 3) {
|
||||
$pass = base64_decode($tmp[1]);
|
||||
$pin = base64_decode($tmp[2]);
|
||||
if ($pass !== false && $pin !== false) {
|
||||
$password = $pin . $pass;
|
||||
/**
|
||||
* perform authentication
|
||||
* @param string $common_name certificate common name for this connection
|
||||
* @param string $serverid server identifier
|
||||
* @param string $method method to use, supply username+password via-env or via-file
|
||||
* @param string $auth_file when using a file, defines the name to use
|
||||
* @return string|bool an error string or true when properly authenticated
|
||||
*/
|
||||
function do_auth($common_name, $serverid, $method, $auth_file)
|
||||
{
|
||||
$username = $password = false;
|
||||
if ($method == 'via-file') {
|
||||
// via-file
|
||||
if (!empty($auth_file) && is_file($auth_file)) {
|
||||
$lines = explode("\n", file_get_contents($auth_file));
|
||||
if (count($lines) >= 2) {
|
||||
$username = $lines[0];
|
||||
$password = $lines[1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// via-env
|
||||
$username = getenv('username');
|
||||
$password = getenv('password');
|
||||
}
|
||||
|
||||
// primary input validation
|
||||
$error_message = null;
|
||||
if (($strictusercn === true) && ($common_name != $username)) {
|
||||
$error_message = sprintf(
|
||||
if (empty($username) || empty($password)) {
|
||||
return "username or password missing ({$method} - {$auth_file})";
|
||||
}
|
||||
$a_server = $serverid !== null ? get_openvpn_server($serverid) : null;
|
||||
if ($a_server == null) {
|
||||
return "OpenVPN '$serverid' was not found. Denying authentication for user {$username}";
|
||||
} elseif (!empty($a_server['strictusercn']) && $username != $common_name) {
|
||||
return sprintf(
|
||||
"Username does not match certificate common name (%s != %s), access denied.",
|
||||
$username,
|
||||
$common_name
|
||||
);
|
||||
} elseif (!is_array($authmodes)) {
|
||||
$error_message = 'No authentication server has been selected to authenticate against. ' .
|
||||
"Denying authentication for user {$username}";
|
||||
} elseif ($a_server == null) {
|
||||
$error_message = "OpenVPN '$modeid' was not found. Denying authentication for user {$username}";
|
||||
} elseif (empty($a_server['authmode'])) {
|
||||
return 'No authentication server has been selected to authenticate against. ' .
|
||||
"Denying authentication for user {$username}";
|
||||
} elseif (!empty($a_server['local_group']) && !in_array($a_server['local_group'], getUserGroups($username))) {
|
||||
$error_message = "OpenVPN '$modeid' requires the local group {$a_server['local_group']}. " .
|
||||
return "OpenVPN '$serverid' requires the local group {$a_server['local_group']}. " .
|
||||
"Denying authentication for user {$username}";
|
||||
}
|
||||
if ($error_message != null) {
|
||||
syslog(LOG_WARNING, $error_message);
|
||||
closelog();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (file_exists("/var/etc/openvpn/{$modeid}.ca")) {
|
||||
putenv("LDAPTLS_CACERT=/var/etc/openvpn/{$modeid}.ca");
|
||||
if (file_exists("/var/etc/openvpn/server{$serverid}.ca")) {
|
||||
putenv("LDAPTLS_CACERT=/var/etc/openvpn/server{$serverid}.ca");
|
||||
putenv("LDAPTLS_REQCERT=never");
|
||||
}
|
||||
|
||||
// perform the actual authentication
|
||||
$authFactory = new OPNsense\Auth\AuthenticationFactory();
|
||||
foreach ($authmodes as $authName) {
|
||||
foreach (explode(',', $a_server['authmode']) as $authName) {
|
||||
$authenticator = $authFactory->get($authName);
|
||||
if ($authenticator) {
|
||||
if ($authenticator->authenticate($username, $password)) {
|
||||
@ -145,18 +142,41 @@ if (count($argv) > 6) {
|
||||
} else {
|
||||
syslog(LOG_NOTICE, "user '{$username}' authenticated using '{$authName}'");
|
||||
}
|
||||
closelog();
|
||||
exit(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deny access and log
|
||||
syslog(LOG_WARNING, "user '{$username}' could not authenticate.");
|
||||
closelog();
|
||||
exit(-1);
|
||||
return "user '{$username}' could not authenticate.";
|
||||
}
|
||||
|
||||
syslog(LOG_ERR, "invalid user authentication environment");
|
||||
closelog();
|
||||
exit(-1);
|
||||
/* setup syslog logging */
|
||||
openlog("openvpn", LOG_ODELAY, LOG_AUTH);
|
||||
|
||||
/* parse environment variables */
|
||||
$parms = [];
|
||||
$parmlist = ['auth_server', 'auth_method', 'common_name', 'auth_file', 'auth_defer', 'auth_control_file'];
|
||||
foreach ($parmlist as $key) {
|
||||
$parms[$key] = isset(getenv()[$key]) ? getenv()[$key] : null;
|
||||
}
|
||||
|
||||
/* perform authentication */
|
||||
$response = do_auth($parms['common_name'], $parms['auth_server'], $parms['auth_method'], $parms['auth_file']);
|
||||
|
||||
if (is_string($response)) {
|
||||
// send failure message to log
|
||||
syslog(LOG_WARNING, $response);
|
||||
closelog();
|
||||
}
|
||||
|
||||
if (!empty($parms['auth_defer'])) {
|
||||
if (!empty($parms['auth_control_file'])) {
|
||||
file_put_contents($parms['auth_control_file'], sprintf("%d", $response === true ? '0' : '1'));
|
||||
}
|
||||
exit(0);
|
||||
} else {
|
||||
if ($response === true) {
|
||||
exit(0);
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -663,7 +663,7 @@
|
||||
<field>
|
||||
<name>crypto</name>
|
||||
<type>select</type>
|
||||
<displayname>Encryption Algorithm</displayname>
|
||||
<displayname>Encryption algorithm (fallback)</displayname>
|
||||
<bindstofield>wizardtemp->step10->crypto</bindstofield>
|
||||
<options>
|
||||
<option>
|
||||
@ -671,7 +671,7 @@
|
||||
<value>dummy</value>
|
||||
</option>
|
||||
</options>
|
||||
<description>The method used to encrypt traffic between endpoints. This setting must match on the client and server side, but is otherwise set however you like. Certain algorithms will perform better on different hardware, depending on the availability of supported VPN accelerator chips.</description>
|
||||
<description>Fallback cipher selection in case none of the default data-ciphers is supported by the client. Only preserved for backwards compatibility reasons</description>
|
||||
</field>
|
||||
<field>
|
||||
<name>digest</name>
|
||||
|
||||
@ -916,20 +916,19 @@ $( document ).ready(function() {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Encryption algorithm"); ?></td>
|
||||
<td><a id="help_for_crypto" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Encryption algorithm (fallback)"); ?></td>
|
||||
<td>
|
||||
<select name="crypto" class="form-control">
|
||||
<?php
|
||||
$cipherlist = openvpn_get_cipherlist();
|
||||
foreach ($cipherlist as $name => $desc) :
|
||||
$selected = "";
|
||||
if ($name == $pconfig['crypto']) {
|
||||
$selected = " selected=\"selected\"";
|
||||
}?>
|
||||
foreach (openvpn_get_cipherlist() as $name => $desc) :
|
||||
$selected = $name == $pconfig['crypto'] ? " selected=\"selected\"" : "";?>
|
||||
<option value="<?=$name;?>"<?=$selected?>><?=htmlspecialchars($desc);?></option>
|
||||
<?php
|
||||
endforeach; ?>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_crypto">
|
||||
<?= gettext('Fallback cipher selection in case none of the default data-ciphers is supported by the client. Only preserved for backwards compatibility reasons.') ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@ -50,6 +50,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$vpnid = 0;
|
||||
$pconfig['verbosity_level'] = 1;
|
||||
$pconfig['digest'] = "SHA1"; // OpenVPN Defaults to SHA1 if unset
|
||||
$pconfig['crypto'] = "";
|
||||
$pconfig['tlsmode'] = "auth";
|
||||
$pconfig['autokey_enable'] = "yes";
|
||||
$pconfig['autotls_enable'] = "yes";
|
||||
@ -991,23 +992,21 @@ $( document ).ready(function() {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Encryption algorithm"); ?></td>
|
||||
<td><a id="help_for_crypto" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Encryption algorithm (fallback)"); ?></td>
|
||||
<td>
|
||||
<select name="crypto" class="selectpicker">
|
||||
<?php
|
||||
$cipherlist = openvpn_get_cipherlist();
|
||||
foreach ($cipherlist as $name => $desc) :
|
||||
$selected = "";
|
||||
if ($name == $pconfig['crypto']) {
|
||||
$selected = " selected=\"selected\"";
|
||||
}
|
||||
?>
|
||||
foreach (openvpn_get_cipherlist() as $name => $desc) :
|
||||
$selected = $name == $pconfig['crypto'] ? " selected=\"selected\"" : "";?>
|
||||
<option value="<?=$name;?>"<?=$selected?>>
|
||||
<?=htmlspecialchars($desc);?>
|
||||
</option>
|
||||
<?php
|
||||
endforeach; ?>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_crypto">
|
||||
<?= gettext('Fallback cipher selection in case none of the default data-ciphers is supported by the client. Only preserved for backwards compatibility reasons.') ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user