Search code examples
powershellsslexceptiondomaincontrollernetworkcredentials

Exception in "ValidateCredentials" "The server cannot handle directory requests."


I use Windows PowerShell to query and validate the user's Windows credentials during an installation process. That worked well until yesterday. Now the IT department in my company has changed some configuration of the domain controller and now I get the following exception.

Exception calling "ValidateCredentials" with "2" argument(s): "The server cannot
handle directory requests."
At line:32 char:5
+ if ($pc.ValidateCredentials($username, $credential.GetNetworkCredenti ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DirectoryOperationException

From my research I already found out that it has to do with the missing SSL connection. I have to add ContextOptions.SecureSocketLayer somewhere in the code. The question is: Where is the right place to put this parameter? I cannot find any examples for PowerShell.

Here's the script I used to check the credentials:

$credential = $Host.UI.PromptForCredential("Need credentials.", "For using Windows Integrated Authentication please provide the login information for the user that has access to the Microsoft SQL Server database.", "", "")
if (!$credential) {
    Write-Output "No credentials provided"
    return
}

[System.Reflection.Assembly]::LoadWithPartialName('System.DirectoryServices.AccountManagement')

$system = Get-WmiObject -Class Win32_ComputerSystem

if ($credential.GetNetworkCredential().Domain) {
    Write-Output "Credentials contain domain"
    if ($credential.GetNetworkCredential().Domain -eq $system.Name) {
        Write-Output "Domain is local system"
        $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Machine', $system.Name
    } else {
        Write-Output "Domain is network domain"
        $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Domain', $credential.GetNetworkCredential().Domain
    }
    $username = $credential.UserName
} elseif (0, 2 -contains $system.DomainRole) {
    $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Machine', $system.Name
    $username = $system.Name + '\' + $credential.GetNetworkCredential().UserName
} else {
    $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Domain', $system.Domain
    $username = $system.Domain + '\' + $credential.GetNetworkCredential().UserName
}

if ($pc.ValidateCredentials($username, $credential.GetNetworkCredential().Password)) {
    Write-Output "Validation successfull"
} else {
    Write-Output "Validation failed"
}

Solution

  • As mentioned by Kiran in the comments, you can pass a ContextOptions value to the PrincipalContext constructor:

    $DefaultNC = "DC=$($system.Domain -replace '\.',',DC=')"
    # ...
    $pc = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext 'Domain', $system.Domain, $DefaultNC, ([System.DirectoryServices.AccountManagement.ContextOptions]'SecureSocketLayer,Negotiate')
    

    An authentication option (Negotiate or SimpleBind) must be specified, thus the 'SecureSocketLayer,Negotiate' value