Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1211 lines
34 KiB

/*++
Copyright (C) 1997-2001 Microsoft Corporation
Module Name:
CSSPI.H
Abstract:
SSPI wrapper implementation for NTLM/MSN network authentication.
History:
raymcc 15-Jul-97 Created
--*/
#include "precomp.h"
#include <tchar.h>
#include <stdio.h>
#include <csspi.h>
// #define trace(x) printf x
#define trace(x)
static BOOL IsNt(void);
//***************************************************************************
//
// String helper macros
//
//***************************************************************************
#define Macro_CloneLPWSTR(x) \
(x ? _wcsdup(x) : 0)
#define Macro_CloneLPSTR(x) \
(x ? _strdup(x) : 0)
#ifdef _UNICODE
#define Macro_CloneLPTSTR(x) (x ? _wcsdup(x) : 0)
#else
#define Macro_CloneLPTSTR(x) (x ? _strdup(x) : 0)
#endif
//***************************************************************************
//
// BOOL IsNt
//
// DESCRIPTION:
//
// Returns true if running windows NT.
//
// RETURN VALUE:
//
// see description.
//
//***************************************************************************
// ok
static BOOL IsNt(void)
{
OSVERSIONINFO os;
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionEx(&os))
return FALSE; // should never happen
return os.dwPlatformId == VER_PLATFORM_WIN32_NT;
}
//***************************************************************************
//
// CSSPI Static Data Members
//
//***************************************************************************
ULONG CSSPI::m_uNumPackages = 0;
PSecPkgInfo CSSPI::m_pEnumPkgInfo = 0;
PSecurityFunctionTable CSSPI::pVtbl = 0;
//***************************************************************************
//
// CSSPI::Initialize
//
// This must be called prior to any other operations, but may be
// called multiple times.
//
// Return value:
// TRUE on success, FALSE on failure.
//
//***************************************************************************
// ok
BOOL CSSPI::Initialize()
{
static HMODULE hLib = 0;
// If we have already called this function and everything
// is already ok, short-circuit.
// ======================================================
if (hLib != 0 && pVtbl != 0)
{
return TRUE;
}
if (IsNt())
{
hLib = LoadLibrary(__TEXT("SECURITY.DLL"));
}
else
{
hLib = LoadLibrary(__TEXT("SECUR32.DLL"));
}
if (hLib == 0)
{
trace(("CSSPI::Startup() Library failed to load\n"));
return FALSE;
}
#ifdef _UNICODE
INIT_SECURITY_INTERFACE_W pInitFn =
(INIT_SECURITY_INTERFACE_W)
GetProcAddress(hLib, "InitSecurityInterfaceW");
#else
INIT_SECURITY_INTERFACE_A pInitFn =
(INIT_SECURITY_INTERFACE_A)
GetProcAddress(hLib, "InitSecurityInterfaceA");
#endif
if (pInitFn == 0)
{
trace(("CSSPI::Startup() ERROR : Unable to locate function InitSecurityInterface()\n"
));
FreeLibrary(hLib);
hLib = 0;
return FALSE;
}
pVtbl = pInitFn();
if (pVtbl == 0)
{
trace(("CSSPI::Startup() ERROR : Function table not found\n"));
FreeLibrary(hLib);
hLib = 0;
return FALSE;
}
return TRUE;
}
//***************************************************************************
//
// CSSPI::TranslateError
//
// Translates an error code to a displayable message.
// Treat the return value as read-only (you must, since it is 'const').
//
//***************************************************************************
// ok
const LPTSTR CSSPI::TranslateError(
ULONG uCode
)
{
LPTSTR p = 0;
switch (uCode)
{
case SEC_E_INSUFFICIENT_MEMORY: p = _T("SEC_E_INSUFFICIENT_MEMORY"); break;
case SEC_E_INVALID_HANDLE : p = _T("SEC_E_INVALID_HANDLE"); break;
case SEC_E_UNSUPPORTED_FUNCTION : p = _T("SEC_E_UNSUPPORTED_FUNCTION"); break;
case SEC_E_TARGET_UNKNOWN : p = _T("SEC_E_TARGET_UNKNOWN"); break;
case SEC_E_INTERNAL_ERROR : p = _T("SEC_E_INTERNAL_ERROR"); break;
case SEC_E_SECPKG_NOT_FOUND : p = _T("SEC_E_SECPKG_NOT_FOUND"); break;
case SEC_E_NOT_OWNER : p = _T("SEC_E_NOT_OWNER"); break;
case SEC_E_CANNOT_INSTALL : p = _T("SEC_E_CANNOT_INSTALL"); break;
case SEC_E_INVALID_TOKEN : p = _T("SEC_E_INVALID_TOKEN"); break;
case SEC_E_CANNOT_PACK : p = _T("SEC_E_CANNOT_PACK"); break;
case SEC_E_QOP_NOT_SUPPORTED : p = _T("SEC_E_QOP_NOT_SUPPORTED"); break;
case SEC_E_NO_IMPERSONATION : p = _T("SEC_E_NO_IMPERSONATION"); break;
case SEC_E_LOGON_DENIED : p = _T("SEC_E_LOGON_DENIED"); break;
case SEC_E_UNKNOWN_CREDENTIALS : p = _T("SEC_E_UNKNOWN_CREDENTIALS"); break;
case SEC_E_NO_CREDENTIALS : p = _T("SEC_E_NO_CREDENTIALS"); break;
case SEC_E_MESSAGE_ALTERED : p = _T("SEC_E_MESSAGE_ALTERED"); break;
case SEC_E_OUT_OF_SEQUENCE : p = _T("SEC_E_OUT_OF_SEQUENCE"); break;
case SEC_E_NO_AUTHENTICATING_AUTHORITY : p = _T("SEC_E_NO_AUTHENTICATING_AUTHORITY"); break;
case SEC_I_CONTINUE_NEEDED : p = _T("SEC_I_CONTINUE_NEEDED"); break;
case SEC_I_COMPLETE_NEEDED : p = _T("SEC_I_COMPLETE_NEEDED"); break;
case SEC_I_COMPLETE_AND_CONTINUE : p = _T("SEC_I_COMPLETE_AND_CONTINUE"); break;
case SEC_I_LOCAL_LOGON : p = _T("SEC_I_LOCAL_LOGON"); break;
case SEC_E_BAD_PKGID : p = _T("SEC_E_BAD_PKGID"); break;
case SEC_E_CONTEXT_EXPIRED : p = _T("SEC_E_CONTEXT_EXPIRED"); break;
case SEC_E_INCOMPLETE_MESSAGE : p = _T("SEC_E_INCOMPLETE_MESSAGE"); break;
case SEC_E_INCOMPLETE_CREDENTIALS : p = _T("SEC_E_INCOMPLETE_CREDENTIALS"); break;
case SEC_E_BUFFER_TOO_SMALL : p = _T("SEC_E_BUFFER_TOO_SMALL"); break;
case SEC_I_INCOMPLETE_CREDENTIALS : p = _T("SEC_I_INCOMPLETE_CREDENTIALS"); break;
case SEC_I_RENEGOTIATE : p = _T("SEC_I_RENEGOTIATE"); break;
default:
p = _T("<UNDEFINED ERROR CODE>");
}
return (const LPTSTR) p;
}
//***************************************************************************
//
// CSSPI::DisplayContextAttributes
//
// Display authentication context attribute bits in readable form.
//
//***************************************************************************
// ok
void CSSPI::DisplayContextAttributes(
ULONG uAttrib
)
{
if (uAttrib & ISC_RET_DELEGATE)
printf("ISC_RET_DELEGATE\n");
if (uAttrib & ISC_RET_MUTUAL_AUTH)
printf("ISC_RET_MUTUAL_AUTH\n");
if (uAttrib & ISC_RET_REPLAY_DETECT)
printf("ISC_RET_REPLAY_DETECT\n");
if (uAttrib & ISC_RET_SEQUENCE_DETECT)
printf("ISC_RET_SEQUENCE_DETECT\n");
if (uAttrib & ISC_RET_CONFIDENTIALITY)
printf("ISC_RET_CONFIDENTIALITY\n");
if (uAttrib & ISC_RET_USE_SESSION_KEY)
printf("ISC_RET_USE_SESSION_KEY\n");
if (uAttrib & ISC_RET_USED_COLLECTED_CREDS)
printf("ISC_RET_USED_COLLECTED_CREDS\n");
if (uAttrib & ISC_RET_USED_SUPPLIED_CREDS)
printf("ISC_RET_USED_SUPPLIED_CREDS\n");
if (uAttrib & ISC_RET_ALLOCATED_MEMORY)
printf("ISC_RET_ALLOCATED_MEMORY\n");
if (uAttrib & ISC_RET_USED_DCE_STYLE)
printf("ISC_RET_USED_DCE_STYLE\n");
if (uAttrib & ISC_RET_DATAGRAM)
printf("ISC_RET_DATAGRAM\n");
if (uAttrib & ISC_RET_CONNECTION)
printf("ISC_RET_CONNECTION\n");
if (uAttrib & ISC_RET_INTERMEDIATE_RETURN)
printf("ISC_RET_INTERMEDIATE_RETURN\n");
if (uAttrib & ISC_RET_CALL_LEVEL)
printf("ISC_RET_CALL_LEVEL\n");
if (uAttrib & ISC_RET_EXTENDED_ERROR)
printf("ISC_RET_EXTENDED_ERROR\n");
if (uAttrib & ISC_RET_STREAM)
printf("ISC_RET_STREAM\n");
if (uAttrib & ISC_RET_INTEGRITY)
printf("ISC_RET_INTEGRITY\n");
}
//***************************************************************************
//
// CSSPI::DisplayPkgInfo
//
// Dumps package information to console.
//
//***************************************************************************
// ok
void CSSPI::DisplayPkgInfo(PSecPkgInfo pPkg)
{
printf("---------------------------------------------\n");
printf("Name = <%s>\n", pPkg->Name);
printf("Comment = <%s>\n", pPkg->Comment);
printf("Version = 0x%X\n", pPkg->wVersion);
printf("Max token = %d bytes\n", pPkg->cbMaxToken);
printf("DCE RPC Id = 0x%X\n", pPkg->wRPCID);
printf("Capabilities = \n");
if (pPkg->fCapabilities & SECPKG_FLAG_INTEGRITY)
printf(" SECPKG_FLAG_INTEGRITY\n");
if (pPkg->fCapabilities & SECPKG_FLAG_PRIVACY)
printf(" SECPKG_FLAG_PRIVACY\n");
if (pPkg->fCapabilities & SECPKG_FLAG_TOKEN_ONLY)
printf(" SECPKG_FLAG_TOKEN_ONLY\n");
if (pPkg->fCapabilities & SECPKG_FLAG_DATAGRAM)
printf(" SECPKG_FLAG_DATAGRAM\n");
if (pPkg->fCapabilities & SECPKG_FLAG_CONNECTION)
printf(" SECPKG_FLAG_CONNECTION\n");
if (pPkg->fCapabilities & SECPKG_FLAG_MULTI_REQUIRED)
printf(" SECPKG_FLAG_MULTI_REQUIRED\n");
if (pPkg->fCapabilities & SECPKG_FLAG_CLIENT_ONLY)
printf(" SECPKG_FLAG_CLIENT_ONLY\n");
if (pPkg->fCapabilities & SECPKG_FLAG_EXTENDED_ERROR)
printf(" SECPKG_FLAG_EXTENDED_ERROR\n");
if (pPkg->fCapabilities & SECPKG_FLAG_IMPERSONATION)
printf(" SECPKG_FLAG_IMPERSONATION\n");
if (pPkg->fCapabilities & SECPKG_FLAG_ACCEPT_WIN32_NAME)
printf(" SECPKG_FLAG_ACCEPT_WIN32_NAME\n");
if (pPkg->fCapabilities & SECPKG_FLAG_STREAM)
printf(" SECPKG_FLAG_STREAM\n");
printf("---------------------------------------------\n");
}
//***************************************************************************
//
// CSSPI::GetNumPkgs
//
// Gets the number of SSPI packages available on the current machine.
//
// Returns 0 if none available.
//
//***************************************************************************
// ok
ULONG CSSPI::GetNumPkgs()
{
// Short-circuit to see if this function has been called before.
// It is not possible for new SSPI packages to appear between
// reboots, so we cache all info.
// =============================================================
if (m_uNumPackages != 0 && m_pEnumPkgInfo)
return m_uNumPackages;
// Enumerate security packages.
// ============================
SECURITY_STATUS SecStatus =
pVtbl->EnumerateSecurityPackages(&m_uNumPackages, &m_pEnumPkgInfo);
if (SecStatus)
{
trace(("EnumerateSecurityPackages() failed. Error = %s\n", TranslateError(SecStatus)
));
return 0;
}
return m_uNumPackages;
}
//***************************************************************************
//
// CSSPI::GetPkgInfo
//
// Retrieves a single read-only PSecPkgInfo pointer, describing the
// requested package. This is to be used in conjunction with GetNumPkgs()
// in order to iterate through the SSPI package list.
//
// Returns NULL on error or a read-only PSecPkgInfo pointer.
//
//***************************************************************************
// ok
const PSecPkgInfo CSSPI::GetPkgInfo(ULONG ulPkgNum)
{
if (ulPkgNum >= m_uNumPackages)
return 0;
if (m_pEnumPkgInfo == 0)
return 0;
return &m_pEnumPkgInfo[ulPkgNum];
}
//***************************************************************************
//
// CSSPI::DumpSecurityPackages
//
// Dumps all available security packages to the console.
//
//***************************************************************************
// ok
BOOL CSSPI::DumpSecurityPackages()
{
// Enumerate security packages.
// ============================
unsigned long lNum = 0;
PSecPkgInfo pPkgInfo;
lNum = GetNumPkgs();
if (lNum == 0)
return FALSE;
trace(("SSPI Supports %d security packages\n", lNum));
for (unsigned long i = 0; i < lNum; i++)
{
pPkgInfo = GetPkgInfo(i);
if (pPkgInfo)
DisplayPkgInfo(pPkgInfo);
}
return TRUE;
}
//***************************************************************************
//
// CSSPI::ServerSupport
//
// Determines whether the a server-side package can be expected to work.
//
// Parameters:
// <pszPkgName> The authentication package. Usually "NTLM"
//
// Return value:
// TRUE if the package will support a server-side authentication, FALSE
// if not supported.
//
//***************************************************************************
BOOL CSSPI::ServerSupport(LPTSTR pszPkgName)
{
if (pszPkgName == 0 || lstrlen(pszPkgName) == 0)
return FALSE;
if (Initialize() == FALSE)
return FALSE;
CSSPIServer Server(pszPkgName);
if (Server.GetStatus() != CSSPIServer::Waiting)
return FALSE;
return TRUE;
}
//***************************************************************************
//
// CSSPI::ClientSupport
//
// Determines whether the a client-side package can be expected to work.
//
// Parameters:
// <pszPkgName> The authentication package. Usually "NTLM"
//
// Return value:
// TRUE if the package will support a client-side authentication, FALSE
// if not supported.
//
//***************************************************************************
BOOL CSSPI::ClientSupport(LPTSTR pszPkgName)
{
if (pszPkgName == 0 || lstrlen(pszPkgName) == 0)
return FALSE;
if (Initialize() == FALSE)
return FALSE;
CSSPIClient Client(pszPkgName);
if (Client.GetStatus() != CSSPIClient::Waiting)
return FALSE;
return TRUE;
}
//***************************************************************************
//
// CSSPIClient constructor
//
// Parameters:
// <pszPkgName> A valid SSPI package. Usually "NTLM".
//
// After construction has completed, GetStatus() should return
// CSSPIClient::Waiting. CSSPIClient::InvalidPackage indicates that
// the object will not function.
//
//***************************************************************************
CSSPIClient::CSSPIClient(LPTSTR pszPkgName)
{
// Initialize variables.
// =====================
m_dwStatus = InvalidPackage;
memset(&m_ClientCredential, 0, sizeof(CredHandle));
m_pPkgInfo = 0;
m_pszPkgName = 0;
m_cbMaxToken = 0;
m_bValidCredHandle = FALSE;
memset(&m_ClientContext, 0, sizeof(CtxtHandle));
m_bValidContextHandle = FALSE;
// Copy package name.
// ===================
m_pszPkgName = Macro_CloneLPTSTR(pszPkgName);
if (!m_pszPkgName)
{
trace(("CSSPIClient failed to construct (out of memory).\n"));
m_dwStatus = InvalidPackage;
return;
}
// Init the requested package.
// ===========================
SECURITY_STATUS SecStatus = 0;
SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo);
if (SecStatus != 0)
{
trace(("CSSPIClient fails to construct. Error = %s\n",
CSSPI::TranslateError(SecStatus)
));
m_dwStatus = InvalidPackage;
return;
}
// If here, things are ok. We are waiting
// for client to set login information.
// ========================================
m_cbMaxToken = m_pPkgInfo->cbMaxToken;
m_dwStatus = Waiting;
}
//***************************************************************************
//
// CSSPIClient
//
// Destructor
//
//***************************************************************************
CSSPIClient::~CSSPIClient()
{
if (m_pPkgInfo)
CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo);
if (m_pszPkgName)
delete [] m_pszPkgName;
if (m_bValidCredHandle)
CSSPI::pVtbl->FreeCredentialHandle(&m_ClientCredential);
if (m_bValidContextHandle)
CSSPI::pVtbl->DeleteSecurityContext(&m_ClientContext);
}
//***************************************************************************
//
// CSSPIClient::SetLoginInfo
//
// Sets login info prior to beginning a login session.
// pszUser, pszDomain, pszPassword must be either all NULL or all not
// NULL. If NULLs are used, the current user's security context
// is propagated.
//
// This must be the first call after constructing the CSSPIClient.
// If this returns NoError, then ContinueLogin must be called next
// before communicating with the server process.
//
// Parameters:
// <pszUser> The user name
// <pszDomain> The NTLM domain
// <pszPassword> The cleartext password
// <dwLoginFlags> Reserved
//
// Return value:
// <NoError> if the caller can immediately proceed to ContinueLogin().
// <InvalidParameter> if one or more parameters were not valid.
// <InvalidPackage> if the SSPI package is not operational
//
// <InvalidUser> if the user/domain/password parameters are
// of the wrong format.
//
// After successful completion, <NoError> is returned and
// GetStatus() will return CSSPIClient::LoginContinue.
//
//***************************************************************************
DWORD CSSPIClient::SetLoginInfo(
IN LPTSTR pszUser,
IN LPTSTR pszDomain,
IN LPTSTR pszPassword,
IN DWORD dwLoginFlags
)
{
if (m_dwStatus != Waiting)
return m_dwStatus = InvalidPackage;
// If the user specifies any of one {user,password,domain}, they
// all must be specified.
// =============================================================
if (pszUser || pszDomain || pszPassword)
{
if (!pszUser || !pszDomain || !pszPassword)
return InvalidParameter;
}
// Acquire a credentials handle for subsequent calls.
// ==================================================
SEC_WINNT_AUTH_IDENTITY AdditionalCredentials;
BOOL bSupplyCredentials = FALSE;
TimeStamp Expiration;
AdditionalCredentials.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
// Due to the parameter validation, we know that if
// a user was specified, the other parameters have been, too.
// ==========================================================
if (pszUser != 0)
{
bSupplyCredentials = TRUE;
AdditionalCredentials.User = pszUser;
AdditionalCredentials.UserLength = lstrlen(pszUser);
AdditionalCredentials.Password = pszPassword;
AdditionalCredentials.PasswordLength = lstrlen(pszPassword);
AdditionalCredentials.Domain = pszDomain;
AdditionalCredentials.DomainLength = lstrlen(pszDomain);
}
// Get client credentials handle.
// ==============================
SECURITY_STATUS SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle(
NULL,
m_pszPkgName,
SECPKG_CRED_OUTBOUND,
NULL,
bSupplyCredentials ? &AdditionalCredentials : 0,
NULL,
NULL,
&m_ClientCredential,
&Expiration
);
if (SecStatus)
{
trace(("AcquireCredentialsHandle() failed. Error=%s\n",
CSSPI::TranslateError(SecStatus) ));
return m_dwStatus = InvalidUser;
}
m_bValidCredHandle = TRUE;
// Signal to caller that login must continue.
// ==========================================
m_dwStatus = LoginContinue;
return NoError;
}
//***************************************************************************
//
// CSSPIClient::SetDefaultLogin
//
// Convenience method to login using 'current' credentials.
//
// Return values same as for SetLoginInfo().
//
//***************************************************************************
DWORD CSSPIClient::SetDefaultLogin(DWORD dwFlags)
{
return SetLoginInfo(0, 0, 0, dwFlags);
}
//***************************************************************************
//
// CSSPIClient::ContinueLogin
//
// Called immediately after SetDefaultLogin() or SetLoginInfo() or
// after prior calls to ContinueLogin after receiving binary tokens
// from the server-side of the conversation.
//
// This is used for both to prepare the logon request and the
// response to the challenge from the server.
//
// Parameters:
// <pInToken> A token received from the server. If no
// token has been received yet, then use NULL.
// This is treated as read-only.
//
// <dwInTokenSize> The number of bytes in the token pointed
// to by <dwInTokenSize>, or 0 if the above
// token is NULL.
//
// <pToken> Receives a pointer to a memory buffer
// containing the token to transfer to the
// server. Deallocate with operator delete.
//
// <pdwTokenSize> Points to a DWORD to receive the size
// of the above token.
//
// Return value:
// <NoError> Returned when no more calls to ContinueLogin
// are completed. <pToken> and <pdwTokenSize>
// are still returned in this case.
// This completes the 'response' portion
// in the challenge-response sequence. The server
// will return an 'access denied' status through
// private means. Note that <NoError> does not
// imply successful logon or that 'access denied' will
// not occur. It merely completes the response
// to the 'challenge'.
//
// After the login request phase (the first call to
// this function) GetStatus() should return
// CSSPIClient::LoginContinue, to indicate that
// another call to this function will be required.
// If GetStatus() CSSPIClient::LoginComplete is
// returned, no more SSPI operations will occur.
// Final success or denial by the server will
// be privately indicated by the server.
//
// <Failed> Internal error. No out-parms are returned in this
// case.
//
//***************************************************************************
DWORD CSSPIClient::ContinueLogin(
IN LPBYTE pInToken,
IN DWORD dwInTokenSize,
OUT LPBYTE *pToken,
OUT DWORD *pdwTokenSize
)
{
// If here, we are ready to build up a token.
// ==========================================
SecBufferDesc OutputBufferDescriptor;
SecBuffer OutputSecurityToken;
ULONG uContextRequirements = 0;
ULONG uContextAttributes;
TimeStamp Expiration;
// Set up the output buffers and out params by
// default. On errors, we simply null the out params.
// ===================================================
OutputBufferDescriptor.cBuffers = 1;
OutputBufferDescriptor.pBuffers = &OutputSecurityToken;
OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
LPBYTE pTokenBuf = new BYTE[m_cbMaxToken];
OutputSecurityToken.BufferType = SECBUFFER_TOKEN;
OutputSecurityToken.cbBuffer = m_cbMaxToken;
OutputSecurityToken.pvBuffer = pTokenBuf;
// Build up the input buffer, if required.
// =======================================
SecBufferDesc InputBufferDescriptor;
SecBuffer InputSecurityToken;
if (pInToken)
{
InputBufferDescriptor.cBuffers = 1;
InputBufferDescriptor.pBuffers = &InputSecurityToken;
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
InputSecurityToken.BufferType = SECBUFFER_TOKEN;
InputSecurityToken.cbBuffer = dwInTokenSize;
InputSecurityToken.pvBuffer = pInToken;
}
PSecBufferDesc pInBuf = pInToken ? &InputBufferDescriptor : 0;
// Determine if this is first-time or continued call.
// ==================================================
PCtxtHandle pTmp = m_bValidContextHandle ? &m_ClientContext : 0;
SECURITY_STATUS SecStatus =
CSSPI::pVtbl->InitializeSecurityContext(
&m_ClientCredential,
pTmp, // Context handle pointer(
NULL, // Target name (server)
uContextRequirements, // Requirements
0, // Reserved
SECURITY_NATIVE_DREP, // Target data representation
pInBuf, // Input buffer for continued calls
0, // Reserved
&m_ClientContext, // Receives client context handle
&OutputBufferDescriptor,
&uContextAttributes,
&Expiration
);
// Determine the next step.
// ========================
if (SecStatus == SEC_E_OK)
{
*pToken = pTokenBuf;
*pdwTokenSize = OutputSecurityToken.cbBuffer;
m_dwStatus = LoginCompleted;
return NoError;
}
if (SecStatus == SEC_I_CONTINUE_NEEDED)
{
// Set up the output parameters.
// =============================
*pToken = pTokenBuf;
*pdwTokenSize = OutputSecurityToken.cbBuffer;
m_bValidContextHandle = TRUE;
m_dwStatus = LoginContinue;
return NoError;
}
// If here, an error occurred.
// ===========================
trace(("CSSPIClient::ContinueLogin failed. Status code = %s",
CSSPI::TranslateError(SecStatus)
));
CSSPI::pVtbl->DeleteSecurityContext(pTmp);
// Deallocate useless return buffer and NULL the out-parameters.
// =============================================================
delete [] pTokenBuf;
*pToken = 0;
*pdwTokenSize = 0;
m_dwStatus = Failed;
return Failed;
}
//***************************************************************************
//
// CSSPIServer constructor
//
// Parameters:
// <pszPkgName> The SSPI package to use, usually "NTLM".
//
// After construction, CSSPIServer::GetStatus() should return
// CSSPIServer::Waiting. Any other value indicates that the object
// is invalid and cannot be used.
//
//***************************************************************************
CSSPIServer::CSSPIServer(LPTSTR pszPkgName)
{
m_dwStatus = 0;
m_cbMaxToken = 0;
m_pPkgInfo = 0;
m_bValidCredHandle = FALSE;
m_bValidContextHandle = FALSE;
memset(&m_ServerCredential, 0, sizeof(CredHandle));
memset(&m_ServerContext, 0, sizeof(CtxtHandle));
m_pszPkgName = Macro_CloneLPTSTR(pszPkgName);
if (!m_pszPkgName)
{
trace(("CSSPIServer failed to construct (out of memory).\n"));
m_dwStatus = InvalidPackage;
return;
}
// Init the requested package.
// ===========================
SECURITY_STATUS SecStatus = 0;
SecStatus = CSSPI::pVtbl->QuerySecurityPackageInfo(pszPkgName, &m_pPkgInfo);
if (SecStatus != 0)
{
trace(("CSSPIServer failed to construct. Error = %s\n",
CSSPI::TranslateError(SecStatus)
));
m_dwStatus = InvalidPackage;
return;
}
m_cbMaxToken = m_pPkgInfo->cbMaxToken;
// Now acquire a default credentials handle for this machine.
// ==========================================================
TimeStamp Expiration;
SecStatus = CSSPI::pVtbl->AcquireCredentialsHandle(
NULL, // No principal
m_pszPkgName, // Authentication package to use
SECPKG_CRED_INBOUND, // We are a 'server'
NULL, // No logon identifier
NULL, // No pkg specific data
NULL, // No GetKey function
NULL, // No GetKey function arg
&m_ServerCredential, // Server credential
&Expiration // Expiration timestamp
);
if (SecStatus != 0)
{
trace(("CSSPIServer failed in AcquireCredentialsHandle(). Error = %s\n",
CSSPI::TranslateError(SecStatus)
));
m_dwStatus = Failed;
return;
}
m_bValidCredHandle = TRUE;
m_dwStatus = Waiting;
}
//***************************************************************************
//
// CSSPIServer destructor
//
//***************************************************************************
CSSPIServer::~CSSPIServer()
{
if (m_pPkgInfo)
CSSPI::pVtbl->FreeContextBuffer(m_pPkgInfo);
delete [] m_pszPkgName;
if (m_bValidCredHandle)
CSSPI::pVtbl->FreeCredentialHandle(&m_ServerCredential);
if (m_bValidContextHandle)
CSSPI::pVtbl->DeleteSecurityContext(&m_ServerContext);
}
//***************************************************************************
//
// CSSPIServer::ContinueClientLogin
//
// Used to
//
// (1) Receive the client's logon request, and compute the challenge as
// the out-parameter <pToken>
//
// (2) Verify the response to the challenge.
//
// Parameters:
// <pInToken> A read-only pointer to the response (on the second call).
// NULL on the initial call.
//
// <dwInTokenSize> The number of bytes pointed to by the above pointers.
//
// <pToken> On the first call, receives a pointer to the
// challenge to send to the client.
//
// <pdwTokenSize> The size of the above challenge.
//
// Return values:
//
// <LoginContinue> Sent back on the first call to indicate that a
// challenge has been computed and returned to the
// client in <pToken>.
//
// <Failed> No out-params assigned. Indicates internal failure.
//
// <NoError> Only possible on the second call. Indicates the
// client was authenticated.
//
// <AccessDenied> Returned on the second call if the user was denied
// logon.
//
//***************************************************************************
DWORD CSSPIServer::ContinueClientLogin(
IN LPBYTE pInToken,
IN DWORD dwInTokenSize,
OUT LPBYTE *pToken,
OUT DWORD *pdwTokenSize
)
{
TimeStamp Expiration;
SecBufferDesc InputBufferDescriptor;
SecBuffer InputSecurityToken;
SecBufferDesc OutputBufferDescriptor;
SecBuffer OutputSecurityToken;
ULONG uContextRequirements = 0;
ULONG uContextAttributes;
// Build up the input buffer.
// ==========================
InputBufferDescriptor.cBuffers = 1;
InputBufferDescriptor.pBuffers = &InputSecurityToken;
InputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
InputSecurityToken.BufferType = SECBUFFER_TOKEN;
InputSecurityToken.cbBuffer = dwInTokenSize;
InputSecurityToken.pvBuffer = pInToken;
// Build the output buffer descriptor.
// ===================================
OutputBufferDescriptor.cBuffers = 1;
OutputBufferDescriptor.pBuffers = &OutputSecurityToken;
OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
LPBYTE pOutBuf = new BYTE[m_cbMaxToken];
OutputSecurityToken.BufferType = SECBUFFER_TOKEN;
OutputSecurityToken.cbBuffer = m_cbMaxToken;
OutputSecurityToken.pvBuffer = pOutBuf;
// Set up partial or final context handle.
// =======================================
PCtxtHandle pTmp = m_bValidContextHandle ? &m_ServerContext : 0;
// Process the client's initial token and see what happens.
// ========================================================
SECURITY_STATUS SecStatus = 0;
SecStatus = CSSPI::pVtbl->AcceptSecurityContext(
&m_ServerCredential,
pTmp, // No context handle yet
&InputBufferDescriptor,
uContextRequirements, // No context requirements
SECURITY_NATIVE_DREP,
&m_ServerContext,
&OutputBufferDescriptor,
&uContextAttributes,
&Expiration
);
*pToken = pOutBuf;
*pdwTokenSize = OutputSecurityToken.cbBuffer;
if (SecStatus == SEC_I_CONTINUE_NEEDED)
{
m_dwStatus = LoginContinue;
m_bValidContextHandle = TRUE;
return LoginContinue;
}
if (SecStatus == SEC_E_OK)
{
m_bValidContextHandle = TRUE;
delete [] pOutBuf;
*pToken = 0;
*pdwTokenSize = 0;
m_dwStatus = LoginCompleted;
return NoError;
}
// If here, an error occurred.
// ===========================
trace(("CSSPIClient::ContinueLogin failed. Status code = %s",
CSSPI::TranslateError(SecStatus)
));
*pToken = 0;
*pdwTokenSize = 0;
delete [] pOutBuf;
if (SecStatus == SEC_E_LOGON_DENIED)
{
m_dwStatus = AccessDenied;
return AccessDenied;
}
return Failed;
}
//***************************************************************************
//
// CSSPIServer::QueryUserInfo
//
// Gets information about a client after authentication.
//
// Parameters:
// <pszUser> Receives a pointer to the user name if TRUE is
// returned. Use operator delete to deallocate.
//
// Return value:
// TRUE if the user name was returned, FALSE if not.
//
//***************************************************************************
BOOL CSSPIServer::QueryUserInfo(
OUT LPTSTR *pszUser // Use operator delete
)
{
SecPkgContext_Names Names;
memset(&Names, 0, sizeof(SecPkgContext_Names));
*pszUser = 0;
SECURITY_STATUS SecStatus = CSSPI::pVtbl->QueryContextAttributes(
&m_ServerContext,
SECPKG_ATTR_NAMES,
&Names
);
if (SecStatus != 0)
{
trace(("QueryContextAttributes() for Name fails with %s ",
CSSPI::TranslateError(SecStatus)
));
return FALSE;
}
if (pszUser)
*pszUser = Macro_CloneLPTSTR(Names.sUserName);
CSSPI::pVtbl->FreeContextBuffer(LPVOID(Names.sUserName));
return TRUE;
}
//***************************************************************************
//
// CSSPIServer::IssueLoginToken
//
// Issues a login token for the client to use in subsequent access.
// This will only succeed if the client successfully computed the
// reponse to the challenge and <NoError> was returned from
// ContinueClientLogin.
//
// Parameters:
// <ClsId> Receives the CLSID which becomes the WBEM access token.
//
// Return value:
// NoError, Failed
//
//***************************************************************************
DWORD CSSPIServer::IssueLoginToken(
OUT CLSID &ClsId
)
{
if (m_dwStatus == LoginCompleted)
{
if (SUCCEEDED(CoCreateGuid(&ClsId)))
return NoError;
else
return Failed;
}
memset(&ClsId, 0, sizeof(CLSID));
return Failed;
}