When implementing logonHours restrictions in ActiveDirectory for Remote Desktop Services (Windows Server 2012+) with Network Level Authentication (NLA) enabled, administrators encounter this problematic behavior:
// Expected behavior (without NLA):
1. Client connects → Server checks logonHours → Returns "Your account has time restrictions"
// Actual behavior (with NLA):
1. NLA pre-authentication fails → Returns "Local Security Authority cannot be contacted"
The discrepancy occurs because NLA performs credential validation before establishing a full session. During this phase:
- logonHours verification happens at session creation stage (post-NLA)
- NLA failure generates generic security errors rather than specific policy messages
Option 1: Registry Modification
Create the following registry entry to force detailed error messages:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]
"LimitBlankPasswordUse"=dword:00000000
"LmCompatibilityLevel"=dword:00000002
Option 2: GPO Adjustment
Configure these Group Policy settings:
Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Security Options:
- Network security: LAN Manager authentication level = Send NTLMv2 response only
- Interactive logon: Message text for users attempting to log on = [Your custom time restriction message]
For programmers needing to handle this programmatically, here's a PowerShell script to modify the behavior:
# Check current NLA settings
$nlaSetting = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" | Select-Object -ExpandProperty UserAuthentication
# Create custom error message handler
if ($nlaSetting -eq 1) {
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name "DisableLoopbackCheck" -Value 1
Write-Host "NLA compatibility mode enabled with detailed error messages"
}
else {
Write-Warning "NLA is not currently enforced on this server"
}
For enterprise environments, we recommend combining these approaches:
- Maintain NLA for security
- Implement pre-login client-side time checks
- Use the registry/GPO modifications above
- Create custom error messages through Group Policy
Example client-side check (batch file):
@echo off
for /f "tokens=2 delims==" %%A in ('wmic OS Get localdatetime /value') do set datetime=%%A
set hour=%datetime:~8,2%
if %hour% LSS 08 (
msg * "Access prohibited before 8AM"
exit /b 1
) else if %hour% GEQ 18 (
msg * "Access prohibited after 6PM"
exit /b 1
)
When implementing logonHours
restrictions in Active Directory for RDP users, the authentication sequence differs based on Network Level Authentication (NLA) settings:
NLA Enabled:
1. Client initiates CredSSP handshake
2. Domain Controller validates time restrictions
3. If denied → LSA termination → Generic auth error
NLA Disabled:
1. Full RDP connection established
2. Server-side GINA checks logonHours
3. If denied → Specific time restriction message
Add this registry key to force the time restriction check to occur later in the authentication chain:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]
"DisableLoopbackCheck"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
"UserAuthentication"=dword:00000000
Create a login script that checks hours before attempting RDP:
function Test-LogonHours {
param([string]$username)
$user = Get-ADUser $username -Properties logonHours
$currentHour = (Get-Date).Hour
$mask = [math]::Pow(2, $currentHour)
return ($user.logonHours[0] -band $mask) -ne 0
}
if (-not (Test-LogonHours -username $env:USERNAME)) {
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show("Your account has time restrictions...")
Exit 1
}
For advanced scenarios, implement a custom CredSSP provider that checks time restrictions:
using System;
using System.Runtime.InteropServices;
namespace CustomCredSSP
{
public class TimeRestrictionProvider
{
[DllImport("secur32.dll", CharSet = CharSet.Auto)]
private static extern int LsaRegisterLogonProcess();
public static bool CheckTimeRestrictions(string username)
{
// Implement AD query logic
return true; // Simplified for example
}
}
}
Enable detailed auditing to track authentication attempts:
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Other Logon/Logoff Events" /success:enable
The logs will show Event ID 4625 with status code 0x52e (TIME restriction) when access is denied.