Change user password on remote computer using WMI - c#

Is there a way to change a users password on a remote computer using WMI? I couldn't locate any resources on this.
I'd just like to add that we are not using active directory and I need to write my code in C#.

Well, these are VB Script examples in this Hey Scripting Guy column, but they should be translatable:
How do I change the local Administrator password for all the computers in an OU?
Set objOU = GetObject("LDAP://OU=Finance, DC=fabrikam, DC=com")
objOU.Filter = Array("Computer")
For Each objItem in objOU
strComputer = objItem.CN
Set objUser = GetObject("WinNT://" & strComputer & "/Administrator")
objUser.SetPassword("i5A2sj*!")
Next
The first part is AD based, but is just being used to find all of the machines in the domain. The second part (that does the actual remote password reset) doesn't rely on AD at all.
So, it's basically bind to WinNT://<ComputeName>/<UserName>, then call SetPassword().
And this other SO question on changing the local admin account password is already in C#:
public static void ResetPassword(string computerName, string username, string newPassword) {
DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1}", computerName, username));
directoryEntry.Invoke("SetPassword", newPassword);
}

Related

Copy files to a network drive (without Impersonation)

I have a WindowsForms vb.net Framework 4.6 Application which must copy some files from c:\sample to a network drive (\\servername\sample).
I must use other credentials to access the share. But I can't use impersonate, because i have to use a local user on the Server. The Login on the server is something like <.\Username> <password>.
Is there a way to copy my files to the network drive using the server's local user credentials?
EDIT 18.06:
I also tried this way, but it did not work for me.
Dim domain, username, passwordStr, remoteServerName As String
Dim password As New System.Security.SecureString
Dim command As New Process
domain = "servername"
username = "username"
passwordStr = "password"
remoteServerName = "\\servername\path\"
Dim impersonator As New AliasAccount(username, passwordStr)
For Each c As Char In passwordStr.ToCharArray
password.AppendChar(c)
Next
command.StartInfo.FileName = "test.pdf"
command.StartInfo.Arguments = remoteServerName
command.StartInfo.UserName = username
command.StartInfo.Password = password
command.StartInfo.Domain = domain
command.StartInfo.Verb = "open"
command.StartInfo.UseShellExecute = False
impersonator.BeginImpersonation()
command.Start()
impersonator.EndImpersonation()
My user on the server does not a have a specific domain, because the user only exists on the server. So i'm using the servername as the domain. I also tried using ".\".
I'm always getting the same error "wrong username or password" (translated from german to englisch). I've checked the username and password more than 30 times... It is definitely correct. I'm also able to connect a network drive with the credentials... So they are correct.
Do you have any other suggestions?

How to keep my application "authenticated" with an AD account? c#

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
}

The user name or password is incorrect-8009030C: LdapErr: DSID-0C090579, comment: AcceptSecurityContext error, data 52e, v3839

I am trying to connect AD with the following code
VMADProfile _VMADProfile = new VMADProfile();
DirectoryEntry searchRoot = new DirectoryEntry("Test AD LDAP Path", "DomainName\\username", "password");
directorySearcher.Filter = "(&(objectCategory=user)(samAccountName=" + username + "))";
directorySearcher.PropertiesToLoad.Add("displayname");
SearchResult one = directorySearcher.FindOne();
but it is giving me following Error
The user name or password is incorrect.
8009030C: LdapErr: DSID-0C090579, comment: AcceptSecurityContext error, data 52e, v3839
I am able to login with same user name and password if I login directly.
My AD that I am trying to connect is different from from which i am running code. Same code works fine if i change test AD to the production AD
I think this is the key:
My AD that I am trying to connect is different from from which i am running code.
It's probably trying to authenticate you on your domain, not the other one. Make sure your LDAP path includes the server to connect to for the other domain, and not just the distinguished name of the domain. The server name can just be the DNS name for the domain. It should look something like this:
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://otherdomain.com/DC=otherdomain,DC=com", "DomainName\\username", "password");
If you just use "LDAP://DC=otherdomain,DC=com", it will try to connect to the domain the current computer is joined to first. That would actually work if there is a trust between the two domains, but if not, you need to specifically give it the server name to connect to.

How to integrate LDAP in WPF application that consumes WCF service

I will start by describing how my application works today without LDAP.
I have WPF application that consumes WCF services (authentication windows or UserName depends on users choice). This services allows communication with database.
I display to user a "Login screen" in order to allow him set her "user name" and "password" then application connects to service and consumes function that checks if UserName and Password exist in database. (see img below)
Now I need also to integrate LDAP for authenticating user accounts against their existing systems instead of having to create another login account.
I'm bit ignorant about LDAP and confused about many things. Please excuse the possible use of wrong terminology.
I googled but I still don't have answers of many questions.
1- What is the relation between users that exist in my database table "User" and profiles that I should be created in LDAP ?
2- What is the check I should do to allow user come from LDAP to access to my application and use all functionnalities of my service ?
3- Should I have service type "LDAP" like other authentications types I have today in my application ("Windows" and "UserName") ?
4- If I want to update my application architecture described in picture above where should I add LDAP ?
First I am going to answer your questions one by one,
The user on LDAP is the same on DB, you can hold LDAP's Username and it's domain in your Users Table,
but the profile on the LDAP may vary with your profile table, but it can be fetched from LDAP address.
It's enough to check username and password over LDAP, just need to hold LDAP addresses in a Table (example ExternalPath) and make a relation between User and ExternalPath tables. LDAP address is contains some specifications.
Yes, you have to have a separate mechanism for identifying LDAP Users which I will explain more further.
This is not hard if everything be atomic and designed right, in further steps you may see it is easy.
So let me tell about my experience in LDAP and Authenticate users on LDAP and DB and our architecture.
I was implemented a WCF service named Auth.svc, this service contains a method named AuthenticateAndAuthorizeUser this is transparent for user which came from LDAP or anywhere.
I hope you get the clue and architecture to Authenticate user over LDAP and DB in below steps:
1- First I have a table named Users which hold users info and one more field named ExternalPath as foreign key, if it is null specify UserName is in DB wit it's password otherwise it is came from UserDirectory.
2- In second step you have to hold LDAP address (in my case LDAP addresses are in ExternalPath table), all LDAP addresses are on port 389 commonly.
3- Implementing authenticate User, if is not found(with Username and Password) then check it's ExternalPath to verify over LDAP address.
4- The DB schema should be something like below screenshot.
As you can see ExternalPath field specify user is from LDAP or not.
5- In presentation layer I am defining LDAP servers like below screenshot also
6- In the other side while adding new user in system you can define LDAP for user in my case I am listing LDAP titles in a DropDown in adding User form (if admin select LDAP address then don't need to get password and save it in DB), as I mentioned just need to hold LDAP username not password.
7- But last thing is authenticating user on LDAP and DB.
So the authenticate method is something like:
User userLogin = User.Login<User>(username, password, ConnectionString, LogFile);
if (userLogin != null)
return InitiateToken(userLogin, sourceApp, sourceAddress, userIpAddress);
else//Check it's LDAP path
{
User user = new User(ConnectionString, LogFile).GetUser(username);
if (user != null && user.ExternalPath != null)
{
LDAPSpecification spec = new LDAPSpecification
{
UserName = username,
Password = password,
Path = user.ExternalPath.Path,
Domain = user.ExternalPath.Domain
};
bool isAthenticatedOnLDAP = LDAPAuthenticateUser(spec);
}
}
If userLogin does not exist in DB by entered UserName and Password then we should authenticate it over related LDAP address.
In else block find User from Users table and get it's ExternalPath if this field was not null means User is on LDAP.
8- The LDAPAuthenticateUser method is :
public bool LDAPAuthenticateUser(LDAPSpecification spec)
{
string pathDomain = string.Format("LDAP://{0}", spec.Path);
if (!string.IsNullOrEmpty(spec.Domain))
pathDomain += string.Format("/{0}", spec.Domain);
DirectoryEntry entry = new DirectoryEntry(pathDomain, spec.UserName, spec.Password, AuthenticationTypes.Secure);
try
{
//Bind to the native AdsObject to force authentication.
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + spec.UserName + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
}
catch (Exception ex)
{
Logging.Log(LoggingMode.Error, "Error authenticating user on LDAP , PATH:{0} , UserName:{1}, EXP:{2}", pathDomain, spec.UserName, ex.ToString());
return false;
}
return true;
}
If exception raised in LDAPAuthenticateUser means User does not exist in User Directory.
The authentication code accepts a domain, a user name, a password, and a path to the tree in the Active Directory.
The above code uses the LDAP directory provider the authenticate method calls LDAPAuthenticateUser and passes in the credentials that are collected from the user. Then, a DirectoryEntry object is created with the path to the directory tree, the user name, and the password. The DirectoryEntry object tries to force the AdsObject binding by obtaining the NativeObject property. If this succeeds, the CN attribute for the user is obtained by creating a DirectorySearcher object and by filtering on the SAMAccountName. After the user is authenticated and exception not happened method returns true means user find on given LDAP address.
To see more info about Lightweight Directory Access Protocol and authenticate over it THIS Link can be useful which tells about specification more.
Hope will help you.

Get groups from Active Directory using C#

I am having issues getting the groups from Active Directory via System.DirectoryServices
Originally I started my application on a computer that was registered on the domain, but as it was a live domain I did not want to do any writes to AD what so ever, so I set up a machine with Windows XP as the host operating system, and installed windows server 2003 on a VM.
I've added another Ethernet port in the machine and set up a switch, the 1 Ethernet port is dedicated to the VM and the other port is used for the host.
After configuring the IP addresses to get them communicating I transferred my application onto the host machine and fired it up, but I was getting an DirectoryServicesCOMException.
With the message that the user name and password was invalid :( just to check that it was not active directory I created a 3rd virtual machine and installed Windows XP, which i added to the domain with the credentials tested in the APP, works a treat.
So I thought it must be because the machine where the application is running is not part of the domain.
Heres the block of code that was causing the issue:
public CredentialValidation(String Domain, String Username, String Password, Boolean Secure)
{
//Validate the Domain!
try
{
PrincipalContext Context = new PrincipalContext(ContextType.Domain, Domain); //Throws Exception
_IsValidDomain = true;
//Test the user login
_IsValidLogin = Context.ValidateCredentials(Username, Password);
//Check the Group Admin is within this user
//******HERE
var Results = UserPrincipal.FindByIdentity(Context, Username).GetGroups(Context);
foreach(Principal Result in Results)
{
if (Result.SamAccountName == "Domain Admins")
{
_IsAdminGroup = true;
break;
}
}
Results.Dispose();
Context.Dispose();
}
catch (PrincipalServerDownException)
{
_IsValidDomain = false;
}
}
The information in the login dialogue is being entered like so:
Domain: test.internal
Username: testaccount
Password: Password01
Hope someone can shed some light in this error.
Update:
After checking the Security Logs on the server i can see that my log in attempts was successful, but this is down to:
_IsValidLogin = Context.ValidateCredentials(Username, Password);
The line after where im checking the groups is causing the error, so the main issue is that the lines of code below are not working correctly from a machine thats not joined to the network:
var Results = UserPrincipal.FindByIdentity(Context, Username).GetGroups(Context);
According to your code snippet, you're failing when you attempt to create the PrincipalContext, before calling ValidateCredentials. At that point the thread running your code is still working under either a local identity (if you're in a web process) or the identity you signed onto your machine with (for a windows process). Either of these won't exist on the test.internal domain.
You might want to try the overload of PrincipalContext that includes the username and password in the constructor. See http://msdn.microsoft.com/en-us/library/bb341016.aspx
I used to do quite a bit of user management via C# .NET. I just dug up some methods you can try.
The following two methods will get a DirectoryEntry object for a given SAM account name. It takes a DirectoryEntry that is the root of the OU you want to start searching for the account at.
The other will give you a list of distinguished names of the groups the user is a member of. You can then use those DN's to search AD and get a DirectoryEntry object.
public List<string> GetMemberOf(DirectoryEntry de)
{
List<string> memberof = new List<string>();
foreach (object oMember in de.Properties["memberOf"])
{
memberof.Add(oMember.ToString());
}
return memberof;
}
public DirectoryEntry GetObjectBySAM(string sam, DirectoryEntry root)
{
using (DirectorySearcher searcher = new DirectorySearcher(root, string.Format("(sAMAccountName={0})", sam)))
{
SearchResult sr = searcher.FindOne();
if (!(sr == null)) return sr.GetDirectoryEntry();
else
return null;
}
}

Categories