ldap_sasl_bind_s(GSSAPI) - 凭证BERVAL结构体应该提供什么?

12
我正在尝试使用Microsoft LDAP C SDK中的ldap_sasl_bind_s方法,使用GSSAPI作为身份验证机制。ldap_sasl_bind_s希望将凭据作为BERVAL结构传递,该结构是不透明的。
给定用户名(或DN)和密码,我如何获得应传递给ldap_sasl_bind_sBERVAL结构
迄今为止,我找到的示例
  • 来自其他LDAP C SDK而不是来自Microsoft的SDK
  • 在需要简单身份验证时使用ldap_sasl_bind_s,但我需要使用GSSAPI
  • 在需要其他SASL身份验证机制时使用ldap_sasl_interactive_bind_s。但是,Microsoft SDK中没有ldap_sasl_interactive_bind_s
顺便说一句,目标是能够通过SASL绑定到各种LDAP服务器;现在是ActiveDirectory和OpenLDAP。
任何指针将不胜感激。
3个回答

17

我成功地使用ldap_sasl_bind_s执行了一个基于GSSAPI的LDAP SASL绑定。对于那些感兴趣的人,这里有一些指针。

关于客户端和服务器在GSSAPI SASL身份验证期间需要执行的操作的抽象描述,应该阅读“Kerberos V5(“GSSAPI”)简单身份验证和安全层(SASL)机制” RFC;具体来说,“身份验证协议交换的客户端部分” 部分很重要,因为它给出了我们需要执行的行动序列,以成功地绑定到使用Kerberos的LDAP服务器。

ldap_sasl_bind_s所期望的凭据的形式和意义取决于实际使用的认证机制,而在我们的情况下是Kerberos。

在Microsoft SDK中,Kerberos通过SSPI可用,这大致上是GSSAPI的Microsoft实现;与我们特定情况相关的方法是:AcquireCredentialsHandleInitializeSecurityContextDecryptMessageEncryptMessage

基于Kerberos的LDAP SASL绑定有3个阶段。

第一阶段

调用AcquireCredentialsHandleInitializeSecurityContext
这里有一些重要的注意事项:

  • 将指向包含实际凭据(域名、用户名、密码)的SEC_WINNT_AUTH_IDENTITY结构体的指针传递给AcquireCredentialsHandle,或者如果要使用当前线程的凭据,则传递NULL
  • 目标名称应该是映射到LDAP服务器运行帐户的SPN
  • 在调用InitializeSecurityContext时,必须请求相互身份验证。
如果所有重要的参数都是正确的 - 有效的凭证、有效的SPN和NULL输入令牌,InitializeSecurityContext调用应该返回SEC_I_CONTINUE_NEEDED并正确填充输出令牌。此输出令牌的内容应在BERVAL结构中,ldap_sasl_bind_s将其作为客户端凭据使用。
使用InitializeSecurityContext的输出令牌作为客户端凭据调用ldap_sasl_bind_s。如果所有参数都正确 - 空DN、GSSAPI作为机制名称 - 实际调用应返回LDAP_SUCCESS,且LDAP会话的最新LDAP错误应为LDAP_SASL_BIND_IN_PROGRESS
另外需要注意的是,可以通过在会话上调用ldap_get_option并将LDAP_OPT_ERROR_NUMBER作为选项来发现最近的LDAP错误。
第二阶段: 成功调用ldap_sasl_bind_s后,它的最后一个参数指向一个包含服务器凭据的BERVAL结构。现在应该使用此BERVAL结构的内容作为第二次调用InitializeSecurityContext的输入令牌。
第二次调用InitializeSecurityContext应返回SEC_OK和一个空输出令牌。
应将此空输出令牌用作另一个调用ldap_sasl_bind_s的客户端凭据。第二次调用ldap_sasl_bind_s应返回LDAP_SUCCESS,且LDAP会话的最新LDAP错误为LDAP_SASL_BIND_IN_PROGRESS
第三阶段:

在第二次成功调用ldap_sasl_bind_s后,它的最后一个参数指向一个包含服务器数据的BERVAL结构体。这些服务器数据应该被作为输入提供给DecryptMessage。正如之前提到的RFC中所规定的那样,解密后的数据必须是4个字节长。

客户端应该根据同一RFC中的信息构建其回复。
注意:在我的情况中,我省略了RFC中提到的授权ID。据我理解,空授权ID会导致使用认证ID进行授权。

然后客户端应该将其构建的回复作为输入传递给EncryptMessageEncryptMessage调用的输出应该作为客户端凭据传递给第三次也是最后一次调用ldap_sasl_bind_s

注意:在Kerberos下使用EncryptMessage的MSDN文档似乎不完整。Google的Code Search可以协助查找工作示例。此外,可查阅Samba源代码以获得上述流程的工作示例。


1
嗨,Catalina!我按照你描述的步骤进行了操作,但是我遇到了一个问题,希望你能帮助我。第一阶段的ldap_sasl_bind_s调用返回成功,ldap_get_option返回LDAP_SASL_BIND_IN_PROGRESS,但是最后一个参数指向一个空的BERVAL结构(大小为零)。如果我使用这些数据来构建第二阶段的SecBufferDesc结构,则对InitializeSecurityContext的以下调用将返回SEC_E_INVALID_TOKEN。ldap_sasl_bind_s是否应该返回一个空的BERVAL,还是我做错了什么?提前致谢! Juan - Juan Alvarez
脑海中唯一想到的是在第一次调用InitializeSecurityContext之前缺少ISC_REQ_MUTUAL_AUTH标志。 - Catalina Iacob
+1 很棒的东西。很遗憾在StackOverflow上没有太多人欣赏你的工作。 - Harvey Kwok
嗨,Catalina!也许你可以帮我解答这个问题:http://stackoverflow.com/q/14394642/475821 谢谢! - esmirnov
@esmirnov:很遗憾,我无法成功保护在ldap_sasl_bind_s(GSSAPI)之后进行的LDAP通信,最终我放弃了。很抱歉我不能提供更多帮助。 - Catalina Iacob
嗨,Catalina!我按照你描述的步骤进行操作,但在最后(第三次)调用ldap_sasl_bind_s时遇到了一些问题。也许你可以在这里回答我的问题:https://dev59.com/QI_ea4cB1Zd3GeqPJRLN,请你帮忙看一下?据我所知,我做的每一步都像你在这里描述的那样,但在最后一次调用ldap_sasl_bind_s之后,我收到了无效凭证错误。 - Grigor Aleksanyan

2

刚刚遇到了同样的 bug,花了一整天时间来解决它。感谢分享,Juan! - kostix

0

来自SunMSDN的文章。如果您尝试创建一个示例程序,可能会得到答案。

另一个

伪代码

struct berval   cred;
char mechanism[BUFSIZ];
getline( mechanism, sizeof(mechanism), stdin, "mechanism? " );
getline( passwd, sizeof(passwd), stdin,"credentials? " );
cred.bv_val = passwd;
cred.bv_len = strlen( passwd );
rc = ldap_sasl_bind_s( ld, dn, mechanism, &cred, NULL, NULL, NULL );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接