.NET Core LDAP

Home / .NET Core LDAP

.NET Core doesn’t have built-in support for LDAP (Active Directory). This can be a show-stopper for a lot of projects. It was a bit of a show-stopper for me earlier as well.

So, references to these libraries won’t be available:

System.DirectoryServices
System.DirectoryServices.AccountManagement
System.DirectoryServices.Protcols

But, there are alternatives to mitigate the problem.


Reading through the Microsoft Github issues, some of these libs may be added in .NET Core 2.0, but I have no idea what the implementation will look like.

In the meantime, here are two ports of Novell’s LDAP libraries that target .NET Core.

https://github.com/VQComms/CsharpLDAP/tree/coreclrPort
https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard

The latter is available in Nuget:

https://www.nuget.org/packages/Novell.Directory.Ldap.NETStandard/

I’ve written some LDAP queries using this library and it works well. The basic flow isn’t much different from using the DirectoryServices DirectoryEntry and search functionality. The only downside I could see is that the binding (authentication) does not take advantage of the currently logged in Windows user. A username/password must be specified. Below are my samples that will search for groups (and their children) and then find any user within those group(s).

private static ILdapConnection _conn;

void Main()
{
	var groups = SearchForGroup("MyAdGroup");
	SearchForUser("MyCompany", groups);
}

static ILdapConnection GetConnection()
{
	LdapConnection ldapConn = _conn as LdapConnection;

	if (ldapConn == null)
	{
		// Creating an LdapConnection instance 
		ldapConn = new LdapConnection() { SecureSocketLayer = true };

		//Connect function will create a socket connection to the server - Port 389 for insecure and 3269 for secure	
		ldapConn.Connect("ADServrName", 3269);

		//Bind function with null user dn and password value will perform anonymous bind to LDAP server 
		ldapConn.Bind(@"domain\username", "password");
	}

	return ldapConn;
}

HashSet<string> SearchForGroup(string groupName)
{
	var ldapConn = GetConnection();
	var groups = new HashSet<string>();

	var searchBase = string.Empty;
	var filter = $"(&(objectClass=group)(cn={groupName}))";
	var search = ldapConn.Search(searchBase, LdapConnection.SCOPE_SUB, filter, null, false);
	while (search.hasMore())
	{
		var nextEntry = search.next();
		groups.Add(nextEntry.DN);
		var childGroups = GetChildren(string.Empty, nextEntry.DN);
		foreach (var child in childGroups)
		{
			groups.Add(child);
		}
	}

	return groups;
}

static HashSet<string> GetChildren(string searchBase, string groupDn, string objectClass = "group")
{
	var ldapConn = GetConnection();
	var listNames = new HashSet<string>();

	var filter = $"(&(objectClass={objectClass})(memberOf={groupDn}))";
	var search = ldapConn.Search(searchBase, LdapConnection.SCOPE_SUB, filter, null, false);

	while (search.hasMore())
	{
		var nextEntry = search.next();
		listNames.Add(nextEntry.DN);
		var children = GetChildren(string.Empty, nextEntry.DN);
		foreach (var child in children)
		{
			listNames.Add(child);
		}
	}

	return listNames;
}

void SearchForUser(string company, HashSet<string> groups = null)
{
	var ldapConn = GetConnection();
	var users = new HashSet<string>();

	string groupFilter = (groups?.Count ?? 0) > 0 ?
		$"(|{string.Join("", groups.Select(x => $"(memberOf={x})").ToList())})" :
		string.Empty;
	var searchBase = string.Empty;
	string filter = $"(&(objectClass=user)(objectCategory=person)(company={company}){groupFilter})";
	var search = ldapConn.Search(searchBase, LdapConnection.SCOPE_SUB, filter, null, false);

	while (search.hasMore())
	{
		var nextEntry = search.next();
		nextEntry.getAttributeSet();
		users.Add(nextEntry.DN);
	}
}

13 thoughts on “.NET Core LDAP”

  1. Good how about.

    First congratulate you on your blog, which is fine.

    Second I wanted to ask you, I am trying to create mailboxes in Ldp from Asp.Net core using this library and I do not see anywhere the method that had the DirectoryEntry class to add a group and commit.

    You could give me a hand saying in this library where that method is or an example of how to call it.
    Thanks in advance.

    1. I’ll play around with it some today. I do have another project using DIrectoryServices in which I’m creating users, but not mailboxes. I think the code I have for creating users can be ported relatively easily. Howevery, for Mailboxes (if you’re using Exchange), my thought was to use the Automation namespaces and Powershell cmdlets in an attempt to create Exchange mailboxes.

      1. Good afternoon
        Thanks for replying to me, I think they are Outlook mailboxes not exchange anyway then I enclose a code of how we created the mailboxes earlier with the DirectoryEntry Class:

        DirectoryEntry new_UsersGroup = objUSERSAD_UsersGroups.Add (“CN =” + des_group_name, “group”);
        New_UsersGroup.Properties [“cn”]. Value = des_group_name;
        New_UsersGroup.Properties [“sAMAccountName”]. Value = des_group_name.Replace (“”, “”);
        New_UsersGroup.Properties [“managedby”] Value = dn_responsable;
        New_UsersGroup.Properties [“displayName”]. Value = des_group_name;
        New_UsersGroup.Properties [“legacyExchangeDN”]. Value = FactoriaServices. ServiceParametrizacion.ObtenerConfiguracion (“legacyExchangeDN”) + des_mail;
        New_UsersGroup.Properties [“mail”]. Value = des_mail;
        New_UsersGroup.Properties [“mailNickName”]. Value = des_group_name.Replace (“”, “.”);
        New_UsersGroup.Properties [“msExchPoliciesIncluded”]. Value = FactoriaServices.ServicioParametrizacion.ObtenerConfiguracion (“msExchPoliciesIncluded”);
        New_UsersGroup.Properties [“msExchRecipientDisplayType”]. Value = FactoriaServices.ServicioParametrizacion.ObtenerConfiguracion (“msExchRecipientDisplayType”);
        New_UsersGroup.Properties [“msExchRequireAuthToSendTo”]. Value = FactoriaServices.ServiceParametrizacion.ObtenerConfiguracion (“msExchRequireAuthToSendTo”);
        New_UsersGroup.Properties [“msExchVersion”]. Value = FactoriaServices.ServicioParametrizacion.ObtenerConfiguration (“msExchVersion”);
        New_UsersGroup.Properties [“internetEncoding”]. Value = FactoriaServices. ServiceParametrizacion.ObtenerConfiguration (“internetEncoding”);
        New_UsersGroup.Properties [“proxyAddresses”]. Value = “SMTP:” + new_UsersGroup.Properties [“mail”].
        New_UsersGroup.Properties [“proxyAddresses”]. Add (“smtp:” + des_group_name.Replace (“”, “.”) + “@ Everis.exch”);
        New_UsersGroup.Properties [“reportToOriginator”]. Value = FactoriaServices. ServiceParametrizacion.ObtenerConfiguracion (“reportToOriginator”);
        New_UsersGroup.Properties [“showInAddressBook”]. Value = showInAddressBook1;
        New_UsersGroup.Properties [“showInAddressBook”]. Add (showInAddressBook2);
        New_UsersGroup.Properties [“groupType”]. Value = FactoriaServicios.ServicioParametrizacion.ObtenerConfiguracion (“groupType”);
        String textHTML = FactoriaServicios.ServicioParametrizacion.ObtenerConfiguracion (“descriptionCreado”);
        String descriptionCreated = Server.HtmlDecode (textHTML);
        New_UsersGroup.Properties [“description”]. Value = descriptionCreated + “” + System.DateTime.Now.ToShortDateString ();
        New_UsersGroup.CommitChanges ();

  2. Hello, I’m using this nuget trying to change user password in an Active Directory, my user has permissions for change password.
    if I use “System.DirectoryServices
    System.DirectoryServices.AccountManagement
    System.DirectoryServices.Protcols”

    I can change password without problem. Using novell ldap I got this error:
    “{LdapException: Insufficient Access Rights (50) Insufficient Access Rights
    LdapException: Server Message: 00002098: SecErr: DSID-03150F93, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0

    with this user I can change other properties but not password using novell ldap. any suggestion? I tryed everithing that I found arround the web.

    I hope anyone can help me.
    Thanks and cheers.

    1. How are you attempting to change the password?

      I think the typical form is like this, but haven’t had cause to attempt changing a user’s password:

      var attr = new LdapAttribute("userPassword", "newPassword123456789!!");
      ldapConn.Modify(nextEntry.DN, new LdapModification[] { new LdapModification(LdapModification.REPLACE, attr) });
      
  3. I used the following code to create a new user in active directory. From this, the new user is created in active directory without any error. But, I could not work with that new user using its password, here i am set the password using “userPassword” attribute.
    Is this correct way to set password for user using “userPassword” attribute or please guide me the correct workflow to achieve this?

    LdapAttributeSet attributeSet = new LdapAttributeSet();
    attributeSet.Add(new LdapAttribute(“objectclass”, “user”));
    attributeSet.Add(new LdapAttribute(“sAMAccountName”, “myuser”));
    attributeSet.Add(new LdapAttribute(“userPRincipalName”, “myuser”));
    attributeSet.Add(new LdapAttribute(“userAccountControl”, (66080).ToString()));
    attributeSet.Add(new LdapAttribute(“userPassword”, “mypassword”));

    string dn = “CN=myuser,CN=Users,DC=mydomain,DC=com”;
    LdapEntry newEntry = new LdapEntry(dn, attributeSet);

    LdapConnection ldapConn = new LdapConnection();
    ldapConn.Connect(ldapHost, 369);
    ldapConn.Bind(loginDN, password);
    ldapConn.Add(newEntry);

    1. I recently wrote an application that does this very thing, but it’s using the older legacy .NET (4.6.x) LDAP mechanisms (DirectoryEntry, mostly). I’ll see if I can convert it over to the Ldap library and provide a working code snippet.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.