PrincipalContext with smartcard inserted

929 Views Asked by At

We have been using an application for a while that uses System.DirectoryServices.AccountManagement to communicate with the Active directory (domain context).

ContextOptions options = ContextOptions.Negotiate | 
ContextOptions.SecureSocketLayer;
Using(PrincipalContext adContext = new PrincipalContext(ContextType.Domain, "AD.DOMAIN", "DC=AD,DC=intranet", options)) 
{

//Do stuff

}

This works fine until we insert a smartcard. As soon as we insert a smartcard with a user certificate, it will prompt for a smartcard pin as soon as it hits the PrincipalContext constructor. When cancelling out, the application will crash. When entering the correct pin, it will just keep on prompting over and over.

It seems to be linked to the TLS session which is set up in the background. The issue does not exist when we do not enable encryption. But encryption is mandatory.

Has anyone run into this issue before? Resources seem to be limited. Closest I could find was:

https://connect.microsoft.com/VisualStudio/feedback/details/3100569/initializing-contextoptions-does-not-work-in-system-directoryservices-accountmanagement-principalcontext-constructor

Thanks in advance

1

There are 1 best solutions below

2
Daniel Fisher  lennybacon On

The PrincipalContext utilizes the internal class CredentialValidator to authenticate during the LDAP bind.

private bool BindLdap(NetworkCredential creds, ContextOptions contextOptions)
{
  LdapConnection current = (LdapConnection) null;
  ...
  current = new LdapConnection(this.directoryIdent);
  ...
  try
  {
    current.SessionOptions.FastConcurrentBind();

The method FastConcurrentBind finds the certificate and as SSL is enabled in the connection options it asks for the PIN.

if fast bind is not supported Bind is called and does the same:

private void lockedLdapBind(
  LdapConnection current, 
  NetworkCredential creds, 
  ContextOptions contextOptions)
{
  current.AuthType = 
    (ContextOptions.SimpleBind & contextOptions) > (ContextOptions) 0 
      ? AuthType.Basic 
      : AuthType.Negotiate;
  current.SessionOptions.Signing = 
    (ContextOptions.Signing & contextOptions) > (ContextOptions) 0;
  current.SessionOptions.Sealing = 
    (ContextOptions.Sealing & contextOptions) > (ContextOptions) 0;

  if (creds.UserName == null && creds.Password == null)
    current.Bind();
  else
    current.Bind(creds);
}

To prevent this the session options must be modified like this

current.SessionOptions.QueryClientCertificate = 
  new QueryClientCertificateCallback((a,b) => null);

The issue is that this cannot be done for the internal class from the outside.

This can only be done when manually constructing the LdapConnection object.