SSPI Sample Application
A version of this page is also available for
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