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]  | 
 Friday, February 04, 2005

So I started a amanged wrapper for Pocket Excel some time ago - probably a year and a half now.  I made good progress - got it so you can real and write workbooks, worksheets, cells - just no support for the formulas yet.  Then I got sidetracked with one thing after another. 

Initially I was going to use it as a commercial product to get revenue to support OpenNETCF, but I honestly don't have the time for it.  If I can find someone, or a group of someones willing to put in a final push to finish it (maybe an industrious student looking for thesis material?) I'll donate it to the community at large as an OpenNETCF namespace.

Any takers?

2/4/2005 9:07:39 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  | 
 Wednesday, February 02, 2005

Pop quiz, what does this code do?

DS1307 d = new DS1307(Cpu.Pin.GPIO20_VTU_TIO1A, Cpu.Pin.GPIO21_VTU_TIO2A);
d.SetRTC(new DateTime(2005, 2, 2, 21, 22, 0, 0));
for (int i = 0; i < 10; i++)
{
    DateTime dt = d.ReadRTC();
    Debug.Print(dt.Hour + ":" + dt.Minute + ":" + dt.Second);
    System.Threading.Thread.Sleep(2000);
}

Okay, so it's not too exciting, until you realize that what it calls - an I2C driver - is written in C# as well.  Yes, I've achieved a managed code driver.  Next question - what's it run on?  I'll give you a hint - it's not a Pocket PC.

2/2/2005 10:33:41 PM (Eastern Standard Time, UTC-05:00)  #    Comments [4]  | 

We've got a lot of cool new things - many of which are attempts to get your code “prepped” for CF 2.0 before it's even here.  A Serial wrapper, FTP classes...and some other stuff that will excite the hell out of a lot of people.  Just take a look at the top level folders in the OpenNETCF Vault and you'll see what I mean....

2/2/2005 10:25:17 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 

A fairly common complaint from the CF community is the time gap between SDF source releases.  Well with the generous donation from SourceGear of Vault, we're now able to give the community direct read-only access to the source in real time.  Of course this added access means that what you get out of source control may not work properly, but there's no more complaining about not being able to get the latest code.

To get the source, navigate over to http://vault.netcf.tv/VaultService/VaultWeb/login.aspx and login with a username and password of 'guest'

Oh, and keep in mind that we now accept donations, so if you'd like to support us it's quick and simple.

2/2/2005 9:07:40 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Tuesday, December 21, 2004

If you're a CF developer READ THIS ARTICLE.  Once done, read it again. 

12/21/2004 8:48:03 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  |