IIS internals

Here I'm not about Kestrel and .NET core. It's article about classic things — Windows, IIS and ASP.NET pools in it. I just bring it all together from different official sources, posts of respected authors and from my personal observations and scrutinizing of Microsoft code.

Windows and IIS are optimized to use hardware. Keeping of TCP connection can be off-loaded to the server-class network card (they have own TCP/IP stack support), while HTTP protocol is processed in kernel mode driver. This dramatically reduces the number of interruptions and context switches, giving more CPU power to perform your code.

It means, that request is passed from kernel to w3wp.exe (executable representing application pool and running under its credentials) directly. There's only one transfer from kernel to user mode (CPU terms) to exactly that user, under which application runs.

Some security is implemented in the kernel, interfacing (through SSPI) to both kernel and w3wp.exe.

Look at the picture:

IIS applications, http.sys and SSPI interactions

IIS applications, http.sys and SSPI interactions

First, take attention at the user mode part. Each combination of "w3wp.exe native code" (green rectangles) and "Managed code" (inside of it) is a single process from OS perspective. Managed code is loaded by Common Language Runtime (which is native DLL itself) inside of the w3wp.exe space.

NTLM, Kerberos and Negotiate protocols are implemented by Security Support Providers in kernel mode. Negotiate is not authentication protocol itself, it's a module used to negotiate one — Kerberos or NTLM. The only way to enable Kerberos in IIS is by enabling Negotiate for Windows Authentication.

For Kerberos protocol to work, there should be a key, belonging to a service account, for decryption of service access ticket (issued by KDC for a client). This service account (to which key belongs to) depend on configuration:

  1. If kernel mode authentication is enabled (by default) — SYSTEM (It is {MACHINE}$ from the perspective of Active Directory).
  2. If kernel mode authentication is disabled — application pool account. In this case, you need to generate SPN (Security Principal Name) for the domain user, under which application pool runs.

For Kerberos and SPN consult this article.

For more about request processing pipeline read in ASP.NET Application Life Cycle Overview for IIS 7.0 (it's old but informative) or in the source code of HttpApplication, HttpRuntime and related classes in Reference Source of .NET Framework.

Request processing and authentication

For every request w3wp.exe forks an OS thread (native thing) and associate pooled instance of HttpApplication class (managed) to it. You can check these instances as counter in Performance Monitoring and understand how many requests was processed in parallel just a moment before.

Windows authentication

New w3wp.exe OS thread doesn't have correct windows user authentication token attached to it. Such token assignment can be made only in native code. Therefore, windows authentication is processed by two modules:

  1. Native WindowsAuthenticationModule (Inetsrv\Authsspi.dll) which works with SSPI to authenticate, holds a session and attaches user authentication token to the OS thread.
  2. Managed WindowsAuthenticationModule which recreates principal in .NET (this principal is based on the token of the thread).

Federated and forms authentication

Despite the matter of protocols and using or not of SSPI, from the perspective of application, authentication is the presence of trusted information about the principal. In ASP.NET such information is expected to be in HttpContext.Current.User and Thread.CurrentPrincipal.

Federated authentication and forms authentication modules are not related to SSPI at all. They both:

  1. Handles authenticate event and recreates .NET principal. Authenticate event is not a moment when user types password (which is the challenge response), it's a step in a pipeline executed for each request to recreate principal in .NET before going further.
  2. Handles EndRequest pipeline event and challenges user with authentication if the response to be sent had 401 status code.

Federated authentication and forms authentication modules works slightly differently in terms of attaching principal to expected places:

  1. Federated authentication modules assigns constructed principal to HttpContext.User, then directly to Thread.CurrentPrincipal.
  2. Forms authentication module does the same for HttpContext.User, but then some magic about assigning to Thread.CurrentPrincipal; I have read module code, but I didn't understand it pretty well (and that magic doesn't happen in 0.001% of cases, see Scott Hanselman).

I said "Federated authentication modules" (plural form) not by mistake. In contrast to forms module, there are two managed modules for federated auth:

  1. WSFederationAuthenticationModule that takes care of token in request after redirection back from authentication service (STS in terms of WS-Federation protocol) and redirects to STS when needed. Recreates principal from token.
  2. SessionAuthenticationModule that creates a fed-auth cookie, holding authentication during a session. Recreates principal from that cookie.

Both federated modules has to be added explicitly in Web.Config, while forms module is auto-added by IIS if forms authentication is configured.

Mixing and tuning authentication

Handlers of the authenticate event in all Microsoft modules take care of existing user passed in HttpContext. If HttpContext.User is not null then module skips its work because it means that the user is somehow authenticated by another module.

You can't strictly control the order in which modules handle EndRequest event. If you use some + windows authentication, I guess (experiments show it's true) native windows authentication module will handle status code 401 last.

How, possibly, you can alter authentication behaviour:

  1. Managed windows authentication module class is sealed and don't have events that could help you hook it up. But other modules sit in the IIS pipeline after it for ingress and before it for egress, so you can manipulate users and challenges in other modules.
  2. Forms module have events you may need to alter behaviour.
  3. Classes of federated authentication modules are open for extending and due its explicitness you can add your altered modules to handle WS-Federation instead of original ones.

See example of Mixing WS-Federation and Windows Authentication in IIS.