我正在尝试使用一个非常简单的 PHP 脚本来更改 Active Directory 域中的用户密码。
以下是我在网上找到的一些脚本:
<?php
$uid = 'Mohammed Noureldin';
$newPassword = '5omeGoodP@ssword';
$bindDn = 'CN=Administrator,OU=UsersOU,DC=example,DC=local';
$bindPassword = 'An0therGoodP@ssword';
$baseDn = 'OU=UsersOU,DC=example,DC=local';
$protocolVersion = 3;
$ldap = ldap_connect('localhost');
if (!ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, $protocolVersion))
{
exit('Failed to set protocol version to '.$protocolVersion);
}
// bind anonymously so that we can verify if the server really is running
ldap_bind($ldap);
if (ldap_errno($ldap) !== 0)
{
exit('Could not connect to LDAP server');
}
// now bind with the correct username and password
ldap_bind($ldap, $bindDn, $bindPassword);
if (ldap_errno($ldap) !== 0)
{
exit('ERROR: '.ldap_error($ldap));
}
$searchResults = ldap_search($ldap, $baseDn, 'cn='.$uid);
// no matching records
if ($searchResults === false)
{
exit('No user found');
}
if (!is_resource($searchResults))
{
exit('Error in search results.');
}
// create the unicode password
$len = strlen($newPassword);
$newPass = '"';
for ($i = 0; $i < $len; $i++)
{
$newPass .= "{$newPassword{$i}}\000";
}
$newPass .= '"';
$entry = ldap_first_entry($ldap, $searchResults);
if (!is_resource($entry))
{
exit('Couldn\'t get entry');
}
$userDn = ldap_get_dn($ldap, $entry);
if (!$userDn)
{
exit('Errrrrrrrrr1');
}
if (!ldap_modify($ldap, $userDn, array('unicodePwd' => $newPass)))
{
exit(ldap_errno($ldap)." ". ldap_error($ldap));
}
?>
此PHP页面的输出是错误消息:
53服务器不愿意执行
脚本根本不起作用(用户的密码 NOT 已更改)。
我知道AD将密码存储在unicodePwd字段中的主要原则(如果到目前为止仍然如此),我知道我必须使用安全连接并且我正在使用它(这是正确的设置)。
我搜索了该错误消息,但我找不到任何功能性解决方案。
我还尝试过其他一些脚本,但是这个脚本是最好的,因为其他脚本在之前的一些步骤中给了我一些错误(例如绑定)。
我非常感谢解决问题的任何帮助,甚至另一个功能脚本可能是个好主意! 提前谢谢。
答案 0 :(得分:6)
除非您通过SSL / TLS连接,否则您不能使用此方法更改密码。如果您使用Google或Bing作为unicodePwd
这个词,因为您已将其包含在帖子中,那么第一个 第一个结果中的第一个将是MSDN documentation for unicodePwd ,在前三个句子中说明:
此属性由LDAP Modify在以下内容中编写 限制条件。 Windows 2000操作系统服务器需要 客户端具有128位(或更好)SSL / TLS加密 连接到DC以修改此属性。在Windows上 Server 2003操作系统,Windows Server 2008操作系统, Windows Server 2008 R2操作系统,Windows Server 2012操作系统 系统,Windows Server 2012 R2操作系统和Windows Server 2016年技术预览操作系统,DC也允许 修改受保护的连接上的unicodePwd属性 128位(或更好)简单身份验证和安全层 (SASL) - 层加密而不是SSL / TLS。在Windows Server 2008中, Windows Server 2008 R2,Windows Server 2012,Windows Server 2012 R2, 和Windows Server 2016技术预览,如果 fAllowPasswordOperationsOverNonSecureConnection的启发式 dSHeuristics属性(第6.1.1.2.4.1.2节)为true且为Active 目录作为AD LDS运行,然后DC允许修改 两者都没有的unicodePwd属性 SSL / TLS加密或SASL加密。 unicodePwd属性是 从未通过LDAP搜索返回。
如果你只是对unicodePwd
进行简单搜索,那么就是第一次
您将得到的结果是逐步代码,了解如何执行此操作:
答案 1 :(得分:2)
正如Ryan Ries所说,您必须建立安全连接才能更改密码,但您发布的代码不会这样做。
有问题的代码是:
$ldap = ldap_connect('localhost');
如您所见,这会产生非安全连接。
To make a secure connection,您需要指定LDAPS URI:
$ldap = ldap_connect('ldaps://localhost');
答案 2 :(得分:2)
意识到我参加这个派对已经晚了一年;但在解决类似问题的同时发现了这篇文章...
怀疑ldaps是否正在提供托管此php脚本的服务器不信任的证书(linux?); OP提到更改了代码以使用ldaps并退出('无法连接到LDAP服务器');但是可以通过Apache Directory Studio连接OK,该目录可能位于信任该证书的计算机上(即一个域加入了信任该DC的工作站)。
要解决此问题,请调查您的私钥基础结构(一个很大的主题,从这里开始:https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx)
或者,只需告诉php服务器信任LDAP自签名证书,请参阅:Authenticating a self-signed certificate for LDAPS connection
要在启动之前验证是这种情况(即不建议在生产中),请考虑在php主机(假设的linux服务器)上配置LDAP,以忽略由证书颁发机构/信任引起的错误
snapshot
在/etc/ldap/ldap.conf的末尾 (更改后重启apache / webserver)
答案 3 :(得分:1)
您正在创建错误的unicode密码。这是在Server 2012上至少对我有效的代码。
// create the unicode password
$newpassword = "\"" . $newpassword . "\"";
$len = strlen($newpassword);
for ($i = 0; $i < $len; $i++) $newpass .= "{$newpassword{$i}}\000";
$entry["unicodePwd"] = $newpass;
// Modify the password
if (ldap_mod_replace($ldap, $userDn, $entry))
{
exit("Successfully updated password!");
}
注意我如何编码&#34;使用新密码,这是让它为我工作的技巧。
答案 4 :(得分:0)
$server = "ldaps://172.1.200.1:636";
$dn = "dc=srv,dc=world";
$message = array();
function changePassword($server,$dn,$user,$oldPassword,$newPassword,$newPasswordCnf){
global $message;
error_reporting(0);
putenv('LDAPTLS_REQCERT=allow');
$con=ldap_connect($server);
ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($con, LDAP_OPT_REFERRALS, 0);
$findWhat = array ("cn","mail");
$findWhat = array();
$findWhere = $dn;
$findFilter = "(sAMAccountName=$user)";
$ldaprdn = 'mydomain' . "\\" . $user;
$ldapbind = ldap_bind($con, $ldaprdn, $oldPassword);
if ($ldapbind) {
$message[] = "ldapbind ($ldapbind) with sAMAccountName=$user";
} else {
$error = ldap_error($con);
$errno = ldap_errno($con);
$message[] = "ldapbind error $errno - $error";
ldap_close($con);
return false;
}
$sr = ldap_search($con,$dn,$findFilter,$findWhat);
$records = ldap_get_entries($con, $sr);
if ($records["count"] != "1") {
$message[] = "Error E100 - Wrong user or password.";
return false;
}else {
$message[] = "Found user <b>".$records[0]["cn"][0]." DN=".$records[0]["dn"]." </b>". print_r($records[0],true);
}
$entry = array();
#seems a more correct way that handles complicated characters
$entry["unicodePwd"] = iconv("UTF-8", "UTF-16LE", '"' . $newPassword . '"');
# base64_encode is only needed in ldif text files !
#$entry["unicodePwd"] = base64_encode($newPassw);
$result = ldap_modify($con,$records[0]["dn"],$entry);
if ($result === false){
$message[] = $newpass.",".$entry["unicodePwd"]." Your password was not changed . with:".print_r($result, true);
$error = ldap_error($con);
$errno = ldap_errno($con);
$message[] = "$errno - $error";
}
else {
$message[] = " Your password has been changed. ";
//mail($records[0]["mail"][0],"Password change notice : ".$user,"Your password has just been changed.");
}
ldap_close($con);
}
答案 5 :(得分:0)
我将SAMBA 4.11作为AD / DC运行,并且必须使用ldap_modify_batch
才能正常工作。
$modifs = [ [ "attrib" => "unicodePwd", "modtype" => LDAP_MODIFY_BATCH_REMOVE, "values" => [iconv("UTF-8", "UTF-16LE", '"' . $curPassword . '"')], ], [ "attrib" => "unicodePwd", "modtype" => LDAP_MODIFY_BATCH_ADD, "values" => [iconv("UTF-8", "UTF-16LE", '"' . $newPassword . '"')], ], ]; $result = ldap_modify_batch($ldapconn, $dn, $modifs); error_log("Batch modify result:".print_r($result, true)); $errorMessage = ldap_errno($ldapconn)." ". ldap_error($ldapconn); error_log("$errorMessage:".$errorMessage);