|
|
/*++
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; }
|