Files
librenms-librenms/html/includes/authentication/ldap-authorization.inc.php
Tony Murray 683a10e723 fix: Improve authentication load time and security (#6615)
* fix: minimize session open time
page/graphs speedup part 2

Write close the session as soon as we no longer need to write to it. Prevents the session from blocking other requests.
Do not run through full authentication functions if the session is already authenticated.
Removes password from the session as well as some items to prevent session fixation from #4608.

WARNING: This will cause issues for ad/ldap users who do not have a bind user configured!

* Do no erase username when using cookie auth.
Properly close the session in ajax_setresolution.php

* write close the session as soon as possible in ajax_setresolution.php

* Remove session regeneration. It is not compatible with the current code and would require more changes.

* Totally refactor authentication.  Extract code to functions for re-use and improved readability

* Use exceptions for authentication and error logging
Tested: mysql, ad_auth with and without bind user

* fix a couple scrutinizer issues

* fix reauthenticate in radius
2017-05-15 22:18:23 -05:00

329 lines
8.9 KiB
PHP

<?php
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* libreNMS HTTP-Authentication and LDAP Authorization Library
* @author Maximilian Wilhelm <max@rfc2324.org>
* @copyright 2016 LibreNMS, Barbarossa
* @license GPL
* @package LibreNMS
* @subpackage Authentication
*
* This Authentitation / Authorization module provides the ability to let
* the webserver (e.g. Apache) do the user Authentication (using Kerberos
* f.e.) and let libreNMS do the Authorization of the already known user.
* Authorization and setting of libreNMS user level is done by LDAP group
* names specified in the configuration file. The group configuration is
* basicly copied from the existing ldap Authentication module.
*
* Most of the code is copied from the http-auth and ldap Authentication
* modules already existing.
*
* To save lots of redundant queries to the LDAP server and speed up the
* libreNMS WebUI, all information is cached within the PHP $_SESSION as
* long as specified in $config['auth_ldap_cache_ttl'] (Default: 300s).
*/
use LibreNMS\Exceptions\AuthenticationException;
if (! isset($_SESSION['username'])) {
$_SESSION['username'] = '';
}
/**
* Set up connection to LDAP server
*/
$ldap_connection = @ldap_connect($config['auth_ldap_server'], $config['auth_ldap_port']);
if (! $ldap_connection) {
echo '<h2>Fatal error while connecting to LDAP server ' . $config['auth_ldap_server'] . ':' . $config['auth_ldap_port'] . ': ' . ldap_error($ldap_connection) . '</h2>';
exit;
}
if ($config['auth_ldap_version']) {
ldap_set_option($ldap_connection, LDAP_OPT_PROTOCOL_VERSION, $config['auth_ldap_version']);
}
if ($config['auth_ldap_starttls'] && ($config['auth_ldap_starttls'] == 'optional' || $config['auth_ldap_starttls'] == 'require')) {
$tls = ldap_start_tls($ldap_connection);
if ($config['auth_ldap_starttls'] == 'require' && $tls === false) {
echo '<h2>Fatal error: LDAP TLS required but not successfully negotiated:' . ldap_error($ldap_connection) . '</h2>';
exit;
}
}
function authenticate($username, $password)
{
global $config;
if (isset($_SERVER['REMOTE_USER'])) {
$_SESSION['username'] = mres($_SERVER['REMOTE_USER']);
if (user_exists($_SESSION['username'])) {
return true;
}
$_SESSION['username'] = $config['http_auth_guest'];
return true;
}
throw new AuthenticationException();
}
function reauthenticate($sess_id, $token)
{
return false;
}
function passwordscanchange($username = '')
{
// Not supported
return 0;
}
function changepassword($username, $newpassword)
{
// Not supported
return 0;
}
function auth_usermanagement()
{
// Not supported
return 0;
}
function adduser($username, $password, $level, $email = '', $realname = '', $can_modify_passwd = 1, $description = '')
{
// Not supported
return false;
}
function user_exists($username)
{
global $config, $ldap_connection;
if (auth_ldap_session_cache_get('user_exists')) {
return 1;
}
$filter = '(' . $config['auth_ldap_prefix'] . $username . ')';
$search = ldap_search($ldap_connection, trim($config['auth_ldap_suffix'], ','), $filter);
$entries = ldap_get_entries($ldap_connection, $search);
if ($entries['count']) {
/*
* Cache positiv result as this will result in more queries which we
* want to speed up.
*/
auth_ldap_session_cache_set('user_exists', 1);
return 1;
}
/*
* Don't cache that user doesn't exists as this might be a misconfiguration
* on some end and the user will be happy if it "just works" after the user
* has been added to LDAP.
*/
return 0;
}
function get_userlevel($username)
{
global $config, $ldap_connection;
$userlevel = auth_ldap_session_cache_get('userlevel');
if ($userlevel) {
return $userlevel;
} else {
$userlevel = 0;
}
// Find all defined groups $username is in
$filter = '(&(|(cn=' . join(')(cn=', array_keys($config['auth_ldap_groups'])) . '))(' . $config['auth_ldap_groupmemberattr'] .'=' . get_membername($username) . '))';
$search = ldap_search($ldap_connection, $config['auth_ldap_groupbase'], $filter);
$entries = ldap_get_entries($ldap_connection, $search);
// Loop the list and find the highest level
foreach ($entries as $entry) {
$groupname = $entry['cn'][0];
if ($config['auth_ldap_groups'][$groupname]['level'] > $userlevel) {
$userlevel = $config['auth_ldap_groups'][$groupname]['level'];
}
}
auth_ldap_session_cache_set('userlevel', $userlevel);
return $userlevel;
}
function get_userid($username)
{
global $config, $ldap_connection;
$user_id = auth_ldap_session_cache_get('userid');
if (isset($user_id)) {
return $user_id;
} else {
$user_id = -1;
}
$filter = '(' . $config['auth_ldap_prefix'] . $username . ')';
$search = ldap_search($ldap_connection, trim($config['auth_ldap_suffix'], ','), $filter);
$entries = ldap_get_entries($ldap_connection, $search);
if ($entries['count']) {
$user_id = $entries[0]['uidnumber'][0];
}
auth_ldap_session_cache_set('userid', $user_id);
return $user_id;
}
function deluser($username)
{
// Not supported
return 0;
}
function get_userlist()
{
global $config, $ldap_connection;
$userlist = array ();
$filter = '(' . $config['auth_ldap_prefix'] . '*)';
$search = ldap_search($ldap_connection, trim($config['auth_ldap_suffix'], ','), $filter);
$entries = ldap_get_entries($ldap_connection, $search);
if ($entries['count']) {
foreach ($entries as $entry) {
$username = $entry['uid'][0];
$realname = $entry['cn'][0];
$user_id = $entry['uidnumber'][0];
$email = $entry[$config['auth_ldap_emailattr']][0];
$ldap_groups = get_group_list();
foreach ($ldap_groups as $ldap_group) {
$ldap_comparison = ldap_compare(
$ldap_connection,
$ldap_group,
$config['auth_ldap_groupmemberattr'],
get_membername($username)
);
if (! isset($config['auth_ldap_group']) || $ldap_comparison === true) {
$userlist[] = array(
'username' => $username,
'realname' => $realname,
'user_id' => $user_id,
'email' => $email,
);
}
}
}
}
return $userlist;
}
function can_update_users()
{
// not supported
return 0;
}
function get_user($user_id)
{
foreach (get_userlist() as $users) {
if ($users['user_id'] === $user_id) {
return $users['username'];
}
}
return 0;
}
function update_user($user_id, $realname, $level, $can_modify_passwd, $email)
{
// Not supported
return 0;
}
function get_membername($username)
{
global $config, $ldap_connection;
if ($config['auth_ldap_groupmembertype'] == 'fulldn') {
$membername = $config['auth_ldap_prefix'] . $username . $config['auth_ldap_suffix'];
} elseif ($config['auth_ldap_groupmembertype'] == 'puredn') {
$filter = '(' . $config['auth_ldap_attr']['uid'] . '=' . $username . ')';
$search = ldap_search($ldap_connection, $config['auth_ldap_groupbase'], $filter);
$entries = ldap_get_entries($ldap_connection, $search);
$membername = $entries[0]['dn'];
} else {
$membername = $username;
}
return $membername;
}
function auth_ldap_session_cache_get($attr)
{
global $config;
$ttl = 300;
if ($config['auth_ldap_cache_ttl']) {
$ttl = $config['auth_ldap_cache_ttl'];
}
// auth_ldap cache present in this session?
if (! isset($_SESSION['auth_ldap'])) {
return null;
}
$cache = $_SESSION['auth_ldap'];
// $attr present in cache?
if (! isset($cache[$attr])) {
return null;
}
// Value still valid?
if (time() - $cache[$attr]['last_updated'] >= $ttl) {
return null;
}
$cache[$attr]['value'];
}
function auth_ldap_session_cache_set($attr, $value)
{
$_SESSION['auth_ldap'][$attr]['value'] = $value;
$_SESSION['auth_ldap'][$attr]['last_updated'] = time();
}