Search code examples
phpldaplumen

Why is ldap_errno() feeded the connection here?


I have a process in lumen project (Laravel 6.2) where a user is identified through an LDAP. The code looks like this:

<?php

namespace App\Http\Helpers;

// Currently unused
// use App\User;
// use Firebase\JWT\JWT;
use Illuminate\Support\Facades\Log;

class LDAP
{
  private $connection, $password;
  protected $domain, $username, $ldap_address, $ldap_port;

  /**
   * Constructs the ldap connector with data used for the connection and
   * bind process.
   */
  function __construct()
  {
    $this->domain = env("LDAP_DOMAIN");
    $this->username = env("LDAP_USERNAME");
    $this->password = env("LDAP_PASSWORD");
    $this->ldap_address = env("LDAP_ADDRESS");
    $this->ldap_port = env("LDAP_PORT");
  }

  /**
   * Establishes a connection to the ldap server and saves it in
   * @var Resource $connection.
   *
   * @return true
   * On success
   * @return false
   * On failure
   */
  private function connect()
  {
    $this->connection = ldap_connect($this->ldap_address, $this->ldap_port);

    if($this->connection)
    {
      Log::info("Connection established");

      ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3);
      ldap_set_option($this->connection, LDAP_OPT_REFERRALS, 0);

      $bind = ldap_bind($this->connection, $this->domain . "\\" . $this->username, $this->password);

      if($bind)
      {
        Log::info("Bind valid");
        return true;
      }
      else
      {
        Log::info("Bind failed");
        return false;
      }
    }
    else
    {
      Log::info("Connection failed");
      return false;
    }
  }

  private function disconnect()
  {
    ldap_unbind($this->connection);
  }

  /**
   * Searches for a specific person in the LDAP-Directory and returns important
   * data from this person which will be used later in the application.
   *
   * @param String $person
   * The person to search for
   * @return Array $result
   * The persons data
   */
  public function getUser($username, $password)
  {
    try
    {
      $is_connected = $this->connect();

      if(!$is_connected)
      {
        $this->disconnect();
        return false;
      }

      $dn = "OU=Benutzer,OU=sdfsfd,DC=sfdsfsf,DC=de";

      $fields = "(|(samaccountname=*$username*))";

      $search = ldap_search($this->connection, $dn, $fields);
      $result = ldap_get_entries($this->connection, $search);

      if($result)
      {
        $bind = ldap_bind($this->connection, $this->domain . "\\" . $username, $password);
        if($bind && strlen($password) > 0)
        {
          return mb_convert_encoding($result, 'UTF-8');
        }
        else
        {
          return "Invalid credentials!";
        }
      }
      else
      {
        return "User does not exist!";
      }
    }
    catch(\Exception $e)
    {
      $errno = ldap_errno($this->connection);

      if ($errno) {
        $ret =  array("ldap_error" => $errno, "message" => ldap_err2str($errno));
      }else{
        $ret =  array("exception_code" => $e->getCode(), "message" => $e->getMessage());
      }

      return $ret;

    }
    finally
    {
      $this->disconnect();
    }
  }
}

Now, we faced some issues when handling errors from the ldap_bind(). The error code thrown by the ldap functions couldnt be evaluated by Lumen, so we had to catch them and evaluate it manually through the ldap_errno functionality.

What puzzles me is that the $this->connection is passed to the ldap_errno() function. Why isnt it the $bind? After all, its the bind which failed, not the connect. AFAIK the ldap_connect() doesnt even establish a connection, but instead verifies whether the credentials are plausible.

However, it works ^^ But why? What is happening in ldap_errno that the connection is passed to it, not the bind?


Solution

  • Because ldap_connect returns an internal handle that is identifying the "connection". ldap_errno and ldap_error then return information regarding the last error on that "connection".

    So when you call them after an ldap_bind (which returns true or false depending on the outcome) you need the connection that this happened on, not the result of the bind.

    Please note that "connection" does not necessarily mean that the connection to the server has already been established.