When we try to search for a user in ActiveDirectory, we get that exception - 0x8007203B.
Basically we deployed a web service, which uses DirectoryEntry & DirectorySearcher class to find a user in AD, and sometimes this exception happens. But when we do IISReset, it again works fine.
Code is very simple like this:
DirectoryEntry domainUser = new DirectoryEntry("LDAP://xxx.yyy/dc=xxx,dc=yyy", "domain\user", "pwd", AuthenticationTypes.Secure);
DirectoryEntry result = new DirectorySearcher(domainUser, filter);
Only some times this happens. I don't have much information to provide, any guess much appreciated
This is how my filter looks like
public static string BuildFilter(DirectoryEntry dirEntry, string userName, string userMail)
{
try
{
string filter = string.Empty;
if (!string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(userMail))
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(|(CN={0})(samaccountname={0})))", userName);
else if (string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(userMail))
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(mail={0}))", userMail);
else
filter = string.Format(#"(&(objectClass=user)(samaccounttype=805306368)(|(CN={0})(samaccountname={0})(mail={1})))", userName, userMail);
return filter;
}
catch (Exception ex)
{
_logger.Error("BuildUserSearch - Failed to build LDAP search", ex);
}
return null;
}
You say that this it's just append after some time. As DirectoryEntry and DirectorySearcher are built on COM object into disposable class I would first just add some using sections to be sure that underlying objects are corectly freed.
using(DirectoryEntry root = new DirectoryEntry(ldapPath))
{
using(DirectorySearcher searcher=new DirectorySearcher(root))
{
...
}
...
}
Any guess are appreciated?
Then here's mine:
ASP.NET: DirectoryServicesCOMException [...];
Windows Error Codes: Repair 0x8007203B. How To Repair 0x8007203B.
What makes me confuse is that you say it works most of the time...
Did this help?
P.S. I'll update if I think of anything else.
Related
I am facing Unknown error (0x80005000) while adding user to LDAP server(Apache), the following is my code. Could anyone please let me know where I am doing mistake.
namespace TestMethods
{
public class Program
{
static void Main(string[] args)
{
var ldi = new LdapDirectoryIdentifier("localhost", 10389);
AddUser("username", "o=Company");
}
public static void AddUser(string username, string group)
{
try
{
DirectoryEntry dirEntry = new
DirectoryEntry("LDAP://localhost:10389,o=Company" + group);
Console.WriteLine("Added to the path");// Working
dirEntry.Invoke("Add", new object[] { username });//Received Exception here
dirEntry.CommitChanges();
Console.WriteLine("Added to the path");
dirEntry.Close();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
I believe you should use a / to separate the server name from the DN in your path:
LDAP://localhost:10389/o=Company
The constructor of DirectoryEntry doesn't make any network requests, so your path isn't validated until you actually use it.
However, if you are not using Active Directory, then I don't think Invoke will work for you. The description of DirectoryEntry.Invoke says:
Calls a method on the native Active Directory Domain Services object.
Even then, I'm not sure which Add method you're trying to use.
The way to create a new object using DirectoryEntry is like this (assuming dirEntry is pointing to a path where it can be created):
var newUser = dirEntry.Children.Add($"uid={username}", "inetOrgPerson");
// Set other attributes like this:
// newUser.Properties["someAttribute"].Value = "something";
//Save - this is where the object is actually created
newUser.CommitChanges();
I've never used Apache's LDAP server (I know AD better), so you may have to edit the schema ("inetOrgPerson") if you need to.
I maintain a C# program which needs to check whether thousands of Active Directory accounts are still in existence & if they are enabled or not. Recently, I've found that my program was hanging while querying the directory for an account. Fortunately, I've just discovered the DirectorySearcher.ClientTimeout property (which I hadn't been setting, meaning that the search goes on indefinitely).
The one problem that I see with using this property is that, if the search hangs while looking up an account that happens to actually exist, the DirectorySearcher.FindOne() method will return 0 results. As you can imagine, that's a problem since at runtime, I don't know whether the search failed or if the account really wasn't found.
Does anyone know if there's another property that gets set in the object that I can use to see if the search aborted? Is there any difference between a result set from an aborted search versus one that really contains zero results?
Here's my method:
public static string UserExists(string username, Log log)
{
string accountStatus;
if (username.Split('\\').Length != 2)
return "Invalid ID,Invalid ID";
else
{
try
{
string[] parts = username.Split('\\');
domain = parts[0];
ScopeDN = "DC=" + domain + ",DC=contoso,DC=com";
DirectoryEntry de = GetDirectoryEntry("LDAP://" + ScopeDN);
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = de;
ds.ClientTimeout = TimeSpan.FromSeconds(30);
ds.Filter = "(&(objectClass=user) (sAMAccountName=" + username + "))";
SearchResult result = ds.FindOne();
if (result == null)
accountStatus = "Does Not Exist,Account Does Not Exist";
else
{
int UAC = (int)result.Properties["userAccountControl"][0];
bool enabled = !Convert.ToBoolean(UAC & 0x00002);
if (enabled)
accountStatus = "Exists,Account is Enabled";
else
accountStatus = "Exists,Account is Disabled";
}
return accountStatus;
}
catch (Exception e)
{
log.exception(LogLevel._ERROR, e, false);
return "Exception,Exception";
}
}
}
Try doing the following optimizations:
Dispose DirectoryEntry
Dispose DirectorySearcher
Create and cache a connection to RootDse object before querying users. In this case all AD queries will use one single cached connection to AD, which significantly increases performance.
For example
var entry = new DirectoryEntry("LDAP://contoso.com/RootDSE");
entry.RefreshCache();
// making user search and other AD work with domain contoso.com here
entry.Dispose();
AFAIK, there is no difference. One thing you could do is retry a couple times before assuming it is missing. Its a matter of confidence. One failure may not give high enough confidence. But 2 or 3 might. Does that help you?
I'm researching what I need to build an Active Directory search tool which ultimately will be used in a C# ASP.NET web app.
I know very little about AD (and don't particularly want to know any more than necessary) so I've asked our tech ops to set up a dummy instance on a server. This they've done giving it the domain dummy.local
I now need to work out my LDAP connection string. Here I'm completely stuck. I'm using a user account which is a member of Domain Admins. After loads of hunting round the web I've tried all sorts of things to work out the various components of the LDAP connection string. For example, if I run the following in cmd.exe for that domain admin user on the server...
dsquery user -samid "username" | dsget user -memberof -expand
...this gives me the following information:
"CN=Domain Admins,CN=Users,DC=dummy,DC=local"
"CN=Remote Desktop Users,CN=Builtin,DC=dummy,DC=local"
"CN=Users,CN=Builtin,DC=dummy,DC=local"
"CN=Administrators,CN=Builtin,DC=dummy,DC=local"
"CN=Domain Users,CN=Users,DC=dummy,DC=local"
"CN=Denied RODC Password Replication Group,CN=Users,DC=dummy,DC=local"
I've also run the following in a C# console app...
using (var context = new PrincipalContext(ContextType.Domain))
using (var comp = ComputerPrincipal.FindByIdentity(context, Environment.MachineName))
{
Console.WriteLine(String.Join(",", comp.DistinguishedName.Split(',').SkipWhile(s => !s.StartsWith("OU=")).ToArray()));
}
...and this gives me the following:
OU=Domain Controllers,DC=dummy,DC=local
I thought I therefore had all the properties I needed to build my LDAP connection string. I ended up with this:
LDAP://COMSECWEBDEV.dummy.local/ou=Domain Controllers,/cn=Domain Admins,/cn=Users,/dc=dummy,/dc=local
I've tried using this connection string, with the username and password of the domain admin which I know is correct, but everything I try always gives me the same error:
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()
Since this error gives me no detail I have no idea what I'm doing wrong. I'm sure I'm just not getting the connection string right, but I've no idea how to work out the correct string.
For completeness, here is the console code which I'm testing with:
static void Main(string[] args)
{
var connString = ConfigurationSettings.AppSettings["lc"];
var username = ConfigurationSettings.AppSettings["lu"];
var password = ConfigurationSettings.AppSettings["lpw"];
using (DirectoryEntry de = new DirectoryEntry(connString, username, password))
{
DirectorySearcher search = new DirectorySearcher(de);
search.PageSize = 1001;// To Pull up more than 100 records.
DirectorySearcher directorySearcher = new DirectorySearcher(de);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
try
{
var result = directorySearcher.FindOne();
var found = false;
if (result != null)
{
if (result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
found = true;
Console.WriteLine("Found: " + result.Properties["msRTCSIP-PrimaryUserAddress"][0]);
}
}
if (!found)
{
Console.WriteLine("Sad face");
}
}
catch (Exception x)
{
Console.WriteLine(x.Message);
}
Console.WriteLine("------------");
}
}
I was trying to figure out how to properly format a LDAP connection string last week, and found this entry over on serverfault:
How can I figure out my LDAP connection string?
I noticed you had a "/" between each OU or DC entry - I didn't include those in mine, and I don't see them included in the above example either.
I'm far from an expert (obviously) but just figured I would throw it out there.
Ive done some google and stuff and i cant seem to figure this out.
Try Catch just doesnt work the way i would expect it to.
here is the code.
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
foreach (DirectoryEntry Node in Discover.Children)
{
try {
if (Node.Properties.Count > 0)
{
//you have access to the computer so yeah
}
}
catch(System.IO.FileNotFoundException err) {
var ErrorMesage = err.Message;
//Yeah you dont have access to this computer, lets write a subroutine to allow the user to put in a username and password to access it
}
so when i run this i get a epic box popup and say something stupid like
{"The network path was not found.\r\n":null}
Try this
try {
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup"); **//Error occured here**
foreach (DirectoryEntry Node in Discover.Children)
{
if (Node.Properties.Count > 0)
{
//you have access to the computer so yeah
}
}
}
catch(System.IO.FileNotFoundException err)
{
var ErrorMesage = err.Message;
//Yeah you dont have access to this computer, lets write a subroutine to allow the user to put in a username and password to access it
}
Now you are only attempt to check Node.Properties.Count access for errors, nothing more. Scope of the code DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup"); has also to be put in try section.
I'm attempting to authenticate a user against ADAM using a user I created in ADAM. However, regardless of the password used (correct, or incorrect), my search comes back with a valid DirectoryEntry object. I would assume that if the password is invalid, then the search would come back with a null object. Are my assumptions wrong or is there a flaw in the code below?
DirectoryEntry de = new DirectoryEntry("LDAP://localhost:389/cn=Groups,cn=XXX,cn=YYY,dc=ZZZ");
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(objectClass=user) (cn=" + userId + "))";
SearchResultCollection results = deSearch.FindAll();
if (results.Count > 0)
{
DirectoryEntry d = new DirectoryEntry(results[0].Path, userId, password);
if (d != null)
DoSomething();
}
You need to access a property of the DirectoryEntry to determine if it's valid. I usually check if the Guid is null or not.
bool valid = false;
using (DirectoryEntry entry = new DirectoryEntry( results[0].Path, userId, password ))
{
try
{
if (entry.Guid != null)
{
valid = true;
}
}
catch (NullReferenceException) {}
}
Note: you'll also want to wrap your search root directory entry and searcher in using statements, or explicitly dispose of them when you are done so that you don't leave the resources in use.
P.S. I'm not sure exactly which exception gets thrown when you attempt to access an invalid directory entries properties. A bit of experimentation is probably in order to figure out which exception to catch. You won't want to catch all exceptions as there are other problems (directory server not available, for instance) that you may want to handle differently than a failed authentication.