Run impersonated code along with wmi management scope [C#] - c#

Scenario:
I've a remote computer without domain with a User, called hereafter Admin.
These are the steps I want to achive:
Connecting to that computer
Create a folder and give full control to Admin
Share the created folder so can be accessed via NFS
Run some code that underneath uses that directory to create temporary backup file and/or permanent ones.
So, I've used the functionalities exposed by the System.Management namespace, creating an object of type ManagementScope like this:
_managementScope = new ManagementScope($#"\\{_host}\root\cimv2",
new ConnectionOptions
{
Username = user,
Password = password
});
I think I can do the second and the third part using the Win32_Directory and Win32_Share class because they expose the ChangeSecurityPermissions and Create methods, respectively, and they seem to match my goal.
The problem to me is the last point, since the ManagementScope is configured to impersonate the user but it does no provide any object related to that so I could use it to run impersonated code.
In a nutshell, the ideal to me would be something like this:
if (_managementScope.Connected())
{
var directory = CreateDirectory(pathName);
SetFullPermission(directory, Admin); //managed with the Win32_Directory management class
ShareDirectory(directory); //managed with the Win32_Share management class
WindowsIdentity.RunImpersonated(_managementScope.Identity.AccessToken, () =>
{
//_managementScope.Identity is not available
Install(); //This method uses the directory and shall be managed by the user Admin
//so I need to run this code as Admin.
};
}
What's the best way to do so? Thanks

Related

C# Active Directory Login

Ive written a program to manage our Active Directory in c# - windows forms.
I'm stuck at the following point:
For managing the Active Directory and Commiting Changes, you have to run the program as administrator.
I want to include a login button to verify as admin and be able to commit changes without starting the .exe directly as admin.
Something like:
group.Properties["member"].Add(distinguishedName);
group.CommitChanges();
If this is not possible I was thinking it could maybe be possible to restart the program when the user has typed in his credentials and putting in the admin-credentials directly into the username and password field as parameters.
Is that possible? If not, do you have other suggestions?
Your program does not need to run as admin. You just need to connect to Active Directory using credentials that have permissions to update that group. By default, it will use the credentials that the program is running with. So it sounds like whichever credentials you are using to run as admin also has permissions to update that group.
If it helps you, you can use alternate credentials for connecting to AD by using the constructor for DirectoryEntry that accepts credentials. For example:
var group = new DirectoryEntry($"LDAP://{groupDn}", "username", "password");
group.Properties["member"].Add(distinguishedName);
group.CommitChanges();

How to use %username% variable in Active Directory user paths using C#?

I developed an Active Directory user management tool in C# that does some things automatically. This is better for my particular case than the "Active Directory Users and Computers" tool from Microsoft. I have to use roaming profiles and home directories which contain the username of a user.
Now I want to change the username of a user with my program in C#. Everything works fine, but I want to use the "%username%" variable instead of putting the new username directly into these paths (home directory and roaming profile), because, by using the variable, I ensure that the new username will be placed into these paths if I copy the user object using Microsoft's AD management tool (right click --> "copy").
If I enter "%username%" when creating or editing a user with Microsoft's AD tool, this variable gets replaced by the username, so it works. But if I put this variable into a path using C#, it just puts the string "%username%" at the end of the path (e.g. "\fileserver1\UserHomes\%username%"). It doesn't replace it with the actual username and "stores" that placeholder.
How can I use this variable within my C# code properly, so that it get's replaced with the actual username?
I'm using this code (reduced, it's just an example) to change the users username (SamAccountName) and, for example, the home directory. "user" is my UserPrincipal object. Of course, I'm renaming the actual folder after this:
[...]
string newUsername = "NewUsername"; // New username
user.SamAccountName = newUsername;
user.UserPrincipalName = $"{ newUsername }#{ domain }";
user.HomeDirectory = "\\fileserver1\UserHomes\%username%";
user.Save(); // Save changes
The %username% environment variable and the one used AD Users and Computers is not the same. AD Users and Computers replaces it with the actual username value immediately when you click OK. It is not something that AD understands, just a token used by the AD Users and Computers application.
Considering you already constructed a string using the username, you shouldn't find it hard to fix this.
user.HomeDirectory = $"\\fileserver1\UserHomes\{newUsername}";
Also powershell would probably be more appropriate for something like this.
see Renaming a User Account Does Not Automatically Change the Profile Path

Caching impersonation in a C# web application

I am working on an MVC web application which has to access files in various shared directories. Currently I am using this implementation of impersonation to access a directory, which works fine. My problem starts when I have to open multiple files from the same directory, since right now I initialize a new impersonator every time and I quickly reach the number of allowed connections to the target computer.
The code right now looks like:
// do stuff that does not require impersonation
using (Impersonation.LogonUser("domain", "username", "password", LogonType.NewCredentials))
{
// access **one** particular file
}
// continue doing stuff that does not require impersonation
However, if I use just one impersonation instance, I can access as many files as I want. This is though impractical for a web application, as I would have to run all my code under impersonation. Furthermore, different directories have different credentials (could also be located on different computers).
So my question is if it's possible to cache an impersonation, undo it for a while and re-enable it when I need to, so that I don't start a new one. Basically something like the following pseudo-code:
// do stuff that does not require impersonation
// get cached impersonation or create a new one
var imp = GetImpersonationFromStaticSharedCache("domain", "username", "password")
// access **one** particular file
// stop impersonation, but don't dispose it, so that I can re-use it later
imp.StopImpersonationForTheCurrentThread()
// continue doing stuff that does not require impersonation

create network folder C#

I have 2 machines EX001 and Ex002 on domain booleanarray.com.
I have credentials of a domain user , (username ABC and password pwd123) to log on to EX002 on domain booleanarray.com.
There is a folder C:\EWS on EX002.
I want to write a C# Windows application that runs on EX001 and creates a new folder under the folder C:\EWS\ on Ex002.
C:\EWS is not a shared folder, but is accessible by ABC when user ABC logs on to EX002.
Can anyone suggest how can I create such a new folder on different computer on same domain in C#?
I won't be able to give you detailed answer. But you probably can first authenticate your windows application on other machine 2 in the domain. Probably you can use ConnectionOptions object. Then you probably can send the command for creating directory to remote machine.
If c:\ews is not a shared folder, I believe you would need to do it via the administrative share. So something like:
String path = #"\\EX002\c$\EWS\";
Directory.CreateDirectory(path + "new folder name");
As the name implies though, this requires that the account have administrative access to EX002.

Can you make a C# program which can not access to any local file system other than the folder it is installed?

Let me ask whether you can make a C# program which can not access to any local file system other than the folder/sub folders it is installed? And how if the answer is yes.
For details if it is necessary and possible, users are supposed to accept the program runs under a special user account which is restricted the file access only to the folder it is installed and to sub folders.
Thank you very much in advance.
Edit : Let me add context. I want users do not change their accounts. And as a programmer I do not have complete control over the program for some reasons.
Can you make a C# program which cannot access any part of the local file system other than the directory in which it is installed?
No, because every C# program will need to at the very least have access to the .NET runtime libraries, which are in the Windows install directory.
My suggestion to you is that you look into isolated storage. The .NET code access security system enables you to set a policy which states that certain programs only get to access the .NET runtime, the installed location of the code, and a special "isolated storage" directory that can be used for the application to store per-user data.
The answer is yes, but how you do this is complicated.
First, you need a user account with extremely limited permissions. It must be able to access files and run programs within the installation directory of the program, and that's pretty much it. You can create such a user with the installer program, using tools in the System.DirectoryServices namespace. Here's an example of creating a user:
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
private void CreateUser(string userName, string password)
{
DirectorySearcher dseSearcher = new DirectorySearcher();
string rootDSE = dseSearcher.SearchRoot.Path;
string userDSE = rootDSE.Insert(7, "OU=Users,");
DirectoryEntry userDE = new DirectoryEntry(userDSE);
DirectoryEntry user = userDE.Children.Add("CN=" + userID, "user");
staff.Properties["samAccountName"].Value = userID;
staff.Properties["UserPrincipalName"].Value = userName +
#"#domain";
staff.CommitChanges();
staff.Properties["userAccountControl"].Value =
ActiveDs.ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT |
ActiveDs.ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD;
staff.CommitChanges();
staff.Invoke("SetPassword", new Object[] { password });
}
Now, once that's happened, you need to make sure your program normally runs in the context of that user account. You can do that by specifying the user account to run the program with in a ProcessStartInfo object, used by a "bootstrapper" program that is what you create shortcuts for. You can then also ensure the program is running in the context of that user account using Environment.CurrentUser, and abort execution of the program if it is being run by any more permissive account.
If you just want to restrict the program to a particular account, you can look up the user's credentials, and exit the program if it's not the right one.
http://msdn.microsoft.com/en-us/library/csyx45b8.aspx
http://msdn.microsoft.com/en-us/library/sfs49sw0.aspx

Categories