通过 LDAP 更改 Windows Active Directory 和 LDS 用户密码

本文介绍如何通过 LDAP 更改 Windows Active Directory 和 LDS 用户密码。

适用于: Windows Active Directory
原始 KB 编号: 269190

总结

根据某些限制,可以通过轻型目录访问协议(LDAP)设置 Windows Active Directory 和轻型目录服务 (LDS) 密码。 本文介绍如何设置或更改密码属性。

这些步骤也适用于 Active Directory 应用程序模式(ADAM)和 LDS 用户和 userProxy 对象,其方式与 AD 用户相同。 有关详细信息,请参阅文章末尾的其他提示。

详细信息

密码存储在 AD 和 LDS 数据库中的 unicodePwd 属性中的用户对象上。 此属性可以在受限条件下写入,但无法读取。 只能修改该属性;不能在创建对象时添加它,也不能通过搜索进行查询。

若要修改此属性,客户端必须具有与服务器的 128 位传输层安全性(TLS)/安全套接字层(SSL)连接。 只要满足最小密钥长度,也可以使用使用 Windows 新技术 LAN 管理器(NTLM)或 Kerberos 创建 SSP 的会话密钥的加密会话。

若要使用此连接,可以使用 TLS/SSL:

  • 服务器必须拥有 128 位 RSA 连接的服务器证书。
  • 客户端必须信任生成服务器证书的证书颁发机构(CA)。
  • 客户端和服务器必须能够进行 128 位加密。

unicodePwd 属性的语法为八进制字符串;但是,目录服务需要八进制字符串将包含 UNICODE 字符串(如属性指示的名称)。 这意味着,在 LDAP 中传递的此属性的任何值必须是 BER 编码的 UNICODE 字符串(基本编码规则)作为八进制字符串。 此外,UNICODE 字符串必须以不属于所需密码的引号开头和结尾。

有两种修改 unicodePwd 属性的可能方法。 第一个类似于常规用户更改密码操作。 在这种情况下,修改请求必须同时包含删除和添加操作。 删除操作必须包含当前密码,并带有引号。 添加操作必须包含所需的新密码,其中包含其周围的引号。

修改此属性的第二种方法类似于管理员重置用户的密码。 为此,客户端必须绑定为具有足够权限的用户才能修改其他用户的密码。 此修改请求应包含单个替换操作,其中包含用引号括起来的新所需密码。 如果客户端具有足够的权限,则无论旧密码是什么,此密码都将成为新密码。

以下两个函数提供了这些操作的示例:

ULONG ChangeUserPassword(WCHAR* pszUserDN, WCHAR* pszOldPassword,WCHAR* pszNewPassword)
{
    ULONG err = 1;
    LDAPMod modNewPassword;
    LDAPMod modOldPassword;
    LDAPMod *modEntry[3];
    BERVAL newPwdBerVal;
    BERVAL oldPwdBerVal;
    BERVAL *newPwd_attr[2];
    BERVAL *oldPwd_attr[2];
    WCHAR pszNewPasswordWithQuotes[1024];
    WCHAR pszOldPasswordWithQuotes[1024];
    
    // Build an array of LDAPMod.
    
    // For setting unicodePwd, this MUST be a double op.
    modEntry[0] = &modOldPassword;
    modEntry[1] = &modNewPassword;
    modEntry[2] = NULL;
    
    // Build mod struct for unicodePwd Add.
    modNewPassword.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
    modNewPassword.mod_type =L"unicodePwd";
    modNewPassword.mod_vals.modv_bvals = newPwd_attr;
    
    // Build mod struct for unicodePwd Delete.
    modOldPassword.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
    modOldPassword.mod_type =L"unicodePwd";
    modOldPassword.mod_vals.modv_bvals = oldPwd_attr;
    
    // Password will be single valued, so we only have one element.
    newPwd_attr[0] = &newPwdBerVal;
    newPwd_attr[1]= NULL;
    oldPwd_attr[0] = &oldPwdBerVal;
    oldPwd_attr[1]= NULL;
    
    // Surround the passwords in quotes.
    wsprintf(pszNewPasswordWithQuotes,L"\"%s\"",pszNewPassword);
    wsprintf(pszOldPasswordWithQuotes,L"\"%s\"",pszOldPassword);
    
    // Build the BER structures with the UNICODE passwords w/quotes.
    newPwdBerVal.bv_len = wcslen(pszNewPasswordWithQuotes) * sizeof(WCHAR);
    newPwdBerVal.bv_val = (char*)pszNewPasswordWithQuotes;
    oldPwdBerVal.bv_len = wcslen(pszOldPasswordWithQuotes) * sizeof(WCHAR);
    oldPwdBerVal.bv_val = (char*)pszOldPasswordWithQuotes;
    
    // Perform single modify.
    err = ldap_modify_s(ldapConnection,
    pszUserDN,
    modEntry
    );
    
    if (err == LDAP_SUCCESS )
    wprintf(L"\nPassword successfully changed!\n");
    else
    wprintf(L"\nPassword change failed!\n");
    
    return err;
}
    
ULONG SetUserPassword(WCHAR* pszUserDN, WCHAR* pszPassword)
{
    ULONG err = 1;
    LDAPMod modPassword;
    LDAPMod *modEntry[2];
    BERVAL pwdBerVal;
    BERVAL *pwd_attr[2];
    WCHAR pszPasswordWithQuotes[1024];
    
    // Build an array of LDAPMod.
    // For setting unicodePwd, this MUST be a single op.
    modEntry[0] = &modPassword;
    modEntry[1] = NULL;
    
    // Build mod struct for unicodePwd. 
    modPassword.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
    modPassword.mod_type =L"unicodePwd";
    modPassword.mod_vals.modv_bvals = pwd_attr;
    
    // Password will be single valued, so we only have one element.
    pwd_attr[0] = &pwdBerVal;
    pwd_attr[1]= NULL;
    
    // Surround the password in quotes.
    wsprintf(pszPasswordWithQuotes,L"\"%s\"",pszPassword);
    
    // Build the BER structure with the UNICODE password.
    pwdBerVal.bv_len = wcslen(pszPasswordWithQuotes) * sizeof(WCHAR);
    pwdBerVal.bv_val = (char*)pszPasswordWithQuotes;
    
    // Perform single modify.
    err = ldap_modify_s(ldapConnection,
    pszUserDN,
    modEntry
    );
    
    if (err == LDAP_SUCCESS )
    wprintf(L"\nPassword succesfully set!\n");
    else
    wprintf(L"\nPassword set failed!\n");
    
    return err;
}

提示

  • 若要使用 UserProxy 对象配置 LDS 实例以进行密码重置,必须允许将 LDS 服务帐户(默认值:LDS 计算机帐户)约束委派到域控制器,以防用户登录使用 Kerberos。
  • 如果使用 LDAP 简单绑定,则必须使用 Windows Server 2022 或更高版本,并设置注册表项以将管理员 LDAP 会话凭据转发到Active Directory 域控制器:
    注册表项HKLM\system\currentcontrolset\services<LDS 实例>\Parameters
    注册表项:允许 ClearText 登录类型
    类型:REG_DWORD
    数据0:不允许转发凭据(默认)
              1:允许转发密码重置的凭据
  • 请注意,这两种情况下的更改都意味着 LDS 服务器应被视为第 0 层设备,因为它可以在域控制器上启动安全敏感任务。

适用于

  • Windows Server 2012 Datacenter
  • Windows Server 2012 Standard
  • Windows Server 2012 R2 Datacenter
  • Windows Server 2012 R2 Standard
  • Windows Server 2016
  • Windows Server 2019
  • Windows Server 2022
  • Windows 8.1 企业版
  • Windows 8.1 专业版
  • Windows 10
  • Windows 11