I'm trying to run a simple LDAP query using directory services in .Net.
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://someserver.contoso.com/DC=contoso,DC=com");
directoryEntry.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
var result = directorySearcher.FindOne();
var resultDirectoryEntry = result.GetDirectoryEntry();
return resultDirectoryEntry.Properties["msRTCSIP-PrimaryUserAddress"].Value.ToString();
And I'm getting the following exception:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
As a snippet in a Console app, this works. But when I run it as part of a WCF service (run under the same credentials), it throws the above exception.
Any suggestions?
Thanks
I had the same again and again and nothing seemed to help.
Changing the path from ldap:// to LDAP:// did the trick.
It's a permission problem.
When you run the console app, that app runs with your credentials, e.g. as "you".
The WCF service runs where? In IIS? Most likely, it runs under a separate account, which is not permissioned to query Active Directory.
You can either try to get the WCF impersonation thingie working, so that your own credentials get passed on, or you can specify a username/password on creating your DirectoryEntry:
DirectoryEntry directoryEntry =
new DirectoryEntry("LDAP://someserver.contoso.com/DC=contoso,DC=com",
userName, password);
OK, so it might not be the credentials after all (that's usually the case in over 80% of the cases I see).
What about changing your code a little bit?
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
var result = directorySearcher.FindOne();
if(result != null)
{
if(result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
var resultValue = result.Properties["msRTCSIP-PrimaryUserAddress"][0];
}
}
My idea is: why not tell the DirectorySearcher right off the bat what attribute you're interested in? Then you don't need to do another extra step to get the full DirectoryEntry from the search result (should be faster), and since you told the directory searcher to find that property, it's certainly going to be loaded in the search result - so unless it's null (no value set), then you should be able to retrieve it easily.
Marc
In the context of Ektron, this issue is resolved by installing the "IIS6 Metabase compatibility" feature in Windows:
Check 'Windows features' or 'Role Services' for IIS6 Metabase
compatibility, add if missing:
Ref: https://portal.ektron.com/KB/1088/
On IIS hosted sites, try recycling the app pool. It fixed my issue.
Thanks
I had the same error - in my case it was extra slash in path argument that made the difference.
BAD:
DirectoryEntry directoryEntry =
new DirectoryEntry("LDAP://someserver.contoso.com/DC=contoso,DC=com/",
userName, password);
GOOD:
DirectoryEntry directoryEntry =
new DirectoryEntry("LDAP://someserver.contoso.com/DC=contoso,DC=com",
userName, password);
I had this error as well and for me it was an OU with a forward slash in the name: "File/Folder Access Groups".
This forum thread pointed me in the right direction. In the end, calling .Replace("/","\\/") on each path value before use solved the problem for me.
Just FYI, I had the same error and was using the correct credentials but my LDAP url was wrong :(
I got the exact same error message and code
Just had that problem in a production system in the company where I live... A webpage that made a LDAP bind stopped working after an IP changed.
The solution...
... I installed Basic Authentication to perform the troubleshooting indicated here: https://support.microsoft.com/en-us/kb/329986
And after that, things just started to work. Even after I re-disabled Basic Authentication in the page I was testing, all other pages started working again with Windows Authentication.
Regards,
Acácio
I encounter this error when I'm querying an entry of another domain of the forrest and this entry have some custom attribut of the other domain.
To solve this error, I only need to specify the server in the url LDAP :
Path with error = LDAP://CN=MyObj,DC=DOMAIN,DC=COM
Path without error : LDAP://domain.com:389/CN=MyObj,DC=Domain,DC=COM
This Error can occur if the physical machine has run out of memory.
In my case i was hosting a site on IIS trying to access the AD, but the server had run out of memory.
I had to change my code from this:
DirectoryEntry entry = new DirectoryEntry(path, ldapUser, ldapPassword);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchRoot = entry;
searcher.SearchScope = SearchScope.Subtree;
To this:
DirectoryEntry entry = new DirectoryEntry(path, ldapUser, ldapPassword);
DirectorySearcher searcher = new DirectorySearcher();
searcher.SearchScope = SearchScope.OneLevel;
SearchResult searchResult = searcher.FindOne();
The same error occurs if in DirectoryEntry.Patch is nothing after the symbols "LDAP//:". It is necessary to check the directoryEntry.Path before directorySearcher.FindOne(). Unless explicitly specified domain, and do not need to "LDAP://".
private void GetUser(string userName, string domainName)
{
DirectoryEntry dirEntry = new DirectoryEntry();
if (domainName.Length > 0)
{
dirEntry.Path = "LDAP://" + domainName;
}
DirectorySearcher dirSearcher = new DirectorySearcher(dirEntry);
dirSearcher.SearchScope = SearchScope.Subtree;
dirSearcher.Filter = string.Format("(&(objectClass=user)(|(cn={0})(sn={0}*)(givenName={0})(sAMAccountName={0}*)))", userName);
var searchResults = dirSearcher.FindAll();
//var searchResults = dirSearcher.FindOne();
if (searchResults.Count == 0)
{
MessageBox.Show("User not found");
}
else
{
foreach (SearchResult sr in searchResults)
{
var de = sr.GetDirectoryEntry();
string user = de.Properties["SAMAccountName"][0].ToString();
MessageBox.Show(user);
}
}
}
Spent a day on my similar issue, but all these answers didn't help.
Turned out in my case, I didn't enable Windows Authentication in IIS setting...
In my case, the problem was that I was trying to reference a DirectoryEntry's property value, even though that DirectoryEntry did not have that property at all.
If you for example, have:
var myGroup = new DirectoryEntry("LDAP://CN=mygroup,OU=mydomain....", myUsername, myPassword);
var groupManager = myGroup.Properties["managedBy"].Value.ToString();
If myGroup has no managedBy attribute set in the AD, this will result in Unknown error (0x80005000)
Related
I've not done any LDAP-based authentication before and also I've not worked with any LDAP server before. So I need a free online LDAP server to play with, I've found this https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
However my code is not working (or the info there has become invalid, I'm not sure), the result of authen is always false, here is my code:
path = "ldap.forumsys.com:389/dc=example,dc=com";
using (var pc = new PrincipalContext(ContextType.Domain, null, path))
{
//this always returns false
var ok = pc.ValidateCredentials("read-only-admin", "password");
}
Could you make it work on your side? Or at least please assert that the info there is invalid, in that case if possible please give me some other info (from other free LDAP servers for testing).
I don't think the server is Active Directory. You can refer to this question for how to connect to a LDAP server in C#.
Second Edit:
Checked with MS people. They also suggest LdapConnection.
https://github.com/dotnet/corefx/issues/31809
Edit:
I can use DirectoryEntry to bind to the server. I am not sure why PrincipalContext does not work, but you can try this way.
Here is a sample code for validating user and password.
Tested on .Net Core 2.1, with System.DirectoryServices package 4.5.0.
using System;
using System.DirectoryServices;
namespace LDAPTest
{
class Program
{
static void Main(string[] args)
{
string ldapServer = "LDAP://ldap.forumsys.com:389/dc=example,dc=com";
string userName = "cn=read-only-admin,dc=example,dc=com";
string password = "password";
var directoryEntry = new DirectoryEntry(ldapServer, userName, password, AuthenticationTypes.ServerBind);
// Bind to server with admin. Real life should use a service user.
object obj = directoryEntry.NativeObject;
if (obj == null)
{
Console.WriteLine("Bind with admin failed!.");
Environment.Exit(1);
}
else
{
Console.WriteLine("Bind with admin succeeded!");
}
// Search for the user first.
DirectorySearcher searcher = new DirectorySearcher(directoryEntry);
searcher.Filter = "(uid=riemann)";
searcher.PropertiesToLoad.Add("*");
SearchResult rc = searcher.FindOne();
// First we should handle user not found.
// To simplify, skip it and try to bind to the user.
DirectoryEntry validator = new DirectoryEntry(ldapServer, "uid=riemann,dc=example,dc=com", password, AuthenticationTypes.ServerBind);
if (validator.NativeObject.Equals(null))
{
Console.WriteLine("Cannot bind to user!");
}
else
{
Console.WriteLine("Bind with user succeeded!");
}
}
}
}
Reference:
https://www.c-sharpcorner.com/forums/ldap-authentication2
I figure it out too, and having no LDAP knowledge I´ve come up with this.
The problem in your solution may be first, you are using "ldap://" instead of "LDAP://", since it was something I came into when coding this. But I use System.DirectoryServices library.
I tested against this magnificent free to test LDAP server
var path = "LDAP://ldap.forumsys.com:389/dc=example,dc=com";
var user = $#"uid={username},dc=example,dc=com";
var pass = "password";
var directoryEntry = new DirectoryEntry(path, user, pass, AuthenticationTypes.None);
var searcher = new DirectorySearcher(directoryEntry);
searcher.PropertiesToLoad.Add("*");
var searchResult = searcher.FindOne();
I don´t understand exactly what all of this lines does, however, and lookign for a solution I found some recommendations.
on the path the "LDAP://" string should be on block mayus.
in the user, sometimes you need to use "cn=username-admin" for validating admins, be sure to also set Authentication type to ServerBind.
It seems as if read-only-admin is not a valid user. Try replacing:
var ok = pc.ValidateCredentials("read-only-admin", "password");
with
var ok = pc.ValidateCredentials("tesla", "password");
If that does not work, the other other issue would be on the LDAP's server side.
A good option regardless is to set up an Amazon Web Services EC2 server (it is free) and load Windows Server onto it. This gives you your own server and you learn how to set up an LDAP server (which is pretty easy).
I'm writing a piece of code that is supposed to search the active directory for a specific users GivenName (forename) and Surname based upon their SamAccountName as the search parameter, and then return a string containing their given name and surname.
The code I have written so far is as follows:
public static string GetName(string uName)
{
StringBuilder builder = new StringBuilder();
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, "serverName"))
{
UserPrincipal user = new UserPrincipal(context);
user.SamAccountName = uName;
PrincipalSearcher srch = new PrincipalSearcher(user);
srch.QueryFilter = user;
PrincipalSearchResult<Principal> res = srch.FindAll();
foreach (UserPrincipal u in res)
{
builder.Append(u.GivenName);
builder.Append(" ");
builder.Append(u.Surname);
}
return builder.ToString();
}
}
The problem I'm having with the above code is that during run-time the line
user.SamAccountName = uName;
throws the following error: DirectoryServicesCOMException unhandled "A local error has occured"
The principal context object is created just fine, as is the user principal object, it only throws the error when executing the line mentioned above. What's even more bizarre is that this code seemed to work a couple of days ago. If anyone reading this has any ideas as to why I'm getting this error I'd be greatly appreciative!
P.S.
I resorted to asking about this as the bloody error message is a bit too cryptic to actually figure out, or at least for me anyway (a local error occured) really? Whichever developer thought that was a useful error message is an idiot.
Possible causes:
There is an issue with the computer's domain membership or authentication. For example, is the clock on the computer running the code synchronized (within 5 minutes) of the DCs in the target domain?
The user name is invalid. For example, it contains invalid characters.
I access the AD properties thru the below method. It works fine in my Local VHD (where I'm the domain/local/enterprise Admin) - but the same doesn't work when I access from a Domain user(who has only local admin access).
But the same Domain user(only with local admin access) access all the AD property details using the ADExplorer(SysInternal) tools.
Is it because that is unmanaged code and have Windows APIs to access and in .Net I need domain admin or some privilege ?
Or is there another way - which I'm missing in .Net to access the AD Properties without having an extra domain-level-privilege ??
public void getCurrentUserADDetails(string UserName)
{
string ladpQueryStr = "LDAP://sp.com";
DirectoryEntry dirEntry = new DirectoryEntry(ladpQueryStr);
DirectorySearcher srch = new DirectorySearcher(dirEntry);
srch.Filter = "(cn=" + UserName.ToLowerInvariant().Trim() + ")";
srch.PropertiesToLoad.Add("name");
srch.PropertiesToLoad.Add("memberOf");
srch.PropertiesToLoad.Add("prop123");
SearchResult searcResult = srch.FindOne();
if (searcResult != null)
{
ResultPropertyCollection propertiesCollection = searcResult.Properties;
List<DisplayClass> grdDataList = new List<DisplayClass>();
foreach (string strKey in propertiesCollection.PropertyNames)
{
DisplayClass dispC = new DisplayClass();
dispC.pName = strKey;
dispC.pValue = Convert.ToString(propertiesCollection[strKey][0]);
grdDataList.Add(dispC);
}
dataGridView1.DataSource = grdDataList;
}
}
This is going to run in ASP.Net
thanks in advance :)
I assume you're using integrated authentification - in order for this to work you have to setup account delegation, unless you're running your application on a domain controller. This is a pretty tricky process, but there are a ton of info in Google.
By using Explicit authentication and changing the Search filter, i got the results.
DirectoryEntry dirEntry = new DirectoryEntry(path, username, password, AuthenticationType);
I want to know if user account enable. I use this code:
var usersList = new List<DirectoryEntry>();
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + computerName + ",Computer", Settings.UserName,Settings.UserPassword);
DirectoryEntry admGroup = localMachine.Children.Find(Settings.AdministratorsGroup, "group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
var b = member.Properties["userAccountControl"].Value; // <---- value == null
usersList.Add(member);
}
I get all members correctly.
But an error is appeared in member.Properties["userAccountControl"].Value. I know of using System.DirectoryServices.AccountManagement namepsace, but i want to know why this code doesn't work.
You're using the WinNT:// provider, which is very limited in its abilities. It doesn't support many of the usual properties that the full-blown LDAP:// provider has - and I would think that's probably the reason why this code setting userAccountControl (which is an LDAP attribute, most likely not present and not support on a local user account) doesn't work.
See Richard Mueller's article WinNT vs. LDAP for a lot more information on what WinNT:// can do (or cannot do)
I'm attempting to query AD in an ASP.Net (4.0) application that is running on Windows Server 2008 R2 (IIS7 installed). (It also fails when running as a 2.0 application as well)
This is nothing new for me, as I've done this many times before. I wrote a small ASP.Net program that runs fine on my own machine (Windows XP with IIS6), but fails when run on the 2008 box.
(The result is that you see a list of groups the user is a member of in a textbox)
(on button_click)
var userName = txtUserName.Text;
if (userName.Trim().Length == 0)
{
txtResults.Text = "-- MISSING USER NAME --";
return;
}
var entry = new DirectoryEntry("LDAP://blah.blah/DC=blah,DC=blah",
"cn=acct, dc=blah, dc=blah",
"pass");
var search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + userName + ")";
search.PropertiesToLoad.Add("memberOf");
var groupsList = new StringBuilder();
var result = search.FindOne();
if (result != null)
{
int groupCount = result.Properties["memberOf"].Count;
for (int counter = 0; counter < groupCount; counter++)
{
groupsList.Append((string)result.Properties["memberOf"][counter]);
groupsList.Append("\r\n");
}
}
txtResults.Text = groupsList.ToString();
When I run this code I get the following error on search.FindOne():
System.DirectoryServices.DirectoryServicesCOMException (0x8007203B): A local error has occurred.
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at WebApplication1._Default.btnSearch_Click(Object sender, EventArgs e)
We've done a lot of research with this and twiddled every IIS7 setting we can think of, but no go so far. Any clues?
Change the username parameter from "cn=xxx, dc=yyy, dc=zzz" to "Domain\Username"
You can also change the IIS Application Pool to run a domain account with the query priveleges you are searching for.
I have a few other comments as well:
Make sure the first entry for the DirectoryEntry constructor includes the container for the users as well. This should help the DirectorySearcher to work more reliably.
I believe the second parameter in the DirectoryEntry constructor should be the user name, not the AD query path.
You should set the AuthenticationType property as well. With Server 2008, by default, this needs to be set to AuthenticationTypes.Secure | AuthenticationTypes.ServerBind | AuthenticationTypes.Sealing. I'd guess that 2008R2 has a simliar requirement.
I see that the question is rather old, but after struggling with this I thought to mention that it is indeed possible to use the LDAP-style of the username (in opposite to the DNS style). This works well for me:
string connString = "LDAP://MyDomain/CN=blah,DC=blah,DC=blah";
string username = "CN=MyAdmin,CN=Users,CN=blah,DC=blah,DC=blah";
string password = "myLittleSecret";
DirectoryEntry root = new DirectoryEntry(
connString,
username,
password,
AuthenticationTypes.None);
Where MyAdmin is a member in the Administrators role.
One little thing that took me a while to find is the AuthenticationTypes.None parameter that is needed if you do not want to communicate over SSL. Surely, you want to do this in production, but for development purposes it may be OK to skip the encryption.
Environment: Windows 7
I was also getting this exception when tried to query the active directory:
SearchResult result = srch.FindOne();
To resolve this, just put the above code inside Security.RunWithElevatedPrivileges().
Final Solution:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
result = srch.FindOne();
});