2009-08-10 38 views
2

我试图在未加入域的计算机上验证用户的Windows凭据。看起来应该可以使用SSPI API,但我一直无法使它工作。不同域上的Windows用户凭据验证

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

域控制器: control.dundermifflin.com
域: DUNDERMIFFLIN
用户: jim_halpert
通行证:比斯利

(I” m在有空隙的网络上进行测试,因此与真正的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); 
} 
+0

你可能想编辑出安全漏洞的用户名pass ...; – 2009-08-10 22:59:55

回答

2

这不会起作用,因为您位于不知道control.dundermifflin.com域的同一台计算机上。

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

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

我希望这会有所帮助。

+0

这肯定有帮助。 SSPI不是必需品,它看起来像是在阅读KB 180548之后要走的路。 请原谅我,如果这是一个愚蠢的问题:要使用服务进行身份验证,是否需要打开套接字? – thudbang 2009-08-11 00:43:17

+1

这取决于服务。您需要将由SSPI生成的斑点与服务器端交换,因此它取决于服务所使用的传输。我建议使用LDAP的原因是,如果您使用LDAP API并选择Negotiate作为SSP,则不必担心一些细节以及是否正确调用SSPI。 我会建议尝试ksetup配置并再次尝试该示例。另外,请记住,如果SEC_I_CONTINUE_NEEDED,您的代码必须围绕InitializeSecurityContext/AcceptSecurityContext循环。 让我知道它是否成功。 – Nasko 2009-08-11 15:29:31