LDAP SASL (Kerberos) is successful but I can't perform ldap_search_s

139 views Asked by At

Wireshark packet here I would like to ask you a question about implementing multiple authentication with Kerberos, using SSPI and LDAP API. My environment is Windows Server 2019 and the client machine is a member of the domain.

I am using the guidelines described at: How to use ldap_sasl_bind in WinLDAP? and ldap_sasl_bind_s(GSSAPI) - What should be provided in the credentials BERVAL structure

After two days, I finally found out how to use ldap_sasl_bind_s with the GSSAPI mechanism. But I have trouble when combining ldap_sasl_bind_s with ldap_search_s though I can see ldap_sasl_bind_s is successful through wireshark tools.

Below is my code is updated:

void LDAP_SASLBindGSSAPI()
{
    LDAP* ld;
    int rc = 0;
    const int version = LDAP_VERSION3;
    SEC_WINNT_AUTH_IDENTITY wincreds;
    struct berval* serversp = NULL;
    SECURITY_STATUS res;
    CredHandle credhandle;
    CtxtHandle newhandle;
    SecBufferDesc OutBuffDesc;
    SecBuffer OutSecBuff;

    SecBufferDesc InBuffDesc;
    SecBuffer InSecBuff;
    unsigned long contextattr;
    static PTCHAR lpPackageName = (PTCHAR)"Kerberos";
    TimeStamp timeExpired;

    ZeroMemory(&wincreds, sizeof(wincreds));

    std::string username = "username";
    std::string password = "Mypassword";
    std::string domain = "TEST.COM";

    // Set credential information
    wincreds.User = (unsigned char*)username.c_str();
    wincreds.UserLength = username.length();
    wincreds.Password = (unsigned char*)password.c_str();
    wincreds.PasswordLength = password.length();
    wincreds.Domain = (unsigned char*)domain.c_str();
    wincreds.DomainLength = domain.length();  
    wincreds.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

    res = AcquireCredentialsHandleA(NULL, lpPackageName, SECPKG_CRED_BOTH, NULL, &wincreds, NULL, NULL, &credhandle, &timeExpired);

    // Buffer for the output token.
    OutBuffDesc.ulVersion = 0;
    OutBuffDesc.cBuffers = 1;
    OutBuffDesc.pBuffers = &OutSecBuff;

    OutSecBuff.BufferType = SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer = NULL;

    ld = ldap_init((PCHAR)"WIN-CAV3F6MES9M.TEST.COM", LDAP_PORT);
    if (ld == nullptr)
    {
        std::cout << "Initialize ldap failed\n";
    }

    rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
    rc = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);

    ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_OFF);
    ldap_set_option(ld, LDAP_OPT_TLS, LDAP_OPT_OFF);

    if (rc != LDAP_SUCCESS)
    {
        std::cout << "set LDAP_OPT_REFERRALS failed\n";
    }
    rc = ldap_connect(ld, NULL); // Need to connect before SASL bind!
    std::cout << "result: " << rc << std::endl;
    if (rc != LDAP_SUCCESS) {
        // Connection failed
        std::cout << "Connection failed\n";
        ldap_unbind(ld);
        return;
    }

    //InitializeBaseLDAP(ld);

    do {
        if (serversp != NULL)
        {
            InBuffDesc.ulVersion = 0;
            InBuffDesc.cBuffers = 1;
            InBuffDesc.pBuffers = &InSecBuff;

            InSecBuff.cbBuffer = serversp->bv_len;
            InSecBuff.BufferType = SECBUFFER_TOKEN;
            InSecBuff.pvBuffer = serversp->bv_val;

            res = InitializeSecurityContextA(
                &credhandle,
                &newhandle,
                (PCHAR)"ldap/[email protected]",
                ISC_REQ_INTEGRITY | ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE,
                0,
                SECURITY_NATIVE_DREP,
                &InBuffDesc,
                0,
                &newhandle,
                &OutBuffDesc,
                &contextattr,
                &timeExpired);

            std::cout << "Second InitializeSecurityContextA-Res: " << res << std::endl;

            struct berval cred;
            cred.bv_len = OutSecBuff.cbBuffer;
            cred.bv_val = (char*)OutSecBuff.pvBuffer;
            rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &cred, NULL, NULL, &serversp);

            std::cout << "rc= " << rc << std::endl;
            ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
            std::cout << "res - second call ldap_sasl_bind_sA: " << res << std::endl;
            break;
        }
        else
        {
            res = InitializeSecurityContextA(
                &credhandle,
                NULL,
                (PCHAR)"ldap/[email protected]",
                ISC_REQ_INTEGRITY | ISC_REQ_MUTUAL_AUTH | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_DELEGATE,
                0,
                SECURITY_NATIVE_DREP,
                NULL,
                0,
                &newhandle,
                &OutBuffDesc,
                &contextattr,
                &timeExpired);

            std::cout << "First InitializeSecurityContextA-Res: " << res << std::endl;

            struct berval cred;
            cred.bv_len = OutSecBuff.cbBuffer;
            cred.bv_val = (char*)OutSecBuff.pvBuffer;
            rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &cred, NULL, NULL, &serversp);

            std::cout << "rc= " << rc << std::endl;
            ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
            std::cout << "res - first call ldap_sasl_bind_sA: " << res << std::endl;
        }

    } while (res == LDAP_SASL_BIND_IN_PROGRESS);

    if (rc != LDAP_SUCCESS) 
    {
        printf("Bind failed with 0x%x\n", rc);
    }
    else 
    {
        printf("First and Second Bind succeeded\n");
        PrintHexDump(serversp->bv_len, (PBYTE)serversp->bv_val);

        SecBufferDesc DecryptBuffDesc;
        SecBuffer DecryptSecBuffer[2];
        

        DecryptBuffDesc.ulVersion = 0;
        DecryptBuffDesc.cBuffers = 2;
        DecryptBuffDesc.pBuffers = DecryptSecBuffer;

        DecryptSecBuffer[0].BufferType = SECBUFFER_STREAM;
        DecryptSecBuffer[0].cbBuffer = serversp->bv_len;
        DecryptSecBuffer[0].pvBuffer = serversp->bv_val;

        DecryptSecBuffer[1].BufferType = SECBUFFER_DATA;
        ULONG ulQop;

        res = DecryptMessage(&newhandle, &DecryptBuffDesc, 0, &ulQop);

        std::cout << "res-DecryptMessage: " << res << std::endl;

        if (DecryptSecBuffer[1].pvBuffer != NULL)
        {
            std::cout << "Hexa dump: " << std::endl;
            PrintHexDump(DecryptSecBuffer[1].cbBuffer, (PBYTE)DecryptSecBuffer[1].pvBuffer);

            unsigned char* ptr = (unsigned char*)DecryptSecBuffer[1].pvBuffer;
            int maxsize = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
            std::cout << "maxsize: " << maxsize << std::endl;
            ptr = (unsigned char*)malloc(4);
            ptr[0] = 0x04;
            ptr[1] = maxsize >> 16;
            ptr[2] = maxsize >> 8;
            ptr[3] = maxsize;
            // Encrypt Message
            SecBufferDesc EncryptBuffDesc;
            SecBuffer EncryptSecBuffer[3];

            EncryptBuffDesc.ulVersion = 0;
            EncryptBuffDesc.cBuffers = 3;
            EncryptBuffDesc.pBuffers = EncryptSecBuffer;


            unsigned char* buffer = (unsigned char*)malloc(maxsize);
            EncryptSecBuffer[0].BufferType = SECBUFFER_TOKEN;
            EncryptSecBuffer[0].cbBuffer = maxsize;
            EncryptSecBuffer[0].pvBuffer = buffer;

            EncryptSecBuffer[1].cbBuffer = 4;
            EncryptSecBuffer[1].BufferType = SECBUFFER_DATA;
            EncryptSecBuffer[1].pvBuffer = ptr;

            EncryptSecBuffer[2].BufferType = SECBUFFER_PADDING;

            res = EncryptMessage(&newhandle, 0, &EncryptBuffDesc, 0);
            std::cout << "res-EncryptMessage: " << res << std::endl;

            if (EncryptSecBuffer[0].pvBuffer != NULL)
            {
                std::cout << "EncryptSecBuffer token size: " << EncryptSecBuffer[0].cbBuffer << std::endl;
                std::cout << "EncryptSecBuffer data size: " << EncryptSecBuffer[1].cbBuffer << std::endl;
                std::cout << "EncryptSecBuffer padding size: " << EncryptSecBuffer[2].cbBuffer << std::endl;
                PrintHexDump(EncryptSecBuffer[0].cbBuffer, (PBYTE)EncryptSecBuffer[0].pvBuffer);
            }

            int cbLastClientBuff = EncryptSecBuffer[0].cbBuffer + EncryptSecBuffer[1].cbBuffer + EncryptSecBuffer[2].cbBuffer;
            unsigned char* lastClientBuff = (unsigned char*)malloc(cbLastClientBuff);

            int index = 0;
            memcpy(lastClientBuff, EncryptSecBuffer[0].pvBuffer, EncryptSecBuffer[0].cbBuffer);
            index += EncryptSecBuffer[0].cbBuffer;
            memcpy(lastClientBuff + index, EncryptSecBuffer[1].pvBuffer, EncryptSecBuffer[1].cbBuffer);
            index += EncryptSecBuffer[1].cbBuffer;
            memcpy(lastClientBuff + index, EncryptSecBuffer[2].pvBuffer, EncryptSecBuffer[2].cbBuffer);


            struct berval lastClientCred;
            lastClientCred.bv_len = cbLastClientBuff;
            lastClientCred.bv_val = (char*)lastClientBuff;
            rc = ldap_sasl_bind_sA(ld, (PCHAR)"", (PCHAR)"GSSAPI", &lastClientCred , NULL, NULL, &serversp);

            std::cout << "rc= " << rc << std::endl;
            ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res);
            std::cout << "res - last call ldap_sasl_bind_sA: " << res << std::endl;
            if (rc == LDAP_SUCCESS && res == LDAP_SUCCESS)
            {
                std::cout << "ldap_sasl_bind_s successfully\n";
            }
        }
    }
    // CAN NOT SEARCH BECAUSE BIND PROCESS IS NOT YET SUCCESSED!!!
    PCHAR pMyAttributes[9];
    pMyAttributes[0] = (PCHAR)"minPwdLength"; 
    pMyAttributes[1] = (PCHAR)"pwdHistoryLength"; 
    pMyAttributes[2] = (PCHAR)"maxPwdAge"; 
    pMyAttributes[3] = (PCHAR)"minPwdAge"; 
    pMyAttributes[4] = (PCHAR)"pwdProperties"; 
    pMyAttributes[5] = (PCHAR)"lockoutThreshold"; 
    pMyAttributes[6] = (PCHAR)"lockoutDuration"; 
    pMyAttributes[7] = (PCHAR)"lockoutObservationWindow";
    pMyAttributes[8] = NULL;

    ULONG ldapResult;
    LDAPMessage* searchResult = nullptr;
    std::string filter = "((objectClass=domain))";
    ldapResult = ldap_search_sA(ld, (PCHAR)"DC=TEST,DC=COM", LDAP_SCOPE_SUBTREE, (PCHAR)filter.c_str(), pMyAttributes, 0, &searchResult);

}

I have followed the guidelines as well as read the answer from the link: SASL bind over GSSAPI using Kerberos credentials the with ldap_sasl_bind_s function

But I don't know whether the sasl bind process is finished or not even though I can see sals bind success through the Wireshark tool. The reason I said that I'm not sure sals bind process is done or not due that I can't perform ldap_search_s after that. LDAP server didn't respond to anything when I requested a search.

Thank you in advance!

0

There are 0 answers