Connect to Active Directory using LdapConnection class on remote server - c#

I have a problem: I need to connect from a remote server to Active Directory, but the code has to be using the LdapConnection class. I need this because that way I can only test change notifiers when some event happen (such as user is deactivated or he changed group, data etc). OS on the remote server is Windows Server 2012.
I managed to do this from local using DirectoryServices with the following code:
String ldapPath = "LDAP://XRMSERVER02.a24xrmdomain.info";
directoryEntry = new DirectoryEntry(ldapPath, #"A24XRMDOMAIN\username", "pass");
//// Search AD to see if the user already exists.
DirectorySearcher search = new DirectorySearcher(directoryEntry);
search.Filter = "(&(objectClass=user))";
SearchResult result = search.FindOne();
This is okay and connection works but now I need to connect using the LdapConnection class.
I tried something like this on many ways but none of that helped me:
LdapConnection connection = new LdapConnection(XRMSERVER02.a24xrmdomain.info);
var credentials = new NetworkCredential(#"A24XRMDOMAIN\username", "pass");
connection.Credential = credentials;
connection.Bind();
It says that credentials are invalid but that is not true.
Explanations:
XRMSERVER02 - Domain controller
a24xrmdomain.info - Domain
A24XRMDOMAIN - Domain used for logging
Thanks for your help.

Even though I solved my problem I want to share with other developers what I achieved so far. Problem that I encountered was that I had remote server with OS Windows server 2012 and Active directory on it. I needed to connect on him via my local machine(Windows 10).
As I stated in my question it is possible to do that via DirectoryServices with the following code:
String ldapPath = "LDAP://(DomainController).a24xrmdomain.info";
directoryEntry = new DirectoryEntry(ldapPath, #"DOMAIN\username","pass");
//// Test search on AD to see if connection works.
DirectorySearcher search = new DirectorySearcher(directoryEntry);
search.Filter = "(&(objectClass=user))";
SearchResult result = search.FindOne();
This is one of the solutions, but since my task was to get notification and to identify when ever some object has changed in Active Directory, I needed connection to Active Directory on Remote server via LDAP class. Code for getting notifiers is taken from:
- Registering change notification with Active Directory using C#
I succeeded to connect with LDAP class via next code:
String ldapPath2 = "(DomainController).a24xrmdomain.info";
LdapConnection connection = new LdapConnection(ldapPath2);
var credentials = new NetworkCredential(#"username", "pass");
connection.Credential = credentials;
connection.Bind();
Want to mention that no IP address of remote server is needed, just Domain Controller that is used on him, and that Domain used for logging is unnecessary.
Happy coding

Try using NetworkCredential constructor with 3 parameters: username, password and domain. Specify domain separately from user name

Related

LDAPS connection with ASP.Net/C#

I have a connection string for LDAP protocol
ldap://ldap.example.com:636/DC=users,DC=buyers
which works fine.
But I need to use a LDAPS connection :
ldaps://ldap.example.com/DC=users,DC=buyers
which does show up in ldp.exe windows form when I test the connection.
Unfotunately it does not work in the Asp.Net application. I get "Unknown error (0x80005000)".
I am not sure whether LDAPS string is even possible with Asp.Net. I downloaded the source code into LDAPConnection.cs class and was unable to find any valuable information.
The method you found that works is indeed using LDAPS:
ldap://ldap.example.com:636/DC=users,DC=buyers
That's the only way to do it. I do that in one of my existing projects. It doesn't understand "LDAPS://".
If you don't believe me :) fire up Wireshark as you debug. When it connects, you'll see the SSL handshake to your domain controller.
Port 636 is only for LDAPS. Port 389 is the non-SSL port.
If you have more than one domain, you can use port 3269 for the global catalog via SSL.
Below code worked for me to connect to AD using LDAPS
ldapConnection = new LdapConnection(new LdapDirectoryIdentifier("your.LDAPSserver.com", 636));
var networkCredential = new NetworkCredential("UsernameWithoutDomain", "yourPassword", "AD.yourDOMAIN.com");
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback);
ldapConnection.AuthType = AuthType.Negotiate;
ldapConnection.Bind(networkCredential);
SearchRequest Srchrequest = new SearchRequest("CN=Users,DC=AD,DC=YOURCOMPANY,DC=COM", "mail=useremail#company.com", System.DirectoryServices.Protocols.SearchScope.Subtree);
SearchResponse SrchResponse = (SearchResponse)ldapConnection.SendRequest(Srchrequest);
// ServerCallback
private static bool ServerCallback(LdapConnection connection, X509Certificate certificate)
{
return true;
}
Surprisingly it is also working when I am not using networkCredential and just using ldapConnection.Bind(); Seems it is using my local credentials as default on my local machine.

How to know that Active Directory exists with only ip address?

How to know that AD exists?
I have only ip address. I tried to use those methods:
if(DirectoryEntry.Exists("LDAP://192.168.1.1"))
also
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://192.168.1.1")
but it didn't help.
I use LdapConnection right now, but I have a problem
LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("192.168.1.1"));
connection.AuthType = AuthType.Basic;
NetworkCredential credential =
new NetworkCredential("a", '1");
connection.Credential = credential;
connection.Timeout = new TimeSpan(1000);
connection.Bind();
I'm getting 81 code and The LDAP unavailable.
Does somebody know is possible just to know is ip is correct and AD exists?
P.S. I use .NET 2
You can try this (works with .NET 2.0 and does not need credentials):
...
using System.DirectoryServices.Protocols;
...
string server = "192.168.1.1";
using (LdapConnection ldapConnection = new LdapConnection(server))
{
ldapConnection.AuthType = AuthType.Anonymous;
SearchRequest request = new SearchRequest(null, "(objectclass=*)",
SearchScope.Base, "defaultNamingContext");
SearchResponse result = (SearchResponse)ldapConnection.SendRequest(request);
if (result.Entries.Count == 1)
{
Console.WriteLine(result.Entries[0].Attributes["defaultNamingContext"][0]);
}
}
It binds anonymously to the AD domain controller and retrieves the rootDSE entry. It displays the DN of the AD domain.
You can also query another attributes, see https://msdn.microsoft.com/en-us/library/ms684291(v=vs.85).aspx
AD can only be set to run on port 389 and/or 636. So if the port is open, it is a pretty good chance that LDAP is present.
Know if it is AD or not, would, typically, require you to have a valid LDAP account to BIND to the LDAP service.
You can perform a LDAP query against the LDAP service and probably learn the VendorName.

Validate users of Remote Active Directory in C#

I try to authenticate users belonging to remote ActiveDirectory from my machine, which is not the same domain as the current machine or user domain. There will be no trust between my machine and remote ActiveDirectory machine.
Initial Try
I tried to authenticate a user(Input: sAMAccountName, machine's ipaddress, machine's domain username("Administrator") and machine's password(***). Able to get result that the user with 'sAMAccountName' do exist in ActiveDirectory.
My Requirement:
Imagine that already a user("qwerty") is created in ActiveDirectory
From my local machine, I will have the following information,
a. Remote ActiveDirectory ipaddress
b. Remote ActiveDirectory machine's username and password.
c. Username and password of User "qwerty"
I need to check whether User "qwerty" is present in remote ActiveDirectory's users list and validate whether the password entered is same in ActiveDirectory's Users list
Code I tried:
DirectoryEntry entry = new DirectoryEntry("LDAP://ipaddress/DC=dinesh,DC=com", name, password);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(sAMAccountName=" + name + ")";
try
{
SearchResult adsSearchResult = adsSearcher.FindOne();
isValid = true;
adsEntry.Close();
}
catch (Exception ex)
{
adsEntry.Close();
}
Do I need to create a trust between local machine and remote ActiveDirectory machine before validating Users in a remote ActiveDirectory? If yes please tell how it can be done;
After creating trust, how can I validate Users?
===========================================================================
I am able to use the solution suggested by Rainer, but with a new problem. When I create a new user via C# code from a different machine, then some properties do not set properly.
Does this need to be set compulsorily while creating user?
First some basics (independent of this question)
Authentication
The system checks if Bob is really Bob. In an Active Directory environment, this is usually done with a domain login from the workstation, Bob enters his username and password, and he gets a Kerberos ticket. Later, if he wants to access e.g. a file share on a remote fileserver, he does not need to login anymore, and can access the files without entering username/password.
Authorization
The system checks which resources Bob is allowed to access. Usually Bob is in domain groups, and a group is in the ACL (access control list) of the resource.
If there are multiple trusting domains, Bob needs to login in one domain, and can access resources in all other domains.
This is one of the main reasons using Active Directory: single sign on
Checking if user / password is valid
If you have a username and password and want to check if the password is valid, you have to do a login to the domain. There is no way of just “checking if the password is correct”.
Login means: if there is a security policy “lock account if more than 3 invalid logins”, the account will be locked out checking with wrong password, even if you “only want to check the user+password”.
Using .NET Directory Service functions
I assume here that the process is either run by a human account as a normal program, or the program is a Windows service or a scheduled task which runs under a domain “technical user” account. In this case, you do not need to provide credentials for using the AD functions. If accessing other trusting AD domains, this is also true.
If you want to login to a “foreign domain”, and there is no trust, you need to provide a username+password (as in your code).
"Manually" authenticating a user
Normally, this should not be needed. Example: ASP.NET intranet usage. The user access a web application on the current domain or trusting domain, the authentication is done “in the background” by browser and IIS (if integrated Windows authentication is on). So you never need to handle user passwords in the application.
I don’t see many use cases where a password is handled by code.
One may that your program is a helper tool for storing emergency user accounts/passwords. And you want to check periodically if these accounts are valid.
This is a simple way to check:
using System.DirectoryServices.AccountManagement;
...
PrincipalContext principalContext =
new PrincipalContext(ContextType.Domain, "192.168.1.1");
bool userValid = principalContext.ValidateCredentials(name, password);
One can also use the older, raw ADSI functions:
using System.DirectoryServices;
....
bool userOk = false;
string realName = string.Empty;
using (DirectoryEntry directoryEntry =
new DirectoryEntry"LDAP://192.168.1.1/DC=ad,DC=local", name, password))
{
using (DirectorySearcher searcher = new DirectorySearcher(directoryEntry))
{
searcher.Filter = "(samaccountname=" + name + ")";
searcher.PropertiesToLoad.Add("displayname");
SearchResult adsSearchResult = searcher.FindOne();
if (adsSearchResult != null)
{
if (adsSearchResult.Properties["displayname"].Count == 1)
{
realName = (string)adsSearchResult.Properties["displayname"][0];
}
userOk = true;
}
}
}
If your real requirement is actually a validity check of user+password, you can do it in one of these ways.
However, if it is a "normal application", which just wants to check if the entered credentials are valid, you should rethink your logic. In this case, you better should rely on the single sign on capabilities of AD.
If there are further questions, please comment.
b. Remote ActiveDirectory machine's username and password.
This sounds a bit unclear. I assume you mean "a username and corresponding password in the remote domain".
There is also the concept of a machine account, which is the hostname appended with $. But that's another topic.
Creating new user
Option 1
using (DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://192.168.1.1/CN=Users,DC=ad,DC=local",
name, password))
{
using (DirectoryEntry newUser = directoryEntry.Children.Add("CN=CharlesBarker", "user"))
{
newUser.Properties["sAMAccountName"].Value = "CharlesBarker";
newUser.Properties["givenName"].Value = "Charles";
newUser.Properties["sn"].Value = "Barker";
newUser.Properties["displayName"].Value = "CharlesBarker";
newUser.Properties["userPrincipalName"].Value = "CharlesBarker";
newUser.CommitChanges();
}
}
Option 2
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, "192.168.1.1",
"CN=Users,DC=ad,DC=local", name, password))
{
using (UserPrincipal userPrincipal = new UserPrincipal(principalContext))
{
userPrincipal.Name = "CharlesBarker";
userPrincipal.SamAccountName = "CharlesBarker";
userPrincipal.GivenName = "Charles";
userPrincipal.Surname = "Barker";
userPrincipal.DisplayName = "CharlesBarker";
userPrincipal.UserPrincipalName = "CharlesBarker";
userPrincipal.Save();
}
}
I leave as an exercise to you to find out which attribute goes into which User dialog entry field :-)

DirectorySearcher defaulting to Production Active Directory even though I'm passing in the Dev LDAP (edir) Server IP

I've noticed an interesting behavior when trying to connect to edirectory using DirectoryServices.
This is the code that I used to pull information from our dev edirectory, but I've noticed that it's retrieving information from our production Active Directory (from what I've read, DirectorySearcher can be used on edir as well):
string devIP = "xxx.xxx.xxx.xxx:636";
DirectorySearcher directorySearcher = new DirectorySearcher(devIP);
directorySearcher.Filter = "(&(objectClass=user)(uid=" + "jsmith" + "))";
SearchResultCollection searchResults = directorySearcher.FindAll();
(I know it's hitting production because jsmith doesn't exist in dev. Another thing I noticed was that the attributes that were returned are AD attributes like memberOf etc.)
What finally got it to work was using System.DirectoryServices.Protocols:
LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier("d1.domain.com:636"));
con.Credential = new NetworkCredential("cn=USERNAME,ou=XXX,o=XXX", "password");
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback);
con.AuthType = AuthType.Basic;
using (con)
{
con.Bind();
}
I've done some research, but I couldn't figure out why it would have been routing the DirectorySearcher to prod even though I explicitly specified the IP address and the username?
The dev server is on a different domain from my local machine (and I'm running the code on my local machine). Could it be possible that since my machine is on the same domain as prod, it's defaulting to the prod Active Directory and just ignoring the devIP that I'm passing it?
The same root cause with the following:
Check if the DirectoryEntry is valid in DirectorySearcher
The constructor you used DirectorySearcher(string) is actually expecting the filter, but not the search root path.

Default behaviour of LDAP connection without LDAP URL

This is how I created DirectoryEntry to connect to AD server(13.198.123.456)
DirectoryEntry ldap_connection = new DirectoryEntry("LDAP://13.198.123.456/OU=Abc,DC=def,DC=ijk,DC=com", "username", "password");
But if I created DirectoryEntry without LDAP URL, It will connect to the AD that uses to logging to my PC
DirectoryEntry ldap_connection = new DirectoryEntry("", "username", "password");
Is this expected behavior? any documentation about this?
It's not particularly clear, but the version you use is another case of the default DirectoryEntry constructor, but with non-default credentials - as illustrated on this MSDN page, when you use:
DirectoryEntry ent = new DirectoryEntry();
it indicates that you bind to the domain that provides authentication for the user.
In the case of:
DirectoryEntry ldap_connection = new DirectoryEntry("", "username", "password");
The empty string implies that you bind to the domain that provides authentication to the logged in user, but using alternate credentials for the username and password.
I don't have a windows system to hand to test the difference, if any, between passing in an empty string "", as opposed to a null reference - it may barf in this situation.

Categories

Resources