如何在WinLDAP中使用ldap_sasl_bind?

问题描述 投票:0回答:3

我目前使用 ldap_bind_s 通过 SEC_WINNT_AUTH_IDENTITY 结构绑定到我的 C 应用程序中的服务器,但该函数被标记为已弃用。因此我想将其更改为 ldap_sasl_bind_s 函数。

int main(void) { 
    LDAP *ld;
    int rc = 0;
    char *binddn = "cn=admin,dc=local";
    const int version = LDAP_VERSION3;
    SEC_WINNT_AUTH_IDENTITY wincreds;
    struct berval saslcred;

    wincreds.User = "admin";
    wincreds.UserLength = 5;
    wincreds.Password = "secret";
    wincreds.PasswordLength = 6;
    wincreds.Domain = NULL;
    wincreds.DomainLength = 0;
    wincreds.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

    ld = ldap_initA("localhost", LDAP_PORT);
    ldap_set_optionA(ld, LDAP_OPT_PROTOCOL_VERSION, &version);

    rc = ldap_bind_sA(ld, binddn, (PCHAR)&wincreds, LDAP_AUTH_DIGEST);
    printf("0x%x\n", rc); // It's OK (0x0) 
    ldap_unbind(ld);

    saslcred.bv_val = "secret";
    saslcred.bv_len = 6;

    rc = ldap_sasl_bind_sA(ld, binddn, "DIGEST-MD5", &saslcred, NULL, NULL, NULL);
    printf("0x%x\n", rc); // Returns with 0x59
    ldap_unbind(ld)

    return 0;
}

ldap_sasl_bind_s 返回 LDAP_PARAM_ERROR 代码。显然,上面的函数参数是错误的,但我找不到 winldap 和 SASL 绑定的工作示例代码。

我将不胜感激一些指导,如何使该代码工作。

c windows winapi ldap sasl
3个回答
1
投票

ldap_sasl_bind_sA
的最后一个参数不能为NULL。它必须指向函数可以放置服务器响应的位置 (
struct berval*
)。

...
struct berval* serverResponse = NULL;
rc = ldap_sasl_bind_sA(ld, binddn, "DIGEST-MD5", &saslcred, NULL, NULL, &serverResponse);
...

1
投票

最后,经过过去两周的一些研究和调试,我成功编写了一个工作示例代码,该代码使用 DIGEST-MD5 身份验证和 WinLDAP 的 ldap_sasl_bind_s 函数。相应的RFCthisanswer以及官方的SSPI文档给了我很多帮助。

我遇到的一些问题:

  • 无论文档如何描述 ldap_connect 函数:如果您想使用 ldap_sasl_bind_s 函数,首先调用它不仅仅是一个 “良好的编程实践”这是必要的。如果没有它,ldap_sasl_bind_s将返回并带有LDAP_SERVER_DOWN(0x51)错误代码。
  • 有效的 pszTargetName (digest-uri) 参数对于 InitializeSecurityContext 函数避免无效令牌错误至关重要。

我希望它能帮助其他人花更少的时间来了解如何将 SASL 绑定机制与 WinLDAP 结合使用。

#include <stdio.h>
#include <windows.h>
#include <winldap.h>

#define SECURITY_WIN32 1

#include <security.h>
#include <sspi.h>

int _tmain(int argc, _TCHAR* argv[]) {
    LDAP *ld;
    int rc = 0;
    const int version = LDAP_VERSION3;
    SEC_WINNT_AUTH_IDENTITY wincreds;
    struct berval *servresp = NULL;
    SECURITY_STATUS res;
    CredHandle credhandle;
    CtxtHandle newhandle;
    SecBufferDesc OutBuffDesc;
    SecBuffer OutSecBuff;
    SecBufferDesc InBuffDesc;
    SecBuffer InSecBuff;
    unsigned long contextattr;

    ZeroMemory(&wincreds, sizeof(wincreds));

    // Set credential information
    wincreds.User = (unsigned short *)L"root";
    wincreds.UserLength = 4;
    wincreds.Password = (unsigned short *)L"p@ssword";
    wincreds.PasswordLength = 8;
    wincreds.Domain = NULL;
    wincreds.DomainLength = 0;
    wincreds.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

    res = AcquireCredentialsHandle(NULL, L"WDigest", SECPKG_CRED_OUTBOUND,
        NULL, &wincreds, NULL, NULL, &credhandle, NULL);

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

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

    ld = ldap_init(L"localhost", LDAP_PORT);
    rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
    rc = ldap_connect(ld, NULL); // Need to connect before SASL bind!

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

            /* The digest-challenge will be passed as an input buffer to
            InitializeSecurityContext function */
            InSecBuff.cbBuffer = servresp->bv_len;
            InSecBuff.BufferType = SECBUFFER_TOKEN;
            InSecBuff.pvBuffer = servresp->bv_val;

            /* The OutBuffDesc will contain the digest-response. */
            res = InitializeSecurityContext(&credhandle, &newhandle, L"ldap/localhost", ISC_REQ_MUTUAL_AUTH | ISC_REQ_ALLOCATE_MEMORY,
                0, 0, &InBuffDesc, 0, &newhandle, &OutBuffDesc, &contextattr, NULL);
        }
        else {
            res = InitializeSecurityContext(&credhandle, NULL, L"ldap/localhost", ISC_REQ_MUTUAL_AUTH, 0, 0, NULL, 0, &newhandle, &OutBuffDesc, &contextattr, NULL);
        }

        switch (res) {
            case SEC_I_COMPLETE_NEEDED:
            case SEC_I_COMPLETE_AND_CONTINUE:
            case SEC_E_OK:
            case SEC_I_CONTINUE_NEEDED:
                break;
            case SEC_E_INVALID_HANDLE:
                return -2;
            case SEC_E_INVALID_TOKEN:
                return -1;
            default:
                break;
        }

        struct berval cred;
        cred.bv_len = OutSecBuff.cbBuffer;
        /* The digest-response will be passed to the server
        as credential after the second (loop)run. */
        cred.bv_val = (char *)OutSecBuff.pvBuffer;

        // The servresp will contain the digest-challange after the first call.
        rc = ldap_sasl_bind_s(ld, L"", L"DIGEST-MD5", &cred, NULL, NULL, &servresp);
        ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &res)
    } while (res == LDAP_SASL_BIND_IN_PROGRESS);

    if (rc != LDAP_SUCCESS) {
        printf("Bind failed with 0x%x\n", rc);
    } else {
        printf("Bind succeeded\n");
    }

    return 0;
}

0
投票

我尝试使用诺伊雷洛的例子。它工作正常。但是当我想使用ldap_search_s时,它不能与ldap_sasl_bind_s一起使用,相反,如果我使用ldap_bind_s,ldap_search_s可以正常工作。谁能给我解释一下这个案例吗?

提前致谢

© www.soinside.com 2019 - 2024. All rights reserved.