Check username/password in Active Directory from P

2019-08-27 16:05发布

问题:

Edit: I modified the code according to Andrei Galatyn's comment and hints that I shall not rely on token being nil when invalid, but it's still not working for PC's that are not part of the domain.


I want to verify if a user entered a username/password combination that is valid in a LDAP server.

Currently I use this code:

function CheckWinUserAccount(Username, Password, Domain : string) : boolean;
var token: THandle;
begin
     result:=False;
     if LogonUser( PChar(Username), PChar(Domain), PChar(Password),
                   LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, token) then
     begin
          CloseHandle(token);
          result:=True;
     end;
end;

It works perfectly if executed on a PC that is part of the LDAP domain, but not on a PC that only uses the LDAP PC as DNS but is not part of the domain.

My data:

  • Domain: graz.local
  • Username: LDTest

I tried entering the username as LDTest, as graz\LDTest and as graz.local\LDTest.

I also tried specifying the domain as graz, graz.local, ldap://graz.local

None of that worked. Any idea?

Btw: I was not sure if this is possible at all (accessing the domain server from a non-domain-PC), but using the LDAP Administrator (by Softerra) this works.

回答1:

As noted by Andrei Galatyn use LOGON32_LOGON_NETWORK instead of LOGON32_LOGON_INTERACTIVE when calling "LogonUser". The username should not include the domain name, the domain name can either be the NetBIOS domain name ("graz") or the DNS domain name ("graz.local").

EDIT: Using "LogonUser" only works if the client has already established a connection to the domain.

Here is the code which performs the authentication using LDAP.

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  JwaWinLDAP,
  JwaRpcDce;

var
    sUsername, sDomain, sPassword, sDC : String;
    LDAP : PLDAP;
    SWAI : SEC_WINNT_AUTH_IDENTITY;

begin
    if (ParamCount <> 4) then
    begin
        WriteLn ('WinLdapTest [username] [domain] [password] [domain controller]');
        Halt (1);
    end; { if }

    sUsername := ParamStr (1);
    sDomain := ParamStr (2);
    sPassword := ParamStr (3);
    sDC := ParamStr (4);

    LDAP := ldap_openW (PChar (sDC), LDAP_PORT);

    if (Assigned (LDAP)) then
        try
            SWAI.User := PChar (sUserName);
            SWAI.UserLength := Length (sUserName);
            SWAI.Domain := PChar (sDomain);
            SWAI.DomainLength := Length (sDomain);
            SWAI.Password := PChar (sPassword);
            SWAI.PasswordLength := Length (sPassword);
            SWAI.Flags := SEC_WINNT_AUTH_IDENTITY_UNICODE;

            if (ldap_bind_sW (LDAP, PChar (sDC), PChar (@SWAI),
                              LDAP_AUTH_NTLM) = LDAP_SUCCESS) then
                WriteLN ('"ldap_bind" success')
            else WriteLN ('"ldap_bind" failure');

        finally
            ldap_unbind (LDAP);
        end { try / finally }
    else WriteLn ('"ldap_open" failed');
end.

The code uses the JEDI API library and assumes that you're using Delphi 2009 or higher (Unicode strings). To automatically retrieve the DC name you could call DsGetDcName.