Currently I authenticate users against some AD using the following code:
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
try
{
// Bind
I know this answer is a few years late, but we just ran into the same situation as the original poster. Unfortunately, in our environment, we can't use LogonUser -- we needed a pure LDAP solution. It turns out there is a way to get the extended error code from a bind operation. It's a bit ugly, but it works:
catch(DirectoryServicesCOMException exc)
{
if((uint)exc.ExtendedError == 0x80090308)
{
LDAPErrors errCode = 0;
try
{
// Unfortunately, the only place to get the LDAP bind error code is in the "data" field of the
// extended error message, which is in this format:
// 80090308: LdapErr: DSID-0C09030B, comment: AcceptSecurityContext error, data 52e, v893
if(!string.IsNullOrEmpty(exc.ExtendedErrorMessage))
{
Match match = Regex.Match(exc.ExtendedErrorMessage, @" data (?[0-9A-Fa-f]+),");
if(match.Success)
{
string errCodeHex = match.Groups["errCode"].Value;
errCode = (LDAPErrors)Convert.ToInt32(errCodeHex, fromBase: 16);
}
}
}
catch { }
switch(errCode)
{
case LDAPErrors.ERROR_PASSWORD_EXPIRED:
case LDAPErrors.ERROR_PASSWORD_MUST_CHANGE:
throw new Exception("Your password has expired and must be changed.");
// Add any other special error handling here (account disabled, locked out, etc...).
}
}
// If the extended error handling doesn't work out, just throw the original exception.
throw;
}
And you'll need definitions for the error codes (there are a lot more of these at http://www.lifeasbob.com/code/errorcodes.aspx):
private enum LDAPErrors
{
ERROR_INVALID_PASSWORD = 0x56,
ERROR_PASSWORD_RESTRICTION = 0x52D,
ERROR_LOGON_FAILURE = 0x52e,
ERROR_ACCOUNT_RESTRICTION = 0x52f,
ERROR_INVALID_LOGON_HOURS = 0x530,
ERROR_INVALID_WORKSTATION = 0x531,
ERROR_PASSWORD_EXPIRED = 0x532,
ERROR_ACCOUNT_DISABLED = 0x533,
ERROR_ACCOUNT_EXPIRED = 0x701,
ERROR_PASSWORD_MUST_CHANGE = 0x773,
ERROR_ACCOUNT_LOCKED_OUT = 0x775,
ERROR_ENTRY_EXISTS = 0x2071,
}
I couldn't find this information anywhere else -- everyone just says you should use LogonUser. If there's a better solution, I'd love to hear it. If not, I hope this helps other people who can't call LogonUser.