Win32: How to validate credentials against Active Directory? Win32: How to validate credentials against Active Directory? windows windows

Win32: How to validate credentials against Active Directory?


Here is Microsoft's recommendation.

As for the other answers, I'm not really sure why you're shooting them down. You are complaining about (relatively edge case) failures while trying to validate credentials, but if you are going to actually do something with those credentials then that operation is just going to fail anyway. If you are not going to actually do something with those credentials, then why do you need to validate them in the first place? It seems like a somewhat contrived situation, but obviously I don't know what you're trying to accomplish.


For the native equivalnt of your valid .NET solution see this MSDN page and ldap_bind

Howerver I think that LogonUser is the right API for the task when use with LOGON32_LOGON_NETWORK. Note that the limitation of SE_CHANGE_NOTIFY_NAME is only for Windows 2000 (so Windows XP and newer do not require this priviledge) and that by default SE_CHANGE_NOTIFY_NAME is enabled for all users. Also the MSDN page says

The SE_TCB_NAME privilege is not required for this function unless you are logging onto a Passport account.

In this case you are logging onto an AD account so SE_TCB_NAME is not required.


I might as well post the native code to validate a set of Windows credentials. It took a while to implement.

function TSSPLogon.LogonUser(username, password, domain: string; packageName: string='Negotiate'): HRESULT;var    ss: SECURITY_STATUS;    packageInfo: PSecPkgInfoA;    cbMaxToken: DWORD;    clientBuf: PByte;    serverBuf: PByte;    authIdentity: SEC_WINNT_AUTH_IDENTITY;    cbOut, cbIn: DWORD;    asClient: AUTH_SEQ;    asServer: AUTH_SEQ;    Done: boolean;begin{    If domain is blank will use the current domain.    To force validation against the local database use domain "."    sspiProviderName is the same of the Security Support Provider Package to use. Some possible choices are:            - Negotiate (Preferred)                        Introduced in Windows 2000 (secur32.dll)                        Selects Kerberos and if not available, NTLM protocol.                        Negotiate SSP provides single sign-on capability called as Integrated Windows Authentication.                        On Windows 7 and later, NEGOExts is introduced which negotiates the use of installed                        custom SSPs which are supported on the client and server for authentication.            - Kerberos                        Introduced in Windows 2000 and updated in Windows Vista to support AES) (secur32.dll)                        Preferred for mutual client-server domain authentication in Windows 2000 and later.            - NTLM                        Introduced in Windows NT 3.51 (Msv1_0.dll)                        Provides NTLM challenge/response authentication for client-server domains prior to                        Windows 2000 and for non-domain authentication (SMB/CIFS)            - Digest                        Introduced in Windows XP (wdigest.dll)                        Provides challenge/response based HTTP and SASL authentication between Windows and non-Windows systems where Kerberos is not available            - CredSSP                        Introduced in Windows Vista and available on Windows XP SP3 (credssp.dll)                        Provides SSO and Network Level Authentication for Remote Desktop Services            - Schannel                        Introduced in Windows 2000 and updated in Windows Vista to support stronger AES encryption and ECC (schannel.dll)                        Microsoft's implementation of TLS/SSL                        Public key cryptography SSP that provides encryption and secure communication for                        authenticating clients and servers over the internet. Updated in Windows 7 to support TLS 1.2.    If returns false, you can call GetLastError to get the reason for the failure}    // Get the maximum authentication token size for this package    ss := sspi.QuerySecurityPackageInfoA(PAnsiChar(packageName), packageInfo);    if ss <> SEC_E_OK then    begin        RaiseWin32Error('QuerySecurityPackageInfo "'+PackageName+'" failed', ss);        Result := ss;        Exit;    end;    try        cbMaxToken := packageInfo.cbMaxToken;    finally        FreeContextBuffer(packageInfo);    end;    // Initialize authorization identity structure    ZeroMemory(@authIdentity, SizeOf(authIdentity));    if Length(domain) > 0 then    begin        authIdentity.Domain := PChar(Domain);        authIdentity.DomainLength := Length(domain);    end;    if Length(userName) > 0 then    begin        authIdentity.User := PChar(UserName);        authIdentity.UserLength := Length(UserName);    end;    if Length(Password) > 0 then    begin        authIdentity.Password := PChar(Password);        authIdentity.PasswordLength := Length(Password);    end;    AuthIdentity.Flags := SEC_WINNT_AUTH_IDENTITY_ANSI; //SEC_WINNT_AUTH_IDENTITY_UNICODE    ZeroMemory(@asClient, SizeOf(asClient));    ZeroMemory(@asServer, SizeOf(asServer));    //Allocate buffers for client and server messages    GetMem(clientBuf, cbMaxToken);    GetMem(serverBuf, cbMaxToken);    try        done := False;        try            // Prepare client message (negotiate)            cbOut := cbMaxToken;            ss := Self.GenClientContext(@asClient, authIdentity, packageName, nil, 0, clientBuf, cbOut, done);            if ss < 0 then            begin                RaiseWin32Error('Error generating client context for negotiate', ss);                Result := ss;                Exit;            end;            // Prepare server message (challenge).            cbIn := cbOut;            cbOut := cbMaxToken;            ss := Self.GenServerContext(@asServer, packageName, clientBuf, cbIn, serverBuf, cbOut, done);            if ss < 0 then            begin                {                    Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED in the case of bad username or password.                    Unexpected Result:   Logon will succeed if you pass in a bad username and the guest account is enabled in the specified domain.                }                RaiseWin32Error('Error generating server message for challenge', ss);                Result := ss;                Exit;            end;            // Prepare client message (authenticate).            cbIn := cbOut;            cbOut := cbMaxToken;            ss := Self.GenClientContext(@asClient, authIdentity, packageName, serverBuf, cbIn, clientBuf, cbOut, done);            if ss < 0 then            begin                RaiseWin32Error('Error generating client client for authenticate', ss);                Result := ss;                Exit;            end;            // Prepare server message (authentication).            cbIn := cbOut;            cbOut := cbMaxToken;            ss := Self.GenServerContext(@asServer, packageName, clientBuf, cbIn, serverBuf, cbOut, done);            if ss < 0 then            begin                RaiseWin32Error('Error generating server message for authentication', ss);                Result := ss;                Exit;            end;        finally            //Free resources in client message            if asClient.fHaveCtxtHandle then                sspi.DeleteSecurityContext(@asClient.hctxt);            if asClient.fHaveCredHandle then                sspi.FreeCredentialHandle(@asClient.hcred);            //Free resources in server message            if asServer.fHaveCtxtHandle then                sspi.DeleteSecurityContext(@asServer.hctxt);            if asServer.fHaveCredHandle then                sspi.FreeCredentialHandle(@asServer.hcred);        end;    finally        FreeMem(clientBuf);        FreeMem(serverBuf);    end;    Result := S_OK;end;

Note: Any code released into public domain. No attribution required.