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.
Related
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.
Could anyone please share any thought on authenticating Active Directory users using the AuthType.Kerberos method.
Ideally, I would like to pass the Username and Password to validate the user credentials using the AuthType.Kerberos method
This type of validation uses LDAP connection (LdapConnection)
Any comments or feedback will be very appreciated.
Cheers! :)
Kerberos doesnt use a username and password in the sense you are talking about here, it uses a ticket based auth system with a central server. Kerberos is quite complicated to implement and is normally only used in cases where you want to do double hop authentication with the logged in user. This means the application wants to use the credentials of the user who has logged in to access a secondry system. For example if you have a SharePoint site which pulls data from exchange server you may want to pass the currently logged in users details from sharepoint to exchange. This is normally done with Kerberos and Constrained Delegation.
In reality what you probably want for your application is Windows authentication (NTLM) which allows the application to authenticate domain users, (However again in the common case this doesnt use a username and password at your application level either).
===EDIT===
To implement kerberos with a .Net webapp you will need to do the following
Enable Constrained delegation for the app pool http://blogs.msdn.com/b/dotnetremoting/archive/2006/07/06/662599.aspx
Setup SPN's for your site http://support.microsoft.com/kb/929650
Setup your code to use kerberos when you call the remote service, this is basically just setting the protocol. You dont need to actually send the username or password
This article has some good advice around how to troubleshoot problems with the system
http://blogs.technet.com/b/askds/archive/2008/05/29/kerberos-authentication-problems-service-principal-name-spn-issues-part-1.aspx
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.
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.