Executing Bind to LDAP instance using .NET LDAPConnection - c#

I'm trying to execute the binding to an LDAP instance using .NET Objects.
Sorry but this is the first time I fight against this kind of enemy (and hope it will be the last one as well!).
This is what I actually do:
LdapDirectoryIdentifier serverId = new LdapDirectoryIdentifier(primaryIP, securePort);
NetworkCredential credentials = new NetworkCredential(username, password);
using (LdapConnection conn = new LdapConnection(serverId, credentials))
{
try
{
//conn.SessionOptions.ProtocolVersion = 3;
conn.SessionOptions.SecureSocketLayer = true;
conn.AuthType = (AuthType)authType;
conn.Bind();
Console.WriteLine("OK!!");
}
catch (LdapException lex)
{
Console.WriteLine($"Errore {lex.ErrorCode}: {lex.Message}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Where:
primaryIP is the name of LDAP instance
securePort is 636
username and password are absolutely correct (I've checked them logging in into the intranet)
I've found many examples, and everything seems pretty plain and simple. Anyway I can't make through it.
Tried also with all the AuthTypes available, with no luck.
As said, the user exists because I've been able to log into different apps that use this kind of authentication.

Ok, got it on my own.
The username must be set with the full DN.
Now it works correctly.

Related

LDAP signing and channel binding for the march 10th update in c# .net

I have been looking into how these two new settings will effect with our c# code that connects to an ldap server and performs user lookups
Using the code below to connect to an AD i have found a few scenarios that no longer work when these settings are switched on.
private static LdapConnection ConnectAndBind(
string server,
int port,
int timeout,
string userName,
string pwd,
AuthType authType,
bool useSSL,
bool useV3)
{
var con = new LdapConnection(new LdapDirectoryIdentifier(server, port));
if (useSSL)
{
con.SessionOptions.SecureSocketLayer = useSSL;
con.SessionOptions.VerifyServerCertificate = VerifyServerCertificate;
con.SessionOptions.QueryClientCertificate = QueryClientCertificate;
}
con.Timeout = new TimeSpan(0, 0, timeout);
con.SessionOptions.ProtocolVersion = useV3 ? 3 : 2;
try
{
con.AuthType = authType;
con.Credential = new NetworkCredential(userName, pwd);
con.Bind();
}
catch (Exception e)
{
throw new ProviderException(
ProviderException.ErrorIdentifier.AuthenticationFailed,
LanguageLogic.GetString("AuthenticationProvider.ConnectError"),
e.Message);
}
return con;
}
This is used in the context of a webforms/mvc asp.net (4.5) app once connected its used to import user details in the the app
but at the moment depending on how the registry keys for the two settings on the AD server are set i am finding some situations where it does not connect (the error returned is that the supplied credentials are invalid)
The first two tables are kinda how i expected it to work with non signed/non ssl basic bind not working
how ever i cannot find a reason why when the Channel binding is set to required (table 3) it does not work for the other 3 red entries
Has any one else been working on this that could shed some light on the matter. would newer version of .net support this setting.
Thanks for any info
UPDATE 1
so i downloaded Softerra LDAP browser. i get the same results using that . so i dont think its my code.
as soon as i turn on the reg key for Channel Binding i get the specified credentials are invalid for those connection methods over SSL
i have updated the AD server with all the latest patches but no change.

Trying to test LDAP-based authentication from forumsys?

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).

Login Validation Against Active Directory C# - Visual Studio for Mac

I'm trying to validate credentials in Active Directory for an MVC Web App (C#) I'm writing on Visual Studio for Mac. In searching for answers, I've noticed a lot of NotImplementedExceptions and other strange occurrences.
Here is a (non-comprehensive) list of things I've tried and things that have failed.
First, this code:
string domainAndUsername = domain + #"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
//Bind to the native AdsObject to force authentication.
// This line throws a not implemented exception
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(SAMAccountName=" + username + ")"
};
search.PropertiesToLoad.Add("cn");
Console.WriteLine(search.ToString());
SearchResult result = search.FindOne();
//Debug.WriteLine(result.ToString());
if (null == result)
{
return false;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
The line object obj = entry.NativeObject throws a NotImplementedException I've tried commenting out the line (since obj is never used elsewhere) but to no avail. I have tried other variations of very similar code as well.
The other route I attempted was this code:
var creds = new NetworkCredential(username, password);
var srvid = new LdapDirectoryIdentifier(adPath);
// This line throws a NotImplementedException
var conn = new LdapConnection(srvid, creds);
try
{
conn.Bind();
}
catch (Exception)
{
return false;
}
conn.Dispose();
return true;
And other variations of the same idea. The line var conn = new LdapConnection(srvid, creds); throws a NotImplementedException
Finally, I went a simpler route and used
Membership.ValidateUser(model.username, model.password)
Since this is a Web Api and I am using a controller. This requires some code in the Web.config available here. It, too, throws a NotImplementedException.
So do these three common methods all rely on the same underlying function that hasn't been implemented in VS for Mac yet? Or is there something I'm missing? Also if there is any workaround someone could offer, it would be very well appreciated.
Thanks!
First of all you need to reference Microsoft.DirectoryServices.dll
System.DirectoryServices
Second, you need to run this queries with a domain account with grant access. Take care when you publish in server (iis) or other because the service run this code and it had to have permission on it.
check this
Sorry for my English :D
Thanks to the kind efforts of Javier Jimenez Matilla, bnem, and Lexi Li, I got it to work. Part of the issue was a configuration problem that I unfortunately can't disclose (confidentiality and whatnot), but here's the final code:
using Novell.Directory.Ldap;
try
{
var conn = new LdapConnection();
conn.Connect(domain, 389);
conn.Bind(LdapConnection.Ldap_V3, $"{subDomain}\\{username}", password);
return true;
}
catch (LdapException ex)
{
Console.WriteLine(ex.Message);
return false;
}
A few things to note. First, that LdapConnection is actually Novell.Directory.Ldap.LdapConnection. Secondly, the hard-coded 389 is the port which LDAP likes to communicate over. Thirdly, the parameter $"{subDomain}\\{username}" is the way the username appears to AD. For my particular case, the domain is of the form "corporation.mycompany.com", and in AD the username appears corporation\username. So in this example, string subDomain = "corporation"

Caching an LDAP connection

I'm working on an internal portal that allows users to request accounts to various other domains we manage.
I have to check if a certain account already exists via LDAP, so this is what I do to make a connection.
const string server = "ldap.mydomain.net:636";
using (var ldapSSLConn = new LdapConnection(server))
{
var networkCredential = new NetworkCredential("user", "supersecurepassword");
ldapSSLConn.SessionOptions.SecureSocketLayer = true;
ldapSSLConn.AuthType = AuthType.Basic;
ldapSSLConn.SessionOptions.SecureSocketLayer = true;
ldapSSLConn.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((con, cer) => true);
ldapSSLConn.Bind(networkCredential);
// Search happens here
// Return results
}
I can then use the ldapSSLConn to search for existing accounts.
Everytime I need to make a connection, it takes me +/- 20 seconds, the search itself 85ms.
Is there a way I can cache the connection? For example open it on Application_Start(), and then reference it when I need it?
I found a solution that works for me.
I'm checking if a connection is cached now, with;
if (System.Web.HttpContext.Current.Cache["LDAPConnection"] == null)
If it isn't, I make a connection (see question), and at the end, cache it:
System.Web.HttpContext.Current.Cache.Add("LDAPConnection", ldapSSLConn, null, DateTime.Now.AddHours(8), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);

Create Active Directory user in .NET (C#)

I need to create a new user in Active Directory. I have found several examples like the following:
using System;
using System.DirectoryServices;
namespace test {
class Program {
static void Main(string[] args) {
try {
string path = "LDAP://OU=x,DC=y,DC=com";
string username = "johndoe";
using (DirectoryEntry ou = new DirectoryEntry(path)) {
DirectoryEntry user = ou.Children.Add("CN=" + username, "user");
user.Properties["sAMAccountName"].Add(username);
ou.CommitChanges();
}
}
catch (Exception exc) {
Console.WriteLine(exc.Message);
}
}
}
}
When I run this code I get no errors, but no new user is created.
The account I'm running the test with has sufficient privileges to create a user in the target Organizational Unit.
Am I missing something (possibly some required attribute of the user object)?
Any ideas why the code does not give exceptions?
EDIT
The following worked for me:
int NORMAL_ACCOUNT = 0x200;
int PWD_NOTREQD = 0x20;
DirectoryEntry user = ou.Children.Add("CN=" + username, "user");
user.Properties["sAMAccountName"].Value = username;
user.Properties["userAccountControl"].Value = NORMAL_ACCOUNT | PWD_NOTREQD;
user.CommitChanges();
So there were actually a couple of problems:
CommitChanges must be called on user (thanks Rob)
The password policy was preventing the user to be created (thanks Marc)
I think you are calling CommitChanges on the wrong DirectoryEntry. In the MSDN documentation (http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentries.add.aspx) it states the following (emphasis added by me)
You must call the CommitChanges method on the new entry to make the creation permanent. When you call this method, you can then set mandatory property values on the new entry. The providers each have different requirements for properties that need to be set before a call to the CommitChanges method is made. If those requirements are not met, the provider might throw an exception. Check with your provider to determine which properties must be set before committing changes.
So if you change your code to user.CommitChanges() it should work, if you need to set more properties than just the account name then you should get an exception.
Since you're currently calling CommitChanges() on the OU which hasn't been altered there will be no exceptions.
Assuming your OU path OU=x,DC=y,DC=com really exists - it should work :-)
Things to check:
you're adding a value to the "samAccountName" - why don't you just set its value:
user.Properties["sAMAccountName"].Value = username;
Otherwise you might end up with several samAccountNames - and that won't work.....
you're not setting the userAccountControl property to anything - try using:
user.Properties["userAccountControl"].Value = 512; // normal account
do you have multiple domain controllers in your org? If you, and you're using this "server-less" binding (not specifying any server in the LDAP path), you could be surprised where the user gets created :-) and it'll take several minutes up to half an hour to synchronize across the whole network
do you have a strict password policy in place? Maybe that's the problem. I recall we used to have to create the user with the "doesn't require password" option first, do a first .CommitChanges(), then create a powerful enough password, set it on the user, and remove that user option.
Marc
Check the below code
DirectoryEntry ouEntry = new DirectoryEntry("LDAP://OU=TestOU,DC=TestDomain,DC=local");
for (int i = 3; i < 6; i++)
{
try
{
DirectoryEntry childEntry = ouEntry.Children.Add("CN=TestUser" + i, "user");
childEntry.CommitChanges();
ouEntry.CommitChanges();
childEntry.Invoke("SetPassword", new object[] { "password" });
childEntry.CommitChanges();
}
catch (Exception ex)
{
}
}

Categories

Resources