Tuesday, February 08, 2005

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;
}
2/8/2005 1:29:26 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):