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.
Related
We are having several departments with individual Service-Accounts on a Windows Domain (Active Directory) and want to use them to impersonate a console command to run it with Process.Start and set the username and password in the code. Process.Start requires a ProcessInfo class object, where a domain, username and password can be provided.
The commands need to be run under these Service-Account credentials to access related network-shares and manipule their structure and files with a cli-program.
The problem is, that all tries so far with running Process.Start within a webservice or api on a IIS server, we still got into the same problem: It seems to be required, that ProcessInfo can only use the other credentials, if the particular user is having login permissions on the running machine.
All the service users are not having rights to directly login to the machine, so the command cannot be run and an Exception is throwing.
Exception message:
Logon failure: the user has not been granted the requested logon type at this computer
Workaround:
To properly run these commands, we are thinking about creating multiple instances of the webservice/api and impersonate it via the IIS AppPool and assign the relevant service user. Each instance will then have a specific service run in which context IIS is serving the webservice/api.
Our goal is to accomplish the webservice/api in a single instance and change user credentials dynamically grabbed from database (username/password).
Is this possible in any way?
Like ServiceUsers can be assigned to windows service (machine/server), which are not needing login permissions.
I just want to ask, before we going the way of the workaround and creating several instances for this solution... Any helping comments or different ideas of solving this challenge are welcome. Thank you, folkz.
The missing logon type may well be "Log on as a batch job" - see if granting the service accounts this right helps.
The exact steps to achieve this might vary between different versions of Windows but will be something like this;
Start the "Local Security Policy" app (AKA secpol) Go to Security
Go to Settings | Local Policies | User Rights Assignment
Edit "Log on as a batch job" and add your service account
If this does not work then don't forget to undo the above - don't leave experimental security changes lying around!
I've got an IdentityServer setup to connect to an external ADFS server. I'm able to login and obtain an access token via OAuth2 / OpenId Connect. An AspNet.Core WebApi runs within an IIS AppPool with a user that has Kerberos delegation enabled (should have, not sure how to check since I'm not into infrastructure).
What I want to achieve is that the ClaimsIdentity I get from IdentityServer on the WebApi is transformed back to a WindowsIdentity using impersonation, without a password. Just a username should be sufficient. With the actual impersonated WindowsIdentity I should then logon to a database and do some mutations.
I'm not sure if this is even possible, but I've been struggling with this for quite some time now.
I took this as a base for the Impersonation implementation. The only problem I have is that the Context.User.Identity doesn't return a WindowsIdentity, but a ClaimsIdentity.
Another approach I tried is using var user = new WindowsIdentity(username); where username would be the UPN. That creates an Identity, with ImpersonationLevel.Identity instead of ImpersonationLevel.Impersonated. When I run WindowsIdentity.RunImpersonated() with this users access token will give an Access Denied, or IOException when loading the needed assemblies.
So first question: how do I check if the IIS AppPool user has the correct Kerberos delegation rights?
Second question: is it even possible to impersonate a user only with a username?
What you're describing is delegation with "any" authentication method. Protocol-wise this is called Service-for-User-to-Proxy. It requires that the service account is granted that right and you need to check the Active Directory service account is configured for that. You also need to indicate which services can receive impersonated connections.
Once you've configured the service account you should restart the machine running the code (things get negatively cached in memory which takes a while to time out). If your app is running in IIS your service account should already be configured to use the correct Windows local privileges to impersonate.
However, keep in mind that this permission gives your application global permissions for whatever service you're requesting a service ticket to. That means your app can impersonate everyone, which is rightfully a little dangerous.
Given that I have a WCF service using windows authentication, and I want to impersonate them and call another WCF service, like so:
using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
// call another WCF service
}
I've set all the config settings and it works fine, as long as on the client side,they include the following line:
client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
But, how do I verify before trying to make the call that the user token has delegation rights? i.e. the client, which I don't control, has set the AllowedPersonationLevel?
If they haven't set it, all sorts of weird exceptions get thrown (like cannot load assembly X etc).
Ideally, I'd like to be able to do the following:
using (ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
if (UserDoesntHaveDelegationRights())
throw new SecurityException("No delegation rights");
// call another WCF service
}
Note that WindowsIdentity.GetCurrent().ImpersonationLevel is always equal to TokenImpersonationLevel.Impersonation, so that unfortunately is not an option.
There might be some confusion here in definitions. In terms of impersonation levels a windows identity can be:
Impersonated - the service can impersonate the user locally
Delegated - the service can impersonate the user remotely
The ability to delegate is so powerful that its highly restricted in Active Directory:
The Client has to allow delegation
The service account which is doing the delegation must be marked as "trusted for delegation" in Active Directory.
Here's how to enable an account for delegation. It requires a Active Directory domain admin to the make the change. Every corporate environment that I've ever worked in has a policy that does not allow Delegation.
Back to your question:
So while TokenImpersonationLevel.Delegation exists, its considered a security risk and rarely (if ever) used. TokenImpersonationLevel.Impersonation is the highest level that you will probably ever get.
TokenImpersonationLevel.Impersonation is useful. You can still connect to a database or make a remote service call as the impersonated user. But a remote service (not on the same box) can't impersonate the user a second time. The basic rule of thumb is "impersonation enables two machines hops". If the user's credentials have to "hop" farther, it will fail.
If you need to pass a user's credentials between many servers the best choice is a federated security model such as Windows Identity Foundation (WIF). See Identity Management in Active Directory.
What about
if (WindowsIdentity.GetCurrent().ImpersonationLevel != TokenImpersonationLevel.Delegation) ...
Trying to list the directories and files within a specific folder. This folder will depend on the current user (Page.User) which logs in by Windows Authentication (NTLM) and is retrieved from the Active Directory (homedirectory property).
I am using a domain user to access the AD and retrieve the folder location, this works fine.
What fails is retrieving the sub folders using System.IO.DirectoryInfo.GetDirectories() even with impersonation.
Here's the code I'm using for impersonation:
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = ((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
I have checked that the user being impersonated has access to the folder.
From what I have found so far it seems that I either need to set up delegation or Kerberos authentication, is this true? Are these the only ways to achieve this? Shouldn't impersonation be enough?
Impersonation allows the website service account to impersonate (pretend to be) another user on that machine. So querying AD to see what rights you (or the impersonated user) have is allowed.
Requesting access to a UNC share on another machine is asking the other machine to accept that the website service account is acting on behalf of the user being impersonated. This is delegating. The other machine is not checking the users credentials itself but delegating that check to the webserver.
If the client is connecting the the website from yet another machine (normally the case for webservers), then you have a "double hop" from client to webserver to UNC file server.
I'd suggest you need to configure Kerberos (via the SetSPN utility) and look enabling delegation rights for the website service account (witihin AD users and Computers). if you have problems setting this up, I heartily recommend a tool called DeleConfig.
Currently I have a windows service written in C# (running as LocalSystem) which creates a user account, needed for impersonation, by using the DirectoryEntry to add the user/password and associated UserFlags. Then it simply uses this account to perform some tasks (using impersonation) using the LogonUser() functionality - works perfectly.
However this account should ONLY be used for impersonation by my service, a user should NEVER be able to login (even if he has the credentials) locally or via the network.
To accomplish this I tried setting the Local Policies for “Deny logon locally” and “Deny access to this computer from the network” and added the user my service creates. Now however impersonation fails with the following:
Logon failure: the user has not been granted the requested logon type at this computer (1385)
So, I guess this is NOT the right way to do it … but I need to secure lockdown the account so it can only be used by my service for impersonation purposes and to ensure that no one else can ever logon to the account (even if they have all the credentials).
Is there something in LSA I can use? Or using the DirectoryEntry code similar to when the account was created? Is there a way to allow for an account to exist but not allow users to interactively logon?
Any help would be much appreciated.
Thanks,
You can run the service under the "Network Service" account and grant the server access to the applicable domain resources. Other than that, I am not certain there is a way to solve your problem with a user account.