I'd like to ask for your help with following issue. I am working on ad-hoc application, will be used only by me and just once.
Part of it is to perform password reset for over 3000 users in AD and send them new credentials.
I can read from AD as normal user, but I have to use privileged account to modify it. How can I do that? I know, I can use PowerShell and have it done in a seconds, but I'd like to learn how to do it in C#.
My code to search for user is simple
public class ADSecurity
{
public static string getUserName(string sam)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam);
return user.Name;
}
}
How can I do the same, but as different user?
I've seen some guides, but none of them explained a-z ... just advice on how to impersonate, but nothing about how to use it. There was one article here about impersonation, but using LDAP protocol (DirectoryEntry). But as I understand, it is really slow.
Any advice appreciated. I need to run it 2 days from now, so in worst case scenario I use PowerShell to do it.
Thanks.
There are a few ways to do it:
Run your application under the needed credentials (Shift+right-click on the .exe file and use 'Run as a different user'). If you're just doing this once, this is the easiest.
Use the PrincipalContext constructor that accepts a username and password.
public class ADSecurity
{
public static string getUserName(string sam)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, username, password);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, sam);
return user.Name;
}
}
Use the DirectoryEntry constructor that accepts a username and password.
I'm not sure which example you're talking about that's "slow", but in my experience, using DirectoryEntry directly is almost always faster, as long as you use it correctly. The System.DirectoryServices.AccountManagement namespace (what you are using in your example) uses DirectoryEntry behind the scenes anyway.
Of course, options 2 & 3 require you know the password.
Related
I am pretty new to C#
I have been using Powershell scripts to code things like Unlocking an AD user or Enabling/Disabling an account. however, I do this with a different account, so I will log in with the admin account (Get-Credential) and storing it as '$cred' for example.
I am currently trying to do a similar thing in C# and I have found how to effectively "Authenticate"
But I am not sure how to store that Authentication, or have my app Authenticated to do things with it like Disable or Unlock an AD Account.
I have this:
public bool ADauthenticate(string username, string password)
{
bool result = false;
using (DirectoryEntry _entry = new DirectoryEntry())
{
_entry.Username = username;
_entry.Password = password;
DirectorySearcher _searcher = new DirectorySearcher(_entry);
_searcher.Filter = "(objectclass=user)";
try
{
SearchResult _sr = _searcher.FindOne();
string _name = _sr.Properties["displayname"][0].ToString();
MessageBox.Show("authenticated!");
result = true;
this.Close();
}
catch
{
MessageBox.Show("Incorrect credentials");
this.ADUsername.Text = "";
this.ADPwd.Text = "";
}
}
return result; //true = user Authenticated.
}
Which just tells me that the account is correct of course, but doesn't keep my application "authenticated", any ideas?
It's not accurate to say that your "application" was authenticated. All that was authenticated is a single network connection to your domain controller. As soon as _entry is destroyed, you lose that authentication.
If you want everything to happen using those credentials, then you have several options, ranging from easy (for you) to more difficult:
Have your users run your application under the credentials they need. Then you don't need to bother getting their username and password or setting the username and password on the DirectoryEntry object. Users can do this by:
Using Shift + right-click on the application icon and click "Run as a different user", or
Create a shortcut to: runas.exe /user:DOMAIN\username "yourapplication.exe". This will open a command window asking for the password, then start your application under those credentials.
You still ask for the username and password, but restart your application under those credentials using Process.Start().
Keep the username and password variables alive for the life of the application and pass them to every DirectoryEntry object you create in your application.
Options 1 and 2 require the computer that you're running this from is joined to the same or trusted domain as the domain you are connecting to. But since I see you're not specifying the domain name, I'm guessing that's the case.
You can do this a lot easier by using the System.DirectoryServices.AccountManagement assembly and namespace.
Add a reference to the System.DirectoryServices.AccountManagement assembly to your project, and then use this code to validate username/password against AD:
using System.DirectoryServices.AccountManagement;
// create the principal context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YourDomain"))
{
bool accountValidated = ctx.ValidateCredentials(userName, password);
// do whatever you want to do with this information
}
I have tried to look for the c# code example to see how the AD service account is created but not much luck. Anyone can provide an example code for creating AD service account please?
I have tried UserPrincipal with $ at the end of the name but not much luck. Errors with Access Denied (Cant create under root MyDomain or under a CN)
// Domain Context to use specific LDAP path.
domainContext = new PrincipalContext(ContextType.Domain, domainContext.ConnectedServer, "CN=Managed Service Accounts,dc=mydomain");
UserPrincipal userAccount = new UserPrincipal(domainContext)
{
DisplayName = userName,
SamAccountName = $"{userName}$",
Description = description
};
userAccount.Save();
Little late, but I think I can answer it. You need to use the NetAddServiceAccount function through logoncli.dll. I hadn't been able to get it to work in PowerShell, even with adding what I thought was an appropriate type shim, but I just came across a module that seems to work.
https://github.com/beatcracker/Powershell-Misc/blob/master/Use-ServiceAccount.ps1
The C# code for the type definition in that script should have everything you need to implement it for yourself.
To preface, I've got a client and a server program, and the client connects to the server over SSL.
I'm looking for a way to verify, with Active Directory, a PrincipalContext or UserPrinicpal that is passed to a server, over the SSL tunnel. This is to verify the identity of the client. Does anyone know how I would go about doing this?
Or, does anyone know of a different/simpler way of doing this?
NOTE: If you want to authenticate user using LDAP, then it has already been answered here for how to "Validate a username and password against Active Directory?".
From what I can understand from your question, you simply want to search if the user exists in AD.
On the basis of my assumption, I've given a similar answer here on C# PrincipalContext only changes password for some users, not all, but that is a step-ahead of what you require. The subset of that answer answers your queries.
Sample code:
try
{ // assuming _userID is the user-id to be checked in AD.
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, "domain.name", "DC=domain,DC=name", ContextOptions.SimpleBind, "bindUserID", "bindPassword");
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, _userID);
if(null != oUserPrincipal){
// user-id found and valid, continue further.
// If you want to authenticate user, go as per NOTE section in my answer instead.
}
else{
// return the message that the user-id could not be found.
// preferably the user-id should be **SamAccountName**
}
}
catch (Exception e)
{
message = e.ToString();
}
EDIT (Based on your comment):
J. Doe -> Despite the flack this might get me...It's going to be a broker
between a DMZ and clients on an internal network.
It seems you're looking for something like ADFS. Read more about ADFS from MSDN.
In my application I have the following method which I pass the AD Username to it (example: Domain1\User1) and try to find the groups that user belongs to in Active Directory.
public ActionResult Login(string userName)
{
PrincipalContext up = new PrincipalContext(ContextType.Domain);
UserPrincipal users = UserPrincipal.FindByIdentity(up,IdentityType.SamAccountName, userName);
PrincipalSearchResult<Principal> groups = users.GetGroups();
IEnumerable<string> userGroupList = groups.Select(p =>p.SamAccountName);
return userGroupList ;
}
The code works perfectly, However I am guessing this code will failto work in an environment where there are multiple domains.
For example:
1) User try to login with (Domain1\User1) and it will go through,
2) User try to login with (Domain2\User2) the app will try to look for User2 in Domain1, and becasue there is no such user in Domain1 it will fail.
Is this true? if yes, how can I resolve this issue so it will works with multiple domain?
I needed to pass the Domain name as the second parameter when trying to instantiate new PrincipalContext object.
By doing this I was able to retrieve user groups with multiple domains.
So the code will be something like this :
PrincipalContext up = new PrincipalContext(ContextType.Domain,"DomainName");
I have a .NET 3.5 web application that uses the System.DirectoryServices.AccountManagement classes. When I search for some users I get a PrincipalOperationException: A referral was returned from the server. If I did this the old school way with my own LDAP code I could enable chasing of referrals. Do I need to rewrite my code?
My code looks like this:
using (var principalContext = new PrincipalContext(ContextType.Domain, null, adPath))
{
// Find the principal object for which you wish to enumerate group
// membership.
using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, identity))
{
if (userPrincipal != null)
{
Name = userPrincipal.DisplayName;
DistinguishedName = userPrincipal.DistinguishedName;
EmailAddress = userPrincipal.EmailAddress;
Sid = userPrincipal.Sid.Value;
}
}
}
My adPath can be one of 2 values. One of the values is a domain that was recently joined, and can be accessed using different tools. I believe this is a problem with how this .NET library makes the LDAP calls.
Here is a partial Answer, as it's too long for a comment.
According to this Microsoft documentation, as you even know, Referrals are a hint that the client can chase. But concerning RODC they add "For example, in the case of an LDAP application, if chase referrals is enabled on the LDAP connection between the client and the RODC, the application never knows that the client received a referral from the RODC. The client is automatically redirected to the writable domain controller that is specified in the referral. ".
So I look how to enable LDAP chasing on a connexion in Microsoft site and I found this which means ADSI use. I'am very interested in the answer.
Do you try to query the global catalog like this :
/* Retreiving a principal context
*/
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "YourGCServer:3268", "dc=dom,dc=fr", "User", "Password");
It's supposed to contains all the forest domain's datas.
I hope it helps.
Have you tried code of the form(put the domain in as the second argument):
var principalContext = new PrincipalContext(ContextType.Domain, "office.local", "OU=Users, DC=office, DC=local" ))
Also make sure that the adPath is from most specific to least specific.