opennetadmin / ona

OpenNetAdmin IP Address Management (IPAM) system
opennetadmin.com
GNU General Public License v2.0
136 stars 34 forks source link

LDAP authentication failed with 'Unknown User' #179

Open HugoTH85 opened 8 months ago

HugoTH85 commented 8 months ago

Hi, I have an ONA server (v17.12.22) which is running on a Debian 7.11 wheezy server using LDAP authentication. I wanted to upgrade it to the v19.0.1 release on a Debian 10 buster server so I made any changes in my MySQL database and in the ona folder, except that I kept the same 'auth_ldap.config.php' file as the v17.12.22 release one. Through these changes, my server works well except for the LDAP authentication part which returns me an 'Unknown User' and 'auth_ldap: can not bind anonymously' errors, even if my username and my password are correct. Have you an idea if I need to make some modifications in the 'auth_ldap.config.php' file due to the new release, or other actions that could help me ? Regards

mattpascoe commented 7 months ago

Hello. If you could share what type of LDAP system your are pointing to that may help in getting this resolved. Also if you could share the contents of your auth_ldap.config.php file.

Based on the error that it can not bind anonymously. Depending on your setup, it may not be sending credentials and trying the anonymous bind which your LDAP server does not seem to allow. I would assume you need to set something like $conf['auth']['ldap']['binddn'] in your case.

Since I based my code off of Docuwikis LDAP plugin, you may find their documentation helpful: https://www.dokuwiki.org/plugin:authldap

HugoTH85 commented 7 months ago

Hello, Thank you for your response. As this project is my company's property, it is necesary for me to keep its integrity so unfortunately I cannot share information (I know the difficulty to help me without this information though). However, after analyzing php config files, especially the ldap.class.php one, I've conclued that the program is never detecting my ldap server configuration in my auth_ldap.config.php file. In the checkPass($user, $pass) function, the program only goes on the else condition for anonymous bind (whatever I have configured in my ldap conf file), like it can't retrieve variables in the auth_ldap.config.php. I hope these information may help you to understand my issue, and if it cannot I will send you screenshots of my conf files with information that I'll hide.

mattpascoe commented 7 months ago

Understood. You share what you are willing to share. If it is possible to share at least the kind of server you are using, that would help. For instance is it Active Directory, FreeIPA etc?

First of, make sure you make your configuration changes in /opt/ona/www/local/config/auth_ldap.config.php

And for a reference, here is my config file. My ldap server is a freeIPA based server in this case. Note that it begins with the <?php line.

<?php
/*

Uncomment and set the following to enable ldap auth settings for your environment
It is best to make a copy of this file and put it into the following path:

/opt/ona/www/local/config/auth_ldap.config.php

This file is for documentation purposes and will be overwritten during
upgrades of ONA.  The ldap code was patterend from the DokuWiki auth
plugins.  You can find documentation here that may be of use in
defining values below: http://www.dokuwiki.org/auth:ldap

*/

// Common settings and debugging
$conf['auth']['ldap']['debug'] = 'false';
$conf['auth']['ldap']['starttls'] = 'true';
$conf['auth']['ldap']['version'] = '3';
$conf['auth']['ldap']['server'] = 'ldap://ldap.example.com:389';

// bind as user example
$conf['auth']['ldap']['binddn']      = 'uid=%{user},cn=users,cn=accounts,dc=example,dc=com';
$conf['auth']['ldap']['usertree']    = 'cn=users,cn=accounts,dc=example,dc=com';
$conf['auth']['ldap']['userfilter']  = '(uid=%{user})';
$conf['auth']['ldap']['grouptree']   = 'cn=groups,cn=accounts,dc=example,dc=com';
$conf['auth']['ldap']['groupfilter'] = '(&(member=%{dn})(objectClass=groupOfNames))';
HugoTH85 commented 7 months ago

My file is pretty like yours, and it works well for Debian 7 (with ONA v17.12.22) without binddn, but in Debian 10 it doesn't work at all with the most recent release (with or without binddn). I have checked the ldap.class.php and I discovered in this function :

function checkPass($user,$pass){
        // reject empty password
        if(empty($pass)) return false;
        if(!$this->_openLDAP()) return false;

        // indirect user bind
        if($this->cnf['binddn'] && $this->cnf['bindpw']){
            // use superuser credentials
            if(!@ldap_bind($this->con,$this->cnf['binddn'],$this->cnf['bindpw'])){
                if($this->cnf['debug'])
                    printmsg('DEBUG => auth_ldap: LDAP bind as superuser: '.htmlspecialchars(ldap_error($this->con)),1);
                return false;
            }
            $this->bound = 2;
        }else if($this->cnf['binddn'] &&
                 $this->cnf['usertree'] &&
                 $this->cnf['userfilter']) {
            // special bind string
            $dn = $this->_makeFilter($this->cnf['binddn'],
                                     array('user'=>$user,'server'=>$this->cnf['server']));

        }else if(strpos($this->cnf['usertree'], '%{user}')) {
            // direct user bind
            $dn = $this->_makeFilter($this->cnf['usertree'],
                                     array('user'=>$user,'server'=>$this->cnf['server']));

        }else{
            // Anonymous bind
            if(!@ldap_bind($this->con)){
                printmsg("ERROR => auth_ldap: can not bind anonymously",0);
                if($this->cnf['debug'])
                    printmsg('DEBUG => auth_ldap: LDAP anonymous bind: '.htmlspecialchars(ldap_error($this->con)),1);
                return false;
            }
        }

that the process always goes in the else condition, as if it cannot retrieve information in my ldap conf file. Then I compared my PHP modules with those that are in my Debian 7 server but it doesn't resolve the problem... By the way, here is the log content that displays in the ona.log file when I am trying to connect to LDAP : 2023-11-21 09_16_07-10 49 199 252 - PuTTY Is my issue could just be an incompatibility of my LDAP server and xajax implementation ? I get xajax erros when I am making tests, especially in ./ona/www/include/xajax/xajax_js/xajax_core.js (line 416) .

HugoTH85 commented 7 months ago

I have discovered that ONA can't retrieve the data from the auth_ldap.config.php file. Therefore I have made a solution and it works pretty well, there it is :

 if ($user!='admin'){
                global $base;
                $ldap_conf="{$base}/local/config/auth_ldap.config.php";

                //Opening of the LDAP conf file
                $confFile=fopen($ldap_conf,"r") or die("Unable to open the file !");

                //=======================VALUES TO RETRIEVE FOR LDAP CONNECTION==============================//
                //Edit this list depending on the values of the LDAP conf file you want to retrieve        //
                //Pay attention : the order of the values in the list and in the conf file must be the same  //
                $var=['debug','version','server','usertree','grouptree','groupfilter'];  //
                //===========================================================================================//

                //Repeat this step for each values to retrieve
                for ($i=0;$i<count($var);$i++){
                        //Read the first line in the conf file
                        $reading=fgets($confFile);
                        //if there are some commentary lines, don't take them
                        while($reading[0]!="$"){
                                $reading=fgets($confFile);
                        };
                        //Only take the part between " = '" and "';"  of the line
                        $string=explode(" = '",$reading);
                        $this->cnf[$var[$i]]=explode("';",$string[1])[0];
                };

                //close the LDAP conf file
                fclose($confFile);

I have added this code just at the begining of the checkPass function in ldap.class.php (before the "reject empty password" commentary line). However, I have now another issue (I cannot be lucky everyday ^^') : when I am connecting with a LDAP account, ONA can't retieve the groups that the user belongs to. It is not an issue due to LDAP server permissions because I have tried to disable all restrictions and it isn't fixed. So I am asking myself if you have made any changes since the 13.03.01 about getting LDAP user data ?

mattpascoe commented 7 months ago

This feels like there is something else going on here if you are seeing xajax errors. Xajax has nothing to do with the authentication/ldap portion of code. Ajax is all about the GUI and javascript side of things.

The fact that your php environment is unable to open up the auth_ldap.config.php also makes me wonder about your php context as a whole. There are too many other things that could be happening at that level to provide any real suggestions beyond this speculation.

It sounds like we need to figure out why it is unable to open the config file. Does the user that your web server runs as have access to the www/local/config directory? Do you have any web server configurations that may be preventing access to some of these files?

Also you might try updating the config in www/config/auth_ldap.config.php since it should also be reading that file in if it exists. Maybe that path works and the other does not? Basically just put your config in one or the other of these two files, not both. Also remember these files need to be valid PHP syntax so check that as well.

Setting $conf['auth']['ldap']['debug'] = 'true'; will also help to give more log information provided you can get it to load the config in the first place.

Another hack would be to add the values into the www/config/config.inc.php file directly. I'm assuming that file is properly read in. The auth_ldap file is just a convenient way to separate out those configs so it is not required that it must be in that file, just that the $conf array variable has the appropriate values in it when it is ran.

Clearly there are a lot of thoughts and ideas as to what could be happening here. But bottom line is that there should be no need to make changes to the code. It would be best to focus on finding out why the configuration is not being processed. Once that is addressed, then the code should function properly.

Also in looking at the history of the ldap class, it does not have any real changes to it since it was first introduced. https://github.com/opennetadmin/ona/commits/master/www/include/auth/ldap.class.php

Hope that gets us somewhere.

HugoTH85 commented 7 months ago

Hello, I have resolved all my issues. So with the changes I have showed you in my last comment, I have added this in the part of code getting groups that user is binded with (in ldap.class.php) :

$this->cnf['groupkey']='cn';

just before these lines (around line 250) :

 if ($this->cnf['grouptree'] && $this->cnf['groupfilter']) {
            $g = 0;
            $base   = $this->_makeFilter($this->cnf['grouptree'], $user_result);
            $filter = $this->_makeFilter($this->cnf['groupfilter'], $user_result);
            $sr = @ldap_search($this->con, $base, $filter, array($this->cnf['groupkey']));
            //le programme rentre ici
            if(!$sr){
                printmsg("ERROR => auth_ldap: Reading group memberships failed",0);
                if($this->cnf['debug']){
                    printmsg('DEBUG => auth_ldap: LDAP group search: '.htmlspecialchars(ldap_error($this->con)),1);
                    printmsg('DEBUG => auth_ldap: LDAP search at: '.htmlspecialchars($base.' '.$filter),1);
                }
                return false;
            }
...

because I saw that $this->cnf['groupkey'] was always NULL so I gave it the 'cn' value that I could see when I print the value of this variable on my actual working server. I want to know your point of view about this, because I don't see any issues remaining with these 2 changes and it doesn't work without them.

HugoTH85 commented 7 months ago

By the way, I have noticed a problem in your index.php (line 19) : for the default guest authentication, you've written this :

if (!$_SESSION['ona']['auth']['user']['username'] and !$conf['disable_guest']) {
    $_SESSION['ona']['auth']['user']['username'] = 'guest';
    list($status, $js) = get_authentication('guest','guest');
    get_perms('guest');
}

However, the guest password isn't "guest" but "test" (as we can check it through its md5 hash in the MySQL autogenerated database and reverse it to retrieve the password). So to me, it should be more like :

if (!$_SESSION['ona']['auth']['user']['username'] and !$conf['disable_guest']) {
    $_SESSION['ona']['auth']['user']['username'] = 'guest';
    list($status, $js) = get_authentication('guest','test');
    get_perms('guest');
}

This problem doesn't affect the behaviour of OpenNetAdmin, but just display this error message in my log file every time a user logs out :

Login failure for guest using authtype local: Password incorrect
kmnbeni commented 4 months ago

Hi, I have the same problem. It looks like the include of the auth_ldap.config.php isn`t done because the constructor is not called. The vars are empty when arrives to checkPass function in ldap.class.php

Prior to PHP 8.0.0, classes in the global namespace will interpret a method named the same as the class as an old-style constructor. That syntax is deprecated, and will result in an E_DEPRECATED error but still call that function as a constructor. If both __construct() and a same-name method are defined, __construct() will be called.

In namespaced classes, or any class as of PHP 8.0.0, a method named the same as the class never has any special meaning.

Always use __construct() in new code.