Win32: How to validate credentials against Active Directory?

前端 未结 5 667
一个人的身影
一个人的身影 2020-11-30 01:08

It has been asked, and answered for .NET, but now it\'s time to get an answer for native Win32 code:

How do i validate a Windows username and password?

相关标签:
5条回答
  • 2020-11-30 01:49

    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.

    0 讨论(0)
  • 2020-11-30 01:57

    There is a win32 API function called ldap_bind_s. The ldap_bind_s function authenticates a client against LDAP. See MSDN documentation for more information.

    0 讨论(0)
  • 2020-11-30 02:00

    I authenticated user, by username & password like this :

    username is user sn attribute value in Ldap server, like U12345

    userDN is user DistinguishedName in LdapServer

    public bool AuthenticateUser(string username, string password)
    {
    try
    {
    var ldapServerNameAndPort = "Servername:389";
    var userDN = string.Format("CN=0},OU=Users,OU=MyOU,DC=MyDC,DC=com",username);
    var conn = new LdapConnection(ldapServerNameAndPort)
    {
     AuthType = AuthType.Basic
    };
    conn.Bind(new NetworkCredential(userDN , password));
    return true;
    }
    catch (Exception e)
    {
     return false;
    }
    

    }

    0 讨论(0)
  • 2020-11-30 02:02

    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.

    0 讨论(0)
  • 2020-11-30 02:14

    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.

    0 讨论(0)
提交回复
热议问题