Active Directory (LDAP) - Текущий счет, заблокированный / Пароль, истек

В дополнение к другим ответам, бросая решение в чистом функциональном отношении вынуждает понять проблему лучше. С другой стороны размышление в функциональном стиле разработает лучше* навыки решения проблем.

*Either, потому что функциональная парадигма лучше или потому что это предоставит дополнительный угол атаки.

18
задан Jabezz 8 September 2009 в 13:25
поделиться

3 ответа

A little late but I'll throw this out there.

If you want to REALLY be able to determine the specific reason that an account is failing authentication (there are many more reasons other than wrong password, expired, lockout, etc.), you can use the windows API LogonUser. Don't be intimidated by it - it is easier than it looks. You simply call LogonUser, and if it fails you look at the Marshal.GetLastWin32Error() which will give you a return code that indicates the (very) specific reason that the logon failed.

However, you're not going to be able to call this in the context of the user you're authenticating; you're going to need a priveleged account - I believe the requirement is SE_TCB_NAME (aka SeTcbPrivilege) - a user account that has the right to 'Act as part of the operating system'.

//Your new authenticate code snippet:
        try
        {
            if (!LogonUser(user, domain, pass, LogonTypes.Network, LogonProviders.Default, out token))
            {
                errorCode = Marshal.GetLastWin32Error();
                success = false;
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            CloseHandle(token);    
        }            
        success = true;

if it fails, you get one of the return codes (there are more that you can look up, but these are the important ones:

 //See http://support.microsoft.com/kb/155012
    const int ERROR_PASSWORD_MUST_CHANGE = 1907;
    const int ERROR_LOGON_FAILURE = 1326;
    const int ERROR_ACCOUNT_RESTRICTION = 1327;
    const int ERROR_ACCOUNT_DISABLED = 1331;
    const int ERROR_INVALID_LOGON_HOURS = 1328;
    const int ERROR_NO_LOGON_SERVERS = 1311;
    const int ERROR_INVALID_WORKSTATION = 1329;
    const int ERROR_ACCOUNT_LOCKED_OUT = 1909;      //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!!
    const int ERROR_ACCOUNT_EXPIRED = 1793;
    const int ERROR_PASSWORD_EXPIRED = 1330;  

The rest is just copy/paste to get the DLLImports and values to pass in

  //here are enums
    enum LogonTypes : uint
        {
            Interactive = 2,
            Network =3,
            Batch = 4,
            Service = 5,
            Unlock = 7,
            NetworkCleartext = 8,
            NewCredentials = 9
        }
        enum LogonProviders : uint
        {
            Default = 0, // default for platform (use this!)
            WinNT35,     // sends smoke signals to authority
            WinNT40,     // uses NTLM
            WinNT50      // negotiates Kerb or NTLM
        }

//Paste these DLLImports

[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);
16
ответ дан 30 November 2019 в 08:15
поделиться

Проверить, что срок действия пароля истек, относительно просто. - по крайней мере, в Windows (не знаю, как с этим справляются другие системы): когда значение Int64 для «pwdLastSet» равно 0, тогда пользователю придется изменить свой (или ее) пароль при следующем входе в систему. Самый простой способ проверить это - включить это свойство в свой DirectorySearcher:

DirectorySearcher search = new DirectorySearcher(entry)
      { Filter = "(sAMAccountName=" + username + ")" };
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("pwdLastSet");

SearchResult result = search.FindOne();
if (result == null)
{
    return false;
}

Int64 pwdLastSetValue = (Int64)result.Properties["pwdLastSet"][0];

Что касается проверки «учетная запись заблокирована» - сначала это кажется простым, но не ... Флаг «UF_Lockout» в «userAccountControl» "не выполняет свою работу надежно.

Начиная с Windows 2003 AD, есть новый вычисляемый атрибут, который вы можете проверить: msDS-User-Account-Control-Computed .

При наличии DirectoryEntry пользователь , вы можете сделать:

string attribName = "msDS-User-Account-Control-Computed";
user.RefreshCache(new string[] { attribName });

const int UF_LOCKOUT = 0x0010;

int userFlags = (int)user.Properties[attribName].Value;

if(userFlags & UF_LOCKOUT == UF_LOCKOUT) 
{
   // if this is the case, the account is locked out
}

Если вы можете использовать .NET 3.5, все стало намного проще - ознакомьтесь со статьей MSDN о том, как работать с пользователями и группами в .NET 3.5 с помощью System.DirectoryServices.AccountManagement пространство имен. Например, теперь у вас есть свойство IsAccountLockedOut в классе UserPrincipal, которое надежно сообщает вам, заблокирована ли учетная запись.

Надеюсь, это поможет!

Marc

4
ответ дан 30 November 2019 в 08:15
поделиться

Вот атрибуты AD LDAP, которые изменяются для пользователя, когда пароль заблокирован (первое значение) по сравнению с тем, когда пароль не заблокирован (второе значение). badPwdCount и lockoutTime , очевидно, наиболее актуальны. Я не уверен, нужно ли обновлять uSNChanged и whenChanged вручную или нет.

$ diff LockedOut.ldif NotLockedOut.ldif :

< badPwdCount: 3
> badPwdCount: 0

< lockoutTime: 129144318210315776
> lockoutTime: 0

< uSNChanged: 8064871
> uSNChanged: 8065084

< whenChanged: 20100330141028.0Z
> whenChanged: 20100330141932.0Z
1
ответ дан 30 November 2019 в 08:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: