NTLM Domain Authentication in CE

A question came up in todays CF chat about authenticating a CE device to a domain.  While I don’t have ready-made CF code for it (though that might be an interesting task) here’s a C version:

#include <WINDOWS.H>
#include <SECURITY.H>
#include <SSPI.H>
#define SEC_PACKAGE _T(“Microsoft Unified Security Protocol Provider”)
typedef struct _AUTH_SEQ {
BOOL fInitialized;
BOOL fHaveCredHandle;
BOOL fHaveCtxtHandle;
CredHandle hcred;
struct _SecHandle hctxt;
} AUTH_SEQ, *PAUTH_SEQ;
BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone);
BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,
PDWORD pcbOut, PBOOL pfDone);
BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword);

TCHAR *lpszUserName = _T(“ctacke”);
TCHAR *lpszDomainName = _T(“mydomain”);
TCHAR *lpszPassword = _T(“mypassword”);

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nCmShow)
{
return SSPLogonUser(lpszDomainName, lpszUserName, lpszPassword);
}
BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone)
{
DEBUGMSG(TRUE, (_T(“+GenServerContextrn”)));
SECURITY_STATUS ss;
TimeStamp tsExpiry;
SecBufferDesc sbdOut;
SecBuffer sbOut;
SecBufferDesc sbdIn;
SecBuffer sbIn;
ULONG fContextAttr;
if (!pAS->fInitialized)
{
DEBUGMSG(TRUE, (_T(“Calling AcquireCredentialsHandle…”)));
ss = AcquireCredentialsHandle(NULL, SEC_PACKAGE,
SECPKG_CRED_OUTBOUND, NULL, pAuthIdentity, NULL, NULL,
&pAS->hcred, &tsExpiry);

if (ss < 0)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
if(ss < 0)
{
fprintf(stderr, “AcquireCredentialsHandle failed with %08Xn”, ss);
return FALSE;
}
pAS->fHaveCredHandle = TRUE;
}
// Prepare output buffer
sbdOut.ulVersion = 0;
sbdOut.cBuffers = 1;
sbdOut.pBuffers = &sbOut;
sbOut.cbBuffer = *pcbOut;
sbOut.BufferType = SECBUFFER_TOKEN;
sbOut.pvBuffer = pOut;
// Prepare input buffer
if (pAS->fInitialized) {
sbdIn.ulVersion = 0;
sbdIn.cBuffers = 1;
sbdIn.pBuffers = &sbIn;
sbIn.cbBuffer = cbIn;
sbIn.BufferType = SECBUFFER_TOKEN;
sbIn.pvBuffer = pIn;
}
ss = InitializeSecurityContext(&pAS->hcred,
pAS->fInitialized ? &pAS->hctxt : NULL, NULL, 0, 0,
SECURITY_NATIVE_DREP, pAS->fInitialized ? &sbdIn : NULL,
0, &pAS->hctxt, &sbdOut, &fContextAttr, &tsExpiry);
if (ss < 0) {
//
fprintf(stderr, “InitializeSecurityContext failed with %08Xn”, ss);
return FALSE;
}
pAS->fHaveCtxtHandle = TRUE;
// If necessary, complete token
if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
ss = CompleteAuthToken(&pAS->hctxt, &sbdOut);
if (ss < 0) {
fprintf(stderr, “CompleteAuthToken failed with %08Xn”, ss);
return FALSE;
}
}
*pcbOut = sbOut.cbBuffer;
if (!pAS->fInitialized)
pAS->fInitialized = TRUE;
*pfDone = !(ss == SEC_I_CONTINUE_NEEDED
|| ss == SEC_I_COMPLETE_AND_CONTINUE );
DEBUGMSG(TRUE, (_T(“-GenServerContextrn”)));
return TRUE;
}

///////////////////////////////////////////////////////////////////////////////

BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,
PDWORD pcbOut, PBOOL pfDone)
{
DEBUGMSG(TRUE, (_T(“+GenServerContextrn”)));
SECURITY_STATUS ss;
TimeStamp tsExpiry;
SecBufferDesc sbdOut;
SecBuffer sbOut;
SecBufferDesc sbdIn;
SecBuffer sbIn;
ULONG fContextAttr;
if (!pAS->fInitialized)
{
DEBUGMSG(TRUE, (_T(“Calling AcquireCredentialsHandle…”)));
ss = AcquireCredentialsHandle(NULL, SEC_PACKAGE,
SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred,
&tsExpiry);

if (ss < 0)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
pAS->fHaveCredHandle = TRUE;
}
// Prepare output buffer
sbdOut.ulVersion = 0;
sbdOut.cBuffers = 1;
sbdOut.pBuffers = &sbOut;
sbOut.cbBuffer = *pcbOut;
sbOut.BufferType = SECBUFFER_TOKEN;
sbOut.pvBuffer = pOut;
// Prepare input buffer
sbdIn.ulVersion = 0;
sbdIn.cBuffers = 1;
sbdIn.pBuffers = &sbIn;
sbIn.cbBuffer = cbIn;
sbIn.BufferType = SECBUFFER_TOKEN;
sbIn.pvBuffer = pIn;
DEBUGMSG(TRUE, (_T(“Calling AcceptSecurityContext…”)));
ss = AcceptSecurityContext(&pAS->hcred,
pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, 0,
SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr,
&tsExpiry);
if (ss < 0)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
pAS->fHaveCtxtHandle = TRUE;
// If necessary, complete token
if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE)
{
DEBUGMSG(TRUE, (_T(“Calling CompleteAuthToken…”)));
ss = CompleteAuthToken(&pAS->hctxt, &sbdOut);
if (ss < 0)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
}
*pcbOut = sbOut.cbBuffer;
if (!pAS->fInitialized)
pAS->fInitialized = TRUE;
*pfDone = !(ss = SEC_I_CONTINUE_NEEDED
|| ss == SEC_I_COMPLETE_AND_CONTINUE);
DEBUGMSG(TRUE, (_T(“-GenServerContextrn”)));
return TRUE;
}

///////////////////////////////////////////////////////////////////////////////

BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword)
{
DEBUGMSG(TRUE, (_T(“rn+SSPLogonUserrn”)));
AUTH_SEQ asServer = {0};
AUTH_SEQ asClient = {0};
BOOL fDone = FALSE;
BOOL fResult = FALSE;
DWORD cbOut = 0;
DWORD cbIn = 0;
DWORD cbMaxToken = 0;
PVOID pClientBuf = NULL;
PVOID pServerBuf = NULL;
SecPkgInfo *pSPI = NULL;
HMODULE hModule = NULL;
SECURITY_STATUS ss;
SEC_WINNT_AUTH_IDENTITY ai;
ULONG packages = 0;
PSecPkgInfo pPackageInfo = NULL;
__try
{
EnumerateSecurityPackages(&packages, &pPackageInfo);
DEBUGMSG(TRUE, (_T(” Available security packages:rn”)));
for(UINT i = 0 ; i < packages ; i++)
{
DEBUGMSG(TRUE, (_T(“t%srn”), pPackageInfo[i].Name));
}
DEBUGMSG(TRUE, (_T(“rn”)));

DEBUGMSG(TRUE, (_T(“Calling FreeContextBuffer…”)));
ss =FreeContextBuffer(pPackageInfo);
if(ss != SEC_E_OK)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
__leave;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
// Get max token size
DEBUGMSG(TRUE, (_T(“Calling QuerySecurityPackageInfo…”)));
ss = QuerySecurityPackageInfo(SEC_PACKAGE, &pSPI);
if(ss != SEC_E_OK)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
__leave;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
cbMaxToken = pSPI->cbMaxToken;
DEBUGMSG(TRUE, (_T(“Calling FreeContextBuffer…”)));
ss =FreeContextBuffer(pSPI);
if(ss != SEC_E_OK)
{
DEBUGMSG(TRUE, (_T(“failed with 0x%08xrn”), ss));
__leave;
}
DEBUGMSG(TRUE, (_T(“okrn”)));
// Allocate buffers for client and server messages
DEBUGMSG(TRUE, (_T(“Allocating heaps…”)));
pClientBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);
pServerBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);
DEBUGMSG(TRUE, (_T(“okrn”)));
// Initialize auth identity structure
ZeroMemory(&ai, sizeof(ai));
ai.Domain = szDomain;
ai.DomainLength = _tcslen(szDomain);
ai.User = szUser;
ai.UserLength = _tcslen(szUser);
ai.Password = szPassword;
ai.PasswordLength = _tcslen(szPassword);
ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
// Prepare client message (negotiate) .
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T(“Calling GenClientContextrn”)));
if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
__leave;
// Prepare server message (challenge) .
cbIn = cbOut;
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T(“Calling GenServerContextrn”)));
if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, &fDone))
__leave;
// Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
// in the case of bad szUser or szPassword.
// Unexpected Result: Logon will succeed if you pass in a bad szUser and
// the guest account is enabled in the specified domain.
// Prepare client message (authenticate) .
cbIn = cbOut;
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T(“Calling GenClientContextrn”)));
if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut, &fDone))
__leave;
// Prepare server message (authentication) .
cbIn = cbOut;
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T(“Calling GenServerContextrn”)));
if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, &fDone))
__leave;
fResult = TRUE;
}
__finally
{
// Clean up resources
if (asClient.fHaveCtxtHandle)
{
DEBUGMSG(TRUE, (_T(“Calling DeleteSecurityContextrn”)));
DeleteSecurityContext(&asClient.hctxt);
}
if (asClient.fHaveCredHandle)
{
DEBUGMSG(TRUE, (_T(“Calling FreeCredentialsHandlern”)));
FreeCredentialsHandle(&asClient.hcred);
}
if (asServer.fHaveCtxtHandle)
{
DEBUGMSG(TRUE, (_T(“Calling DeleteSecurityContextrn”)));
DeleteSecurityContext(&asServer.hctxt);
}
if (asServer.fHaveCredHandle)
{
DEBUGMSG(TRUE, (_T(“Calling FreeCredentialsHandlern”)));
FreeCredentialsHandle(&asServer.hcred);
}
DEBUGMSG(TRUE, (_T(“Freeing heaps…”)));
HeapFree(GetProcessHeap(), 0, pClientBuf);
HeapFree(GetProcessHeap(), 0, pServerBuf);
DEBUGMSG(TRUE, (_T(“okrn”)));
}
DEBUGMSG(TRUE, (_T(“-SSPLogonUserrn”)));
return fResult;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s