2011-03-10 77 views
11

我似乎無法使用php安全地綁定到Active Directory。未加密的連接正常工作。使用其他客戶端能夠安全地綁定,例如,使用LDAPAdmin over SSL進行連接。這裏有什麼問題?是否有一些我缺少的LDAP SSL模塊?如何使用PHP安全地綁定到服務器?使用PHP安全綁定到Active Directory的問題

我注意到phpinfo() cURL支持ldap/ldaps - 在php中使用它來執行安全綁定的一個很好的例子是什麼?這是一種可行的解決方法嗎?

phpinfo();

ldap 
LDAP Support enabled 
RCS Version  $Id: ldap.c 293036 2010-01-03 09:23:27Z sebastian $ 
Total Links  0/unlimited 
API Version  3001 
Vendor Name  OpenLDAP 
Vendor Version 20421 
SASL Support Enabled 

試圖使用PHP版本5.3.2-1ubuntu4.7從Ubuntu的10.04回購

$username = 'user'; 
$password = 'passwd'; 
$account_suffix = '@example.com'; 
$hostnameSSL = 'ldaps://ldap.example.com:636'; 
$hostnameTLS = 'ldap.example.com'; 
$portTLS = 389; 

ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); 

// Attempting fix from http://www.php.net/manual/en/ref.ldap.php#77553 
putenv('LDAPTLS_REQCERT=never'); 

#################### 
# SSL bind attempt # 
#################### 
// Attempting syntax from http://www.php.net/manual/en/function.ldap-bind.php#101445 
$con = ldap_connect($hostnameSSL); 
if (!is_resource($con)) trigger_error("Unable to connect to $hostnameSSL",E_USER_WARNING); 

// Options from http://www.php.net/manual/en/ref.ldap.php#73191 
if (!ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3)) 
{ 
    trigger_error("Failed to set LDAP Protocol version to 3, TLS not supported",E_USER_WARNING); 
} 
ldap_set_option($con, LDAP_OPT_REFERRALS, 0); 

if (ldap_bind($con,$username . $account_suffix, $password)) die('All went well using SSL'); 
ldap_close($con); 

#################### 
# TLS bind attempt # 
#################### 
$con = ldap_connect($hostnameTLS,$portTLS); 
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3); 
ldap_set_option($con, LDAP_OPT_REFERRALS, 0); 
$encrypted = (ldap_start_tls($con)); 
if ($encrypted) ldap_bind($con,$username . $account_suffix, $password); // Unecrypted works, but don't want logins sent in cleartext 
ldap_close($con); 

##################### 
# SASL bind attempt # 
##################### 
$con = ldap_connect($hostnameTLS,$portTLS); 
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3); 
ldap_set_option($con, LDAP_OPT_REFERRALS, 0); 
ldap_sasl_bind($con, NULL, $password, 'DIGEST-MD5', NULL, $username. $account_suffix); 
ldap_close($con); 

以上未能全部綁定到Active Directory服務器。從日誌錯誤:

ldap_create 
ldap_url_parse_ext(ldaps://ldap.example.com:636) 
ldap_bind_s 
ldap_simple_bind_s 
ldap_sasl_bind_s 
ldap_sasl_bind 
ldap_send_initial_request 
ldap_new_connection 1 1 0 
ldap_int_open_connection 
ldap_connect_to_host: TCP ldap.example.com:636 
ldap_new_socket: 27 
ldap_prepare_socket: 27 
ldap_connect_to_host: Trying 1.1.1.1:636 
ldap_pvt_connect: fd: 27 tm: -1 async: 0 
ldap_open_defconn: successful 
ldap_send_server_request 
ldap_result ld 0x215380c0 msgid 1 
wait4msg ld 0x215380c0 msgid 1 (infinite timeout) 
wait4msg continue ld 0x215380c0 msgid 1 all 1 
** ld 0x215380c0 Connections: 
* host: ldap.example.com port: 636 (default) 
    refcnt: 2 status: Connected 
    last used: Thu Mar 10 11:15:53 2011 


** ld 0x215380c0 Outstanding Requests: 
* msgid 1, origid 1, status InProgress 
    outstanding referrals 0, parent count 0 
    ld 0x215380c0 request count 1 (abandoned 0) 
** ld 0x215380c0 Response Queue: 
    Empty 
    ld 0x215380c0 response count 0 
ldap_chkResponseList ld 0x215380c0 msgid 1 all 1 
ldap_chkResponseList returns ld 0x215380c0 NULL 
ldap_int_select 
read1msg: ld 0x215380c0 msgid 1 all 1 
ldap_err2string 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning: ldap_bind() [<a href='function.ldap-bind'>function.ldap-bind</a>]: Unable to bind to server: Can't contact LDAP server in /..test.php on line 28 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace: 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 1. {main}() /..test.php:0 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 2. ldap_bind() /..test.php:28 
ldap_free_request (origid 1, msgid 1) 
ldap_free_connection 1 1 
ldap_free_connection: actually freed 
ldap_create 
ldap_err2string 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning: ldap_start_tls() [<a href='function.ldap-start-tls'>function.ldap-start-tls</a>]: Unable to start TLS: Not Supported in /..test.php on line 37 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace: 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 1. {main}() /..test.php:0 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 2. ldap_start_tls() /..test.php:37 
ldap_create 
ldap_sasl_interactive_bind_s: user selected: DIGEST-MD5 
ldap_err2string 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Warning: ldap_sasl_bind() [<a href='function.ldap-sasl-bind'>function.ldap-sasl-bind</a>]: Unable to bind to server: Not Supported in /..test.php on line 47 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP Stack trace: 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 1. {main}() /..test.php:0 
[Thu Mar 10 11:15:53 2011] [error] [client ::1] PHP 2. ldap_sasl_bind() /..test.php:47 

在SSL響應展望:

>> openssl s_client -connect my.example.com:636 -prexit 

(...) 
SSL handshake has read 5732 bytes and written 443 bytes 
--- 
New, TLSv1/SSLv3, Cipher is RC4-MD5 
Server public key is 2048 bit 
Secure Renegotiation IS supported 
Compression: NONE 
Expansion: NONE 
SSL-Session: 
    Protocol : TLSv1 
    Cipher : RC4-MD5 
    Session-ID: 111111111111111111111111 
    Session-ID-ctx: 
    Master-Key: AAAAAAAAAAAAAAAAAAAAA 
    Key-Arg : None 
    Start Time: 1299071105 
    Timeout : 300 (sec) 
    Verify return code: 20 (unable to get local issuer certificate) 

結果從 'strace的PHP test.php的':

write(2, " refcnt: 2 status: Connected\n", 31 refcnt: 2 status: Connected 
    ) = 31 
    write(2, " last used: Tue Mar 15 10:59:19"..., 39 last used: Tue Mar 15 10:59:19 2011 

    ) = 39 
    write(2, "\n", 1 
    )      = 1 
    write(2, "** ld 0x954e0b8 Outstanding Requ"..., 38** ld 0x954e0b8 Outstanding Requests: 
    ) = 38 
    write(2, " * msgid 1, origid 1, status In"..., 41 * msgid 1, origid 1, status InProgress 
    ) = 41 
    write(2, " outstanding referrals 0, pare"..., 43 outstanding referrals 0, parent count 0 
    ) = 43 
    write(2, " ld 0x954e0b8 request count 1 ("..., 45 ld 0x954e0b8 request count 1 (abandoned 0) 
    ) = 45 
    write(2, "** ld 0x954e0b8 Response Queue:\n", 32** ld 0x954e0b8 Response Queue: 
    ) = 32 
    write(2, " Empty\n", 9 Empty 
    )    = 9 
    write(2, " ld 0x954e0b8 response count 0\n", 32 ld 0x954e0b8 response count 0 
    ) = 32 
    write(2, "ldap_chkResponseList ld 0x954e0b"..., 48ldap_chkResponseList ld 0x954e0b8 msgid 1 all 1 
    ) = 48 
    write(2, "ldap_chkResponseList returns ld "..., 47ldap_chkResponseList returns ld 0x954e0b8 NULL 
    ) = 47 
    write(2, "ldap_int_select\n", 16ldap_int_select 
    )  = 16 
    poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, -1) = 1 ([{fd=3, revents=POLLIN}]) 
    write(2, "read1msg: ld 0x954e0b8 msgid 1 a"..., 37read1msg: ld 0x954e0b8 msgid 1 all 1 
    ) = 37 
    read(3, "", 8)       = 0 
    write(2, "ldap_err2string\n", 16ldap_err2string 
    )  = 16 
    write(2, "PHP Warning: ldap_bind(): Unabl"..., 158PHP Warning: ldap_bind(): Unable to bind to server: Can't contact LDAP server in 

而且我也有在/ etc/LDAP。 conf修復'TLS_REQCERT永不' - 即使此修復程序針對的是不同的錯誤,但它提供了相當明確的錯誤消息。

+0

您的示例工作得很好,在OS X上使用PHP 5.3.3-dev,所以我不認爲問題出現在您的代碼中,如果這有幫助... – dearlbry 2011-03-10 18:51:34

+0

它有助於一些,因爲我嘗試在不同的機器上運行它,針對相同的ldap服務器,並且SSL綁定工作。這是一個php 5.2的centos 5服務器。調試輸出也有一些TLS行,在ubuntu安裝時缺少。什麼是Ubuntu的錯誤,仍然沒有我 – 2011-03-11 10:52:36

+0

我試圖升級到10.10,其中PHP版本=> 5.3.3-1ubuntu9.3 - 我仍然得到excact與上述相同的結果。 – 2011-03-11 12:16:18

回答

1

由於我的代碼與CentOS一起工作正常,我得出結論,問題不是特定的編程。到目前爲止,我還沒有能夠在我的Ubuntu環境中運行它,但我認爲這是我服務器軟件中的一個錯誤。

0

您的Active Directory是否啓用LDAPS?如果是這樣,請將CA的密鑰的受信任根獲取到受信任的根密鑰庫中。

+0

缺少CA密鑰的解決方法是設置/etc/ldap.conf指令:TLS_REQCERT從不 - 這裏不是問題 – 2011-03-11 09:44:23

3

這是我要做的事:

<?php 
    $username = ''; // username to check 
    $password = ''; // password to check 

/** 
* Is it an Active Directory? 
* 
* <pre> 
* true = yes 
*  set the following values: 
*  SDB_AUTH_LDAP_HOST 
*  SDB_AUTH_LDAP_SSL 
*  SDB_AUTH_LDAP_BASE 
*  SDB_AUTH_LDAP_SEARCH 
*  SDB_AUTH_LDAP_USERDOMAIN 
* false = no, you have to supply an hostname 
*   and configure the following values: 
*   SDB_AUTH_LDAP_HOST 
*   SDB_AUTH_LDAP_PORT 
*   SDB_AUTH_LDAP_SSL 
*   SDB_AUTH_LDAP_BASE 
*   SDB_AUTH_LDAP_SEARCH 
*   SDB_AUTH_LDAP_USERDOMAIN 
* </pre> 
* @see SDB_AUTH_LDAP_HOST 
*/ 
define('SDB_AUTH_IS_AD', true); 
/** 
* Domain name of the LDAP Host or of the AD-Domain 
*/ 
define('SDB_AUTH_LDAP_HOST', 'your-domain.tld'); 
/** 
* LDAP Port? 
* 
* if {@link SDB_AUTH_IS_AD} = true, then the port will be read form DNS. 
*/ 
define('SDB_AUTH_LDAP_PORT', '389'); 
/** 
* Use LDAPS (true) oder LDAP (false) connection? 
*/ 
define('SDB_AUTH_LDAP_SSL', false); 
/** 
* LDAP Base 
*/ 
define('SDB_AUTH_LDAP_BASE', 'CN=Users,DC=your-domain.tld,DC=de'); 
/** 
* LDAP Search, to find a user 
* 
* %s will be replaced by the username.<br> 
* z.B. CN=%s 
*/ 
define('SDB_AUTH_LDAP_SEARCH', '(&(sAMAccountName=%s)(objectclass=user)(objectcategory=person))'); 
/** 
* Die LDAP Domain des Benutzers 
* 
* if the username doesnt contain a domain append this domain to it.<br> 
* in case this is empty, nothing will be appended. 
*/ 
define('SDB_AUTH_LDAP_USERDOMAIN', 'your-domain.tld'); 
/** 
* Path to LDAP Search 
* 
* Will give back better error messages 
* (leave empty in case you don't want to have it.) 
*/ 
define('SDB_AUTH_LDAP_SEARCHBIN', '/usr/bin/ldapsearch'); 




     $ldap_error_codes=array(
     '525' => 'Username doesnt exist.', 
     '52e' => 'Wrong password.', 
     '530' => 'You cannot login at this time.', 
     '531' => 'You cannot login from this host.', 
     '532' => 'Your password was expired.', 
     '533' => 'Your account has been deactivated.', 
     '701' => 'Your account was expired.', 
     '773' => 'Please set another password (at your workstation) before you login.', 
     '775' => 'Your account has been locked.', 
     ); 


    if(SDB_AUTH_LDAP_SSL) $dcs=dns_get_record("_ldaps._tcp.".SDB_AUTH_LDAP_HOST, DNS_SRV); else $dcs=dns_get_record("_ldap._tcp.".SDB_AUTH_LDAP_HOST, DNS_SRV); 
    shuffle($dcs); 

    $_LDAP_ATTRS=array('cn', 'sn', 'description', 'givenName', 'distinguishedName', 'displayName', 'memberOf', 'name', 'sAMAccountName', 'sAMAccountType', 'objectClass', 'objectCategory'); 
    if(SDB_AUTH_LDAP_USERDOMAIN!='' && strstr($username, '@')===false) { 
     $username=$username.'@'.SDB_AUTH_LDAP_USERDOMAIN; 
    } 
    $status=array(); 
    $status['CN']=''; 
    $status['displayName']=''; 
    $status['description']=''; 
    $status['distinguishedName']=''; 
    $status['groups']=array(); 
    $status['RC']=array(); 
    $status['connected']=false; 
    $status['user_exists']=false; 
    $status['is_in_team']=false; 

foreach($dcs as $_LDAP_HOST) { 
$_LDAP_PORT=$_LDAP_HOST['port']; 
$_LDAP_HOST=$_LDAP_HOST['target']; 
// check connection first (http://bugs.php.net/bug.php?id=15637) 
[email protected]($_LDAP_HOST, $_LDAP_PORT, $errno, $errstr, 1); 
@fclose($sock); 
if($errno!=0) continue; 

// then do a "connect"... (the real connect happens with bind) 
[email protected]_connect((SDB_AUTH_LDAP_SSL ? "ldaps://" : "ldap://").$_LDAP_HOST.":".$_LDAP_PORT."/"); 
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); 
// are we connected? actually, this will always return true 
if(is_resource($ds)) { 
    $status['connected']=true; 
    // login sucessful? actually also connection test 
    if(@ldap_bind($ds, $username, $password)) { 
     // search 
     $sr=ldap_search($ds, SDB_AUTH_LDAP_BASE, sprintf(SDB_AUTH_LDAP_SEARCH, $usernode), $_LDAP_ATTRS); 
     // suche successful? 
     if(is_resource($sr)) { 

      // fetch entries 
      $info = ldap_get_entries($ds, $sr); 
      if(isset($info['count']) && $info['count']>0) { 
       $status['user_exists']=true; 
      } 
      // close search result 
      ldap_free_result($sr); 
      $status['CN']=$info[0]['cn'][0]; 
      $status['description']=$info[0]['description'][0]; 
      $status['displayName']=$info[0]['displayname'][0]; 
      $status['distinguishedName']=$info[0]['distinguishedname'][0]; 
      // is the user in the dexteam? 
      for($i=0; $i<$info[0]['memberof']['count']; $i++) { 
       $status['groups'][]=$info[0]['memberof'][$i]; 
       // IS IN TEAM CHECK 
       if(substr($info[0]['memberof'][$i], 0, strlen('CN=DexTeam,'))=='CN=DexTeam,') $status['is_in_team']=true; 
      } 

      $status['RC']['code']=ldap_errno($ds); 
      $status['RC']['string']=ldap_error($ds); 
      ldap_close($ds); 
      break; 
     } 
     else { 
      $status['RC']['code']=ldap_errno($ds); 
      $status['RC']['string']=ldap_error($ds); 
      ldap_close($ds); 
      break; 
     } 
    } 
    else { 
     $status['RC']['code']=ldap_errno($ds); 
     $status['RC']['string']=ldap_error($ds); 
     // do we want better error messages? 
     if(SDB_AUTH_LDAP_SEARCHBIN!='' && is_executable(SDB_AUTH_LDAP_SEARCHBIN)) { 
      $status['RC']['ldapsearchrc']=''; 
      $status['RC']['ldapsearchtxt']=array(); 
      exec(SDB_AUTH_LDAP_SEARCHBIN.' -x -H '.escapeshellarg((SDB_AUTH_LDAP_SSL ? "ldaps://" : "ldap://").$_LDAP_HOST.":".$_LDAP_PORT."/").' -D '.escapeshellarg($username).' -w '.escapeshellarg($password).' 2>&1', $status['RC']['ldapsearchtxt'], $status['RC']['ldapsearchrc']); 
      if($status['RC']['ldapsearchrc']!=0) { 
       if(preg_match("/data ([^, ]+),/", $status['RC']['ldapsearchtxt'][1], $matches)) { 
        if(isset($ldap_error_codes[$matches[1]])) { 
         $status['RC']['code']=$matches[1]; 
         $status['RC']['string']=$ldap_error_codes[$matches[1]]; 
        } 
       } 
       unset($status['RC']['ldapsearchrc']); 
       unset($status['RC']['ldapsearchtxt']); 
      } 
     } 
     ldap_close($ds); 
     break; 
    } 
} 
else { 
    continue; 
} 
} 

並啓用證書?我知道有一個問題,當certifiacte被拒絕。編輯「/etc/ldap/ldap.conf」,並增加「TLS_REQCERT從不」

# 
# LDAP Defaults 
# 
# See ldap.conf(5) for details 
# This file should be world readable but not world writable. 
#BASE dc=example,dc=com 
#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 
#SIZELIMIT  12 
#TIMELIMIT  15 
#DEREF   never 
TLS_REQCERT never 

但是,對我來說它的工作原理與LDAP和LDAPS:

  • 這可能是一個配置問題的廣告配置。可能會降低某些安全限制...
  • 或者它也可能是一個php/ldap庫問題。嘗試更新到新版本:)
+0

您的方法與我在「SSL綁定嘗試」中嘗試的方法完全相同 - 它不會在我的服務器環境中不適合我。 – 2011-03-14 09:21:46

4

你有沒有看到關於一些證書存儲,這是否缺少權限PHP.net頁面的意見:

http://de3.php.net/manual/en/function.ldap-connect.php

bleathem 27- 2008年2月10:30 每個人都在發佈關於獲取ldaps的信息://在WAMP/AD堆棧中工作,我很難找到如何在RHEL 5.1中使用(所有的股票轉售)。良好的舊strace做了這個竅門,並幫我找到了問題......原來,php正在/ etc/pki/CA中查找CA文件,並且我沒有在該文件夾上擁有正確的權限。 chmod'ing它到755解決了我的「無法聯繫LDAP服務器」消息。

所以也許這就是你的問題。如果不是的話,你應該讓strace或wireshark試圖捕捉系統調用和網絡傳輸,並找出出了什麼問題。其中一個將顯示清楚。

+0

strace中沒有任何內容表示訪問文件的權限問題。我試圖在運行PHP 5.2的Centos 5上運行我的代碼,該代碼在相同的ldap服務器上運行正常。但是,發佈的Ubuntu PHP版本在10.04版本中爲我提供了桌面和服務器版本+ 10.10桌面版本的問題。 這幾乎就像ssl的支持完全從圖書館丟失..任何好方法來檢查? – 2011-03-15 09:56:33

1

我終於能夠通過閱讀下面的PHP錯誤線程,讓我的Windows機器上工作的事情: http://bugs.php.net/bug.php?id=48866

不幸的是,這是特定於Windows,但它讓我在正確的方向上至少現在打算在我測試(我知道它應該可以在PHP服務器上運行,只要我已經正確配置了ldap.conf)。在使用PHP 5.3的Windows上,我需要將ldap.conf文件添加到我的C:驅動器的根目錄中(我在網上看到的其他示例已將它放在C:\ openldap \ sysconf中,該文件不工作)。

TLS仍然不完全工作(它給了我一個「無法啓動TLS:無法聯繫LDAP服務器」的消息),但SSL確實出現了工作,我能爲一個帳戶更新密碼在我的測試腳本中。

我猜測ldap.conf文件只是需要在我的Web服務器上設置類似,我應該希望在業務(我只是不知道如果已經存在那個我需要修改或者如果我需要創建一個額外的)。我會看看我能否在這方面報告。

+0

我只能用一個小小的打嗝就能在我的網絡服務器上工作。首先,我發現ldap.conf文件直接位於/ etc(在/ etc/openldap中有另一個,但我認爲這只是一個示例文件),並添加了TLS_REQCERT從不聲明並重新啓動Apache。然而,這仍然給我提出了問題,但它似乎不同於我的Windows機器(當我試圖運行腳本時它會掛起)。這個問題是由於我的csf防火牆阻塞了傳出的636端口。一旦我打開它,它工作正常。不確定Ubuntu有什麼問題,但是我在RHEL/CentOS上運行。 – 2011-03-28 19:54:30

+0

在Windows上,您應該也可以這樣做:putenv('LDAPTLS_REQCERT = never');而不是ldap.conf修復。我也能夠在CentOS上工作,所以我懷疑爲某個地方的Ubuntu構建了apache/ldap/php中的一個bug。 – 2011-03-29 07:45:36