From 64ce4f335f72abf033237d56dc593a32bf2fd47f Mon Sep 17 00:00:00 2001 From: Maximilian Wilhelm Date: Thu, 21 Jan 2016 21:33:15 +0100 Subject: [PATCH] Add HTTP Authentication / LDAP Authorization module. 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. 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). Signed-off-by: Maximilian Wilhelm --- doc/Extensions/Authentication.md | 18 ++ .../authentication/ldap-authorization.inc.php | 303 ++++++++++++++++++ includes/defaults.inc.php | 2 + 3 files changed, 323 insertions(+) create mode 100644 html/includes/authentication/ldap-authorization.inc.php diff --git a/doc/Extensions/Authentication.md b/doc/Extensions/Authentication.md index aaeff8991..2f0a0eccc 100644 --- a/doc/Extensions/Authentication.md +++ b/doc/Extensions/Authentication.md @@ -104,6 +104,24 @@ $config['auth_ldap_groupmemberattr'] = "memberUid"; Replace {id} with the unique ID provided by Jumpcloud. +#### HTTP Authentication / LDAP Authorization + +Config option: `ldap-authorization` + +This module is a combination of ___http-auth___ and ___ldap___ + +LibreNMS will expect the user to have authenticated via your webservice already (e.g. using Kerberos Authentication in Apache) but will use LDAP to determine and assign the userlevel of a user. +The userlevel will be calculated by using LDAP group membership information as the ___ldap___ module does. + +The configuration is the same as for the ___ldap___ module with one extra option: auth_ldap_cache_ttl. +This option allows to control how long user information (user_exists, userid, userlevel) are cached within the PHP Session. +The default value is 300 seconds. +To disabled this caching (highly discourage) set this option to 0. + +```php +$config['auth_ldap_cache_ttl'] = 300; +``` + #### Active Directory Authentication Config option: `active_directory` diff --git a/html/includes/authentication/ldap-authorization.inc.php b/html/includes/authentication/ldap-authorization.inc.php new file mode 100644 index 000000000..89717557a --- /dev/null +++ b/html/includes/authentication/ldap-authorization.inc.php @@ -0,0 +1,303 @@ +. + */ + +/** + * libreNMS HTTP-Authentication and LDAP Authorization Library + * @author Maximilian Wilhelm + * @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). + */ + + +if (! isset ($_SESSION['username'])) { + $_SESSION['username'] = ''; +} + +/** + * Set up connection to LDAP server + */ +$ds = @ldap_connect ($config['auth_ldap_server'], $config['auth_ldap_port']); +if (! $ds) { + echo '

Fatal error while connecting to LDAP server ' . $config['auth_ldap_server'] . ':' . $config['auth_ldap_port'] . ': ' . ldap_error($ds) . '

'; + exit; +} +if ($config['auth_ldap_version']) { + ldap_set_option($ds, 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($ds); + if ($config['auth_ldap_starttls'] == 'require' && $tls === false) { + echo '

Fatal error: LDAP TLS required but not successfully negotiated:' . ldap_error($ds) . '

'; + exit; + } +} + + +function authenticate ($username, $password) { + global $config; + + if (isset ($_SERVER['REMOTE_USER'])) { + $_SESSION['username'] = mres ($_SERVER['REMOTE_USER']); + + if (user_exists ($_SESSION['username'])) { + return 1; + } + + $_SESSION['username'] = $config['http_auth_guest']; + return 1; + } + + return 0; +} + + +function reauthenticate ($sess_id='', $token='') { + // Not supported + return 0; +} + + +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 = '', $twofactor = 0) { + // Not supported + return false; +} + + +function user_exists ($username) { + global $config, $ds; + + if (auth_ldap_session_cache_get ('user_exists')) + return 1; + + $filter = '(' . $config['auth_ldap_prefix'] . $username . ')'; + $search = ldap_search ($ds, trim ($config['auth_ldap_suffix'], ','), $filter); + $entries = ldap_get_entries ($ds, $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, $ds; + + $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 ($ds, $config['auth_ldap_groupbase'], $filter); + $entries = ldap_get_entries($ds, $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, $ds; + + $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 ($ds, trim ($config['auth_ldap_suffix'], ','), $filter); + $entries = ldap_get_entries ($ds, $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, $ds; + $userlist = array (); + + $filter = '(' . $config['auth_ldap_prefix'] . '*)'; + + $search = ldap_search ($ds, trim ($config['auth_ldap_suffix'], ','), $filter); + $entries = ldap_get_entries ($ds, $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( + $ds, + $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) { + // Not supported + return 0; +} + + +function update_user ($user_id, $realname, $level, $can_modify_passwd, $email) { + // Not supported + return 0; +} + + +function get_membername ($username) { + global $config, $ds; + 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($ds, $config['auth_ldap_groupbase'], $filter); + $entries = ldap_get_entries($ds, $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 (); +} diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php index b64026ef1..328ae2ce0 100644 --- a/includes/defaults.inc.php +++ b/includes/defaults.inc.php @@ -590,6 +590,8 @@ $config['auth_ldap_groups']['pfy']['level'] = 7; $config['auth_ldap_groups']['support']['level'] = 1; $config['auth_ldap_groupmemberattr'] = 'memberUid'; $config['auth_ldap_emailattr'] = 'mail'; +$config['auth_ldap_cache_ttl'] = 300; +// How long in seconds should ldap* module cache user information in $_SESSION // Sensors $config['allow_entity_sensor']['amperes'] = 1;