<?php
/*
	system_authservers.php
*/
/* ====================================================================
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
 *	Copyright (c)  2004, 2005 Scott Ullrich
 *	Copyright (c) 2008 Shrew Soft Inc.
 *	Copyright (c) 2010 Ermal Luçi
 *
 *	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.
 *
 *	3. All advertising materials mentioning features or use of this software
 *		must display the following acknowledgment:
 *		"This product includes software developed by the pfSense Project
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
 *
 *	4. The names "pfSense" and "pfSense Project" must not be used to
 *		 endorse or promote products derived from this software without
 *		 prior written permission. For written permission, please contact
 *		 coreteam@pfsense.org.
 *
 *	5. Products derived from this software may not be called "pfSense"
 *		nor may "pfSense" appear in their names without prior written
 *		permission of the Electric Sheep Fencing, LLC.
 *
 *	6. Redistributions of any form whatsoever must retain the following
 *		acknowledgment:
 *
 *	"This product includes software developed by the pfSense Project
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
 *
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
 *	EXPRESSED 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 pfSense PROJECT OR
 *	ITS CONTRIBUTORS 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.
 *
 *	====================================================================
 *
 */
/*
	pfSense_MODULE: auth
*/

##|+PRIV
##|*IDENT=page-system-authservers
##|*NAME=System: Authentication Servers
##|*DESCR=Allow access to the 'System: Authentication Servers' page.
##|*MATCH=system_authservers.php*
##|-PRIV

require("guiconfig.inc");
require_once("auth.inc");

$pgtitle = array(gettext("System"), gettext("Authentication Servers"));
$shortcut_section = "authentication";

if (is_numericint($_GET['id'])) {
	$id = $_GET['id'];
}
if (isset($_POST['id']) && is_numericint($_POST['id'])) {
	$id = $_POST['id'];
}

if (!is_array($config['system']['authserver'])) {
	$config['system']['authserver'] = array();
}

$a_servers = auth_get_authserver_list();
foreach ($a_servers as $servers) {
	$a_server[] = $servers;
}

if (!is_array($config['ca'])) {
	$config['ca'] = array();
}
$a_ca =& $config['ca'];

$act = $_GET['act'];
if ($_POST['act']) {
	$act = $_POST['act'];
}

if ($act == "del") {

	if (!$a_server[$_GET['id']]) {
		pfSenseHeader("system_authservers.php");
		exit;
	}

	/* Remove server from main list. */
	$serverdeleted = $a_server[$_GET['id']]['name'];
	foreach ($config['system']['authserver'] as $k => $as) {
		if ($config['system']['authserver'][$k]['name'] == $serverdeleted) {
			unset($config['system']['authserver'][$k]);
		}
	}

	/* Remove server from temp list used later on this page. */
	unset($a_server[$_GET['id']]);

	$savemsg = gettext("Authentication Server") . " " . htmlspecialchars($serverdeleted) . " " . gettext("deleted") . "<br />";
	write_config($savemsg);
}

if ($act == "edit") {
	if (isset($id) && $a_server[$id]) {

		$pconfig['type'] = $a_server[$id]['type'];
		$pconfig['name'] = $a_server[$id]['name'];

		if ($pconfig['type'] == "ldap") {
			$pconfig['ldap_caref'] = $a_server[$id]['ldap_caref'];
			$pconfig['ldap_host'] = $a_server[$id]['host'];
			$pconfig['ldap_port'] = $a_server[$id]['ldap_port'];
			$pconfig['ldap_urltype'] = $a_server[$id]['ldap_urltype'];
			$pconfig['ldap_protver'] = $a_server[$id]['ldap_protver'];
			$pconfig['ldap_scope'] = $a_server[$id]['ldap_scope'];
			$pconfig['ldap_basedn'] = $a_server[$id]['ldap_basedn'];
			$pconfig['ldap_authcn'] = $a_server[$id]['ldap_authcn'];
			$pconfig['ldap_extended_enabled'] = $a_server[$id]['ldap_extended_enabled'];
			$pconfig['ldap_extended_query'] = $a_server[$id]['ldap_extended_query'];
			$pconfig['ldap_binddn'] = $a_server[$id]['ldap_binddn'];
			$pconfig['ldap_bindpw'] = $a_server[$id]['ldap_bindpw'];
			$pconfig['ldap_attr_user'] = $a_server[$id]['ldap_attr_user'];
			$pconfig['ldap_attr_group'] = $a_server[$id]['ldap_attr_group'];
			$pconfig['ldap_attr_member'] = $a_server[$id]['ldap_attr_member'];
			$pconfig['ldap_utf8'] = isset($a_server[$id]['ldap_utf8']);
			$pconfig['ldap_nostrip_at'] = isset($a_server[$id]['ldap_nostrip_at']);

			if (!$pconfig['ldap_binddn'] || !$pconfig['ldap_bindpw']) {
				$pconfig['ldap_anon'] = true;
			}
		}

		if ($pconfig['type'] == "radius") {
			$pconfig['radius_host'] = $a_server[$id]['host'];
			$pconfig['radius_auth_port'] = $a_server[$id]['radius_auth_port'];
			$pconfig['radius_acct_port'] = $a_server[$id]['radius_acct_port'];
			$pconfig['radius_secret'] = $a_server[$id]['radius_secret'];
			$pconfig['radius_timeout'] = $a_server[$id]['radius_timeout'];

			if ($pconfig['radius_auth_port'] &&
				$pconfig['radius_acct_port']) {
				$pconfig['radius_srvcs'] = "both";
			}

			if ($pconfig['radius_auth_port'] &&
				!$pconfig['radius_acct_port']) {
				$pconfig['radius_srvcs'] = "auth";
				$pconfig['radius_acct_port'] = 1813;
			}

			if (!$pconfig['radius_auth_port'] &&
				$pconfig['radius_acct_port']) {
				$pconfig['radius_srvcs'] = "acct";
				$pconfig['radius_auth_port'] = 1812;
			}

		}
	}
}

if ($act == "new") {
	$pconfig['ldap_protver'] = 3;
	$pconfig['ldap_anon'] = true;
	$pconfig['radius_srvcs'] = "both";
	$pconfig['radius_auth_port'] = "1812";
	$pconfig['radius_acct_port'] = "1813";
}

if ($_POST) {
	unset($input_errors);
	$pconfig = $_POST;

	/* input validation */

	if ($pconfig['type'] == "ldap") {
		$reqdfields = explode(" ",
			"name type ldap_host ldap_port " .
			"ldap_urltype ldap_protver ldap_scope " .
			"ldap_attr_user ldap_attr_group ldap_attr_member ldapauthcontainers");
		$reqdfieldsn = array(
			gettext("Descriptive name"),
			gettext("Type"),
			gettext("Hostname or IP"),
			gettext("Port value"),
			gettext("Transport"),
			gettext("Protocol version"),
			gettext("Search level"),
			gettext("User naming Attribute"),
			gettext("Group naming Attribute"),
			gettext("Group member attribute"),
			gettext("Authentication container"));

		if (!$pconfig['ldap_anon']) {
			$reqdfields[] = "ldap_binddn";
			$reqdfields[] = "ldap_bindpw";
			$reqdfieldsn[] = gettext("Bind user DN");
			$reqdfieldsn[] = gettext("Bind Password");
		}
	}

	if ($pconfig['type'] == "radius") {
		$reqdfields = explode(" ", "name type radius_host radius_srvcs");
		$reqdfieldsn = array(
			gettext("Descriptive name"),
			gettext("Type"),
			gettext("Hostname or IP"),
			gettext("Services"));

		if ($pconfig['radisu_srvcs'] == "both" ||
			$pconfig['radisu_srvcs'] == "auth") {
			$reqdfields[] = "radius_auth_port";
			$reqdfieldsn[] = gettext("Authentication port value");
		}

		if ($pconfig['radisu_srvcs'] == "both" ||
			$pconfig['radisu_srvcs'] == "acct") {
			$reqdfields[] = "radius_acct_port";
			$reqdfieldsn[] = gettext("Accounting port value");
		}

		if (!isset($id)) {
			$reqdfields[] = "radius_secret";
			$reqdfieldsn[] = gettext("Shared Secret");
		}
	}

	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);

	if (preg_match("/[^a-zA-Z0-9\.\-_]/", $_POST['host'])) {
		$input_errors[] = gettext("The host name contains invalid characters.");
	}

	if (auth_get_authserver($pconfig['name']) && !isset($id)) {
		$input_errors[] = gettext("An authentication server with the same name already exists.");
	}

	if (($pconfig['type'] == "radius") && isset($_POST['radius_timeout']) && !empty($_POST['radius_timeout']) && (!is_numeric($_POST['radius_timeout']) || (is_numeric($_POST['radius_timeout']) && ($_POST['radius_timeout'] <= 0)))) {
		$input_errors[] = gettext("RADIUS Timeout value must be numeric and positive.");
	}

	/* if this is an AJAX caller then handle via JSON */
	if (isAjax() && is_array($input_errors)) {
		input_errors2Ajax($input_errors);
		exit;
	}

	if (!$input_errors) {
		$server = array();
		$server['refid'] = uniqid();
		if (isset($id) && $a_server[$id]) {
			$server = $a_server[$id];
		}

		$server['type'] = $pconfig['type'];
		$server['name'] = $pconfig['name'];

		if ($server['type'] == "ldap") {

			if (!empty($pconfig['ldap_caref'])) {
				$server['ldap_caref'] = $pconfig['ldap_caref'];
			}
			$server['host'] = $pconfig['ldap_host'];
			$server['ldap_port'] = $pconfig['ldap_port'];
			$server['ldap_urltype'] = $pconfig['ldap_urltype'];
			$server['ldap_protver'] = $pconfig['ldap_protver'];
			$server['ldap_scope'] = $pconfig['ldap_scope'];
			$server['ldap_basedn'] = $pconfig['ldap_basedn'];
			$server['ldap_authcn'] = $pconfig['ldapauthcontainers'];
			$server['ldap_extended_enabled'] = $pconfig['ldap_extended_enabled'];
			$server['ldap_extended_query'] = $pconfig['ldap_extended_query'];
			$server['ldap_attr_user'] = $pconfig['ldap_attr_user'];
			$server['ldap_attr_group'] = $pconfig['ldap_attr_group'];
			$server['ldap_attr_member'] = $pconfig['ldap_attr_member'];
			if ($pconfig['ldap_utf8'] == "yes") {
				$server['ldap_utf8'] = true;
			} else {
				unset($server['ldap_utf8']);
			}
			if ($pconfig['ldap_nostrip_at'] == "yes") {
				$server['ldap_nostrip_at'] = true;
			} else {
				unset($server['ldap_nostrip_at']);
			}


			if (!$pconfig['ldap_anon']) {
				$server['ldap_binddn'] = $pconfig['ldap_binddn'];
				$server['ldap_bindpw'] = $pconfig['ldap_bindpw'];
			} else {
				unset($server['ldap_binddn']);
				unset($server['ldap_bindpw']);
			}
		}

		if ($server['type'] == "radius") {

			$server['host'] = $pconfig['radius_host'];

			if ($pconfig['radius_secret']) {
				$server['radius_secret'] = $pconfig['radius_secret'];
			}

			if ($pconfig['radius_timeout']) {
				$server['radius_timeout'] = $pconfig['radius_timeout'];
			} else {
				$server['radius_timeout'] = 5;
			}

			if ($pconfig['radius_srvcs'] == "both") {
				$server['radius_auth_port'] = $pconfig['radius_auth_port'];
				$server['radius_acct_port'] = $pconfig['radius_acct_port'];
			}

			if ($pconfig['radius_srvcs'] == "auth") {
				$server['radius_auth_port'] = $pconfig['radius_auth_port'];
				unset($server['radius_acct_port']);
			}

			if ($pconfig['radius_srvcs'] == "acct") {
				$server['radius_acct_port'] = $pconfig['radius_acct_port'];
				unset($server['radius_auth_port']);
			}
		}

		if (isset($id) && $config['system']['authserver'][$id]) {
			$config['system']['authserver'][$id] = $server;
		} else {
			$config['system']['authserver'][] = $server;
		}

		write_config();

		pfSenseHeader("system_authservers.php");
	}
}

include("head.inc");

if ($input_errors)
	print_input_errors($input_errors);
if ($savemsg)
	print_info_box($savemsg);

$tab_array = array();
$tab_array[] = array(gettext("Users"), false, "system_usermanager.php");
$tab_array[] = array(gettext("Groups"), false, "system_groupmanager.php");
$tab_array[] = array(gettext("Settings"), false, "system_usermanager_settings.php");
$tab_array[] = array(gettext("Servers"), true, "system_authservers.php");
display_top_tabs($tab_array);

if (!($act == "new" || $act == "edit" || $input_errors))
{
	?>
	<div class="table-responsive">
		<table class="table table-striped table-hover">
			<thead>
				<tr>
					<th><?=gettext("Server Name")?></th>
					<th><?=gettext("Type")?></th>
					<th><?=gettext("Host Name")?></th>
					<th></th>
				</tr>
			</thead>
			<tbody>
		<?php foreach($a_server as $i => $server): ?>
				<tr>
					<td><?=htmlspecialchars($server['name'])?></td>
					<td><?=htmlspecialchars($auth_server_types[$server['type']])?></td>
					<td><?=htmlspecialchars($server['host'])?></td>
					<td>
					<?php if ($i < (count($a_server) - 1)): ?>
						<a href="system_authservers.php?act=edit&amp;id=<?=$i?>" class="btn btn-xs btn-primary">edit</a>
						<a href="system_authservers.php?act=del&amp;id=<?=$i?>" class="btn btn-xs btn-danger">delete</a>
					<?php endif?>
					</td>
				</tr>
		<?php endforeach; ?>
			</tbody>
		</table>
	</div>

	<nav class="action-buttons">
		<a href="?act=new" class="btn btn-success">add new</a>
	</nav>
<?php
	include("foot.inc");
	exit;
}

require('classes/Form.class.php');
$form = new Form;
$form->setAction('system_authservers.php?act=edit');
$form->addGlobal(new Form_Input(
	'userid',
	null,
	'hidden',
	$id
));

$section = new Form_Section('Server settings');

$section->addInput($input = new Form_Input(
	'name',
	'Descriptive name',
	'text',
	$pconfig['name']
));

if ($act == 'edit')
	$input->setReadonly();

$section->addInput($input = new Form_Select(
	'type',
	'Type',
	$pconfig['type'],
	$auth_server_types
))->toggles();

if ($act == 'edit')
	$input->setDisabled();

$form->add($section);
$section = new Form_Section('LDAP Server Settings');
$section->addClass('toggle-ldap collapse');

if (!isset($pconfig['type']) || $pconfig['type'] == 'ldap')
	$section->addClass('in');

$section->addInput(new Form_Input(
	'ldap_host',
	'Hostname or IP address',
	'text',
	$pconfig['ldap_host']
))->setHelp('NOTE: When using SSL, this hostname MUST match the Common Name '.
	'(CN) of the LDAP server"s SSL Certificate.');

$section->addInput(new Form_Input(
	'ldap_port',
	'Port value',
	'number',
	$pconfig['ldap_port']
));

$section->addInput(new Form_Select(
	'ldap_urltype',
	'Transport',
	$pconfig['ldap_urltype'],
	array_combine(array_keys($ldap_urltypes), array_keys($ldap_urltypes))
));

if (empty($a_ca))
{
	$section->addInput(new Form_StaticText(
		'Peer Certificate Authority',
		'No Certificate Authorities defined.<br/>Create one under <a href="system_camanager.php">System &gt; Cert Manager</a>.'
	));
}
else
{
	$ldapCaRef = [];
	foreach ($a_ca as $ca)
		$ldapCaRef[ $ca['refid'] ] = $ca['descr'];

	$section->addInput(new Form_Select(
		'ldap_caref',
		'Peer Certificate Authority',
		$pconfig['ldap_caref'],
		$ldapCaRef
	))->setHelp('This option is used if \'SSL Encrypted\' option is choosen. '.
		'It must match with the CA in the AD otherwise problems will arise.');
}

$section->addInput(new Form_Select(
	'ldap_protver',
	'Protocol version',
	$pconfig['ldap_protver'],
	array_combine($ldap_protvers, $ldap_protvers)
));

$group = new Form_Group('Search scope');
$group->add(new Form_Select(
	'ldap_scope',
	'Level',
	$pconfig['ldap_scope'],
	$ldap_scopes
));
$group->add(new Form_Input(
	'ldap_basedn',
	'Base DN',
	'text',
	$pconfig['ldap_basedn']
));
$section->add($group);

$group = new Form_Group('Authentication containers');
$group->add(new Form_Input(
	'ldapauthcontainers',
	'Containers',
	'text',
	$pconfig['ldap_authcn']
))->setHelp('Note: Semi-Colon separated. This will be prepended to the search '.
	'base dn above or you can specify full container path containing a dc= '.
	'component.<br/>Example: CN=Users;DC=example,DC=com or OU=Staff;OU=Freelancers');
#FIXME
$group->add(new Form_Button(
	'Select',
	'Select a container',
	'/system_usermanager_settings_ldapacpicker.php?port=389&host=192.168.1.1&scope=one&basedn=CN=pfsense&binddn=&bindpw=&urltype=TCP%20-%20Standard&proto=3&authcn=OU=Staff&cert='
));
$section->add($group);

$section->addInput(new Form_Checkbox(
	'ldap_extended_enabled',
	'Extended query',
	'Enable extended query',
	$pconfig['ldap_extended_enabled']
))->toggles('.toggle-extended');

$group = new Form_Group('Query');
$group->addClass('toggle-extended collapse');
$group->add(new Form_Input(
	'ldap_extended_query',
	'Query',
	'text',
	$pconfig['ldap_extended_query']
))->setHelp('Example: &amp;(objectClass=inetOrgPerson)(mail=*@example.com)');

$section->add($group);

$section->addInput(new Form_Checkbox(
	'ldap_anon',
	'Bind anonymous',
	'Use anonymous binds to resolve distinguished names',
	$pconfig['ldap_anon']
))->toggles('.toggle-anon');

$group = new Form_Group('Bind credentials');
$group->addClass('toggle-anon collapse');
$group->add(new Form_Input(
	'ldap_binddn',
	'User DN:',
	'text',
	$pconfig['ldap_binddn']
));
$group->add(new Form_Input(
	'ldap_bindpw',
	'Password',
	'text',
	$pconfig['ldap_bindpw']
));
$section->add($group);

if (!isset($id)) {
	$template_list = array();

	foreach($ldap_templates as $option => $template) {
		$template_list[$option] = $template['desc'];
	}

	$section->addInput(new Form_Select(
		'ldap_tmpltype',
		'Initial Template',
		$pconfig['ldap_template'],
		$template_list
	));
}

$section->addInput(new Form_Input(
	'ldap_attr_user',
	'User naming attribute',
	'text',
	$pconfig['ldap_attr_user']
));

$section->addInput(new Form_Input(
	'ldap_attr_group',
	'Group naming attribute',
	'text',
	$pconfig['ldap_attr_group']
));

$section->addInput(new Form_Input(
	'ldap_attr_member',
	'Group member attribute',
	'text',
	$pconfig['ldap_attr_member']
));

$section->addInput(new Form_Checkbox(
	'ldap_utf8',
	'UTF8 Encode',
	'UTF8 encode LDAP parameters before sending them to the server.',
	$pconfig['ldap_utf8']
))->setHelp('Required to support international characters, but may not be '.
	'supported by every LDAP server.');

$section->addInput(new Form_Checkbox(
	'ldap_nostrip_at',
	'Username Alterations',
	'Do not strip away parts of the username after the @ symbol',
	$pconfig['ldap_nostrip_at']
))->setHelp('e.g. user@host becomes user when unchecked.');

$form->add($section);
$section = new Form_Section('Radius Server Settings');
$section->addClass('toggle-radius collapse');

$section->addInput(new Form_Input(
	'radius_host',
	'Hostname or IP address',
	'text',
	$pconfig['radius_host']
));

$section->addInput(new Form_Input(
	'radius_secret',
	'Shared Secret',
	'text',
	$pconfig['radius_secret']
));

$section->addInput(new Form_Select(
	'radius_srvcs',
	'Services offered',
	$pconfig['radius_srvcs'],
	$radius_srvcs
));

$section->addInput(new Form_Input(
	'radius_auth_port',
	'Authentication port value',
	'number',
	$pconfig['radius_auth_port']
));

$section->addInput(new Form_Input(
	'radius_acct_port',
	'Accounting port',
	'number',
	$pconfig['radius_acct_port']
));

$section->addInput(new Form_Input(
	'radius_timeout',
	'Authentication Timeout',
	'number',
	$pconfig['radius_timeout']
))->setHelp('This value controls how long, in seconds, that the RADIUS '.
	'server may take to respond to an authentication request. If left blank, the '.
	'default value is 5 seconds. NOTE: If you are using an interactive two-factor '.
	'authentication system, increase this timeout to account for how long it will '.
	'take the user to receive and enter a token.');

if (isset($id) && $a_server[$id])
{
	$section->addInput(new Form_Input(
		'id',
		null,
		'hidden',
		$id
	));
}

$form->add($section);
print $form;
?>
<script>
//<![CDATA[
events.push(function(){
	function ldap_tmplchange() {
		switch ($('#ldap_tmpltype').find(":selected").index()) {
<?php
		$index = 0;
		foreach ($ldap_templates as $tmpldata):
?>
			case <?=$index;?>:
				$('#ldap_attr_user').val("<?=$tmpldata['attr_user'];?>");
				$('#ldap_attr_group').val("<?=$tmpldata['attr_group'];?>");
				$('#ldap_attr_member').val("<?=$tmpldata['attr_member'];?>");
				break;
<?php
			$index++;
		endforeach;
?>
		}
	}

	// On page load . .
	ldap_tmplchange();

	// On click . .
	$('#ldap_tmpltype').on('change', function() {
		ldap_tmplchange();
	});
});
//]]>
</script>
<?php
include("foot.inc");