在Linux上使用C++对Active Directory进行身份验证

6
我很惊讶这方面的例子如此之少。 我基本上需要对Active Directory进行用户/密码身份验证。 我能够初始化与Active Directory的连接,但它总是给我一个“无效凭据”错误。 我想知道是否我传递了错误的内容。 这是我第一次尝试LDAP。 就此而言,我也可以接受另一种(可能有充分文档记录的)解决方案。
#include <iostream>
#include <stdio.h>

#define LDAP_SERVER "ldap://hq.mydomain.local/"

#include <ldap.h>

int main( int argc, char** argv )
{
    LDAP    *ld;
    int     version( LDAP_VERSION3 );
    int     rc;
    char    bind_dn[100];
    berval  creds;
    berval* serverCreds;

    if( ldap_initialize( &ld, LDAP_SERVER ) ) {
        std::cerr << "Error initializing ldap..." << std::endl;
        return 1;
    }

    ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

    creds.bv_val = "password";
    creds.bv_len = strlen("password");

    rc = ldap_sasl_bind_s( ld, "sAMAccountName=MYDOMAIN\\UserName,dc=mydomain,dc=local", "GSSAPI", &creds, NULL, NULL, &serverCreds );

    if ( rc != LDAP_SUCCESS ) {
        std::cerr << "ERROR: " << ldap_err2string( rc ) << std::endl; 
        return 1;
    } else {
        std::cout << "Success." << std::endl;
    }

    return 0;

}

编辑:

我想确保服务器端一切正常,所以进行了一些ldapsearch测试。起初它没有工作,但最终我成功了(至少在使用ldapsearch时是这样的)。

ldapsearch -D first.last@mydomain.local -H "ldap://hq.mydomain.local:389" -b "ou=Development,ou=Domain Users,dc=mydomain,dc=local" -W "sAMAccountName=first.last"

也许不是最好的方法。首先,关键是-D参数,并在末尾传递sAMAccountName。我将没有常见名称-只有Windows登录名和密码。如果密码通过,上述命令将显示用户信息。
其中的警告(我认为)是ldap_sasl_bind_s()没有设置-D(binddn)标志的等效项。查看此问题/答案中的{{link1:链接}},看起来ldap_interactive_bind_s()可能会,但涉及到更多步骤,因为我必须传递回调。
在上面的示例中,我设置了密码,但没有任何绑定dn/用户名,它假设我试图作为谁进行身份验证?

你好!我以前从未使用过Active Directory,但我正在尝试在Ubuntu 20.04上做类似的事情。当我尝试复制你的示例时,编译器找不到ldap.h,并且locate ldap.h命令也没有任何结果。这可能是一个愚蠢的问题,但我需要安装什么才能获取该库?我在网上找到了很多与LDAP相关的说明,但迄今为止似乎没有什么起作用的。 - MikeElmwood
1个回答

2

sAMAccountName=MYDOMAIN\UserName,dc=mydomain,dc=local

这种格式的用户名是不正确的。您不需要在用户名中指定sAMAccountName,也不需要指定dc,除非您正在使用Distinguished Name。您有几个选项可以选择用户名:

  1. 区分名称(Distinguished Name)

CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=Com

  1. sAMaccountName

jsmith

  1. 以前版本的Windows用户路径

"Fabrikam\jeffsmith"

  1. 用户主体名称(User Principal Name, UPN)

jeffsmith@Fabrikam.com

话虽如此,我不确定用户名是否是您遇到的唯一问题。我没有在本地运行您的代码。

虽然这个答案可能并不直接回答您的问题,因为我没有在Linux机器上测试过这个代码,但它可能会给您一个想法或引导您进入正确的方向。如果这个方法只适用于Windows,我也不会感到惊讶。

根据MSDN,有几种方法(methods)可以用来验证用户:

ADsOpenObject函数使用显式用户名和密码凭据绑定到ADSI对象。

此方法接受以下参数:

HRESULT ADsOpenObject(
  _In_  LPCWSTR lpszPathName,
  _In_  LPCWSTR lpszUserName,
  _In_  LPCWSTR lpszPassword,
  _In_  DWORD   dwReserved,
  _In_  REFIID  riid,
  _Out_ VOID    **ppObject
);

使用这种方法,您可以通过指定用户名密码来绑定Active Directory中的对象。
如果绑定成功,则返回代码为S_OK,否则您将收到不同的错误消息。
我不是每天都使用C++编写程序。我通常在C#世界中使用Active DirectoryActive Directory轻量级服务。但是我编写的这个示例代码向您展示了如何调用ADsOpenObject方法,以使用指定的凭据绑定到ADSI对象。在您的情况下,只需进行身份验证即可。
#include <iostream>
#include "activeds.h"

using namespace std;

int main(int argc, char* argv[])
{
   HRESULT hr;
   IADsContainer *pCont;
   IDispatch *pDisp = NULL;
   IADs *pUser;

   CoInitialize(NULL);

   hr = ADsOpenObject( L"LDAP://yourserver",
               L"username",
               L"password",
               ADS_FAST_BIND, //authentication option     
               IID_IADs,
               (void**) &pUser);


   if (SUCCEEDED(hr))
   {
      cout << "Successfully authenticated";
   }
   else
      cout << "Incorrect username or password";
   return hr;
}

根据你的设置,你可能需要调整ADS_AUTHENTICATION_ENUM。我建议你安装SSL证书并使用ADS_USE_SSL绑定。在AD中不使用SSL处理密码可能会成为噩梦。


谢谢您的帮助。我觉得这让我找到了正确的方向,因为我开始尝试不同的ldapsearch选项。请查看我上面的编辑。此外,我认为AdsOpenObject库在Linux上不可用。如果是这样,我无论如何都找不到它。这看起来是一种更好的方法。 - kiss-o-matic
@kiss-o-matic,我认为你的问题在于如何绑定。由于你正在使用默认端口,因此你没有使用安全连接。请查看这个链接。它解释了不同的绑定选项。 - smr5
我没有看到你在LDAP搜索中指定任何密码。 - smr5
你的LDP搜索部分是你发布的C++代码的一部分吗?还是独立存在的?你的LDAP搜索不包含任何密码,因此它会使用你的凭据在后台进行任何身份验证。 - smr5
ldapsearch是一个独立的命令行程序。我只是用它来查看我的AD服务器期望的信息。 - kiss-o-matic
显示剩余2条评论

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