Important:
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
A version of this page is also available for
4/8/2010

The following is an example of a client SSPI application.

Copy Code
#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