SSPI Sample Application

A version of this page is also available for

Windows Embedded CE 6.0 R3

4/8/2010

The following is an example of a client SSPI application.

#define SECURITY_WIN32  1
#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include "sspi.h"

static DWORD g_cbMaxToken;
static PSecurityFunctionTable g_pFuncs;
PTCHAR  g_pszPackage = TEXT("NTLM");
// name of the entity you are authenticating to
PTCHAR  g_pszTarget = NULL;
// name of user being authenticated
PTCHAR  g_pszUser;
// name of user's domain
PTCHAR g_pszDomain;
// password of user being authenticated
PTCHAR g_pszPassword;
PBYTE g_pInBuf = NULL;
PBYTE g_pOutBuf = NULL;
DWORD g_cbMaxMessage = 0;
CredHandle g_hcred;       // Credential Handle
SecHandle  g_hctxt;       // Context Handle

#define SEC_SUCCESS(Status) ((Status) >= 0)


BOOL InitPackage (DWORD *pcbMaxMessage)
//
// Routine Description:
//    Finds, loads, and initializes the security package
// Return Value:
//    Returns TRUE is successful; otherwise FALSE is returned.
//
{
   SECURITY_STATUS ss;
   PSecPkgInfo pkgInfo;


    g_pFuncs = InitSecurityInterface();
   // Query for the package of interest
   //
   ss = g_pFuncs->QuerySecurityPackageInfo (g_pszPackage, &pkgInfo);
   if (!SEC_SUCCESS(ss))  {
      fprintf (stderr, "Could not query package info for %s, error %X\n",
               g_pszPackage, ss);
      return(FALSE);
   }

   *pcbMaxMessage = pkgInfo->cbMaxToken;

   g_pFuncs->FreeContextBuffer (pkgInfo);



   return TRUE;
}

BOOL GenClientContext (
         BYTE *pIn,
         DWORD cbIn,
         BYTE *pOut,
         DWORD *pcbOut,
         BOOL *pfDone)
//
// Routine Description:
//    Optionally takes an input buffer coming from the server and returns
//    a buffer of information to send back to the server.  Also returns
//    an indication of whether or not the context is complete.
// Return Value:
//    Returns TRUE is successful; otherwise FALSE is returned.
//
{
   SECURITY_STATUS   ss;
   TimeStamp         Lifetime;
   SecBufferDesc     OutBuffDesc;
   SecBuffer         OutSecBuff;
   SecBufferDesc     InBuffDesc;
   SecBuffer         InSecBuff;
   ULONG             ContextAttributes;
   BOOL              fNewContext = FALSE;
   // NTLM and KERBEROS packages use the SEC_WINNT_AUTH_IDENTITY structure
   // to pass user credentials
   SEC_WINNT_AUTH_IDENTITY AuthData, *pAuthData = NULL;


   if (!pIn)  {
      // first call - get the credential handle
      fNewContext = TRUE;
      // if the user credentials are available, use them by supplying a 
      // SEC_WINNT_AUTH_IDENTITY structure. This structure is package 
      // specific - it is accepted by NTLM and KERBEROS
      // Otherwise authentication is attempted using the 
      // default cached credentials, if any
       if (g_pszUser)
       {
#ifdef UNICODE
           AuthData.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else
           AuthData.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif
           AuthData.User = g_pszUser;
           AuthData.Domain = g_pszDomain;
           AuthData.Password = g_pszPassword;
           AuthData.UserLength = _tcslen(g_pszUser);
           AuthData.DomainLength = g_pszDomain ? _tcslen(g_pszDomain): 0;
           AuthData.PasswordLength = 
                    g_pszPassword ? _tcslen(g_pszPassword): 0;
           _tprintf(TEXT("User: %s\n"),g_pszUser);

           pAuthData = &AuthData;
       }
      ss = g_pFuncs->AcquireCredentialsHandle (
                     NULL,   // principal
                     g_pszPackage,
                     SECPKG_CRED_OUTBOUND,
                     NULL,   // LOGON id
                     pAuthData,
                     NULL,   // get key fn
                     NULL,   // get key arg
                     &g_hcred,
                     &Lifetime
                     );
      if (!SEC_SUCCESS (ss))
      {
         fprintf (stderr, "AcquireCreds failed: %X\n", ss);
         return(FALSE);
      }
   }

   // prepare output buffer
   //
   OutBuffDesc.ulVersion = 0;
   OutBuffDesc.cBuffers = 1;
   OutBuffDesc.pBuffers = &OutSecBuff;

   OutSecBuff.cbBuffer = *pcbOut;
   OutSecBuff.BufferType = SECBUFFER_TOKEN;
   OutSecBuff.pvBuffer = pOut;

   // prepare input buffer
   //
   if (!fNewContext)  {
      InBuffDesc.ulVersion = 0;
      InBuffDesc.cBuffers = 1;
      InBuffDesc.pBuffers = &InSecBuff;

      InSecBuff.cbBuffer = cbIn;
      InSecBuff.BufferType = SECBUFFER_TOKEN;
      InSecBuff.pvBuffer = pIn;
   }

   ss = g_pFuncs->InitializeSecurityContext (
                  &g_hcred,
                  fNewContext ? NULL : &g_hctxt,
                  g_pszTarget,
                  0,   // context requirements
                  0,   // reserved1
                  SECURITY_NATIVE_DREP,
                  fNewContext ? NULL : &InBuffDesc,
                  0,   // reserved2
                  &g_hctxt,
                  &OutBuffDesc,
                  &ContextAttributes,
                  &Lifetime
                  );
   if (!SEC_SUCCESS (ss))  {
      fprintf (stderr, "init context failed: %X\n", ss);
      return FALSE;
   }


   // Complete token -- if applicable
   //
   if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  {
      if (g_pFuncs->CompleteAuthToken) {
         ss = g_pFuncs->CompleteAuthToken (&g_hctxt, &OutBuffDesc);
         if (!SEC_SUCCESS(ss))  {
            fprintf (stderr, "complete failed: %X\n", ss);
            return FALSE;
         }
      }
      else {
         fprintf (stderr, "Complete not supported.\n");
         return FALSE;
      }
   }

   *pcbOut = OutSecBuff.cbBuffer;

   *pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
            (SEC_I_COMPLETE_AND_CONTINUE == ss));

   return TRUE;
}


BOOL SendBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf)
{
   PBYTE pTemp = pBuf;
   int cbSent, cbRemaining = cbBuf;

   if (0 == cbBuf)
      return(TRUE);
      
   while (cbRemaining) {
      cbSent = send (s, pTemp, cbRemaining, 0);
      if (SOCKET_ERROR == cbSent) {
         fprintf (stderr, "send failed: %u\n", GetLastError ());
         return FALSE;
      }

      pTemp += cbSent;
      cbRemaining -= cbSent;
   }

   return TRUE;
}

BOOL ReceiveBytes (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead)
{
   PBYTE pTemp = pBuf;
   int cbRead, cbRemaining = cbBuf;

   while (cbRemaining) {
      cbRead = recv (s, pTemp, cbRemaining, 0);
      if (0 == cbRead) {
            fprintf(stderr,"!ReceiveBytes: peer closed connection\n");
            break;
        }

      if (SOCKET_ERROR == cbRead) {
         fprintf (stderr, "recv failed: %u\n", GetLastError ());
         return FALSE;
      }

      cbRemaining -= cbRead;
      pTemp += cbRead;
   }

   *pcbRead = cbBuf - cbRemaining;

   return TRUE;
}

BOOL SendMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf)
//
// Routine Description:
//    Sends a message over the socket by first sending a DWORD that
//    represents the size of the message followed by the message itself.
// Return Value:
//    Returns TRUE is successful; otherwise FALSE is returned.
//
{
   if (0 == cbBuf)
      return(TRUE);

   // send the size of the message
   //
   if (!SendBytes (s, (PBYTE)&cbBuf, sizeof (cbBuf)))
      return(FALSE);
   
   // send the body of the message
   //
   if (!SendBytes (s, pBuf, cbBuf))
      return(FALSE);

   return(TRUE);
}   

BOOL ReceiveMsg (SOCKET s, PBYTE pBuf, DWORD cbBuf, DWORD *pcbRead)
//
// Routine Description:
//    Receives a message over the socket.  The first DWORD in the message
//    will be the message size.  The remainder of the bytes will be the
//    actual message.
// Return Value:
//    Returns TRUE is successful; otherwise FALSE is returned.
//
{
   DWORD cbRead;
   DWORD cbData;

   // find out how much data is in the message
   // 
   if (!ReceiveBytes (s, (PBYTE)&cbData, sizeof (cbData), &cbRead))
      return(FALSE);
   
   if (sizeof (cbData) != cbRead) {
      if (cbRead) 
            fprintf(stderr,"!ReceiveMsg: cbRead == 
                   %u, should be %u\n",cbRead,sizeof(cbData));
      return(FALSE);
   }
   
   // Read the full message
   //
   if (!ReceiveBytes (s, pBuf, cbData, &cbRead))
      return(FALSE);
   
   if (cbRead != cbData) {
      if (cbRead) 
         fprintf(stderr,"!ReceiveMsg(2): cbRead == 
                %u, should be %u\n",cbRead,cbData);
      return(FALSE);
    }

   *pcbRead = cbRead;

   return(TRUE);
}   

BOOL DoAuthentication (SOCKET s)
//
// Routine Description:
//    Manages the authentication conversation with the server through the
//    supplied socket handle. 
//    
// Assumptions:
//    The connection to the server is assumed to have been
//    already established. If they are needed, the user name, domain name 
//    and password variables should have been initialized.
// Return Value:
//    Returns TRUE is successful; otherwise FALSE is returned.
//
{
   BOOL done = FALSE;
   BOOL fSuccess = FALSE;
   DWORD cbOut, cbIn;
   if (!InitPackage (&g_cbMaxMessage))
      return FALSE;

   g_pInBuf = (PBYTE) malloc (g_cbMaxMessage);
   g_pOutBuf = (PBYTE) malloc (g_cbMaxMessage);
   SecInvalidateHandle(&g_hcred);
   SecInvalidateHandle(&g_hctxt);
   if (NULL == g_pInBuf || NULL == g_pOutBuf)
      return FALSE;

   cbOut = g_cbMaxMessage;
   //
   // Acquire the credentials Handle and create the security context
   if (!GenClientContext (NULL, 0, g_pOutBuf, &cbOut, &done))
      goto cleanup;

   // Send the first authentication token to the server
   if (!SendMsg (s, g_pOutBuf, cbOut))
      goto cleanup;

   while (!done) {
      // get the response from the server
      if (!ReceiveMsg (s, g_pInBuf, g_cbMaxMessage, &cbIn))
         goto cleanup;

      // generate the subsequent subsequent authentication tokens
      cbOut = g_cbMaxMessage;
      if (!GenClientContext (g_pInBuf, cbIn, g_pOutBuf, &cbOut, &done))
         goto cleanup;

      // send the subsequent authentication tokens to the server
      if (!SendMsg (s, g_pOutBuf, cbOut))
         goto cleanup;
   }

    printf("DoAuthentication, user authenticated\n");
    fSuccess = TRUE;

cleanup:
   g_pFuncs->DeleteSecurityContext(&g_hctxt);
   g_pFuncs->FreeCredentialHandle(&g_hcred);
   free(g_pInBuf);
   free(g_pOutBuf);
   return(fSuccess);
}

See Also

Reference

Authentication Services Reference

Concepts

Security Support Provider Interface Architecture
Security Packages
Authentication Services Security
Authentication Services Registry Settings

Other Resources

Authentication Services