151 lines
5.9 KiB
PHP

#!/usr/local/bin/php
<?php
/*
* Copyright (C) 2008 Shrew Soft Inc. <mgrooms@shrew.net>
* Copyright (C) 2010 Ermal Luçi
* Copyright (C) 2018 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,
* 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.
*/
/*
* 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");
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) {
return $server;
}
}
}
return null;
}
function parse_auth_properties($props)
{
$result = array();
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;
}
if (!empty($props['Framed-Route']) && is_array($props['Framed-Route'])) {
$result['local_network'] = implode(",", $props['Framed-Route']);
}
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);
// primary input validation
$error_message = null;
if (($strictusercn === true) && ($common_name != $username)) {
$error_message = 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['local_group']) && !in_array($a_server['local_group'], getUserGroups($username))) {
$error_message = "OpenVPN '$modeid' 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");
putenv("LDAPTLS_REQCERT=never");
}
// perform the actual authentication
$authFactory = new OPNsense\Auth\AuthenticationFactory;
foreach ($authmodes as $authName) {
$authenticator = $authFactory->get($authName);
if ($authenticator) {
if ($authenticator->authenticate($username, $password)) {
$vpnid = filter_var($a_server['vpnid'], FILTER_SANITIZE_NUMBER_INT);
// fetch or create client specif override
$all_cso = openvpn_fetch_csc_list();
$common_name = empty($a_server['cso_login_matching']) ? $common_name : $username;
$login_type = empty($a_server['cso_login_matching']) ? "CN" : "USER";
if (!empty($all_cso[$vpnid][$common_name])) {
$cso = $all_cso[$vpnid][$common_name];
} else {
$cso = array("common_name" => $common_name);
}
$cso = array_merge($cso, parse_auth_properties($authenticator->getLastAuthProperties()));
$cso_filename = openvpn_csc_conf_write($cso, $a_server);
if (!empty($cso_filename)) {
$tmp = empty($a_server['cso_login_matching']) ? "CSO [CN]" : "CSO [USER]";
syslog(LOG_NOTICE, "user '{$username}' authenticated using '{$authName}' {$tmp}:{$cso_filename}");
} else {
syslog(LOG_NOTICE, "user '{$username}' authenticated using '{$authName}'");
}
closelog();
exit(0);
}
}
}
// deny access and log
syslog(LOG_WARNING, "user '{$username}' could not authenticate.");
closelog();
exit(-1);
}
syslog(LOG_ERR, "invalid user authentication environment");
closelog();
exit(-1);