How to Programmatically Detect Disabled NTLM Authentication in C# and Windows
Understanding the Problem: NTLM and Group Policy Restrictions
When hosting ASP.NET Core applications using Windows Web Server technologies like HttpSys, developers often configure authentication schemes dynamically based on machine states. A common implementation checks if the machine is domain-joined and configures authentication accordingly:
builder.WebHost.UseHttpSys(options =>
{
options.Authentication.Schemes = GetSecurityObjects.IsDomainJoined
? (AuthenticationSchemes.NTLM | AuthenticationSchemes.Kerberos | AuthenticationSchemes.Negotiate)
: (AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate);
});However, enterprise environments frequently enforce strict security policies by disabling NTLM entirely via Group Policy Objects (GPO). The policy setting "Network Security: Restrict NTLM: NTLM authentication in this domain" (or inbound/outbound NTLM restriction policies) can be set to "Deny all".
When NTLM is restricted by GPO, the code snippet above still advertises AuthenticationSchemes.NTLM in the WWW-Authenticate response header. Browsers or clients attempting to authenticate using NTLM will fail, leading to poor user experience or security audit failures.
The Solution: Inspecting Registry & LSA Policy Settings
Unlike standard security packages that remain loaded in Windows memory regardless of policy restrictions, NTLM enforcement is controlled via Local Security Authority (LSA) registry configuration keys populated by Group Policy.
To programmatically check if incoming NTLM authentication is disabled, you can inspect the registry keys where Windows stores both local and domain-enforced NTLM restrictions.
Key Registry Locations for NTLM Enforcement
- Group Policy Override Location:
HKLM\SOFTWARE\Policies\Microsoft\Windows\NTLM\MSV1_0 - System LSA Control Location:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
The DWORD value to look for is RestrictReceivingNTLMTraffic:
0: Allow all (Default)1: Audit all2: Deny all domain accounts / Deny all traffic
C# Implementation: Programmatically Checking NTLM Availability
You can create a robust utility method in C# to verify whether NTLM is disabled before appending AuthenticationSchemes.NTLM to your web host configuration:
using System;
using Microsoft.Win32;
using System.Net;
public static class NtlmSecurityChecker
{
public static bool IsNtlmDisabled()
{
// 1. Check Group Policy location
using (var gpoKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Policies\Microsoft\Windows\NTLM\MSV1_0"))
{
if (gpoKey != null)
{
var value = gpoKey.GetValue("RestrictReceivingNTLMTraffic");
if (value is int intVal && intVal == 2)
{
return true; // Deny All is active
}
}
}
// 2. Fall back to local LSA configuration
using (var lsaKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0"))
{
if (lsaKey != null)
{
var value = lsaKey.GetValue("RestrictReceivingNTLMTraffic");
if (value is int intVal && intVal == 2)
{
return true; // Deny All is active
}
}
}
return false;
}
public static AuthenticationSchemes GetSupportedSchemes(bool isDomainJoined)
{
var schemes = AuthenticationSchemes.Negotiate;
if (isDomainJoined)
{
schemes |= AuthenticationSchemes.Kerberos;
}
// Only offer NTLM if it is not explicitly disabled by Windows policy
if (!IsNtlmDisabled())
{
schemes |= AuthenticationSchemes.NTLM;
}
return schemes;
}
}Updating Your HttpSys Startup Logic
With the helper method in place, update your web host configuration on application startup:
builder.WebHost.UseHttpSys(options =>
{
options.Authentication.Schemes = NtlmSecurityChecker.GetSupportedSchemes(GetSecurityObjects.IsDomainJoined);
});Summary & Next Steps
By reading the registry keys managed by Windows Group Policy, your application can dynamically adapt its authentication offer headers on startup, preventing clients from selecting unsupported authentication protocols. Long-term, applications should plan to transition fully to Negotiate or Kerberos, as Microsoft continues to phase out NTLM in modern Windows releases.