【问题标题】:Windows user credential validation on a different domain不同域上的 Windows 用户凭据验证
【发布时间】:2009-08-10 22:41:37
【问题描述】:

我正在尝试在未加入域的计算机上验证用户的 Windows 凭据。使用 SSPI API 似乎应该可以做到这一点,但我无法让它工作。

我包含了我一直在尝试的代码(为简洁起见,省略了资源清理)。这些是重要的信息:

域控制器: control.dundermifflin.com
域: DUNDERMIFFLIN
用户: jim_halpert
通过:蜜蜂

(我正在一个气隙网络上进行测试,因此与真正的 dundermifflin.com 没有任何 DNS 冲突。)

我得到的错误是 SEC_E_LOGON_DENIED。我很肯定用户名和密码是正确的,因为我可以使用该用户使用其他应用程序登录。谁能指出我正确的方向?

#include <Windows.h>
#define SECURITY_WIN32
#include <Security.h>
#include <crtdbg.h>

#pragma comment( lib, "Secur32.lib" )

int main()
{
    SEC_CHAR* principal = "HOST/control.dundermifflin.com";
    SEC_CHAR* spn       = NULL;

    SEC_CHAR* domain = "DUNDERMIFFLIN";
    SEC_CHAR* user   = "jim_halpert";
    SEC_CHAR* pass   = "beesly";

    /////////////////////////////////////////////
    // Fill out the authentication information //
    /////////////////////////////////////////////

    SEC_WINNT_AUTH_IDENTITY auth;
    auth.Domain         = reinterpret_cast<unsigned char*>( domain );
    auth.DomainLength   = strlen( domain );
    auth.User           = reinterpret_cast<unsigned char*>( user );
    auth.UserLength     = strlen( user );
    auth.Password       = reinterpret_cast<unsigned char*>( pass );
    auth.PasswordLength = strlen( pass );
    auth.Flags          = SEC_WINNT_AUTH_IDENTITY_ANSI;

    ////////////////////////////////////////////
    // Allocate the client and server buffers //
    ////////////////////////////////////////////

    char clientOutBufferData[8192];
    char serverOutBufferData[8192];

    SecBuffer     clientOutBuffer;
    SecBufferDesc clientOutBufferDesc;

    SecBuffer     serverOutBuffer;
    SecBufferDesc serverOutBufferDesc;

    ///////////////////////////////////////////
    // Get the client and server credentials //
    ///////////////////////////////////////////

    CredHandle clientCredentials;
    CredHandle serverCredentials;

    SECURITY_STATUS status;

    status = ::AcquireCredentialsHandle( principal,
                                         "Negotiate",
                                         SECPKG_CRED_OUTBOUND,
                                         NULL,
                                         &auth,
                                         NULL,
                                         NULL,
                                         &clientCredentials,
                                         NULL );

    _ASSERT( status == SEC_E_OK );

    status = ::AcquireCredentialsHandle( principal,
                                         "Negotiate",
                                         SECPKG_CRED_INBOUND,
                                         NULL,
                                         NULL,
                                         NULL,
                                         NULL,
                                         &serverCredentials,
                                         NULL );

    _ASSERT( status == SEC_E_OK );

    //////////////////////////////////////
    // Initialize the security contexts //
    //////////////////////////////////////

    CtxtHandle clientContext = {};
    unsigned long clientContextAttr = 0;

    CtxtHandle serverContext = {};
    unsigned long serverContextAttr = 0;

    /////////////////////////////
    // Clear the client buffer //
    /////////////////////////////

    clientOutBuffer.BufferType = SECBUFFER_TOKEN;
    clientOutBuffer.cbBuffer   = sizeof clientOutBufferData;
    clientOutBuffer.pvBuffer   = clientOutBufferData;

    clientOutBufferDesc.cBuffers  = 1;
    clientOutBufferDesc.pBuffers  = &clientOutBuffer;
    clientOutBufferDesc.ulVersion = SECBUFFER_VERSION;

    ///////////////////////////////////
    // Initialize the client context //
    ///////////////////////////////////

    status = InitializeSecurityContext( &clientCredentials,
                                        NULL,
                                        spn,
                                        0,
                                        0,
                                        SECURITY_NATIVE_DREP,
                                        NULL,
                                        0,
                                        &clientContext,
                                        &clientOutBufferDesc,
                                        &clientContextAttr,
                                        NULL );

    _ASSERT( status == SEC_I_CONTINUE_NEEDED );

    /////////////////////////////
    // Clear the server buffer //
    /////////////////////////////

    serverOutBuffer.BufferType = SECBUFFER_TOKEN;
    serverOutBuffer.cbBuffer   = sizeof serverOutBufferData;
    serverOutBuffer.pvBuffer   = serverOutBufferData;

    serverOutBufferDesc.cBuffers  = 1;
    serverOutBufferDesc.pBuffers  = &serverOutBuffer;
    serverOutBufferDesc.ulVersion = SECBUFFER_VERSION;

    //////////////////////////////////////////////////////
    // Accept the client security context on the server //
    //////////////////////////////////////////////////////

    status = AcceptSecurityContext( &serverCredentials,
                                    NULL,
                                    &clientOutBufferDesc,
                                    0,
                                    SECURITY_NATIVE_DREP,
                                    &serverContext,
                                    &serverOutBufferDesc,
                                    &serverContextAttr,
                                    NULL );

    _ASSERT( status == SEC_I_CONTINUE_NEEDED );

    /////////////////////////////
    // Clear the client buffer //
    /////////////////////////////

    clientOutBuffer.BufferType = SECBUFFER_TOKEN;
    clientOutBuffer.cbBuffer   = sizeof clientOutBufferData;
    clientOutBuffer.pvBuffer   = clientOutBufferData;

    clientOutBufferDesc.cBuffers  = 1;
    clientOutBufferDesc.pBuffers  = &clientOutBuffer;
    clientOutBufferDesc.ulVersion = SECBUFFER_VERSION;

    ///////////////////////////////////////
    // Give the client the server buffer //
    ///////////////////////////////////////

    status = InitializeSecurityContext( &clientCredentials,
                                        &clientContext,
                                        spn,
                                        0,
                                        0,
                                        SECURITY_NATIVE_DREP,
                                        &serverOutBufferDesc,
                                        0,
                                        &clientContext,
                                        &clientOutBufferDesc,
                                        &clientContextAttr,
                                        NULL );

    _ASSERT( status == SEC_E_OK );

    //////////////////////////////////////////////////////
    // Accept the client security context on the server //
    //////////////////////////////////////////////////////

    status = AcceptSecurityContext( &serverCredentials,
                                    &serverContext,
                                    &clientOutBufferDesc,
                                    0,
                                    SECURITY_NATIVE_DREP,
                                    &serverContext,
                                    &serverOutBufferDesc,
                                    &serverContextAttr,
                                    NULL );

    _ASSERT( status == SEC_E_LOGON_DENIED );
}

【问题讨论】:

  • 你可能想编辑掉用户名密码...有点安全漏洞;)

标签: c++ windows credentials sspi


【解决方案1】:

这是行不通的,因为您在同一台机器上,不知道 control.dundermifflin.com 域。

如果要确认用户名和密码,最简单的方法是向实际域中的机器进行身份验证。它可以像“net use \dc\netlogon /u:username password”一样简单,但是您没有提到它是否必须通过 SSPI 完成。如果是,您需要在 DC 上找到一个服务来进行身份验证。例如,您可以使用 LDAP。

另一种可行的方法是告诉您的非域机器您尝试访问的域。这可以通过使用 ksetup 工具来完成。它将允许您为您拥有的域配置 KDC 主机名。查看 /AddKdc 选项。这将使 Kerberos 知道,对于提供的领域(也称为域),它应该转到为 KDC 请求提供的主机名。

我希望这会有所帮助。

【讨论】:

  • 这绝对有帮助。 SSPI 不是必需的,它似乎是阅读 KB 180548 后要走的路。如果这是一个愚蠢的问题,请原谅我:要使用服务进行身份验证,我需要打开一个套接字吗?
  • 这取决于服务。您需要与服务器端交换 SSPI 生成的 blob,因此这取决于服务使用的传输方式。我建议使用 LDAP 的原因是,如果您使用 LDAP API 并选择 Negotiate 作为 SSP,则不必担心某些细节以及是否正确调用 SSPI。我建议尝试 ksetup 配置并再次尝试该示例。另外,请记住,如果 SEC_I_CONTINUE_NEEDED,您的代码必须围绕 InitializeSecurityContext/AcceptSecurityContext 循环。让我知道它是否有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
  • 1970-01-01
  • 2010-09-24
  • 2021-08-10
  • 2014-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多