Windows Authentication Impersonation - Second request gets wrong user identity - c#

I have the following architecture:
Client1(Browser-App) -> Server1 (WebAPI/IIS) -> Server2 (WebAPI/IIS)
I am using ASP.NET for my server-side applications/apis and the user should be authenticated via "windows integrated authentication".
As you can see there is a second hop from server1 to server2. NTML does not support the second hop if both WebAPIs are not on the same server.
So I configured an AD domain to support "kerberos".
It works now with the second hop.
My test-WebAPIs output the identity of the user like this:
server1: test.domain/user1
server2: test.domain/user1
But if I change the logged-in user on Client1 and execute the same request as "otherUser2", only the first hop gets the correct identity:
server1: test.domain/otherUser2
server2: test.domain/user1
On the second hop the old user of the first request is displayed.
I tested multiple scenarios: Same behaviour if the following requests come from another client with another windows user...
It looks like the windows identity of the first request is cached on the server2... This is a big problem for me and I think this should not be possible... It's a big security hole if a request is executed in the wrong user context!
Is this a known problem? Did I do something wrong?
Is there a solution or a better configuration?
On the first ASP.NET WebAPI I use impersonation like this:
WindowsIdentity identity = (WindowsIdentity)HttpContext.Current.User.Identity;
using (var wic = identity.Impersonate())
{
try
{
WebClient c = new WebClient
{
UseDefaultCredentials = true
};
I use the WebClient class of .NET.
Both IIS server have "Windows Authentication" with "Negotiation" and "NTML" configured.
Server1 is the DomainController, DNS and DHCP-Server (+IIS)
Server2 is only a normal server with IIS installed.
All computers are in the same domain.
I cannot explain me this behavior... It makes no sense to me. Why should the first incoming request's identity should be cached on 'server2'?
If I restart the IIS and re-execute the requests with another windows identity, this is the "first working request" and the others get his identity on 'server2'.

I found the solution/problem.
It was in fact a caching problem... The identity of the first user was cached.
You can change this behavior with this "IIS settings":
authPersistNonNTLM
authPersistSingleRequest
Or your HTTP-Client at API1 can disable TCP-Connection caching:
Connection: close
instead of
Connection: keep-alive
But the actual problem in my scenario was fiddler (a HTTP proxy tool).
I configured fiddler as proxy in the web.config at API1. This kept the connection open and the first identity was reused...
I hope I can help some others with this answer.

Related

OpenId Connect User Not Logged Out When Using IP Address in End Session URI (discovery document end_session_endpoint) . Using Gluu identity server

When I click logout in a .Net client application I am successfully logged out if the end session end point ("end_session_endpoint" in the discovery document) uses the domain name. For example, the framework redirects to the identity server (Gluu) with the below url and it logs out the application perfectly.
https://my-gluu-server.com/oxauth/seam/resource/restv1/oxauth/end_session?
post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44300%2Fsignout-callback-oidc
&id_token_hint=<MY JWT...>
&state=CfDJ8AjASIR7C_....
&x-client-SKU=ID_NETSTANDARD2_0
&x-client-ver=5.5.0.00
But when I change it to use the IP address (because the domain name is not resolvable where we intend to host it) it fails to logout. When I click a private page it goes back to the Gluu server and no login is required (because I am not logged out), it then redirects me back to the .Net client application.
https://10.10.10.10/oxauth/seam/resource/restv1/oxauth/end_session?
post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44300%2Fsignout-callback-oidc
&id_token_hint=<MY JWT...>
&state=CfDJ8AjASIR7C_....
&x-client-SKU=ID_NETSTANDARD2_0
&x-client-ver=5.5.0.0
Is Gluu or the OIDC flow checking the url (issuer?) and if so is there a way to work around this, so that both the IP address and domain name can be recognised as valid?
Thanks.
You can not use HTTPS against an IP-address:
https://10.10.10.10
You must always use a domain when you use HTTPS, otherwise you have to use HTTP. But using HTTP will give you other problems, especially with the cookies that the browser will reject. So, get a domain for 10.10.10.10 that the client will trust.

Error C# Dynamics CRM: The authentication endpoint Username was not found on the configured Secure Token Service

We're facing an error while trying to deploy a WCF webservice in a server. While connecting to Dynamics (CRM on premise) we get this error: The authentication endpoint Username was not found on the configured Secure Token Service
If we test it locally, it's working but if the deploy the webservice in the server, this is the code which performs the login:
Uri serviceUri = new Uri(OrgServiceUri);
proxy = new OrganizationServiceProxy(serviceUri, null, authCredentials, null);
proxy.EnableProxyTypes();
_service = (IOrganizationService)proxy;
Guid userid = ((WhoAmIResponse)_service.Execute(new WhoAmIRequest())).UserId;
if (userid != Guid.Empty)
{
Console.WriteLine(userid);
return true;
}
else
{
return false;
}
Any guess?
Thank you!!
First, make sure the user you have set up as the service account has Read/Write access to CRM and has a security role assigned that enables it to log into CRM remotely.
Next, make sure the Username endpoint is configured in the ADFS deployment that this CRM org is using:
Log onto the ADFS server and open the ADFS management console. Go to ADFS > Service > Endpoints
You’ll see a list of endpoint URLs here. Find the one for /adfs/services/trust/13/username of type WS-Trust 1.3
Make sure that this endpoint has “Yes” set for both the Enabled and Proxy Enabled settings.
If you have to make a change to this endpoint, after making the change re-start the ADFS server and the CRM server, then try to register again.
Lastly, if the above looks okay, it could be a resolution or routing issue blocking the connection. Make sure that there are external DNS entries for the path to your ADFS server. Also, make sure that your firewall permits external access to the ADFS server. If you are able to, try to use a computer that is outside of your domain to navigate directly to the ADFS server to test its accessibility.
This is a problem with the same error as you, and it has been resolved, you can refer to: The authentication endpoint Username was not found on the configured Secure Token Service
Finally we found it was an issue with ADFS service per-se, networking related, since it wasnt able to connect SSO site. After fixing that, it started to work as a charm.
If you are running ADFS on-prem, the ADFS windows service might be stopped (Because of a power failure / unexpected server restart).
You just need to start it.

Does Connecting to Remote WMI from an ASP.NET Page using Constrained Delegation Require Protocol Transition?

I'm working on an older web app that I did not originally build, but now maintain. It is a classic ASP app with ASPX pages mixed in.
The site is setup for Kerberos authentication and delegation, and that is working properly to other boxes (e.g. I can run a SQL query against a back-end server from an ASP page in the site and it connects using the front-end client's credentials properly). So SPNs are registered, delegation privileges are configured in AD, etc.
Now, the part I'm having trouble with is an ASPX page which invokes a remote WMI call to check on the status of an IIS website, using the \root\WebAdministration WMI namespace. The ASPX page is itself invoked by way of an XHR which resides in the client-side code of a different ASP page. The ASPX, when invoked, makes the WMI call, then Response.Write's back the necessary data, which the originating ASP page then utilizes to populate the page that the user sees. The problem is, I cannot get the IIS box to properly delegate the user's credentials to the back-end machine that its making the WMI call against.
This all works properly (including the constrained delegation), but only if I enable protocol transition. If I set the delegation on the middle-tier (IIS) box to use only Kerberos authentication, it fails (I get an anonymous logon attempt on the back-end box).
Now, I've done numerous packet captures on both the front-end client and the IIS box to see exactly what is going on here, and I can see several things:
The front-end client is properly getting its Kerberos ticket, and presenting it to the IIS box for authentication.
The IIS box is accepting the Kerberos ticket from the client.
However, the IIS box is not using the ticket received from the client as the "evidence ticket" that it should be presenting to the KDC in order to obtain a service ticket to access the back-end service on behalf of the front-end user. Instead, the IIS box is using a S4U2Self call to the KDC to obtain a ticket on the front-end user's behalf for itself, then using that ticket in the subsequent S4U2Proxy call to try and obtain the ticket to the back-end. This is where the problem lies.
The behavior noted above is why this works when protocol transition is enabled, and does not work when it is not.
I cannot figure out for the life of me, why the IIS box feels the need to obtain a TGS for itself to use as the "evidence ticket" to get the ticket for accessing the back-end, instead of simply using the ticket presented by the client. There is nothing invalid about the client's ticket from what I can tell, and the client is establishing a Kerberos-authenticated connection with the web server just fine, so there should be no need for protocol transition here. I could enable it if really needed, but I really just want to know why its necessary (if there is valid reason and this is by design then so be it).
The IIS app pool is running as the built-in app pool identity, and the delegation settings are thus configured on the IIS machine account in AD. SPNs are registered for the site against the IIS machine account, and for the back-end services against those service and/or machine accounts, and the "allowedToDelegateTo" list is configured on the IIS machine account, allowing constrained delegation to the necessary services. The specific SPN we are trying to delegate creds to in this scenario is RPCSS/[machine] for the WMI call. I've verified via the packet capture that the SPN in the request matches the SPN in the A2D2 list exactly (of course, if it didn't, then it wouldn't be working when protocol transition was enabled anyway).
As for the actual WMI connection code, I've tried a few ways. One was something like this:
ConnectionOptions co = new ConnectionOptions();
// I did try ImpersonationLevel set to both Impersonate and Delegate, but I don't think I need
// Delegate here because I'm not delegating from the remote WMI machine to a different box; instead,
// I'm delegating from the IIS box to the remote WMI machine.
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
co.EnablePrivileges = true;
// Tried this for the Authority line because I noticed in the packet captures that the principal
// specified here becomes the SPN that is used in the S4U2Proxy request.
co.Authority = "kerberos:RPCSS/machine.domain.com";
ManagementScope ms = new ManagementScope(#"\\machine.domain.com\root\WebAdministration", co);
Then I also tried this:
ConnectionOptions co = new ConnectionOptions();
co.Impersonation = ImpersonationLevel.Impersonate;
co.Authentication = AuthenticationLevel.PacketPrivacy;
co.EnablePrivileges = true;
// I also tried this for the Authority line based on various other code examples for similar
// issues, but this resulted in an incorrect SPN being used in the request.
co.Authority = #"kerberos:DOMAIN\machine";
ManagementScope ms = new ManagementScope(#"\\machine.domain.com\root\WebAdministration", co);
I also tried the same as above, but without an Authority line, and the correct SPN was used in the request but it still didn't work.
Finally, I also tried this, with no ConnectionOptions object at all, hoping it would just pass on the default creds:
ManagementScope ms = new ManagementScope(#"\\machine.domain.com\root\WebAdministration");
Any help here on either how I can get this working without enabling protocol transition, or info on why this setup would require protocol transition, would be very much appreciated!

Why redirections on my site take me to azure.websites.net instead my domain?

I have configured my web app to time out on idle by setting the following in the Startup.auth.cs file:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromHours(1),
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
....
}
My web app is behind a Virtual Network--->Application Gateway. The AG forwards the requests to the web app. I have also got rules that prevent direct access (i.e myapp.azurewebsites.com) to the web app.
Now when the session times out, I get redirected to:
https://myapp.azurewebsites.net/Account/Login?ReturnUrl=%2Fcustomerarea
which is a blue screen with error 403(correct error), instead of my own domain like:
https://example.com/Account/Login?ReturnUrl=%2Fcustomerarea
Anyone knows why I would get this behavior? thanks.
Edit: more info...it looks like any redirect causes the above problem. So if I enter a URL https://example.com/customerarea which requires the user to login, then the redirected URL to the login page, has the azurewebsites.net in its address.
So the above answer is correct. I am adding more information on how that fixes the problem in an MVC 5 web app. According to Microsoft's Application Gateway FAQ:
Application Gateway also inserts X-Original-Host header that contains the original Host header with which the request arrived. This header is useful in scenarios like Azure Website integration, where the incoming host header is modified before traffic is routed to the backend.
So to fix, I added the following code at the top of my Configuration (IAppBuilder app) method in the start.cs file:
app.Use(async (context, next) =>
{
if (context.Request.Headers.GetValues("X-Original-Host") != null)
{
var originalHost = context.Request.Headers.GetValues("X-Original-Host").FirstOrDefault();
context.Request.Headers.Set("Host", originalHost);
}
await next.Invoke();
});
Any redirects without explicit host portions in the ASP.NET ecosystem will go to the host portion provided by the current HttpContext.Request.
Your application gateway will make the final request to your application (like a proxy) and addresses it as your .azurewebsites.net domain → meaning your ASP.NET app doesn't know about the original request to the gateway.
What you will need to do is to set the incoming request hostname to the original hostname from the request that went to your Application Gateway.
This documentation page (It's ASP.NET Core, but the same principle holds true for asp.net-mvc-5) should allow you to get an insight on how to overwrite your incoming HttpContext.Request hostname to the original. There are of course multiple ways.
Forwarded Headers (Which would then need to be set by your Application Gateway)
Config entry (Having a config entry with a "hardcoded" hostname)
Both approaches will just require extra middleware to override the current request according to the information gathered by either of these approaches.

Why do I get a 401 Unauthorized when making a HttpWebRequest with the correct credentials?

I have a url and a username and password to an external site.
If I access the URL through a browser, an authentication box pops up. I give it the username and password, and I can get to the page.
I am trying to achieve the same thing through code, using a HttpWebRequest object:
var webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.GetResponse() etc…
This used to work before the web sites owners added some protection to the site, and provided me with the username and password.
The WebRequest has a credentials property, which I have been setting as follows:
webRequest.Credentials = new NetworkCredential("username", "password")
I have also tried:
webRequest.Credentials = new NetworkCredential("username", "password", “url domain”)
this always result in the error:
“The remote server returned an error: (401) Unauthorized.”
Am I missing something obvious?
Using System.Net.NetworkCredential might not be effective depending on the authentication model of the resource. It would be helpful to understand the model used by the remote site.
OpenID, Forms Authentication, and Integrated Windows Authentication all work differently. You can attempt to deduce what authentication method they use using tools as #Christoph and #Lex describe, or simply contact the remote web site provider.
If the remote site uses a negotiating protocol, such as Kerberos or NTLM, then the behavior you are experiencing is strange, indeed; however, some other protocols may require you to go about it another way.
If you use HTTP, please simply use Microsoft Network Monitor or Wireshark to capture what kind of 401 message is returned from the web server. It can be 401.3 or another error code which has in fact another meaning.
If there is no privacy concern, you may wish to post the url you are trying to access.
If the URL is malformed, or references an invalid page (or an invalid page + parameters combination) you will sometimes see server errors like this.
I worked with a legacy web app from the state of Michigan and it would throw 400 style errors every time I tried to access a page with the wrong page parameters.
Are you using request filtering on the web server? Can you check the different verbs which are setup to be denied?
try to debug your request with fiddler (www.fiddler2.com).
Therefore you must start Fiddler, and add the Proxy Settings to your Request:
webRequest.Proxy = New WebProxy("http://127.0.0.1:8888")

Categories