I AM the system administrator. Who do I call?
Posts tagged ldaps
ISA Server 2006 Slow Login with LDAP Authentication
May 19th
This one had dogged me for ages. There are a number of possible solutions out there but none helped my situation. Today I finally cracked it!
Here’s the situation:
- ISA 2006 standard edition in a perimeter network
- Published HTTPS apps (MOSS 2007 + other misc ASP.NET apps)
- Weblistener with forms based authentication configured to use LDAP with secure connection and without GC
- Internal CA used to provision certs to domain controllers
- Password management features enabled (Hence the requirement for secure connection and no GC)
When users login they get a delay of up to a minute before the authentication screen disappears and their application begins to load. I did some traffic dumps and found that this delay was occurring during the TLS handshake between the ISA server and the LDAP server (domain controller).
Immediately after the LDAP server issues the “Server Hello” there would be a delay, or what appeared to be some kind of a time-out, of about 15 seconds. This occurred several times throughout the authentication process which resulted in the long delay. I confirmed this by disabling the secure connection to the domain controller. This got rid of the delay but of course this wasn’t an option because without SSL you can’t have password management features enabled.
After much digging I eventually ran process monitor on the ISA server and found that there was a RSA machine key which the firewall service (wspsrv.exe) was trying to access which it didn’t have permission to.
C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys
Every time it tried to read the key it introduced a few seconds delay as can be seen in the time column above.
The solution: Give the NETWORK SERVICE account read access on that file (not the whole folder). Of course the file name will be different for each installation so you’ll need to use ProcessMon to find out which file can’t be read by wspsrv.exe.
So this raises a few questions for me, some easier to answer than others:
- How, when and why are these machine keys are used? *update* These are private keys for certificates installed on the server. They are used to encrypt data which is later decrypted using a public key. They are called “machine keys” because they are for the local “machine” account, as opposed to a user account who’s keys would be under C:\documents and settings\username\…
- How and by what are these machine keys created? *update* When you create or import a certificate e.g Public certificate for SSL web publishing.
- Is there a specific key associated with an application? *update* The private key is associated with a certificate. Use FindPrivateKey.exe to help you match keys to certificates
- Why doesn’t ISA server set the correct permission on the machine key file during installation? *update* Not really an ISA fault – ISA runs as NETWORK SERVICE so if that doesn’t have access neither does ISA. I’m still not sure how the permissions get messed up.
- Why doesn’t ISA throw an error to the event log when it couldn’t read the file?
- Why does everything still work even though it can’t read the file! (albeit with significant delay)?
Any input would be much appreciated!
Thanks for reading.
SetPassword over domain trust fails – COMException 0×8007202
Mar 2nd
After breaking my head over this for about 3 days and finally coming up with a solution I thought Id share.
Here’s the situation:
- 2 Forests with a one way external trust between them.
- Domain B in Forest B trusts Domain A in forest A.
- An ASP.NET Application running in Domain A
- Configured to impersonate.
- Application Pool running under a service account from domain A which is trusted for delegation
- Application URL (DNS name) is registered as an SPN against the above service account used to the application pool.
- A user (Bob) from Domain A has permission to modify properties and reset passwords of users in Domain B
- When using the Active Directory users and computers MMC Bob is able to reset passwords and modify properties of users in the trusting domain(Domain B) I.e. Permissions are ok!
- When Bob uses the ASP.NET application (System.DirectoryServices.DirectoryEntry) Bob is able to modify properties of users in the trusting domain. I.e. Bob’s creditneials are sucessfuly delegated to the domain controller in the trusitng domain. I.e. Delegation is working!
- When the ASP.NET application specifies Bob’s username/password in the DirectoryEntry object e.g. DirectoryEnty de = new DirectoryEntry(“LDAP://domainB.com/CN=Steve,ou=test,dc=domainb,dc=com”,”bob”,”password1″) then invokes the setpassword method, the password is successfully set. I.e. SSL is working correctly. Necessary firewall ports are open etc
- When Bob uses the ASP.NET application and invokes the setpassword method now using delegation a COMException 0×80072020 is thrown.
I’ve spent some hours trying to work this out, network dumps with WireShark (no good for LDAPS), Kerberos logging on DCs and webserver, etc etc. I have also opened up the firewall to allow DirectoryEntry.Invoke(“setpassword”) to try his other methods. I just can’t get this to work in a domain trust environment with delegation. I have however found that using System.DirectoryServices.Protocols to reset the password in a trusting domain with a delegated credential does work! For this I’m very relieved; I was almost at the point where I was going to go back to our dev guys and tell them to switch back to using a superuser account instead of the delegation method which I had been pushing on them for weeks!
Frustrating though that I still don’t know why this method works where the standard System.DirectoryServices method doesn’t!
This example was taken from chapter 10.16 of The .NET Developer’s Guide to Directory Services Programming by Joe Kaplan and Ryan Dunn. I strongly recommend you get this book. It’s not the newest book out but it’s still packed with really relevant stuff and lots of “gold nuggets” from 2 guys who really know their stuff.
using System;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Text;
public class PasswordModifier
{
public static void Main()
{
NetworkCredential credential = new NetworkCredential(
"someuser",
"Password1",
"domain"
);
DirectoryConnection connection;
try
{
//change these options to use Kerberos encryption
connection = GetConnection(
"domain.com:636",
credential,
true
);
ChangePassword(
connection,
"CN=someuser,CN=users,DC=domain,DC=com",
"Password1",
"Password2"
);
Console.WriteLine("Password modified!");
IDisposable disposable = connection as IDisposable;
if (disposable != null)
disposable.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static DirectoryConnection GetConnection(
string server,
NetworkCredential credential,
bool useSsl
)
{
LdapConnection connection =
new LdapConnection(server);
if (useSsl)
{
connection.SessionOptions.SecureSocketLayer = true;
}
else
{
connection.SessionOptions.Sealing = true;
}
connection.Bind(credential);
return connection;
}
private static void ChangePassword(
DirectoryConnection connection,
string userDN,
string oldPassword,
string newPassword
)
{
DirectoryAttributeModification deleteMod =
new DirectoryAttributeModification();
deleteMod.Name = "unicodePwd";
deleteMod.Add(GetPasswordData(oldPassword));
deleteMod.Operation= DirectoryAttributeOperation.Delete;
DirectoryAttributeModification addMod =
new DirectoryAttributeModification();
addMod.Name = "unicodePwd";
addMod.Add(GetPasswordData(newPassword));
addMod.Operation = DirectoryAttributeOperation.Add;
ModifyRequest request = new ModifyRequest(
userDN,
deleteMod,
addMod
);
DirectoryResponse response =
connection.SendRequest(request);
}
private static void SetPassword(
DirectoryConnection connection,
string userDN,
string password
)
{
DirectoryAttributeModification pwdMod =
new DirectoryAttributeModification();
pwdMod.Name = "unicodePwd";
pwdMod.Add(GetPasswordData(password));
pwdMod.Operation = DirectoryAttributeOperation.Replace;
ModifyRequest request = new ModifyRequest(
userDN,
pwdMod
);
DirectoryResponse response =
connection.SendRequest(request);
}
private static byte[] GetPasswordData(string password)
{
string formattedPassword;
formattedPassword = String.Format("\"{0}\"", password);
return (Encoding.Unicode.GetBytes(formattedPassword));
}
}
In my case this is somehow related to the domain trust but I guess any time 0×8007202 appears during setpassword and you just can’t get it to go away I’d suggest giving this method a go!
