【问题标题】:Authentication against Active Directory with C++ on Linux在 Linux 上使用 C++ 对 Active Directory 进行身份验证
【发布时间】:2015-07-29 15:14:20
【问题描述】:

我很惊讶在这方面的例子如此之少。我基本上需要对 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) 标志。看看this question/answer,它看起来像是 ldap_interactive_bind_s() 可能,但它涉及的更多一些,因为我必须回电。

在上面的示例中,我设置了密码,但没有任何类型的 binddn/用户名,它假定我正在尝试以谁身份进行身份验证?

【问题讨论】:

  • 嗨!我以前从未使用过 Active Directory,但我正在尝试在 Ubuntu 20.04 上做类似的事情。当我尝试重现您的示例时,编译器找不到 ldap.h 并且命令 locate ldap.h 什么也没有产生。这可能是一个愚蠢的问题,但我需要安装什么才能获得该库?我在网上找到了很多与 LDAP 相关的说明,但目前似乎没有任何效果。

标签: c++ linux active-directory kerberos


【解决方案1】:

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

此用户名格式不正确。你不需要在你的用户名中指定sAMAccountName,也不需要指定dc,除非你使用Distinguished Name。用户名选项很少。

  1. 专有名称

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

  1. sAMaccountName

杰史密斯

  1. 旧版 Windows 的用户路径

“Fabrikam\jeffsmith”。

  1. 用户主体名称 (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
);

使用此方法,您可以通过指定 usernamepassword 来绑定到 Active Directory 中的对象。

如果绑定成功,返回码为S_OK,否则会得到不同的错误信息。

我不是每天都在C++ 中编写程序。我通常在C# 世界中使用Active DirectoryActive Directory Lightweight Services。但是我编写的这个示例代码向您展示了如何调用ADsOpenObject 方法以使用指定的凭据绑定到 ADSI 对象。在您的情况下,只需 authenticate

#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,我相信你的问题是你如何绑定。由于您使用的是默认端口,因此您没有使用安全端口。检查此link。它解释了不同的绑定选项。
  • 如果是这样,它是如何与 ldapsearch 一起工作的?我指的是默认端口,它工作得很好。
  • 我没有看到您在 ldap 搜索中指定任何密码。
  • creds 结构——然后传入 ldap_sasl_bind_s()——第四个参数(在机制之后)。对?虽然我想知道它会尝试对哪个用户进行身份验证。