[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/auth/ -> ldap.class.php (source)

   1  <?php
   2  /**
   3   * LDAP authentication backend
   4   *
   5   * @license   GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author    Andreas Gohr <andi@splitbrain.org>
   7   * @author    Chris Smith <chris@jalakaic.co.uk>
   8   */
   9  
  10  class auth_ldap extends auth_basic {
  11      var $cnf = null;
  12      var $con = null;
  13      var $bound = 0; // 0: anonymous, 1: user, 2: superuser
  14  
  15      /**
  16       * Constructor
  17       */
  18      function auth_ldap(){
  19          global $conf;
  20          $this->cnf = $conf['auth']['ldap'];
  21  
  22          // ldap extension is needed
  23          if(!function_exists('ldap_connect')) {
  24              if ($this->cnf['debug'])
  25                  msg("LDAP err: PHP LDAP extension not found.",-1,__LINE__,__FILE__);
  26              $this->success = false;
  27              return;
  28          }
  29  
  30          if(empty($this->cnf['groupkey'])) $this->cnf['groupkey'] = 'cn';
  31  
  32          // try to connect
  33          if(!$this->_openLDAP()) $this->success = false;
  34  
  35          // auth_ldap currently just handles authentication, so no
  36          // capabilities are set
  37      }
  38  
  39      /**
  40       * Check user+password
  41       *
  42       * Checks if the given user exists and the given
  43       * plaintext password is correct by trying to bind
  44       * to the LDAP server
  45       *
  46       * @author  Andreas Gohr <andi@splitbrain.org>
  47       * @return  bool
  48       */
  49      function checkPass($user,$pass){
  50          // reject empty password
  51          if(empty($pass)) return false;
  52          if(!$this->_openLDAP()) return false;
  53  
  54          // indirect user bind
  55          if($this->cnf['binddn'] && $this->cnf['bindpw']){
  56              // use superuser credentials
  57              if(!@ldap_bind($this->con,$this->cnf['binddn'],$this->cnf['bindpw'])){
  58                  if($this->cnf['debug'])
  59                      msg('LDAP bind as superuser: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
  60                  return false;
  61              }
  62              $this->bound = 2;
  63          }else if($this->cnf['binddn'] &&
  64                   $this->cnf['usertree'] &&
  65                   $this->cnf['userfilter']) {
  66              // special bind string
  67              $dn = $this->_makeFilter($this->cnf['binddn'],
  68                                       array('user'=>$user,'server'=>$this->cnf['server']));
  69  
  70          }else if(strpos($this->cnf['usertree'], '%{user}')) {
  71              // direct user bind
  72              $dn = $this->_makeFilter($this->cnf['usertree'],
  73                                       array('user'=>$user,'server'=>$this->cnf['server']));
  74  
  75          }else{
  76              // Anonymous bind
  77              if(!@ldap_bind($this->con)){
  78                  msg("LDAP: can not bind anonymously",-1);
  79                  if($this->cnf['debug'])
  80                      msg('LDAP anonymous bind: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
  81                  return false;
  82              }
  83          }
  84  
  85          // Try to bind to with the dn if we have one.
  86          if(!empty($dn)) {
  87              // User/Password bind
  88              if(!@ldap_bind($this->con,$dn,$pass)){
  89                  if($this->cnf['debug']){
  90                      msg("LDAP: bind with $dn failed", -1,__LINE__,__FILE__);
  91                      msg('LDAP user dn bind: '.htmlspecialchars(ldap_error($this->con)),0);
  92                  }
  93                  return false;
  94              }
  95              $this->bound = 1;
  96              return true;
  97          }else{
  98              // See if we can find the user
  99              $info = $this->getUserData($user,true);
 100              if(empty($info['dn'])) {
 101                  return false;
 102              } else {
 103                  $dn = $info['dn'];
 104              }
 105  
 106              // Try to bind with the dn provided
 107              if(!@ldap_bind($this->con,$dn,$pass)){
 108                  if($this->cnf['debug']){
 109                      msg("LDAP: bind with $dn failed", -1,__LINE__,__FILE__);
 110                      msg('LDAP user bind: '.htmlspecialchars(ldap_error($this->con)),0);
 111                  }
 112                  return false;
 113              }
 114              $this->bound = 1;
 115              return true;
 116          }
 117  
 118          return false;
 119      }
 120  
 121      /**
 122       * Return user info
 123       *
 124       * Returns info about the given user needs to contain
 125       * at least these fields:
 126       *
 127       * name string  full name of the user
 128       * mail string  email addres of the user
 129       * grps array   list of groups the user is in
 130       *
 131       * This LDAP specific function returns the following
 132       * addional fields:
 133       *
 134       * dn     string  distinguished name (DN)
 135       * uid    string  Posix User ID
 136       * inbind bool    for internal use - avoid loop in binding
 137       *
 138       * @author  Andreas Gohr <andi@splitbrain.org>
 139       * @author  Trouble
 140       * @author  Dan Allen <dan.j.allen@gmail.com>
 141       * @author  <evaldas.auryla@pheur.org>
 142       * @author  Stephane Chazelas <stephane.chazelas@emerson.com>
 143       * @return  array containing user data or false
 144       */
 145      function getUserData($user,$inbind=false) {
 146          global $conf;
 147          if(!$this->_openLDAP()) return false;
 148  
 149          // force superuser bind if wanted and not bound as superuser yet
 150          if($this->cnf['binddn'] && $this->cnf['bindpw'] && $this->bound < 2){
 151              // use superuser credentials
 152              if(!@ldap_bind($this->con,$this->cnf['binddn'],$this->cnf['bindpw'])){
 153                  if($this->cnf['debug'])
 154                      msg('LDAP bind as superuser: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 155                  return false;
 156              }
 157              $this->bound = 2;
 158          }elseif($this->bound == 0 && !$inbind) {
 159              // in some cases getUserData is called outside the authentication workflow
 160              // eg. for sending email notification on subscribed pages. This data might not
 161              // be accessible anonymously, so we try to rebind the current user here
 162              $pass = PMA_blowfish_decrypt($_SESSION[DOKU_COOKIE]['auth']['pass'],auth_cookiesalt());
 163              $this->checkPass($_SESSION[DOKU_COOKIE]['auth']['user'], $pass);
 164          }
 165  
 166          $info['user']   = $user;
 167          $info['server'] = $this->cnf['server'];
 168  
 169          //get info for given user
 170          $base = $this->_makeFilter($this->cnf['usertree'], $info);
 171          if(!empty($this->cnf['userfilter'])) {
 172              $filter = $this->_makeFilter($this->cnf['userfilter'], $info);
 173          } else {
 174              $filter = "(ObjectClass=*)";
 175          }
 176  
 177          $sr     = @ldap_search($this->con, $base, $filter);
 178          $result = @ldap_get_entries($this->con, $sr);
 179          if($this->cnf['debug'])
 180              msg('LDAP user search: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 181  
 182          // Don't accept more or less than one response
 183          if($result['count'] != 1){
 184              return false; //user not found
 185          }
 186  
 187          $user_result = $result[0];
 188          ldap_free_result($sr);
 189  
 190          // general user info
 191          $info['dn']   = $user_result['dn'];
 192          $info['gid']  = $user_result['gidnumber'][0];
 193          $info['mail'] = $user_result['mail'][0];
 194          $info['name'] = $user_result['cn'][0];
 195          $info['grps'] = array();
 196  
 197          // overwrite if other attribs are specified.
 198          if(is_array($this->cnf['mapping'])){
 199              foreach($this->cnf['mapping'] as $localkey => $key) {
 200                  if(is_array($key)) {
 201                      // use regexp to clean up user_result
 202                      list($key, $regexp) = each($key);
 203                      if($user_result[$key]) foreach($user_result[$key] as $grp){
 204                          if (preg_match($regexp,$grp,$match)) {
 205                              if($localkey == 'grps') {
 206                                  $info[$localkey][] = $match[1];
 207                              } else {
 208                                  $info[$localkey] = $match[1];
 209                              }
 210                          }
 211                      }
 212                  } else {
 213                      $info[$localkey] = $user_result[$key][0];
 214                  }
 215              }
 216          }
 217          $user_result = array_merge($info,$user_result);
 218  
 219          //get groups for given user if grouptree is given
 220          if ($this->cnf['grouptree'] && $this->cnf['groupfilter']) {
 221              $base   = $this->_makeFilter($this->cnf['grouptree'], $user_result);
 222              $filter = $this->_makeFilter($this->cnf['groupfilter'], $user_result);
 223  
 224              $sr = @ldap_search($this->con, $base, $filter, array($this->cnf['groupkey']));
 225              if(!$sr){
 226                  msg("LDAP: Reading group memberships failed",-1);
 227                  if($this->cnf['debug'])
 228                      msg('LDAP group search: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 229                  return false;
 230              }
 231              $result = ldap_get_entries($this->con, $sr);
 232              ldap_free_result($sr);
 233  
 234              foreach($result as $grp){
 235                  if(!empty($grp[$this->cnf['groupkey']][0])){
 236                      if($this->cnf['debug'])
 237                          msg('LDAP usergroup: '.htmlspecialchars($grp[$this->cnf['groupkey']][0]),0,__LINE__,__FILE__);
 238                      $info['grps'][] = $grp[$this->cnf['groupkey']][0];
 239                  }
 240              }
 241          }
 242  
 243          // always add the default group to the list of groups
 244          if(!in_array($conf['defaultgroup'],$info['grps'])){
 245              $info['grps'][] = $conf['defaultgroup'];
 246          }
 247          return $info;
 248      }
 249  
 250      /**
 251       * Make LDAP filter strings.
 252       *
 253       * Used by auth_getUserData to make the filter
 254       * strings for grouptree and groupfilter
 255       *
 256       * filter      string  ldap search filter with placeholders
 257       * placeholders array   array with the placeholders
 258       *
 259       * @author  Troels Liebe Bentsen <tlb@rapanden.dk>
 260       * @return  string
 261       */
 262      function _makeFilter($filter, $placeholders) {
 263          preg_match_all("/%{([^}]+)/", $filter, $matches, PREG_PATTERN_ORDER);
 264          //replace each match
 265          foreach ($matches[1] as $match) {
 266              //take first element if array
 267              if(is_array($placeholders[$match])) {
 268                  $value = $placeholders[$match][0];
 269              } else {
 270                  $value = $placeholders[$match];
 271              }
 272              $value = $this->_filterEscape($value);
 273              $filter = str_replace('%{'.$match.'}', $value, $filter);
 274          }
 275          return $filter;
 276      }
 277  
 278      /**
 279       * Escape a string to be used in a LDAP filter
 280       *
 281       * Ported from Perl's Net::LDAP::Util escape_filter_value
 282       *
 283       * @author Andreas Gohr
 284       */
 285      function _filterEscape($string){
 286          return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
 287                              '"\\\\\".join("",unpack("H2","$1"))',
 288                              $string);
 289      }
 290  
 291      /**
 292       * Opens a connection to the configured LDAP server and sets the wanted
 293       * option on the connection
 294       *
 295       * @author  Andreas Gohr <andi@splitbrain.org>
 296       */
 297      function _openLDAP(){
 298          if($this->con) return true; // connection already established
 299  
 300          $this->bound = 0;
 301  
 302          $port = ($this->cnf['port']) ? $this->cnf['port'] : 389;
 303          $this->con = @ldap_connect($this->cnf['server'],$port);
 304          if(!$this->con){
 305              msg("LDAP: couldn't connect to LDAP server",-1);
 306              return false;
 307          }
 308  
 309          //set protocol version and dependend options
 310          if($this->cnf['version']){
 311              if(!@ldap_set_option($this->con, LDAP_OPT_PROTOCOL_VERSION,
 312                                   $this->cnf['version'])){
 313                  msg('Setting LDAP Protocol version '.$this->cnf['version'].' failed',-1);
 314                  if($this->cnf['debug'])
 315                      msg('LDAP version set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 316              }else{
 317                  //use TLS (needs version 3)
 318                  if($this->cnf['starttls']) {
 319                      if (!@ldap_start_tls($this->con)){
 320                          msg('Starting TLS failed',-1);
 321                          if($this->cnf['debug'])
 322                              msg('LDAP TLS set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 323                      }
 324                  }
 325                  // needs version 3
 326                  if(isset($this->cnf['referrals'])) {
 327                      if(!@ldap_set_option($this->con, LDAP_OPT_REFERRALS,
 328                         $this->cnf['referrals'])){
 329                          msg('Setting LDAP referrals to off failed',-1);
 330                          if($this->cnf['debug'])
 331                              msg('LDAP referal set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 332                      }
 333                  }
 334              }
 335          }
 336  
 337          //set deref mode
 338          if($this->cnf['deref']){
 339              if(!@ldap_set_option($this->con, LDAP_OPT_DEREF, $this->cnf['deref'])){
 340                  msg('Setting LDAP Deref mode '.$this->cnf['deref'].' failed',-1);
 341                  if($this->cnf['debug'])
 342                      msg('LDAP deref set: '.htmlspecialchars(ldap_error($this->con)),0,__LINE__,__FILE__);
 343              }
 344          }
 345  
 346          return true;
 347      }
 348  }
 349  
 350  //Setup VIM: ex: et ts=4 enc=utf-8 :


Generated: Fri Nov 21 01:30:02 2008 Cross-referenced by PHPXref 0.7