2017-11-18 11:33:03 +01:00
< ? 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
2019-10-26 00:29:12 +00:00
* libreNMS WebUI , all information is cached within the Laravel Session as
2019-06-23 00:29:12 -05:00
* long as specified in the 'auth_ldap_cache_ttl' setting ( Default : 300 s ) .
2017-11-18 11:33:03 +01:00
*/
namespace LibreNMS\Authentication ;
2018-09-12 12:51:24 -05:00
use App\Models\User ;
2017-11-18 11:33:03 +01:00
use LibreNMS\Config ;
use LibreNMS\Exceptions\AuthenticationException ;
2019-02-21 12:08:35 -06:00
use LibreNMS\Exceptions\LdapMissingException ;
2017-11-18 11:33:03 +01:00
class LdapAuthorizationAuthorizer extends AuthorizerBase
{
2018-09-18 07:57:23 -05:00
use LdapSessionCache ;
2017-11-18 11:33:03 +01:00
protected $ldap_connection ;
2018-01-09 17:13:47 -05:00
protected static $AUTH_IS_EXTERNAL = 1 ;
2017-11-18 11:33:03 +01:00
public function __construct ()
{
2020-09-21 14:54:51 +02:00
if ( ! function_exists ( 'ldap_connect' )) {
2019-02-21 12:08:35 -06:00
throw new LdapMissingException ();
2017-11-28 09:19:34 -06:00
}
2017-11-18 11:33:03 +01:00
/**
* Set up connection to LDAP server
*/
$this -> ldap_connection = @ ldap_connect ( Config :: get ( 'auth_ldap_server' ), Config :: get ( 'auth_ldap_port' ));
if ( ! $this -> ldap_connection ) {
2017-11-28 09:19:34 -06:00
throw new AuthenticationException ( 'Fatal error while connecting to LDAP server ' . Config :: get ( 'auth_ldap_server' ) . ':' . Config :: get ( 'auth_ldap_port' ) . ': ' . ldap_error ( $this -> ldap_connection ));
2017-11-18 11:33:03 +01:00
}
if ( Config :: get ( 'auth_ldap_version' )) {
ldap_set_option ( $this -> ldap_connection , LDAP_OPT_PROTOCOL_VERSION , Config :: get ( 'auth_ldap_version' ));
}
if ( Config :: get ( 'auth_ldap_starttls' ) && ( Config :: get ( 'auth_ldap_starttls' ) == 'optional' || Config :: get ( 'auth_ldap_starttls' ) == 'require' )) {
$tls = ldap_start_tls ( $this -> ldap_connection );
if ( Config :: get ( 'auth_ldap_starttls' ) == 'require' && $tls === false ) {
2017-11-28 09:19:34 -06:00
throw new AuthenticationException ( 'Fatal error: LDAP TLS required but not successfully negotiated:' . ldap_error ( $this -> ldap_connection ));
2017-11-18 11:33:03 +01:00
}
}
}
2019-03-05 00:24:14 -06:00
public function authenticate ( $credentials )
2017-11-18 11:33:03 +01:00
{
2019-03-05 00:24:14 -06:00
if ( isset ( $credentials [ 'username' ]) && $this -> userExists ( $credentials [ 'username' ])) {
2018-09-12 12:51:24 -05:00
return true ;
}
2017-11-18 11:33:03 +01:00
2018-09-12 12:51:24 -05:00
$guest = Config :: get ( 'http_auth_guest' );
if ( $guest && User :: thisAuth () -> where ( 'username' , $guest ) -> exists ()) {
2017-11-18 11:33:03 +01:00
return true ;
}
throw new AuthenticationException ();
}
public function userExists ( $username , $throw_exception = false )
{
if ( $this -> authLdapSessionCacheGet ( 'user_exists' )) {
return 1 ;
}
2020-09-21 14:54:51 +02:00
$filter = '(' . Config :: get ( 'auth_ldap_prefix' ) . $username . ')' ;
$search = ldap_search ( $this -> ldap_connection , trim ( Config :: get ( 'auth_ldap_suffix' ), ',' ), $filter );
2017-11-18 11:33:03 +01:00
$entries = ldap_get_entries ( $this -> ldap_connection , $search );
if ( $entries [ 'count' ]) {
/*
* Cache positiv result as this will result in more queries which we
* want to speed up .
*/
$this -> authLdapSessionCacheSet ( 'user_exists' , 1 );
2020-09-21 14:54:51 +02:00
2017-11-18 11:33:03 +01:00
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 ;
}
public function getUserlevel ( $username )
{
$userlevel = $this -> authLdapSessionCacheGet ( 'userlevel' );
if ( $userlevel ) {
return $userlevel ;
} else {
$userlevel = 0 ;
}
// Find all defined groups $username is in
2020-09-21 14:54:51 +02:00
$filter = '(&(|(cn=' . join ( ')(cn=' , array_keys ( Config :: get ( 'auth_ldap_groups' ))) . '))(' . Config :: get ( 'auth_ldap_groupmemberattr' ) . '=' . $this -> getMembername ( $username ) . '))' ;
$search = ldap_search ( $this -> ldap_connection , Config :: get ( 'auth_ldap_groupbase' ), $filter );
2017-11-18 11:33:03 +01:00
$entries = ldap_get_entries ( $this -> ldap_connection , $search );
// Loop the list and find the highest level
foreach ( $entries as $entry ) {
$groupname = $entry [ 'cn' ][ 0 ];
$authLdapGroups = Config :: get ( 'auth_ldap_groups' );
if ( $authLdapGroups [ $groupname ][ 'level' ] > $userlevel ) {
$userlevel = $authLdapGroups [ $groupname ][ 'level' ];
}
}
$this -> authLdapSessionCacheSet ( 'userlevel' , $userlevel );
2020-09-21 14:54:51 +02:00
2017-11-18 11:33:03 +01:00
return $userlevel ;
}
public function getUserid ( $username )
{
$user_id = $this -> authLdapSessionCacheGet ( 'userid' );
if ( isset ( $user_id )) {
return $user_id ;
}
2018-09-12 12:51:24 -05:00
$guest_username = Config :: get ( 'http_auth_guest' );
$user_id = User :: thisAuth () -> where ( 'username' , $guest_username ) -> value ( 'auth_id' ) ? : - 1 ;
2020-09-21 14:54:51 +02:00
$filter = '(' . Config :: get ( 'auth_ldap_prefix' ) . $username . ')' ;
$search = ldap_search ( $this -> ldap_connection , trim ( Config :: get ( 'auth_ldap_suffix' ), ',' ), $filter );
2017-11-18 11:33:03 +01:00
$entries = ldap_get_entries ( $this -> ldap_connection , $search );
if ( $entries [ 'count' ]) {
2020-09-21 14:54:51 +02:00
$user_id = ( int ) $entries [ 0 ][ 'uidnumber' ][ 0 ];
2018-09-12 12:51:24 -05:00
}
if ( $user_id === - 1 ) {
// no user or guest user, don't allow
if ( $guest_username ) {
throw new AuthenticationException ();
} else {
throw new AuthenticationException ( 'Guest login allowed.' );
}
2017-11-18 11:33:03 +01:00
}
$this -> authLdapSessionCacheSet ( 'userid' , $user_id );
2020-09-21 14:54:51 +02:00
2017-11-18 11:33:03 +01:00
return $user_id ;
}
public function getUserlist ()
{
2020-09-21 14:54:51 +02:00
$userlist = [];
2017-11-18 11:33:03 +01:00
$filter = '(' . Config :: get ( 'auth_ldap_prefix' ) . '*)' ;
2020-09-21 14:54:51 +02:00
$search = ldap_search ( $this -> ldap_connection , trim ( Config :: get ( 'auth_ldap_suffix' ), ',' ), $filter );
2017-11-18 11:33:03 +01:00
$entries = ldap_get_entries ( $this -> ldap_connection , $search );
if ( $entries [ 'count' ]) {
foreach ( $entries as $entry ) {
2020-09-21 14:54:51 +02:00
$username = $entry [ 'uid' ][ 0 ];
$realname = $entry [ 'cn' ][ 0 ];
$user_id = $entry [ 'uidnumber' ][ 0 ];
$email = $entry [ Config :: get ( 'auth_ldap_emailattr' )][ 0 ];
2017-11-18 11:33:03 +01:00
$ldap_groups = $this -> getGroupList ();
foreach ( $ldap_groups as $ldap_group ) {
$ldap_comparison = ldap_compare (
$this -> ldap_connection ,
$ldap_group ,
Config :: get ( 'auth_ldap_groupmemberattr' ),
$this -> getMembername ( $username )
);
if ( ! Config :: has ( 'auth_ldap_group' ) || $ldap_comparison === true ) {
2020-09-21 14:54:51 +02:00
$userlist [] = [
2017-11-18 11:33:03 +01:00
'username' => $username ,
'realname' => $realname ,
'user_id' => $user_id ,
'email' => $email ,
2020-09-21 14:54:51 +02:00
];
2017-11-18 11:33:03 +01:00
}
}
}
}
return $userlist ;
}
public function getUser ( $user_id )
{
2018-09-12 12:51:24 -05:00
foreach ( $this -> getUserlist () as $user ) {
2020-09-21 14:54:51 +02:00
if (( int ) $user [ 'user_id' ] === ( int ) $user_id ) {
2018-09-12 12:51:24 -05:00
$user [ 'level' ] = $this -> getUserlevel ( $user [ 'username' ]);
2020-09-21 14:54:51 +02:00
2018-09-12 12:51:24 -05:00
return $user ;
2017-11-18 11:33:03 +01:00
}
}
2020-09-21 14:54:51 +02:00
2017-11-18 11:33:03 +01:00
return 0 ;
}
protected function getMembername ( $username )
{
if ( Config :: get ( 'auth_ldap_groupmembertype' ) == 'fulldn' ) {
$membername = Config :: get ( 'auth_ldap_prefix' ) . $username . Config :: get ( 'auth_ldap_suffix' );
} elseif ( Config :: get ( 'auth_ldap_groupmembertype' ) == 'puredn' ) {
2020-09-21 14:54:51 +02:00
$filter = '(' . Config :: get ( 'auth_ldap_attr.uid' ) . '=' . $username . ')' ;
$search = ldap_search ( $this -> ldap_connection , Config :: get ( 'auth_ldap_groupbase' ), $filter );
2017-11-18 11:33:03 +01:00
$entries = ldap_get_entries ( $this -> ldap_connection , $search );
$membername = $entries [ 0 ][ 'dn' ];
} else {
$membername = $username ;
}
return $membername ;
}
public function getGroupList ()
{
2020-09-21 14:54:51 +02:00
$ldap_groups = [];
2017-11-18 11:33:03 +01:00
$default_group = 'cn=groupname,ou=groups,dc=example,dc=com' ;
if ( Config :: has ( 'auth_ldap_group' )) {
if ( Config :: get ( 'auth_ldap_group' ) !== $default_group ) {
$ldap_groups [] = Config :: get ( 'auth_ldap_group' );
}
}
foreach ( Config :: get ( 'auth_ldap_groups' ) as $key => $value ) {
2020-09-21 14:54:51 +02:00
$dn = " cn= $key , " . Config :: get ( 'auth_ldap_groupbase' );
2017-11-18 11:33:03 +01:00
$ldap_groups [] = $dn ;
}
return $ldap_groups ;
}
}