Je souhaite valider un ensemble d'informations d'identification sur le contrôleur de domaine. par exemple:
Username: STACKOVERFLOW\joel
Password: splotchy
Méthode 1. Interroger Active Directory avec l'emprunt d'identité
Beaucoup de gens suggèrent d'interroger Active Directory pour quelque chose. Si une exception est levée, vous savez que les informations d'identification ne sont pas valides - comme suggéré dans cette question de stackoverflow .
Cette approche présente cependant de sérieux inconvénients :
Vous authentifiez non seulement un compte de domaine, mais vous effectuez également une vérification d'autorisation implicite. Autrement dit, vous lisez les propriétés de l'AD à l'aide d'un jeton d'emprunt d'identité. Que faire si le compte par ailleurs valide n'a aucun droit de lecture à partir de l'AD? Par défaut, tous les utilisateurs ont un accès en lecture, mais les stratégies de domaine peuvent être définies pour désactiver les autorisations d'accès pour les comptes restreints (et / ou les groupes).
La liaison avec l'AD a une surcharge importante, le cache de schéma AD doit être chargé au niveau du client (cache ADSI dans le fournisseur ADSI utilisé par DirectoryServices). C'est à la fois le réseau et le serveur AD, qui consomme des ressources - et est trop cher pour une opération simple comme l'authentification d'un compte utilisateur.
Vous comptez sur un échec d'exception pour un cas non exceptionnel, et supposez que cela signifie un nom d'utilisateur et un mot de passe invalides. D'autres problèmes (par exemple une panne de réseau, une panne de connectivité AD, une erreur d'allocation de mémoire, etc.) sont alors interprétés à tort comme un échec d'authentification.
Méthode 2. API LogonUser Win32
D'autres ont suggéré d'utiliser la LogonUser()
fonction API. Cela semble bien, mais malheureusement, l'utilisateur appelant a parfois besoin d'une autorisation généralement donnée uniquement au système d'exploitation lui-même:
Le processus appelant LogonUser requiert le privilège SE_TCB_NAME. Si le processus appelant ne dispose pas de ce privilège, LogonUser échoue et GetLastError renvoie ERROR_PRIVILEGE_NOT_HELD.
Dans certains cas, le processus qui appelle LogonUser doit également avoir le privilège SE_CHANGE_NOTIFY_NAME activé; sinon, LogonUser échoue et GetLastError renvoie ERROR_ACCESS_DENIED. Ce privilège n'est pas requis pour le compte système local ou les comptes membres du groupe administrateurs. Par défaut, SE_CHANGE_NOTIFY_NAME est activé pour tous les utilisateurs, mais certains administrateurs peuvent le désactiver pour tout le monde.
Attribuer le privilège « Agir en tant que partie du système d'exploitation » n'est pas quelque chose que vous voulez faire bon gré mal gré - comme Microsoft le souligne dans un article de la base de connaissances :
... le processus qui appelle LogonUser doit avoir le privilège SE_TCB_NAME (dans le Gestionnaire des utilisateurs, il s'agit du droit « Agir en tant que partie du système d'exploitation »). Le privilège SE_TCB_NAME est très puissant et ne doit être accordé à aucun utilisateur arbitraire uniquement pour qu'il puisse exécuter une application qui doit valider les informations d'identification.
En outre, un appel à LogonUser()
échouera si un mot de passe vide est spécifié.
Quelle est la bonne façon d'authentifier un ensemble d'informations d'identification de domaine?
Il se trouve que j'appelle à partir de code managé, mais c'est une question Windows générale. On peut supposer que les clients ont installé .NET Framework 2.0.
la source
Réponses:
C # dans .NET 3.5 à l'aide de System.DirectoryServices.AccountManagement .
bool valid = false; using (PrincipalContext context = new PrincipalContext(ContextType.Domain)) { valid = context.ValidateCredentials( username, password ); }
Cela validera par rapport au domaine actuel. Consultez le constructeur PrincipalContext paramétré pour d'autres options.
la source
new PrincipalContext(ContextType.Machine)
place.using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security; using System.DirectoryServices.AccountManagement; public struct Credentials { public string Username; public string Password; } public class Domain_Authentication { public Credentials Credentials; public string Domain; public Domain_Authentication(string Username, string Password, string SDomain) { Credentials.Username = Username; Credentials.Password = Password; Domain = SDomain; } public bool IsValid() { using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain)) { // validate the credentials return pc.ValidateCredentials(Credentials.Username, Credentials.Password); } } }
la source
Domain
paramètre lors de la création duPrincipalContext
, quelque chose que je voulais savoir et que j'ai trouvé dans cette réponse.J'utilise le code suivant pour valider les informations d'identification. La méthode indiquée ci-dessous confirmera si les informations d'identification sont correctes et si ce n'est pas le cas, le mot de passe a expiré ou doit être modifié.
Je cherche quelque chose comme ça depuis des lustres ... J'espère donc que cela aide quelqu'un!
using System; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; using System.Runtime.InteropServices; namespace User { public static class UserValidation { [DllImport("advapi32.dll", SetLastError = true)] static extern bool LogonUser(string principal, string authority, string password, LogonTypes logonType, LogonProviders logonProvider, out IntPtr token); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr handle); enum LogonProviders : uint { Default = 0, // default for platform (use this!) WinNT35, // sends smoke signals to authority WinNT40, // uses NTLM WinNT50 // negotiates Kerb or NTLM } enum LogonTypes : uint { Interactive = 2, Network = 3, Batch = 4, Service = 5, Unlock = 7, NetworkCleartext = 8, NewCredentials = 9 } public const int ERROR_PASSWORD_MUST_CHANGE = 1907; public const int ERROR_LOGON_FAILURE = 1326; public const int ERROR_ACCOUNT_RESTRICTION = 1327; public const int ERROR_ACCOUNT_DISABLED = 1331; public const int ERROR_INVALID_LOGON_HOURS = 1328; public const int ERROR_NO_LOGON_SERVERS = 1311; public const int ERROR_INVALID_WORKSTATION = 1329; public const int ERROR_ACCOUNT_LOCKED_OUT = 1909; //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!! public const int ERROR_ACCOUNT_EXPIRED = 1793; public const int ERROR_PASSWORD_EXPIRED = 1330; public static int CheckUserLogon(string username, string password, string domain_fqdn) { int errorCode = 0; using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain_fqdn, "ADMIN_USER", "PASSWORD")) { if (!pc.ValidateCredentials(username, password)) { IntPtr token = new IntPtr(); try { if (!LogonUser(username, domain_fqdn, password, LogonTypes.Network, LogonProviders.Default, out token)) { errorCode = Marshal.GetLastWin32Error(); } } catch (Exception) { throw; } finally { CloseHandle(token); } } } return errorCode; } }
la source
Voici comment déterminer un utilisateur local:
public bool IsLocalUser() { return windowsIdentity.AuthenticationType == "NTLM"; }
Édité par Ian Boyd
Vous ne devriez plus du tout utiliser NTLM. Il est si ancien et si mauvais que le vérificateur d'application de Microsoft (qui est utilisé pour détecter les erreurs de programmation courantes) lancera un avertissement s'il vous détecte en utilisant NTLM.
Voici un chapitre de la documentation Application Verifier expliquant pourquoi ils ont un test si quelqu'un utilise par erreur NTLM:
la source
using System; using System.Collections.Generic; using System.Text; using System.DirectoryServices.AccountManagement; class WindowsCred { private const string SPLIT_1 = "\\"; public static bool ValidateW(string UserName, string Password) { bool valid = false; string Domain = ""; if (UserName.IndexOf("\\") != -1) { string[] arrT = UserName.Split(SPLIT_1[0]); Domain = arrT[0]; UserName = arrT[1]; } if (Domain.Length == 0) { Domain = System.Environment.MachineName; } using (PrincipalContext context = new PrincipalContext(ContextType.Domain, Domain)) { valid = context.ValidateCredentials(UserName, Password); } return valid; } }
Kashif Mushtaq Ottawa, Canada
la source