Searching through Active Directory in C# and authenticate with current User - c#

What i want to do:
A user is logged into my application with his user credentials. A form has as input only a pNumber, the application should search through active directory to find the user with that number and fill out different input fields automatically (in this example only name and email).
What i already have (C# Code, .Net 4.0):
public static string[] getUser(string pNumber) {
string[] user = new string[4];
NetworkCredential credential = CredentialCache.DefaultNetworkCredentials;
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string DomainPath = "LDAP://DC=****,DC=com";
string strAccountId = userName;
string strPassword = "******";
DirectoryEntry adsEntry = new DirectoryEntry(DomainPath, strAccountId, strPassword);
DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry);
adsSearcher.Filter = "(pNumber=" + pNumber + ")";
SearchResult adsSearchResult = adsSearcher.FindOne();
if (adsSearchResult != null) {
user[0] = adsSearchResult.Properties["sAMAccountName"][0].ToString();
user[1] = adsSearchResult.Properties["mail"][0].ToString();
}
return user;
}
If i put in values for strAccountId and strPassword i know have access to AD this works fine. But this is only a workaround. I would like to authenticate to AD with the current users credentials. I can get the current username, but i think it is not possible to get the password. So i looked into other posibilitys to authenticate with DirectoryEntry here. (What i didnt really get was the DirectoryEntry(Object) Constructor)
My Question:
Are there alternatives i can search through AD with C# with the current user credentials?

DirectorySearcher ds = new DirectorySearcher(de);
if the current user has access it will be good if not you get an exception.

Related

Active Directory The object already exists using SetPassword and new object []{}

I'm hoping someone can explain this to me because I am at my wits end trying to resolve an issue I am having.
The error I am receiving is, "The object already exists." whenever I am trying to run our ResetPassword function. The weird thing is I ONLY receive this error if the user account has had their password reset before. If I either create a new account with a new password, or search for a user in the database that has not had the ResetPassword function called on their account, then it will let me call this function once. Note: On this one time it lets me run the function the password does get reset. Any account that has already run ResetPassword will prompt the object already exists error.
public static void ResetPassword(DirectoryEntry User, string password)
{
User.Invoke("SetPassword", new object[] { password });
User.Properties["LockOutTime"].Value = 0x0000; //unlock account
int val = (int)User.Properties["userAccountControl"].Value;
User.Properties["userAccountControl"].Value = val & ~0x2; //Enable account
User.CommitChanges();
Logs.CreateLogEntry("Reset password", User);
User.Close();
}
As you can see, we are passing in a DirectoryEntry user, along with a generated new password. We are using an anonymous login on the backend of our IIS website to have admin credentials high enough to use SetPassword.
You need to provide credentials of a user in AD that has admin privileges to reset passwords.
public static void ResetPassword(string username, string password)
{
string adminUser = "YourAdminUserIdInAD";
string adminPass = "YourAdminUserPasswordInAD";
string ldapString = "LDAP://YourLDAPString";
DirectoryEntry dEntry = new DirectoryEntry(ldapString , adminUser, adminPass, AuthenticationTypes.Secure);
DirectorySearcher deSearch = new DirectorySearcher(dEntry) {
SearchRoot = dEntry,
Filter = "(&(objectCategory=user)(cn=" + username + "))"
};
var directoryEntry = deSearch.FindOne();
directoryEntry.Invoke("SetPassword", new object[] { password });
directoryEntry.Properties["LockOutTime"].Value = 0x0000; //unlock account
int val = (int)directoryEntry.Properties["userAccountControl"].Value;
directoryEntry.Properties["userAccountControl"].Value = val & ~0x2;
directoryEntry.CommitChanges();
Logs.CreateLogEntry("Reset password", User);
directoryEntry.Close();
}

Can I get a users identity from an email address instead of a username in ASP.NET Core and DirectoryServices?

I have a need for users to be able to enter an email address and after clicking submit, I'd like to get some Active Directory information from the email address entered. Our usernames unfortunately don't follow a single naming convention so I'm unable to reliably guess the username from the email address entered.
I'm fine with looking up information from a username, is there a way of back tracking to an identity from an email address entered using DirectoryServices or something similar?
Thanks,
Edit and some clarifiction:
On the form, it's not the authenticated users email address that I would be looking up, it's the email address that was entered in the form.
public class ManagerDetails
{
public string GetManagersName(string emailAddress)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal mgr = UserPrincipal.FindByIdentity(ctx, emailAddress);
string managersName = mgr.GivenName;
return managersName;
}
}
This piece of code works, but only if the username matches the email address.
For example, this works, Joe.Bloggs#mydomain.com (email) and Joe.Bloggs (domain username).
If it's Joe.Bloggs#mydomain.com (email) and BloggsJ (domain username) it fails.
This is obviously because it's using the email address as the full username but hopefully it explains what I'm trying to achieve.
I'm still refining but this works, it allows me to use directory searcher to get a name from an email address entered.
public string GetManagersName(string emailAddress)
{
string userName = GetManagersUserNameByEmailAddress(emailAddress);
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal usr = UserPrincipal.FindByIdentity(ctx, userName);
string managersName = usr.GivenName;
return managersName;
}
private string GetManagersUserNameByEmailAddress(string emailAddress)
{
DirectorySearcher adSearcher = new DirectorySearcher();
adSearcher.Filter = ("mail=" + emailAddress);
adSearcher.PropertiesToLoad.Add("samaccountname");
SearchResult result = adSearcher.FindOne();
DirectoryEntry user = result.GetDirectoryEntry();
string userName = user.Properties["samaccountname"].Value.ToString();
return userName;
}

Given user id, password, and a subdomain name, how can I obtain the top-level domain name for a user account?

I'm writing a program (WinForms, C#) that runs on a Win 7 client machine. It obtains credentials from the user (user id, password, and subdomain name) and uses them to authenticate (via Active Directory) to other servers to which the program remotely connects. The other servers are on a domain different than the domain the Win 7 client machine is on.
Using the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes, I can test the credentials with no more than the user id, password, and subdomain name (See answer for S.O. Why does Active Directory validate last password?).
For example, for the user account ssmith#xyz.gov, I need only provide ssmith (user id), the password for ssmith, and xyz (the subdomain). I don't need to provide the top-level domain name (gov in this case).
Now, I want to programmatically obtain the top-level domain name for this user account (gov in this case). I've examined the properties and methods of the NetworkCredential, LdapDirectoryIdentifier, and LdapConnection classes. I've looked over the other classes in the System.DirectoryServices.Protocols Namespace. I don't see a way to programmatically obtain the top-level domain name.
Given user id, password, and subdomain name, how can I obtain the top-level domain name for a user account?
Here is my code. Given a user account of ssmith#xyz.gov, My call looks like this (asterisks represent a SecureString password)
bool result = ValidateCredentials("ssmith","******", "xyz");
Here is my code for the method.
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, SecureString ssPassword, string domain)
{
//suports secure string
NetworkCredential credentials = new NetworkCredential(username, ssPassword, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using (LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
// The only way to test credentials on a LDAP connection seems to be to attempt a
// Bind operation, which will throw an exception if the credentials are bad
connection.Bind();
}
catch (LdapException lEx)
{
credentials = null;
id = null;
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
}
credentials = null;
id = null;
return true;
}
After a successful bind, then the full DNS name of the domain will be in the LdapConnection object:
var domain = connection.SessionOptions.DomainName;
In this case, that would be "xyz.gov". If you need just "gov", then you can just take everything after the last dot:
var tld = domain.Substring(domain.LastIndexOf('.') + 1);

connect active directory using c#

i m trying to connect to Active Directory code that i have used
string domain = "domain.com.pk";
string container = "DC=mycompnay,DC=com,DC=pk";
string Admin = "salman.zafar";
string Password = "password";
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain, container, Admin, Password))
{
string userPrincipalName = "dotnettest" + "#" + domain;
// validate the credentials
bool isValid = pc.ValidateCredentials(userPrincipalName, "Ascertia 12");
if (isValid) {
UserPrincipal up = UserPrincipal.FindByIdentity(pc, IdentityType.UserPrincipalName, userPrincipalName);
}
code works fine when the code running on machine which is in domain but if i try to connect to the AD machine that is remote then i get error
i tried to use
string domain = "192.168.0.150:389/domain.com.pk";
then it didn't work and validate credentials method always return false can some one help me how can i connect to remote active directory using IP with port with PrincipalContext or i have to use directory entry
any help will be appreciated
First note:
code works fine when the code running on machine which is in domain
In this case, you do not need to provide adminuser+pw in the PrincipalContext constructor if the machine is a domain member (which I assume here).
If you want to connect to any other AD server (domain controller) with no trust between the foreign domain and the current domain, use the IP address or server name as the "domain" name:
string domain = "192.168.0.150";
If your goal is to just check if credentials are valid, you can even omit the admin user + pw:
string domainController = "192.168.0.150";
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainController))
{
string userPrincipalName = "dotnettest" + "#" + domain;
// validate the credentials
bool isValid = pc.ValidateCredentials(userPrincipalName, "Ascertia 12");
}
In this case, however, you cannot have
UserPrincipal up = UserPrincipal.FindByIdentity(...
because the PrincipalContext itself is not logged on.
You can also see my answer in a similar question: https://stackoverflow.com/a/28690682/4547223
or this SO article Validate a username and password against Active Directory?

How do I see the password of the current user, c#

I am developing a program in C# and I am having a problem with Windows credentials.
I need the program return me the user name and password.
using (WebClient client = new WebClient())
{
string[] user = Convert.ToString(WindowsIdentity.GetCurrent().Name).Split('\\');
string userName = user[1];
label1.Text = userName.ToString();
label2.Text = passwd.ToString();
//client.Credentials = new NetworkCredential(userName, "1234"); //1234 = password
//client.DownloadFile("http://**intranet**/servicosuporte/Documentos%20Partilhados/assistente_remoto.zip", #"C:\assistremoto.zip");
}
You are unable to access a user's password in this way. Passwords are not reversible, and are hashed. This is a one-way operation.
If you want to use the user's existing credentials, you can use:
System.Net.CredentialCache.DefaultNetworkCredentials //for network
and
System.Net.CredentialCache.DefaultCredentials //for local

Categories