views:

616

answers:

2

Hi,

I'm trying to validate a user's windows credentials on a computer that's not joined to the domain. It seems like this should be possible to do using the SSPI API, but I haven't been able to get it to work.

I included the code that I've been trying (resource cleanup omitted for brevity). These are the important pieces of information:

Domain Controller: control.dundermifflin.com
Domain: DUNDERMIFFLIN
User: jim_halpert
Pass: beesly

(I'm testing on an air-gapped network, so there isn't any DNS conflict with the real dundermifflin.com.)

The error I get is SEC_E_LOGON_DENIED. I'm positive that the username and password are correct, since I can logon with that user with other applications. Can anyone point me in the right direction?

#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 );
}
+1  A: 

This is not going to work, since you are on the same machine that doesn't know about the control.dundermifflin.com domain.

If you want to confirm the username and password, the easiest way is to to authenticate to a machine in the actual domain. It can be as easy as "net use \dc\netlogon /u:username password", but you didn't mention whether it is a must to be done through SSPI. If it is, you need to find a service on the DC to authenticate to. You can use LDAP for example.

Another way that can work is to tell your non-domain machine about the domain you are trying to reach. This can be done by using the ksetup tool. It will allow you to configure a KDC hostname for the domain you have. Look at the /AddKdc option. This will let Kerberos know that for the realm supplied (aka domain) it should go to the hostname provided as for KDC requests.

I hope this helps.

Nasko
It definitely helps. SSPI isn't a requirement, it just seemed like the way to go after reading KB 180548.Forgive me if this is a stupid question: To authenticate with a service, would I need to open a socket?
thudbang
It depends on the service. You need to exchange the blobs generated by SSPI with the server side, so it depends on the transport the service is using. The reason I suggested using LDAP is that if you use the LDAP API and select Negotiate as the SSP, you don't have to worry about some of the details and whether you are calling SSPI correctly.I would suggest trying the ksetup configuration and trying the example again. Also, keep in mind that your code must be looping around the InitializeSecurityContext/AcceptSecurityContext if SEC_I_CONTINUE_NEEDED.Let me know if it works out.
Nasko
A: 

Hi there.

What if I need to programmatically validate if a certain usergroup exists in the local domain? Which command/API/etc. can I use to check that?

Thanks in advance! :)