Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a DirectoryEntry from LDAP over SSL?

I'm trying to get the root DirectoryEntry from LDAP so I can show a nice graphical tree view of it.

It all works beautifully under normal connections but I can't get it to work with SSL.

var root = this.checkBoxSSL.Checked
                    ? new DirectoryEntry("LDAP://" + this.textBoxServer.Text,
                        this.textBoxUsername.Text,
                        this.textBoxPassword.Text,
                        AuthenticationTypes.SecureSocketsLayer)
                    : new DirectoryEntry("LDAP://" + this.textBoxServer.Text,
                        this.textBoxUsername.Text,
                        this.textBoxPassword.Text);
var dn = root.Properties["distinguishedName"].Value;

And so on...

But I get a "Server not operational" exception. It all seems to go down to the bind process. Based on internet research it might be a problem with the certificate and/or the Authentication method (NTLM, etc).

So how can I get a working DirectoryEntry over SSL?

I am open to alternative solutions, as long as I can retrieve all the LDAP Properties of the nodes I need. (Root, DC, OU, CN, Groups and Users)

EDIT: As it seems the problem comes down to the SSL certificate. We only have a self-signed cert atm. And this seems to be rejected by .NET by default. We're going to try this with a properly signed cert later on but it's likely I need to be able to handle selfsigned ones too.

This is where my knowledge about certificates has limits. I'm currently exploring a different code solution because it seems to be the only one that allows me to influence the whole certificate handling:

System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new System.Security.Cryptography.X509Certificates.X509Certificate2();
cert.Import("..\\..\\test certificate.cer");

LdapConnection con = new LdapConnection("ip:636");
con.Credential = new NetworkCredential("un", "pw");
con.AuthType = AuthType.Ntlm;
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((ldapcon, cer) => {
    var cer2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(cer);

    StringBuilder strb = new StringBuilder();

    strb.AppendFormat("{0} {1} matches: {2}\n", "Subject", cert.Subject, cert.Subject.Equals(cer2.Subject));
    strb.AppendFormat("{0} {1} matches: {2}\n", "Cert Hash", cert.GetCertHashString(), Enumerable.SequenceEqual<byte>(cer.GetCertHash(), cert.GetCertHash()));
    strb.AppendFormat("{0} matches: {2}\n", "Public Key", cert.GetPublicKeyString(), Enumerable.SequenceEqual<byte>(cer.GetPublicKey(), cert.GetPublicKey()));
    strb.AppendFormat("{0}: {1}, {2}", "Verification", cert.Verify(), cer2.Verify());

    var res = MessageBox.Show(strb.ToString(),
        "Allow certificate?", MessageBoxButtons.YesNo);
    return res == System.Windows.Forms.DialogResult.Yes;
});

con.Bind();

Essentially, if that VerifyServerCertificateCallback returns true then the connection succeeds, if it returns falls the connection fails with the same exception as with any other solution I've tried.

Curiously, neither installing the AD certificate or the root certificate of the AD controller didn't help the other solutions but it does change the result of the Verify() method.

What kind of checks do I have to perform on the certificate in the callback to maintain the sanctity of the SSL connection?

like image 340
Kempeth Avatar asked Feb 19 '15 14:02

Kempeth


People also ask

Does LDAP Use SSL?

Secure your LDAP server connection between client and server application to encrypt the communication. In case of simple bind connection using SSL/TLS is recommended to secure the authentication as simple bind exposes the user crendetials in clear text.

What is LDAP SSL?

The Lightweight Directory Access Protocol (LDAP /ˈɛldæp/) is an open, vendor-neutral, industry standard application protocol for accessing and maintaining distributed directory information services over an Internet Protocol (IP) network.


1 Answers

I suggest you to use PrincipalContext from System.DirectoryServices.AccountManagement. The initialization would look like this:

PrincipalContext context = new PrincipalContext(
            ContextType.Domain, NAME_OF_THE_DOMAIN + ":636", 
            null, 
            ContextOptions.SecureSocketLayer | ContextOptions.Negotiate, 
            this.textBoxUsername.Text, 
            this.textBoxPassword.Text);

After that you can search for a UserPrincipal and its DistinguishedName:

string dn = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, this.textBoxUsername.Text).DistinguishedName;

If you want to itterate through the AD-tree just do something like this with the help of the PrincipalSearcher:

using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
    foreach (var result in searcher.FindAll())
    {
        DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;

        //DO watherever you want
    }
}
like image 122
Claudio P Avatar answered Oct 16 '22 06:10

Claudio P