mirror of https://github.com/tongzx/nt5src
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
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;
|
|
}
|
|
|