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("+GenServerContext\r\n")));
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%08x\r\n"), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
if(ss < 0)
{
fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", 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 %08X\n", 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 %08X\n", 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("-GenServerContext\r\n")));
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,
PDWORD pcbOut, PBOOL pfDone)
{
DEBUGMSG(TRUE, (_T("+GenServerContext\r\n")));
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%08x\r\n"), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
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%08x\r\n"), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
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%08x\r\n"), ss));
return FALSE;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
}
*pcbOut = sbOut.cbBuffer;
if (!pAS->fInitialized)
pAS->fInitialized = TRUE;
*pfDone = !(ss = SEC_I_CONTINUE_NEEDED
|| ss == SEC_I_COMPLETE_AND_CONTINUE);
DEBUGMSG(TRUE, (_T("-GenServerContext\r\n")));
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword)
{
DEBUGMSG(TRUE, (_T("\r\n+SSPLogonUser\r\n")));
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:\r\n")));
for(UINT i = 0 ; i < packages ; i++)
{
DEBUGMSG(TRUE, (_T("\t%s\r\n"), pPackageInfo[i].Name));
}
DEBUGMSG(TRUE, (_T("\r\n")));
DEBUGMSG(TRUE, (_T("Calling FreeContextBuffer...")));
ss =FreeContextBuffer(pPackageInfo);
if(ss != SEC_E_OK)
{
DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
__leave;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
// 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%08x\r\n"), ss));
__leave;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
cbMaxToken = pSPI->cbMaxToken;
DEBUGMSG(TRUE, (_T("Calling FreeContextBuffer...")));
ss =FreeContextBuffer(pSPI);
if(ss != SEC_E_OK)
{
DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
__leave;
}
DEBUGMSG(TRUE, (_T("ok\r\n")));
// 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("ok\r\n")));
// 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 GenClientContext\r\n")));
if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
__leave;
// Prepare server message (challenge) .
cbIn = cbOut;
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T("Calling GenServerContext\r\n")));
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 GenClientContext\r\n")));
if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut, &fDone))
__leave;
// Prepare server message (authentication) .
cbIn = cbOut;
cbOut = cbMaxToken;
DEBUGMSG(TRUE, (_T("Calling GenServerContext\r\n")));
if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, &fDone))
__leave;
fResult = TRUE;
}
__finally
{
// Clean up resources
if (asClient.fHaveCtxtHandle)
{
DEBUGMSG(TRUE, (_T("Calling DeleteSecurityContext\r\n")));
DeleteSecurityContext(&asClient.hctxt);
}
if (asClient.fHaveCredHandle)
{
DEBUGMSG(TRUE, (_T("Calling FreeCredentialsHandle\r\n")));
FreeCredentialsHandle(&asClient.hcred);
}
if (asServer.fHaveCtxtHandle)
{
DEBUGMSG(TRUE, (_T("Calling DeleteSecurityContext\r\n")));
DeleteSecurityContext(&asServer.hctxt);
}
if (asServer.fHaveCredHandle)
{
DEBUGMSG(TRUE, (_T("Calling FreeCredentialsHandle\r\n")));
FreeCredentialsHandle(&asServer.hcred);
}
DEBUGMSG(TRUE, (_T("Freeing heaps...")));
HeapFree(GetProcessHeap(), 0, pClientBuf);
HeapFree(GetProcessHeap(), 0, pServerBuf);
DEBUGMSG(TRUE, (_T("ok\r\n")));
}
DEBUGMSG(TRUE, (_T("-SSPLogonUser\r\n")));
return fResult;
}