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) ...
Related
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.
Seems like my local machine does not have the required permissions to write data into the Active Directory. I can read data but can not change and update it. Upon calling the .save() command I receive the next message: "Access is denied".
I do not use any login details in order to log into the Active Directory and I wish not to use any as well. I know it has something to do with the application pools and IIS generally, but I can't seem to find a working solution other than trying and changing some minor options and features.
EDIT:
This is the code I'm trying to execute:
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "DOMAINANME"))
{
GroupPrincipal group = new GroupPrincipal.FindByIdentity(pc, "GROUPNAME");
group.Members.Remove(pc, IdentityType.SamAccountName, "USERNAME");
group.Save();
}
Your intuition is correct - you need proper permissions on the application pool account.
A simplest way would be:
Create a new domain user account
Add it to "Domain admins" group
In your iis server, locate the pool your application uses and change the pool identity to the newly created user
This way all requests from users to iis are run in the context of the domain admin and thus all requests from iis to AD will succeed - domain admins can operate the AD.
Although the above solution would work, it is probably not recommended. This is because the application does many things other than just connecting to the AD and if there are places that can be misused, you risk running unwanted requests in the domain admin context.
Usually then, such application would have two layers, a front layer and a back layer. The front layer runs in a restricted context and is responsible for all user requests. This is your application. A back layer is another web application that is not accessible from internet, only from the local intranet. This application runs in the domain admin context and serves as the gateway to the AD. The front application uses the back application to talk to the AD.
In order to execute code that needs specific permissions to your Active Directory, follow these steps:
Require the user to log into the ASP.NET application.
Have your code impersonate the currently-logged in user when executing the the Active Directory portion that needs that access.
There may be slight variations depending on the version of ASP.NET you are using, but basically...
Step 1
For the user login, just use the standard ASP.NET authentication configuration. Because it's the easiest to configure (and just works with AD) I'll document Windows Authentication here. This will prompt the user with a standard Windows login dialog box - where they will need to enter their domain credentials. To configure the application, make the following changes to the Web.config
<configuration>
...
<system.web>
...
<authentication mode="Windows" />
...
</system.web>
...
</configuration>
By default, ASP.NET should already know how to talk to the Active Directory when configured for Windows Authentication, so nothing else should be required, but if you find you need a separate Role Provider, feel free to take the one we use. (We also have instructions.) But again, you shouldn't need it.
Edit: You also need to make sure IIS is configured - using inetmgr.exe - to not allow anonymous access for the application. Otherwise, it will never prompt the user to log in. We typically disable Anonymous access and enable Windows and Basic authentication - because we need to support browsers that don't support NTLM.
Step 2
Now that you have an authenticated user, you can programmatically impersonate them. The following is adapted from Impersonating the authenticating user in code:
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = ((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
try
{
// Do your writing to the AD here
}
finally
{
impersonationContext.Undo();
}
I also just discovered (while searching for reference links for this answer) that this is all pretty well documented in the MSDN article How To: Use Windows Authentication in ASP.NET 2.0
Let us know how it goes :-)
I have a few newbie web service/Windows rights questions since I've typically been a LINUX/embedded dev in the past.
What directories does a web service executing on a server have access to by default?
I ask because I tried to write to C:\ and got an access violation. I kind of assumed I would in this case, but I assume there are some areas of the file system the web service can write to and read from by default, right? Or is it just the current working directory?
*How can I give a web service permissions to look at a new directory it didn't have default access to?*
This is C# - ASMX - .NET 3.5 - IIS
The WebService doesn't really have any associated access controls associated with it (in a sense). It is however tied to the access control of the user account which is being used to run the application. By default this is usually some built in user account with limited permissions.
IIS uses a number of built-in Windows accounts, as well as accounts
that are specific to IIS. For security reasons, you should be aware of
the different accounts and their default user privileges. It can be a
security risk to change the identity of a worker process so that it
runs as an account with a high level of access, such as the
LocalSystem user account.
See a list of possible user accounts here: http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/3648346f-e4f5-474b-86c7-5a86e85fa1ff.mspx?mfr=true
If you have Anonymous Authentication enabled you can usually check the settings on that to see which account is being used to run the web service. (Depending on which version of IIS you are using, clicking Edit should let you view the default user account)
After finding the account, usually you will have to explicitly grant it the permissions on the folders (read and write) that you want to give it access to. The default user account usually has pretty limited access (and for good reason). You can grant permissions on the Security tab of the properties of any of the folders on a Windows file system (Properties->Security)
If you're using Windows Authentication, then you should have the same access rights as the authenticated user using the application as long as the resources are local to the IIS server.
I'm developing a WCF service that will host business logic of the application. The application is mostly for intranet, but can be accessed from internet. We have an active directory domain up and running, so I plan to authenticate and authorize users according to their username and groups they are in. This service will be used mostly be an ASP.NET MVC site
So, first question is how to authenticate and authorize users based on their AD profile?
Secondly, I need to store additional info about each user. The problem is that I can't modify AD scheme. The number of added fields is about 10 or so.
Can I somehow use SQL server for profile storage? Of course I can, but how to tie this with AD auth?
You can use WIF for this.
You would configure your WCF service for WIF in the normal way and then use a custom ClaimsAuthenticationManager class deriving from the base ClaimsAuthenticationManager and overriding its Authenticate method. This is a normal extensibility point of WIF. WIF will
get hold of the security token from the incoming request and add claims for each of the relevant AD properties. In your override of the Authenticate method, you will add new claims to represent your extra properties.
The basic use of WIF for WCF services is described here:
http://msdn.microsoft.com/en-us/library/ee748476.aspx
To see how to use ClaimsAuthenticationManager, start here:
http://msdn.microsoft.com/en-us/library/ee748211.aspx
Well, I think you have a couple of choices here, but you will have to carefully consider the implementation.
The primary issue with using active directory authentication is that by default a user's credentials can only be passed successfully between two machines. In the case of a web application, this means that the user's credentials can travel between the end user's machine and the web server, but no further.
However, this behavior can be changed through the use of Kerberos authentication, which essentially allows an authentication ticket to be passed among all of the trusted machines in the chain (i.e. from the web server to the application server to the database, for example). Successfully configuring Kerberos can be extremely challenging, especially if you have had no prior experience with it.
I think your best bet is to configure your web site to accept only Windows Authentication. This means that IIS will perform the validation of the user against active directory. In your ASP.Net application you can pickup the domain name of the authorized user from Request.ServerVariables("logon_user").
At this point, you can log the user on with FormsAuthentication, for example, without requiring them to login again.
You could then either implement the SQL Server Membership Provider or create your own interface to your database for further user validation and extra information storage. We have used both mechanisms, but I prefer the self-built one due to the additional control it provides and, in this case, you won't need a lot of the functionality (password reset, recovery, etc) that the membership provider offers.
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.