2

我有一个 MVC 4 应用程序,它允许用户通过密码重置功能页面更改其 Active Directory 密码。我有以下设置新密码的代码:

 DirectoryEntry de = sr.GetDirectoryEntry();
 de.Invoke("SetPassword", new object[] { newPassword });
 de.Properties["LockOutTime"].Value = 0;

在尝试使用新密码详细信息提交表单时,我在应用程序事件日志中写入了以下错误:

 0x80070005 (E_ACCESSDENIED))

我已将应用程序池的 Identity 属性设置为 NetworkService,并认为这可以解决连接问题。还有什么我需要确保的,以便我的 ASPNET 应用程序可以连接到 AD。

4

1 回答 1

1

tl;博士

在我们的例子中,这开始随机发生。原来是因为我们的自签名 SSL 证书已经过期。在 IIS 中新建一个后,问题就解决了。

解释

这个线程引导我找到原因。

我将简要回顾一下SetPassword这里的作用,以便您了解为什么需要它。该特定的 ADSI 方法实际上包含了 3 种方法。它首先尝试使用 LDAP 通过安全 SSL 通道设置密码。接下来,它尝试使用 Kerberos 设置密码协议进行设置。最后,它NetUserSetInfo用来尝试设置它。

主要问题是只有前两种方法通常会尊重您放在DirectoryEntry. 例如,如果您提供正确的凭据和 SSL 通道,LDAP 更改密码机制将使用这些凭据...

如果您检查 NetUserSetInfo 方法,您会注意到没有地方放置用户名/密码以进行授权。换句话说,它只能使用非托管线程的安全上下文。这意味着为了让它工作,它必须首先模拟您以编程方式提供的用户名/密码组合......

LDAP over SSL 显然是最好的方法(也是我们一直在使用的方法),并且似乎(欢迎澄清)一旦我们的自签名 SSL 证书过期,它会跳过 Kerberos 并退回到NetUserSetInfo,但失败了因为它没有使用我们提供的凭据。(或者它只是在 Kerberos 上失败了,因为发布者说他从未见过为 Kerberos 传递的凭据)

所以在创建新的自签名证书(for COMPUTER.DOMAIN.local)后,问题就解决了。

这是代码(以防有人在寻找它):

DirectoryEntry myDE = new DirectoryEntry(@"LDAP://OU=GroupName,DC=DOMAIN,DC=local");
myDE.Username = "administrator";
myDE.Password = "adminPassword";
DirectoryEntries myEntries = myDE.Children;
DirectoryEntry myDEUser = myEntries.Find("CN=UserName");

myDEUser.Invoke("SetPassword", new object[] { "NewPassword" });
myDEUser.Properties["LockOutTime"].Value = 0;
// the following 2 lines are free =)
myDEUser.Properties["userAccountControl"].Value = (int)myDEUser.Properties["userAccountControl"].Value | 0x10000;  // don't expire password
myDEUser.Properties["userAccountControl"].Value = (int)myDEUser.Properties["userAccountControl"].Value & ~0x0002;  // ensure account is enabled
myDEUser.CommitChanges();
于 2014-01-08T19:22:36.660 回答