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;