I have a process which is running within the security context of the local system account. From C#, how can I start (using System.Diagnostics.Process.Start) a process that will run within the security context of the currently logged-on user, not the system account?
You will need to get the token of the currently logged on user and call CreateProcessAsUser. Remember with Fast-User-Switching (FUS) that more than one user may be logged in to the machine, so you have to choose which user you want to start your process as.
To enumerate user sessions, you can use the WTSEnumerateSessions API. Use this to find the current session identifiers. Then call WTSQueryUserToken to obtain the token for the currently logged on user. Using this token, you can use CreateProcessAsUser.
Unfortunately, you will need to use P/Invoke for all of these method calls. If you have the username and password, then you can simply use the System.Diagnostics.Process.Start() overload.
I believe you will need to determine/evaluate which session is logged on locally - see WTSEnumerateSessions/WTSQuerySessionInformation/WTSQueryUserToken and I think you are limited to the Win32 API, I don't think theres a managed code wrapper.
Related
I'm looking for an idea/solution that works on both Windows 8.1 and Windows 10.
For critical actions in my WPF application I want the user to explicitly authenticate again against the operating system. On the web exist many custom solutions like NuGet packages that look like a Windows prompt that asks the user for the password. Like the one that shows up, if you try to access network shares you are not authenticated for.
I do not want to use those custom solutions, I want to use those provided by operating system, if any.
In detail I'm looking for something like that:
WindowsIdentity identity = WindowsIdentity.GetCurrent();
bool authenticated = [WindowsAPI].Authenticate(identity);
if(authenticated)
{
//do critical action
}
Does such a WindowsAPI exist?
EDIT: The WindowsAPI should have an own UI and, in best case, validates the credentials and does not store any.
bool authenticated = [WindowsAPI].Authenticate(identity);
Should it ask user to enter the login and password one more time?
You can try to use Windows API LogonUser. However it does not supply UI to collect user name and password. You can try to create a dialog form with two textboxes by your own.
There is IPublicClientApplication and AcquireToken* methods. Please check the AcquireTokenByIntegratedWindowsAuth.
However it works with AzureAD to get the token so I'm not sure does it fit to your requirements.
The overall answer is: No, there does not exist a Windows API that includes an own UI and additionally validates the entered credentials.
In short, I used CredUIPromptForWindowsCredentials that pops up a configurable operation system dialog to enter credentials, CredUnPackAuthenticationBuffer to unpack credentials and LogonUser to verify the unpacked credentials. At the end clear the memory allocated by CredUIPromptForWindowsCredentials using CoTaskMemFree.
MS Docs:
CredUIPromptForWindowsCredentials
CredUnPackAuthenticationBuffer
LogonUser
CoTaskMemFree
P/Invoke Docs:
CredUIPromptForWindowsCredentials
CredUnPackAuthenticationBuffer
LogonUser
CoTaskMemFree
... I want to grant a user temporary administrator privileges via a c# tool. Therefore i add the user to the administrator user group (a background service running under a priviledged service account does this job). However the changes take effect only in new sessions (after Logout/Re-Login) which is inconvenient. Is there a way to refresh the current user profile?
I tried several ways:
via .net Process.Start with the LoadUserProfile property set to true.
The next try was via the LoadUserProfile Win32 Api function.
Another way was to impersonate the user with .net Impersonation
All these tries did not recognize the privilege changes. It seems to me that there is a cached user profile which contains the old privileges and is reloaded anytime.
The only way where a new profile was loaded is the runas command, however we do not want the user to play around with the command line, it should be possible to do this from .net
Would be great if there is another option? Maybe there is a tool which can do this?
Ok, I relog-in the user and then I use the new Token in combination with e.g. CreateProcessAsUser to run a new process using the new token. Is there a way to assign the current windows environment the new token and therefore all actions the user performs in windows use this new token? Otherwise I could use some other tools like lsrunas to open a process in a new context and avoid the winapi calls.
From our perspective we trust the user (it is his private notebook), he can install the software he wants (for a temporary time), but we document each installation with our tool. We are running a service with local admin account in background which manages the group assignment and system documentation.
When logging on to Windows a security token is created, where all group memberships (including nested memberships) are resolved. The token contains a list of SIDs (Security Identifiers) that the user belongs to. The first SID is the user's own ID and then any groups and roles (such as interactive log on, everyone etc) are included. This means that group membership doesn not have to be looked up against the user DB - possibly an Active Directory Server - each time an access control is done. Instead the ACL (Access Control List) of the object (e.g. a file) is checked against the token.
To refresh the group membership you need to have a new token created. Either you can log out and then log in again as you describe it, or you have to do another login with LogonUser for the same user. That function is not accessible for normal user however.
I think that it all boils down to finding another design. Adding and removing a user from the admin group dynamically looks strange from a security perspective. Is there any other way you can give the user access to the resources needed? Can you do the actual work from within a Windows Service that runs under the LocalSystem account?
I am using C# and .Net Framework 4.
I am looking for a foolproof method to get the login id of the currently logged in windows user that is not susceptible to impersonation or hacking. I am looking for this in the form of: DOMAINNAME\USERNAME
e.g. SOMEDOMAIN\JohnDoe
Currently the best I have is:
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var currentLoginId = identity.Name;
Is this open to impersonation (outside of someone knowing both the username and password) and if so is there a better way of doing this?
There can be at least four different identities involved at this point:
The identity assigned to the thread
The identity assigned to the process
The account of the user who started the process
The account of the user who logged onto the PC
In your code you're getting (1). This is normally fine, and is usually the same as (2).
To retrieve (2), you could:
Call WindowsIdentity.GetCurrent to get the impersonated identity
Undo impersonation by calling the Win32 RevertToSelf function
Look at WindowsIdentity.GetCurrent to get the underlying process identity
Reset the thread identity by impersonating the identity from step (1)
(2) and (3) will be the same unless you've written unmanaged code that changes the process identity. As #Daniel points out, (3) and (4) could legitimately be different in the presence of the Windows "run as" command.
Edit: You can trust that the current WindowsIdentity is who it says it is, insofar as you can trust any given piece of data in your application.
You could spoof it by attaching a debugger to your process and faking the return value from this call, but if you're going to do that, you might as well fake the piece of code where you pass the user name to the database.
The 100% safe way to do this is to use integrated authentication/SSPI to connect to the database.
Only a partial answer:
I believe System.Security.Principal.WindowsIdentity.GetCurrent(); will return the windows user account associaed with the currently executing thread. If the thread or application was started under a different user account from the logged in user, you won't get what you're after using this.
If you can be sure that your aplication was not started using the "run-as" feature (or programatic equialent) and you're not doing anything internally with respect to the thread's identity, you can probably be sure this is the logged in user's account but I'm not 100% on this.
It may be possible to find the user account associated with the widows "session" within which the application is running by using ADSI (see System.DirectoryServices).
I have written a service which a website can execute a command on remotely using the ExecuteCommand method. I have noticed that if the website is not running under a user that is an admin on the remote machine then I get a permission denied exception on trying to execute command.
The servicecontroller class doesn't even allow you to specify any authentication parameters. Is this right? Are there a specific set of privelages the user need rather than an admin, as I am reluctant to add all the webservers users in our cluster as local admins on the service machine?
You will have to impersonate an admin user for the current thread temporarily then revert back. Look up WindowsIdentity.Impersonate() in MSDN.
ServiceController will use the current thread's principal to make the registry and service control manager calls and you have to make sure it is set to an admin user if you want to manipulate the services. The downside is that you will need to store the password somewhere for the account, make sure you are using SecureString and not storing the password in plain-text.
I need to impersonate a user using C#.
I use the LogonUser Win32 API.
This works fine when impersonating users from the same domain as the currently logged-in user.
However I get "false" as response when I try to impersonate users from other domains.
What can cause this?
As Joel says you need trust between the domains.
You also need to be carefull with respect to the security context of the process doing the delegation, and which domain the machine you are running on is in.
Both the machine and the user account of the process must be trusted for delegation, by the domain that you are trying to access.
This means that your code should be running on the domain that you are trying to access.
Hope this helps
Shiraz
You should try calling GetLastError right after LogonUser fail to see if any error information is given.
http://msdn.microsoft.com/en-us/library/ms679360(VS.85).aspx
There may be some issue with calling GetLastError from c#.
Look here for more information on this : http://blogs.msdn.com/adam_nathan/archive/2003/04/25/56643.aspx
Is there a trust between your two domains? If not, LogonUser will fail.