Logon failure: unknown user name or bad password

             

Hi guys,

This article deals with windows user account authentication failure.

I'll try to explain somes tricks and tips I experienced while using the multiple ways available in VBScript, C# , C++ or even JAVA to authenticate a user account on a local machine or against Active Directory domain using LDAP.

I'll focus on failure issues when you get the following error or exception :

ERROR_LOGON_FAILURE
Logon failure: unknown user name or bad password.

Basically you get this error message when a logon attempt failed if the given password was bad, if the user account does not exist or if the password is expired, or if the password must be changed.

As a developer or IT Administrator you may need to know exactly why the attempt failed. 

There are actually multiple APIs to do so, let's take .NET case and  PrincipalContext class : https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.principalcontext.aspx

From Namespace : System.DirectoryServices.AccountManagement

This class behind the scene instatiates a COM object that makes a LDAP query to the Active Directory domain to try to authenticate the user.

You'll understand later that this is basically the reason why you can get the same error message for multiple distinguished cause.
Indeed, Active Directory Services Interface (ADSI) supports the following categories of error codes:
- ADSI error codes
- COM general error codes
- Win32 error codes for the Lightweight Directory Access Protocol (LDAP) provider.

All ADSI error codes are returned as COM HRESULT values and can be grouped into these three categories.

Because a Windows API can mix heterogeneous components, the system-supplied LDAP provider maps the standard LDAP error codes into the following Win32 error codes.

Standard Win32 error codes are also used to return ADSI error messages. In particular, the ADSI LDAP provider maps all the LDAP-defined error codes to Win32 error codes.

The following KB articles deals with this topic :
https://support.microsoft.com/kb/232282/ 

The error we got "ERROR_LOGON_FAILURE" is actually the Win32 error code for "LDAP_INVALID_CREDENTIALS".
The issue is that at an LDAP layer, there is no disction made with the cases we would like to differenciate.

The "LDAP_INVALID_CREDENTIALS" will be returned in any cases, if the password is wrong, expired or needs to be changed.

There are actually multiple workarounds you can use to achieve your goal.

Once you get the error : “Logon failure: unknown user name or bad password. ” If you want to determine if the login attempt failed because the password is bad or because it is expired, what you can do is to query for that user on Active Directory using LDAP.
Then look for value of PasswordExpired or ms-DS-User-Password-Expired attributes.

According to the documentation : https://msdn.microsoft.com/en-us/library/ms677838(VS.85).aspx
It Indicates whether the password for the account that this attribute references has expired. True if the password has expired; otherwise, False.

To finish, there is Win32 API you can use to authenticate a user that is not based on that COM component and will return you the correct error code depending if the password is expired or if the password entered by the user is wrong.

You can use LogonUser https://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx that will return one of the following error :

ERROR_PASSWORD_EXPIRED 1330
ERROR_LOGON_FAILURE 1326
ERROR_PASSWORD_MUST_CHANGE 1907

Here we go, I hope that post helped you.

Cheers,

--Michel

Salut à tous,

Cet article traite des problèmes d'authentification des comptes utilisateur Windows.

Je vais tenter partager avec vous quelques expériences rencontrées lors de développements VBScript, C# , C++ ou même JAVA pour authentifier un compte utilisateur Windows sur une machine locale ou dans un domaine Active Directory en utilisant LDAP.

Je vous propose de nous interesser à l'erreur ou l'exception :

ERROR_LOGON_FAILURE
Logon failure: unknown user name or bad password.

Typiquement vous obtenez ce message d'erreur losque qu'une tentative d'authentification échoue, si le mot de passe renseigné est incorrecte, si le compte utilisateur n'existe pas, si le mot de passe du compte est expiré ou alors doit être changé.

En tant que développeur ou Administrateur d'un parc informatique nous pouvons avoir besoin de connaitre la source exacte de l'erreur.

Il existe divers méthodes pour authentifier un compte, prenons par exemple l'exemple .Net avec la classe PrincipalContext : https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.principalcontext.aspx

De l'espace de nom : System.DirectoryServices.AccountManagement

Le méchanisme interne de cette classe instancie un objet COM qui effectue des requêtes LDAP vers le domaine  Active Directory afin de tenter une authentification.

Vous comprendrez pourquoi cette implémentation est en fait problématique dans notre effort de différentiation des types d'erreurs liés à l'authentification.

En effet,  Active Directory Services Interface (ADSI) supporte les catégories suivantes de code d'erreurs :
- Les codes d'erreurs ADSI
- Les codes d'erreurs standards de COM
- Les codes d'erreurs WIN32 spécifique au fournisseur Lightweight Directory Access Protocol (LDAP)

Toutes les erreurs ADSI sont retournées comme des valeurs HRESULT COM qui peuvent être regroupées dans ces trois catégories.

Parce qu'une API Windows peut reposer sur des composants hétérogènes, le système propose un fournisseur LDAP dont les codes d'erreurs ont une correspondance avec les codes d'erreurs Win32.

Les codes d'erreurs standards Win32 sont aussi utilisés pour retourner des message d'erreur ADSI. Plus précisément les fournisseurs ADSI LDAP font la correspondance entre toutes les erreurs définies par LDAP  et leurs équivalents Win32.

L'article KB suivant traite de ce sujet :
https://support.microsoft.com/kb/232282/ 

L'erreur  "ERROR_LOGON_FAILURE" que nous traitons dans cet article est en fait l'équivalent Win32 de l'erreur LDAP "LDAP_INVALID_CREDENTIALS".

Le problème qui se pose c'est qu'au niveu LDAP, il n'y a pas de disctinction faite entre les différents cas de figures qui nous interressent.

L'erreur "LDAP_INVALID_CREDENTIALS" sera retournée par l'API appelante dans tous les cas, que le mot de passe du compte soit mauvais, expiré ou doive être changé.
C'est donc assez problématique et cela masque la vraie nature du problème.

Il existe cependant des moyens de contournement afin de remonter à la source du problème.

Une fois que vous avez l'erreur : “Logon failure: unknown user name or bad password. ” si vous souhaiter déterminer si l'authentification a échoué parce que le mot de passe du compte fournit était incorrect ou s'il a expiré vous pouvez faire une requête LDAP afin d'obtenir les valeurs des attributs du compte suivants  PasswordExpired ou ms-DS-User-Password-Expired.

D'après la documentation : https://msdn.microsoft.com/en-us/library/ms677838(VS.85).aspx
Cet attribue indique si le mot de passe est expiré. La valeur True  sera renseignée si le mot de passe est expiré; sinon, False.

Pour terminer, il existe aussi des API Win32 pour authentifier un compte qui ne sont pas basés sur ce composant COM et qui retourneront un code d'erreur approprié.

Vous pouvez utiliser par exemple LogonUser https://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx qui vous retournera l'une des erreurs suivantes :

ERROR_PASSWORD_EXPIRED 1330
ERROR_LOGON_FAILURE 1326
ERROR_PASSWORD_MUST_CHANGE 1907

Voilà j'espère que cet article vous a été utile.

Bon code.

--Michel