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.
3335 lines
85 KiB
3335 lines
85 KiB
/**********************************************************************/
|
|
/** Microsoft Windows NT **/
|
|
/** Copyright(c) Microsoft Corp., 1993 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
security.c
|
|
|
|
This module manages security for the Internet Services.
|
|
|
|
|
|
FILE HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
MuraliK 05-Jan-1995 Enable statistics query on RPC to go free.
|
|
|
|
*/
|
|
|
|
|
|
#include "tcpdllp.hxx"
|
|
#pragma hdrstop
|
|
#include <string.h>
|
|
#include <mbstring.h>
|
|
#include <limits.h>
|
|
|
|
#include "infosec.hxx"
|
|
#include <inetsvcs.h>
|
|
#include "TokenAcl.hxx"
|
|
|
|
//
|
|
// Token Cache lock. Controls access to the token cache list
|
|
//
|
|
|
|
#define LockTokenCache() EnterCriticalSection( &csTokenCacheLock )
|
|
#define UnlockTokenCache() LeaveCriticalSection( &csTokenCacheLock )
|
|
|
|
|
|
//
|
|
// The check period for how long a token can be in the cache. Tokens can
|
|
// be in the cache for up to two times this value (in seconds)
|
|
//
|
|
|
|
#define DEFAULT_CACHED_TOKEN_TTL (15 * 60)
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
CRITICAL_SECTION csTokenCacheLock;
|
|
HANDLE g_hProcessImpersonationToken = NULL;
|
|
HANDLE g_hProcessPrimaryToken = NULL;
|
|
BOOL g_fUseSingleToken = FALSE;
|
|
BOOL g_fAlwaysCheckForDuplicateLogon = FALSE;
|
|
BOOL g_fUseAdvapi32Logon = FALSE;
|
|
BOOL g_fCertCheckForRevocation = FALSE;
|
|
TS_TOKEN g_pctProcessToken;
|
|
BOOL g_fCertCheckCA = TRUE;
|
|
HINSTANCE g_hWinTrust = NULL;
|
|
PFN_WinVerifyTrust g_pfnWinVerifyTrust = NULL;
|
|
BOOL g_fLastPriorityUPNLogon = FALSE;
|
|
|
|
//
|
|
// Well-known SIDs.
|
|
//
|
|
|
|
PSID psidWorld;
|
|
PSID psidLocalSystem;
|
|
PSID psidAdmins;
|
|
PSID psidServerOps;
|
|
PSID psidPowerUsers;
|
|
PSID g_psidGuestUser;
|
|
PSID g_psidProcessUser;
|
|
|
|
# define GUEST_USER_SID_BUFFER_LEN (200)
|
|
|
|
BYTE g_GuestUserSidBuffer[GUEST_USER_SID_BUFFER_LEN];
|
|
|
|
//
|
|
// The API security object. Client access to the TCP Server APIs
|
|
// are validated against this object.
|
|
//
|
|
|
|
PSECURITY_DESCRIPTOR sdApiObject;
|
|
|
|
LUID g_ChangeNotifyPrivilegeTcbValue;
|
|
PTOKEN_PRIVILEGES g_pTokPrev = NULL;
|
|
|
|
//
|
|
// This table maps generic rights (like GENERIC_READ) to
|
|
// specific rights (like TCP_QUERY_SECURITY).
|
|
//
|
|
|
|
GENERIC_MAPPING TCPApiObjectMapping = {
|
|
TCP_GENERIC_READ, // generic read
|
|
TCP_GENERIC_WRITE, // generic write
|
|
TCP_GENERIC_EXECUTE, // generic execute
|
|
TCP_ALL_ACCESS // generic all
|
|
};
|
|
|
|
//
|
|
// List of cached tokens, the token list lock and the cookie to the token
|
|
// scavenger schedule item. The token cache TTL gets converted to msecs
|
|
// during startup
|
|
//
|
|
|
|
BOOL IsTokenCacheInitialized = FALSE;
|
|
LIST_ENTRY TokenCacheList;
|
|
DWORD dwScheduleCookie = 0;
|
|
DWORD cmsecTokenCacheTTL = DEFAULT_CACHED_TOKEN_TTL;
|
|
CHAR g_achComputerName[DNLEN+1];
|
|
|
|
LIST_ENTRY CredentialCacheList;
|
|
CRITICAL_SECTION csCredentialCacheLock;
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
DWORD
|
|
CreateWellKnownSids(
|
|
HINSTANCE hDll
|
|
);
|
|
|
|
VOID
|
|
FreeWellKnownSids(
|
|
VOID
|
|
);
|
|
|
|
DWORD
|
|
CreateApiSecurityObject(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
DeleteApiSecurityObject(
|
|
VOID
|
|
);
|
|
|
|
TS_TOKEN
|
|
ValidateUser(
|
|
PCHAR pszDomainName,
|
|
PCHAR pszUserName,
|
|
PCHAR pszPassword,
|
|
BOOL fAnonymous,
|
|
BOOL * pfAsGuest,
|
|
DWORD dwLogonMethod,
|
|
TCHAR * pszWorkstation,
|
|
LARGE_INTEGER * pExpiry,
|
|
BOOL * pfExpiry,
|
|
BOOL fUseSubAuthIfAnonymous
|
|
);
|
|
|
|
|
|
VOID EnableTcbPrivilege(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
BuildAcctDesc(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fUseSubAuth,
|
|
OUT CHAR * pchAcctDesc, // must be MAX_ACCT_DESC_LEN
|
|
OUT LPDWORD pdwAcctDescLen
|
|
);
|
|
|
|
BOOL
|
|
AddTokenToCache(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fUseSubAuth,
|
|
IN HANDLE hToken,
|
|
IN DWORD dwLogonMethod,
|
|
OUT CACHED_TOKEN * * ppct,
|
|
BOOL fCheckAlreadyExist,
|
|
LPBOOL pfAlreadyExist
|
|
);
|
|
|
|
|
|
BOOL
|
|
FindCachedToken(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fResetTTL,
|
|
IN BOOL fUseSubAuth,
|
|
IN DWORD dwLogonMethod,
|
|
OUT CACHED_TOKEN * * ppct
|
|
);
|
|
|
|
VOID
|
|
WINAPI
|
|
TokenCacheScavenger(
|
|
IN VOID * pContext
|
|
);
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: InitializeSecurity
|
|
|
|
SYNOPSIS: Initializes security authentication & impersonation
|
|
routines.
|
|
|
|
RETURNS: DWORD - NO_ERROR if successful, otherwise a Win32
|
|
error code.
|
|
|
|
NOTES: This routine may only be called by a single thread
|
|
of execution; it is not necessarily multi-thread safe.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
DWORD
|
|
InitializeSecurity(
|
|
IN HINSTANCE hDll
|
|
)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
DWORD err;
|
|
DWORD nName;
|
|
HANDLE hAccToken;
|
|
HKEY hKey;
|
|
DWORD dwType;
|
|
DWORD dwValue;
|
|
DWORD nBytes;
|
|
|
|
//
|
|
// Read the registry key to see whether tsunami caching is enabled
|
|
//
|
|
|
|
err = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
INETA_PARAMETERS_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
);
|
|
|
|
if ( err == ERROR_SUCCESS ) {
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
INETA_W3ONLY_NO_AUTH,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fW3OnlyNoAuth = (BOOL)!!dwValue;
|
|
if ( g_fW3OnlyNoAuth ) {
|
|
DbgPrint("W3OnlyNoAuth set to TRUE in Registry.\n");
|
|
} else {
|
|
DbgPrint("W3OnlyNoAuth set to FALSE in Registry.\n");
|
|
}
|
|
}
|
|
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
INETA_ALWAYS_CHECK_FOR_DUPLICATE_LOGON,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fAlwaysCheckForDuplicateLogon = (BOOL)dwValue;
|
|
}
|
|
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
INETA_USE_ADVAPI32_LOGON,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fUseAdvapi32Logon = (BOOL)dwValue;
|
|
}
|
|
|
|
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
INETA_CHECK_CERT_REVOCATION,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fCertCheckForRevocation = (BOOL)dwValue;
|
|
}
|
|
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
"CertCheckCA",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fCertCheckCA = (BOOL)dwValue;
|
|
}
|
|
|
|
nBytes = sizeof(dwValue);
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
"LastPriorityUPNLogon",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwValue,
|
|
&nBytes
|
|
);
|
|
|
|
if ( (err == ERROR_SUCCESS) && (dwType == REG_DWORD) ) {
|
|
g_fLastPriorityUPNLogon = !!dwValue;
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "Initializing security\n" ));
|
|
}
|
|
|
|
IsTokenCacheInitialized = TRUE;
|
|
InitializeListHead( &TokenCacheList );
|
|
INITIALIZE_CRITICAL_SECTION( &csTokenCacheLock );
|
|
|
|
InitializeListHead( &CredentialCacheList );
|
|
INITIALIZE_CRITICAL_SECTION( &csCredentialCacheLock );
|
|
|
|
if ( g_fW3OnlyNoAuth ) {
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"InitializeSecurity: NT Security disabled for W3OnlyNoAuth\n"));
|
|
|
|
g_fUseSingleToken = TRUE;
|
|
|
|
if ( !(g_pctProcessToken = new CACHED_TOKEN) )
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
g_pctProcessToken->_cRef = INT_MAX/2;
|
|
InitializeListHead( &g_pctProcessToken->_ListEntry );
|
|
|
|
if ( !OpenProcessToken (
|
|
GetCurrentProcess(),
|
|
TOKEN_DUPLICATE|TOKEN_IMPERSONATE|TOKEN_QUERY,
|
|
&hAccToken
|
|
) )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "fail OpenProcessToken\n"));
|
|
return GetLastError();
|
|
}
|
|
|
|
if ( !pfnDuplicateTokenEx( hAccToken,
|
|
0,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&g_hProcessPrimaryToken ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "fail pfnDuplicateTokenEx primary\n"));
|
|
CloseHandle( hAccToken );
|
|
return GetLastError();
|
|
}
|
|
|
|
if ( !pfnDuplicateTokenEx( hAccToken,
|
|
0,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&g_hProcessImpersonationToken ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "fail pfnDuplicateTokenEx impersonate\n"));
|
|
CloseHandle( hAccToken );
|
|
CloseHandle( g_hProcessPrimaryToken );
|
|
return GetLastError();
|
|
}
|
|
|
|
err = CreateWellKnownSids( hDll );
|
|
|
|
if ( err != NO_ERROR ) {
|
|
DBGPRINTF((DBG_CONTEXT,"CreateWellKnownSids failed with %d\n",err));
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create the API security object.
|
|
//
|
|
|
|
err = CreateApiSecurityObject();
|
|
|
|
if ( err != NO_ERROR ) {
|
|
DBGPRINTF((DBG_CONTEXT,"CreateApiSecurityObjects failed with %d\n",err));
|
|
goto exit;
|
|
}
|
|
|
|
g_pctProcessToken->_hToken = g_hProcessPrimaryToken;
|
|
g_pctProcessToken->m_hImpersonationToken = g_hProcessImpersonationToken;
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
//
|
|
// Create well-known SIDs.
|
|
//
|
|
|
|
err = CreateWellKnownSids( hDll );
|
|
|
|
if ( err != NO_ERROR ) {
|
|
DBGPRINTF((DBG_CONTEXT,"CreateWellKnownSids failed with %d\n",err));
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create the API security object.
|
|
//
|
|
|
|
err = CreateApiSecurityObject();
|
|
|
|
if ( err != NO_ERROR ) {
|
|
DBGPRINTF((DBG_CONTEXT,"CreateApiSecurityObjects failed with %d\n",err));
|
|
goto exit;
|
|
}
|
|
|
|
{
|
|
HKEY hkey;
|
|
|
|
//
|
|
// Get the default token TTL, must be at least one second
|
|
//
|
|
|
|
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
INETA_PARAMETERS_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkey )) {
|
|
|
|
cmsecTokenCacheTTL = ReadRegistryDword( hkey,
|
|
"UserTokenTTL",
|
|
DEFAULT_CACHED_TOKEN_TTL);
|
|
|
|
RegCloseKey( hkey );
|
|
}
|
|
|
|
cmsecTokenCacheTTL = max( 1, cmsecTokenCacheTTL );
|
|
cmsecTokenCacheTTL *= 1000;
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Scheduling token cached scavenger to %d seconds\n",
|
|
cmsecTokenCacheTTL/1000 ));
|
|
}
|
|
|
|
//
|
|
// Schedule a work item for the token scavenger
|
|
//
|
|
|
|
dwScheduleCookie = ScheduleWorkItem( TokenCacheScavenger,
|
|
NULL,
|
|
cmsecTokenCacheTTL,
|
|
TRUE ); // Periodic
|
|
}
|
|
|
|
pfnLogon32Initialize( NULL, DLL_PROCESS_ATTACH, NULL );
|
|
|
|
if ( g_pTokPrev = (PTOKEN_PRIVILEGES)LocalAlloc( LMEM_FIXED,
|
|
sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) )
|
|
{
|
|
if ( !LookupPrivilegeValue(
|
|
NULL,
|
|
"SeChangeNotifyPrivilege",
|
|
&g_ChangeNotifyPrivilegeTcbValue
|
|
) )
|
|
{
|
|
g_pTokPrev->PrivilegeCount = 0;
|
|
}
|
|
else
|
|
{
|
|
g_pTokPrev->PrivilegeCount = 1;
|
|
|
|
g_pTokPrev->Privileges[0].Luid = g_ChangeNotifyPrivilegeTcbValue;
|
|
g_pTokPrev->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
}
|
|
}
|
|
|
|
nName = sizeof(g_achComputerName);
|
|
if ( !GetComputerName( g_achComputerName, &nName ) )
|
|
{
|
|
g_achComputerName[0] = '\0';
|
|
}
|
|
|
|
g_hWinTrust = LoadLibrary( "wintrust.dll" );
|
|
if ( g_hWinTrust != NULL )
|
|
{
|
|
g_pfnWinVerifyTrust = (PFN_WinVerifyTrust)GetProcAddress( g_hWinTrust, "WinVerifyTrust" );
|
|
}
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "Security initialized\n" ));
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
|
|
} // InitializeSecurity
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TerminateSecurity
|
|
|
|
SYNOPSIS: Terminate security authentication & impersonation
|
|
routines.
|
|
|
|
NOTES: This routine may only be called by a single thread
|
|
of execution; it is not necessarily multi-thread safe.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
VOID
|
|
TerminateSecurity(
|
|
VOID
|
|
)
|
|
{
|
|
CACHED_TOKEN * pct;
|
|
CACHED_CREDENTIAL * pcred;
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"TerminateSecurity called\n"));
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Terminating security\n" ));
|
|
}
|
|
|
|
//
|
|
// Delete any tokens still in the cache
|
|
//
|
|
|
|
if ( IsTokenCacheInitialized )
|
|
{
|
|
|
|
LockTokenCache();
|
|
|
|
while ( !IsListEmpty( &TokenCacheList ))
|
|
{
|
|
pct = CONTAINING_RECORD( TokenCacheList.Flink,
|
|
CACHED_TOKEN,
|
|
_ListEntry );
|
|
|
|
RemoveEntryList( &pct->_ListEntry );
|
|
pct->_ListEntry.Flink = NULL;
|
|
|
|
//
|
|
// If the ref count isn't zero then somebody didn't delete all of
|
|
// their tokens
|
|
//
|
|
|
|
DBG_ASSERT( pct->_cRef == 1 );
|
|
|
|
CACHED_TOKEN::Dereference( pct );
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
|
|
DeleteCriticalSection( &csTokenCacheLock );
|
|
}
|
|
|
|
//
|
|
// Delete any credential in the cache
|
|
//
|
|
|
|
EnterCriticalSection( &csCredentialCacheLock );
|
|
|
|
while ( !IsListEmpty( &CredentialCacheList ))
|
|
{
|
|
pcred = CONTAINING_RECORD( CredentialCacheList.Flink,
|
|
CACHED_CREDENTIAL,
|
|
_ListEntry );
|
|
|
|
RemoveEntryList( &pcred->_ListEntry );
|
|
pcred->_ListEntry.Flink = NULL;
|
|
|
|
delete pcred;
|
|
}
|
|
|
|
LeaveCriticalSection( &csCredentialCacheLock );
|
|
|
|
DeleteCriticalSection( &csCredentialCacheLock );
|
|
|
|
if ( g_fUseSingleToken ) {
|
|
CloseHandle( g_hProcessImpersonationToken );
|
|
CloseHandle( g_hProcessPrimaryToken );
|
|
delete g_pctProcessToken;
|
|
return;
|
|
}
|
|
|
|
FreeWellKnownSids();
|
|
DeleteApiSecurityObject();
|
|
|
|
//
|
|
// Remove the scheduled scavenger
|
|
//
|
|
|
|
if ( dwScheduleCookie )
|
|
{
|
|
RemoveWorkItem( dwScheduleCookie );
|
|
}
|
|
|
|
if ( g_pTokPrev )
|
|
{
|
|
LocalFree( g_pTokPrev );
|
|
g_pTokPrev = NULL;
|
|
}
|
|
|
|
if ( g_hWinTrust != NULL )
|
|
{
|
|
g_pfnWinVerifyTrust = NULL;
|
|
FreeLibrary( g_hWinTrust );
|
|
g_hWinTrust = NULL;
|
|
}
|
|
|
|
pfnLogon32Initialize( NULL, DLL_PROCESS_DETACH, NULL );
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Security terminated\n" ));
|
|
}
|
|
} // TerminateSecurity
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TsLogonUser
|
|
|
|
SYNOPSIS: Validates a user's credentials, then sets the
|
|
impersonation for the current thread. In effect,
|
|
the current thread "becomes" the user.
|
|
|
|
ENTRY: pUserData - The user initiating the request (NULL for
|
|
the default account).
|
|
|
|
pszPassword - The user's password. May be NULL.
|
|
|
|
pfAsGuest - Will receive TRUE if the user was validated
|
|
with guest privileges.
|
|
|
|
pfAsAnonymous - Will receive TRUE if the user received the
|
|
services anonymous token
|
|
|
|
pszWorkstation - workstation name for remote user
|
|
can be NULL if default ( local computer) to be used
|
|
|
|
pExpiry - updated with pwd expiration date/time
|
|
|
|
pfExpiryAvailable - updated with TRUE if pwd expiration
|
|
date/time available
|
|
|
|
RETURNS: HANDLE - Token handle to use for impersonation or NULL
|
|
if the user couldn't be validated. Call GetLastError
|
|
for more information.
|
|
|
|
HISTORY:
|
|
KeithMo 18-Mar-1993 Created.
|
|
Johnl 14-Oct-1994 Mutilated for TCPSvcs
|
|
|
|
********************************************************************/
|
|
|
|
TS_TOKEN
|
|
TsLogonUser(
|
|
IN CHAR * pszUser,
|
|
IN CHAR * pszPassword,
|
|
OUT BOOL * pfAsGuest,
|
|
OUT BOOL * pfAsAnonymous,
|
|
IN PIIS_SERVER_INSTANCE psi,
|
|
PTCP_AUTHENT_INFO pTAI,
|
|
IN CHAR * pszWorkstation,
|
|
OUT LARGE_INTEGER * pExpiry,
|
|
OUT BOOL * pfExpiryAvailable
|
|
)
|
|
{
|
|
DBG_ASSERT( pfAsGuest != NULL );
|
|
DBG_ASSERT( pfAsAnonymous != NULL );
|
|
|
|
STACK_STATSTR (strAnonPwd, PWLEN+1);
|
|
STACK_STATSTR (strDomainAndUser, IIS_DNLEN+UNLEN+2);
|
|
STACK_STATSTR (strAnonUser, UNLEN+1);
|
|
CHAR * pszUserOnly;
|
|
CHAR * pszDomain;
|
|
TS_TOKEN hToken;
|
|
BOOL fUseDefaultDomain = TRUE;
|
|
TCP_AUTHENT_INFO InstanceAuthentInfo;
|
|
|
|
if ( g_fUseSingleToken ) {
|
|
*pfAsGuest = TRUE;
|
|
*pfAsAnonymous = TRUE;
|
|
*pfExpiryAvailable = FALSE;
|
|
CACHED_TOKEN::Reference( g_pctProcessToken );
|
|
return g_pctProcessToken;
|
|
}
|
|
|
|
// If the client didn't pass in metabase info, grab what we need from
|
|
// the instance.
|
|
//
|
|
|
|
if (pTAI == NULL)
|
|
{
|
|
InstanceAuthentInfo.strAnonUserName.Copy( "iusr_xxx" ); //(CHAR *)psi->QueryAnonUserName();
|
|
InstanceAuthentInfo.strAnonUserPassword.Copy( "" );
|
|
InstanceAuthentInfo.strDefaultLogonDomain.Copy( "" ); //(CHAR *)psi->QueryDefaultLogonDomain();
|
|
InstanceAuthentInfo.dwLogonMethod = MD_LOGON_INTERACTIVE; //psi->QueryLogonMethod();
|
|
InstanceAuthentInfo.fDontUseAnonSubAuth = TRUE;
|
|
pTAI = &InstanceAuthentInfo;
|
|
}
|
|
|
|
//
|
|
// Make a quick copy of the anonymous user for this server for later
|
|
// usage
|
|
//
|
|
|
|
if ( !pTAI->strAnonUserName.Clone( &strAnonUser) ||
|
|
!pTAI->strAnonUserPassword.Clone( &strAnonPwd) )
|
|
{
|
|
goto InvalidParamError;
|
|
}
|
|
|
|
// if the password is stored hashed, unhash it for usage
|
|
if (pTAI->fPwdIsHashed)
|
|
{
|
|
strAnonPwd.Unhash();
|
|
}
|
|
|
|
//
|
|
// Empty user defaults to the anonymous user
|
|
//
|
|
|
|
if ( !pszUser || *pszUser == '\0' )
|
|
{
|
|
pszUser = strAnonUser.QueryStr();
|
|
pszPassword = strAnonPwd.QueryStr();
|
|
fUseDefaultDomain = FALSE;
|
|
*pfAsAnonymous = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfAsAnonymous = FALSE;
|
|
}
|
|
|
|
//
|
|
// Validate parameters & state.
|
|
//
|
|
|
|
if ( strlen(pszUser) >= (IIS_DNLEN+UNLEN+2) )
|
|
{
|
|
goto InvalidParamError;
|
|
}
|
|
|
|
if( pszPassword == NULL )
|
|
{
|
|
pszPassword = "";
|
|
}
|
|
else if ( strlen(pszPassword) > PWLEN )
|
|
{
|
|
goto InvalidParamError;
|
|
}
|
|
|
|
//
|
|
// Did the user specify a domain in the domain\user format?
|
|
//
|
|
|
|
PSTR pszDefDom = NULL;
|
|
|
|
if (strchr( pszUser, '/' ) || _mbschr( (PUCHAR)pszUser, '\\' ))
|
|
{
|
|
//
|
|
// Save a copy of the domain\user so we can squirrel around
|
|
// with it a bit.
|
|
//
|
|
|
|
if ( !strDomainAndUser.Copy( pszUser))
|
|
{
|
|
goto InvalidParamError;
|
|
}
|
|
|
|
//
|
|
// Crack the name into domain/user components.
|
|
//
|
|
|
|
if ( !CrackUserAndDomain( strDomainAndUser.QueryStr(),
|
|
&pszUserOnly,
|
|
&pszDomain ))
|
|
{
|
|
goto InvalidParamError;
|
|
}
|
|
|
|
fUseDefaultDomain = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// it's either a user only, or UPN format
|
|
//
|
|
pszUserOnly = pszUser;
|
|
pszDomain = NULL;
|
|
|
|
//
|
|
// we may need to use the default domain, so let's see if it's valid
|
|
//
|
|
pszDefDom = pTAI->strDefaultLogonDomain.QueryStr();
|
|
|
|
if ( !pszDefDom ||
|
|
!*pszDefDom ||
|
|
strchr( pszDefDom, '/' ) ||
|
|
_mbschr( (PUCHAR)pszDefDom, '\\' ))
|
|
{
|
|
fUseDefaultDomain = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, here is what we do:
|
|
// - if the user specified a domain in the domain\user format, we'll only try that.
|
|
// - if we had any reason not to use the default domain, we will not try to.
|
|
// - if the username has a '@' in it and no '\', we'll try a UPN logon and also the default domain
|
|
//
|
|
|
|
PSTR pszD1, pszD2;
|
|
BOOL fAttemptSecondLogon;
|
|
|
|
if (pszDomain)
|
|
{
|
|
//
|
|
// user specified domain\user
|
|
//
|
|
pszD1 = pszDomain;
|
|
pszD2 = NULL;
|
|
fAttemptSecondLogon = FALSE;
|
|
}
|
|
else if (!fUseDefaultDomain)
|
|
{
|
|
//
|
|
// we are not trying the default domain, so it's either a local user or UPN format
|
|
//
|
|
pszD1 = "";
|
|
pszD2 = NULL;
|
|
fAttemptSecondLogon = FALSE;
|
|
}
|
|
else if (!strchr( pszUserOnly, '@' ))
|
|
{
|
|
//
|
|
// it's not a UPN format, so use the default domain
|
|
//
|
|
pszD1 = pszDefDom;
|
|
pszD2 = NULL;
|
|
fAttemptSecondLogon = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// here is the tricky part:
|
|
// - no domain\user was specified,
|
|
// - we could use a default domain name
|
|
// - there is a '@' in the username, so it might be a UPN format
|
|
// we resolve this ambiguity by attempting logon twice, the order depends on the
|
|
// registry key LastPriorityUPNLogon
|
|
//
|
|
if (g_fLastPriorityUPNLogon)
|
|
{
|
|
//
|
|
// try the default domain first
|
|
//
|
|
pszD1 = pszDefDom;
|
|
pszD2 = "";
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// try UPN logon first
|
|
//
|
|
pszD1 = "";
|
|
pszD2 = pszDefDom;
|
|
}
|
|
fAttemptSecondLogon = TRUE;
|
|
}
|
|
|
|
//
|
|
// Validate the domain/user/password combo and create
|
|
// an impersonation token.
|
|
//
|
|
|
|
hToken = ValidateUser( pszD1,
|
|
pszUserOnly,
|
|
pszPassword,
|
|
*pfAsAnonymous,
|
|
pfAsGuest,
|
|
pTAI->dwLogonMethod,
|
|
pszWorkstation,
|
|
pExpiry,
|
|
pfExpiryAvailable,
|
|
!pTAI->fDontUseAnonSubAuth
|
|
);
|
|
|
|
if (hToken == NULL &&
|
|
fAttemptSecondLogon &&
|
|
GetLastError() == ERROR_LOGON_FAILURE)
|
|
{
|
|
//
|
|
// the logon failed, but we get to try again with a different format
|
|
//
|
|
hToken = ValidateUser( pszD2,
|
|
pszUserOnly,
|
|
pszPassword,
|
|
*pfAsAnonymous,
|
|
pfAsGuest,
|
|
pTAI->dwLogonMethod,
|
|
pszWorkstation,
|
|
pExpiry,
|
|
pfExpiryAvailable,
|
|
!pTAI->fDontUseAnonSubAuth
|
|
);
|
|
}
|
|
|
|
strAnonPwd.Clear();
|
|
|
|
if( hToken == NULL )
|
|
{
|
|
STR strError;
|
|
const CHAR * psz[2];
|
|
DWORD dwErr = GetLastError();
|
|
|
|
psi->LoadStr( strError, dwErr, FALSE );
|
|
|
|
psz[0] = pszUser;
|
|
psz[1] = strError.QueryStr();
|
|
|
|
psi->m_Service->LogEvent(
|
|
INET_SVCS_FAILED_LOGON,
|
|
2,
|
|
psz,
|
|
dwErr );
|
|
|
|
//
|
|
// Validation failure.
|
|
//
|
|
|
|
if ( dwErr == ERROR_LOGON_TYPE_NOT_GRANTED ||
|
|
dwErr == ERROR_ACCOUNT_DISABLED )
|
|
{
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Reset LastError(), as LogEvent() may have overwritten it
|
|
// e.g log is full
|
|
//
|
|
|
|
SetLastError( dwErr );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
return hToken;
|
|
|
|
InvalidParamError:
|
|
return NULL;
|
|
|
|
} // TsLogonUser
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ValidateUser
|
|
|
|
SYNOPSIS: Validate a given domain/user/password tuple.
|
|
|
|
ENTRY: pszDomainName - The user's domain (NULL = current).
|
|
|
|
pszUserName - The user's name.
|
|
|
|
pszPassword - The user's (plaintext) password.
|
|
|
|
fAnonymous - TRUE if this is the anonymous user
|
|
|
|
pfAsGuest - Will receive TRUE if the user was validated
|
|
with guest privileges.
|
|
|
|
dwLogonMethod - interactive or batch
|
|
|
|
pszWorkstation - workstation name for remote user
|
|
can be NULL if default ( local computer) to be used
|
|
|
|
pExpiry - updated with pwd expiration date/time
|
|
|
|
pfExpiryAvailable - updated with TRUE if pwd expiration
|
|
date/time available
|
|
|
|
fUseSubAuthIfAnonymous - TRUE if logon anonymous user
|
|
using IIS sub-auth
|
|
|
|
RETURNS: HANDLE - An impersonation token, NULL if user cannot
|
|
be validated. Call GetLastError for more information.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
TS_TOKEN ValidateUser(
|
|
PCHAR pszDomainName,
|
|
PCHAR pszUserName,
|
|
PCHAR pszPassword,
|
|
BOOL fAnonymous,
|
|
BOOL * pfAsGuest,
|
|
DWORD dwLogonMethod,
|
|
CHAR * pszWorkstation,
|
|
LARGE_INTEGER * pExpiry,
|
|
BOOL * pfExpiryAvailable,
|
|
BOOL fUseSubAuthIfAnonymous
|
|
)
|
|
{
|
|
CACHED_TOKEN * pct = NULL;
|
|
HANDLE hToken;
|
|
HANDLE hImpersonationToken = NULL;
|
|
BOOL fExpiry = FALSE;
|
|
DWORD dwSubAuth = 0;
|
|
CHAR achCookie[32];
|
|
BOOL fExist;
|
|
|
|
if ( pfExpiryAvailable )
|
|
{
|
|
*pfExpiryAvailable = FALSE;
|
|
}
|
|
|
|
if ( fAnonymous && fUseSubAuthIfAnonymous )
|
|
{
|
|
if ( !pfnNetUserCookieA( pszUserName,
|
|
IIS_SUBAUTH_SEED,
|
|
achCookie,
|
|
sizeof(achCookie ) ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
dwSubAuth = IIS_SUBAUTH_ID;
|
|
pszPassword = achCookie;
|
|
dwLogonMethod = LOGON32_LOGON_IIS_NETWORK;
|
|
}
|
|
|
|
//
|
|
// Is it in the cache? References the token if we find it
|
|
//
|
|
|
|
if ( FindCachedToken( pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
fAnonymous, // Reset the TTL if anonymous
|
|
fAnonymous && fUseSubAuthIfAnonymous,
|
|
dwLogonMethod,
|
|
&pct ))
|
|
{
|
|
*pfAsGuest = pct->IsGuest();
|
|
|
|
if ( NULL != pExpiry) {
|
|
memcpy( pExpiry, pct->QueryExpiry(), sizeof(LARGE_INTEGER) );
|
|
}
|
|
|
|
if ( pfExpiryAvailable )
|
|
{
|
|
*pfExpiryAvailable = TRUE;
|
|
}
|
|
|
|
return pct;
|
|
}
|
|
|
|
if ( (dwLogonMethod == LOGON32_LOGON_NETWORK ||
|
|
dwLogonMethod == LOGON32_LOGON_BATCH ||
|
|
dwLogonMethod == LOGON32_LOGON_INTERACTIVE ||
|
|
dwLogonMethod == LOGON32_LOGON_IIS_NETWORK ||
|
|
dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT ) &&
|
|
( !g_fUseAdvapi32Logon || dwSubAuth == IIS_SUBAUTH_ID ) )
|
|
{
|
|
if ( !pfnLogonNetUserA( pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
pszWorkstation,
|
|
dwSubAuth,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
pExpiry ))
|
|
{
|
|
if ( fAnonymous &&
|
|
( GetLastError() == ERROR_LOGON_TYPE_NOT_GRANTED ) &&
|
|
( dwLogonMethod == LOGON32_LOGON_INTERACTIVE ) )
|
|
{
|
|
// try again
|
|
|
|
dwLogonMethod = LOGON32_LOGON_BATCH;
|
|
|
|
if ( !pfnLogonNetUserA( pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
pszWorkstation,
|
|
dwSubAuth,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken,
|
|
pExpiry ))
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
fExpiry = TRUE;
|
|
|
|
if ( pfExpiryAvailable )
|
|
{
|
|
*pfExpiryAvailable = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !LogonUserA( pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
dwLogonMethod,
|
|
LOGON32_PROVIDER_WINNT50,
|
|
&hToken ))
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if ( dwLogonMethod == LOGON32_LOGON_NETWORK ||
|
|
dwLogonMethod == LOGON32_LOGON_IIS_NETWORK ||
|
|
dwLogonMethod == LOGON32_LOGON_NETWORK_CLEARTEXT )
|
|
{
|
|
hImpersonationToken = hToken;
|
|
|
|
if ( !pfnDuplicateTokenEx( hImpersonationToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityDelegation,
|
|
TokenPrimary,
|
|
&hToken ))
|
|
{
|
|
if ( !pfnDuplicateTokenEx( hImpersonationToken,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenPrimary,
|
|
&hToken ))
|
|
{
|
|
CloseHandle( hImpersonationToken );
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pfAsGuest = IsGuestUser(hToken);
|
|
|
|
//
|
|
// Add this new token to the cache, hToken gets replaced by the
|
|
// cached token object
|
|
//
|
|
|
|
if ( !AddTokenToCache( pszUserName,
|
|
pszDomainName,
|
|
pszPassword,
|
|
fAnonymous && fUseSubAuthIfAnonymous,
|
|
hToken,
|
|
dwLogonMethod,
|
|
&pct,
|
|
g_fAlwaysCheckForDuplicateLogon | fAnonymous,
|
|
&fExist ))
|
|
{
|
|
if ( hImpersonationToken != NULL )
|
|
{
|
|
CloseHandle( hImpersonationToken );
|
|
}
|
|
CloseHandle( hToken );
|
|
return NULL;
|
|
}
|
|
|
|
pct->SetGuest(*pfAsGuest);
|
|
if ( fExpiry )
|
|
{
|
|
pct->SetExpiry( pExpiry );
|
|
}
|
|
|
|
//
|
|
// DuplicateToken() apparently returns an impersonated token
|
|
// so it is not necessary to call pfnDuplicateTokenEx
|
|
//
|
|
|
|
if ( !fExist )
|
|
{
|
|
if ( hImpersonationToken == NULL
|
|
&& !pfnDuplicateTokenEx( hToken, // hSourceToken
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityDelegation, // Obtain impersonation
|
|
TokenImpersonation,
|
|
&hImpersonationToken) // hDestinationToken
|
|
) {
|
|
if ( !pfnDuplicateTokenEx( hToken, // hSourceToken
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation, // Obtain impersonation
|
|
TokenImpersonation,
|
|
&hImpersonationToken) // hDestinationToken
|
|
) {
|
|
hImpersonationToken = NULL;
|
|
}
|
|
}
|
|
|
|
// Bug 86489:
|
|
// Grant all access to the token for "Everyone" so that ISAPIs that run out of proc
|
|
// can do an OpenThreadToken call
|
|
if (FAILED( GrantAllAccessToToken( hImpersonationToken ) ) )
|
|
{
|
|
CloseHandle( hImpersonationToken );
|
|
DBG_ASSERT( FALSE );
|
|
return NULL;
|
|
}
|
|
|
|
pct->SetImpersonationToken( hImpersonationToken);
|
|
}
|
|
else if ( hImpersonationToken )
|
|
{
|
|
CloseHandle( hImpersonationToken );
|
|
}
|
|
|
|
return pct;
|
|
|
|
} // ValidateUser
|
|
|
|
|
|
|
|
|
|
|
|
# define MAX_TOKEN_USER_INFO (300)
|
|
BOOL
|
|
IsGuestUser(IN HANDLE hToken)
|
|
/*++
|
|
Given a user token, this function determines if the token belongs
|
|
to a guest user. It returns true if the token is a guest user token.
|
|
|
|
Arguments:
|
|
hToken - handle for the Security token for a user.
|
|
|
|
|
|
Returns:
|
|
BOOL.
|
|
|
|
History:
|
|
MuraliK 22-Jan-1996 Created.
|
|
--*/
|
|
{
|
|
BOOL fGuest = FALSE;
|
|
BYTE rgbInfo[MAX_TOKEN_USER_INFO];
|
|
DWORD cbTotalRequired;
|
|
|
|
//
|
|
// Get the user information associated with the token.
|
|
// Using this we can then query to find out if it belongs to a guest user.
|
|
//
|
|
|
|
if (GetTokenInformation( hToken,
|
|
TokenUser,
|
|
(LPVOID ) rgbInfo,
|
|
MAX_TOKEN_USER_INFO,
|
|
&cbTotalRequired)
|
|
) {
|
|
|
|
TOKEN_USER * pTokenUser = (TOKEN_USER *) rgbInfo;
|
|
PSID pSid = pTokenUser->User.Sid;
|
|
|
|
fGuest = EqualSid( pSid, g_psidGuestUser);
|
|
|
|
} else {
|
|
|
|
IF_DEBUG( DLL_SECURITY) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"GetTokenInformation(%08x) failed. Error = %d."
|
|
" sizeof(TOKEN_USER) = %d, cb = %d\n",
|
|
hToken,
|
|
GetLastError(),
|
|
sizeof(TOKEN_USER), cbTotalRequired
|
|
));
|
|
}
|
|
}
|
|
|
|
return ( fGuest);
|
|
|
|
} // IsGuestUser()
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TsImpersonateUser
|
|
|
|
SYNOPSIS: Causes the current thread to impersonate the user
|
|
represented by the given impersonation token.
|
|
|
|
ENTRY: hToken - A handle to an impersonation token created
|
|
with ValidateUser. This is actually a pointer to
|
|
a cached token object.
|
|
|
|
RETURNS: BOOL - TRUE if successful, FALSE otherwise.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
MuraliK 21-Feb-1996 Optimized Token caching
|
|
|
|
********************************************************************/
|
|
BOOL TsImpersonateUser( TS_TOKEN hToken )
|
|
{
|
|
HANDLE hTok;
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"impersonating user token %08lX : Imperonation(%08lx)\n",
|
|
CTO_TO_TOKEN(hToken),
|
|
((CACHED_TOKEN *) hToken)->QueryImpersonationToken()
|
|
));
|
|
}
|
|
|
|
hTok = ((CACHED_TOKEN *) hToken)->QueryImpersonationToken();
|
|
if ( hTok == NULL) {
|
|
// if there is no impersonation token use the normal token itself.
|
|
hTok = CTO_TO_TOKEN(hToken);
|
|
}
|
|
|
|
#if DBG
|
|
if( !ImpersonateLoggedOnUser( hTok ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot impersonate user token %08lX, error %08lX\n",
|
|
CTO_TO_TOKEN(hToken),
|
|
GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
# else
|
|
|
|
return ( ImpersonateLoggedOnUser(hTok));
|
|
|
|
# endif // DBG
|
|
|
|
} // TsImpersonateUser
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TsDeleteUserToken
|
|
|
|
SYNOPSIS: Deletes a token created with ValidateUser.
|
|
|
|
ENTRY: hToken - An impersonation token created with
|
|
ValidateUser.
|
|
|
|
RETURNS: BOOL - TRUE if successful, FALSE otherwise.
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
BOOL TsDeleteUserToken(
|
|
TS_TOKEN hToken
|
|
)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
CACHED_TOKEN::Dereference( (CACHED_TOKEN *) hToken );
|
|
|
|
return TRUE;
|
|
} // DeleteUserToken
|
|
|
|
HANDLE
|
|
TsTokenToHandle(
|
|
TS_TOKEN hToken
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Converts the token object into a real impersonation handle
|
|
|
|
Arguments:
|
|
|
|
hToken - pointer to cached token object
|
|
|
|
Returns:
|
|
Handle of real impersonation token
|
|
--*/
|
|
{
|
|
DBG_ASSERT( hToken != NULL );
|
|
|
|
return CTO_TO_TOKEN( hToken );
|
|
}
|
|
|
|
|
|
HANDLE
|
|
TsTokenToImpHandle(
|
|
TS_TOKEN hToken
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Converts the token object into an impersonation handle
|
|
|
|
Arguments:
|
|
|
|
hToken - pointer to cached token object
|
|
|
|
Returns:
|
|
Handle of impersonation token
|
|
--*/
|
|
{
|
|
DBG_ASSERT( hToken != NULL );
|
|
|
|
return CTO_TO_IMPTOKEN( hToken );
|
|
}
|
|
|
|
BOOL
|
|
BuildAnonymousAcctDesc(
|
|
PTCP_AUTHENT_INFO pTAI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds the anonymous account description based on the authentication
|
|
info structure.
|
|
|
|
Arguments:
|
|
|
|
pTAI - Pointer to authentication info to build
|
|
|
|
Returns:
|
|
|
|
TRUE if Success, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
STACK_STATSTR (strDomainAndUser, IIS_DNLEN+UNLEN+2);
|
|
STACK_STATSTR (strPassword, PWLEN+1);
|
|
PCHAR pszUserOnly;
|
|
PCHAR pszDomain;
|
|
CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
|
|
DWORD cbDescLen;
|
|
BOOL RetVal;
|
|
|
|
|
|
if ( g_fUseSingleToken ) {
|
|
pTAI->cbAnonAcctDesc = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !pTAI->strAnonUserName.Clone( &strDomainAndUser) ||
|
|
!pTAI->strAnonUserPassword.Clone( &strPassword) )
|
|
{
|
|
RetVal = FALSE;
|
|
goto BuildAnonymousAcctDesc_exit;
|
|
}
|
|
|
|
// if the password is stored hashed, unhash it for usage
|
|
if (pTAI->fPwdIsHashed) {
|
|
strPassword.Unhash();
|
|
}
|
|
|
|
if ( !CrackUserAndDomain( strDomainAndUser.QueryStr(),
|
|
&pszUserOnly,
|
|
&pszDomain ))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"BuildAnonymousAcctDesc: Call to CrackUserAndDomain failed\n"));
|
|
|
|
RetVal = FALSE;
|
|
goto BuildAnonymousAcctDesc_exit;
|
|
}
|
|
|
|
if ( !BuildAcctDesc( pszUserOnly,
|
|
pszDomain,
|
|
strPassword.QueryStr(),
|
|
!pTAI->fDontUseAnonSubAuth,
|
|
achAcctDesc,
|
|
&pTAI->cbAnonAcctDesc ) ||
|
|
!pTAI->bAnonAcctDesc.Resize( pTAI->cbAnonAcctDesc ))
|
|
{
|
|
RetVal = FALSE;
|
|
goto BuildAnonymousAcctDesc_exit;
|
|
}
|
|
|
|
memcpy( pTAI->bAnonAcctDesc.QueryPtr(),
|
|
achAcctDesc,
|
|
pTAI->cbAnonAcctDesc );
|
|
|
|
RetVal = TRUE;
|
|
|
|
BuildAnonymousAcctDesc_exit:
|
|
|
|
strPassword.Clear();
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
BOOL
|
|
BuildAcctDesc(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fUseSubAuth,
|
|
OUT CHAR * pchAcctDesc,
|
|
OUT LPDWORD pdwAcctDescLen
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Builds a cache descriptor for account cache
|
|
|
|
Arguments:
|
|
|
|
pszUser - User name attempting to logon
|
|
pszDomain - Domain the user belongs to
|
|
pszPwd - password (case sensitive)
|
|
fUseSubAuth - TRUE if sub-authenticator used
|
|
pchAcctDesc - updated with descriptor
|
|
pdwAcctDescLen - updated with descriptor length
|
|
|
|
Returns:
|
|
TRUE on success, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
if ( fUseSubAuth )
|
|
{
|
|
pszPwd = "";
|
|
}
|
|
|
|
size_t lU = strlen( pszUser ) + 1;
|
|
size_t lD = strlen( pszDomain ) + 1;
|
|
size_t lP = strlen( pszPwd ) + 1;
|
|
|
|
if ( lU > (UNLEN+1) ||
|
|
lD > (IIS_DNLEN+1) ||
|
|
lP > (PWLEN+1) )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwAcctDescLen = (DWORD)(1 + lU + lD + lP);
|
|
|
|
LPBYTE pD = (BYTE *) pchAcctDesc;
|
|
|
|
*pD++ = (BYTE)fUseSubAuth;
|
|
|
|
memcpy( pD, pszUser, lU );
|
|
CharLower( (LPSTR)pD );
|
|
|
|
memcpy( pD + lU, pszDomain, lD );
|
|
_strlwr( (LPSTR)(pD+lU) );
|
|
|
|
memcpy( pD + lU + lD, pszPwd, lP );
|
|
|
|
DBG_ASSERT( (lU + lD + lP) < MAX_ACCT_DESC_LEN );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FindCachedToken(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fResetTTL,
|
|
IN BOOL fUseSubAuth,
|
|
IN DWORD dwLogonMethod,
|
|
OUT CACHED_TOKEN * * ppct
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Checks to see if the specified user token handle is cached
|
|
|
|
Arguments:
|
|
|
|
pszUser - User name attempting to logon
|
|
pszDomain - Domain the user belongs to
|
|
pszPwd - password (case sensitive)
|
|
fResetTTL - Resets the TTL for this token
|
|
fUseSubAuth - TRUE if sub-authenticator used
|
|
dwLogonMethod - Logon method (Batch, Interactive, Network)
|
|
ppct - Receives token object
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if the entry couldn't be found
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
CACHED_TOKEN * pct;
|
|
CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
|
|
DWORD dwAcctDescLen;
|
|
LPBYTE pAcctDesc;
|
|
|
|
DBG_ASSERT( pszUser != NULL );
|
|
|
|
if ( !BuildAcctDesc( pszUser, pszDomain, pszPwd, fUseSubAuth, achAcctDesc, &dwAcctDescLen) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DBG_ASSERT( dwAcctDescLen < sizeof(achAcctDesc ));
|
|
|
|
pAcctDesc = (LPBYTE)achAcctDesc;
|
|
|
|
LockTokenCache();
|
|
|
|
for ( pEntry = TokenCacheList.Flink;
|
|
pEntry != &TokenCacheList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
|
|
|
|
if ( pct->m_dwAcctDescLen == dwAcctDescLen &&
|
|
pct->m_dwLogonMethod == dwLogonMethod &&
|
|
!memcmp( pct->_achAcctDesc, pAcctDesc, dwAcctDescLen ) )
|
|
{
|
|
CACHED_TOKEN::Reference( pct );
|
|
*ppct = pct;
|
|
|
|
//
|
|
// Reset the TTL if this is the anonymous user so items in the
|
|
// cache don't get invalidated (token handle used as a
|
|
// discriminator)
|
|
|
|
if ( fResetTTL )
|
|
{
|
|
pct->_TTL = 2;
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if( !_stricmp( pct->m_achUserName, pszUser ) &&
|
|
!_stricmp( pct->m_achDomainName, pszDomain ) &&
|
|
pct->m_dwLogonMethod == dwLogonMethod )
|
|
{
|
|
UnlockTokenCache();
|
|
|
|
RemoveTokenFromCache( pct );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
|
|
return FALSE;
|
|
|
|
} // FindCachedToken
|
|
|
|
|
|
|
|
TS_TOKEN
|
|
FastFindAnonymousToken(
|
|
IN PTCP_AUTHENT_INFO pTAI
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Checks to see if the specified anonymous user token handle is cached.
|
|
|
|
Don't call this function when using the sub-authenticator!
|
|
|
|
Arguments:
|
|
|
|
pTAI - pointer to the anonymous authentication info
|
|
|
|
Returns:
|
|
Pointer to the cached object.
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
CACHED_TOKEN * pct;
|
|
|
|
LockTokenCache();
|
|
|
|
for ( pEntry = TokenCacheList.Flink;
|
|
pEntry != &TokenCacheList;
|
|
pEntry = pEntry->Flink ) {
|
|
|
|
pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
|
|
|
|
DBG_ASSERT(pct->m_dwAcctDescLen > 0);
|
|
|
|
if ( (pct->m_dwAcctDescLen == pTAI->cbAnonAcctDesc ) &&
|
|
RtlEqualMemory(
|
|
pct->_achAcctDesc,
|
|
pTAI->bAnonAcctDesc.QueryPtr(),
|
|
pct->m_dwAcctDescLen ) ) {
|
|
|
|
CACHED_TOKEN::Reference( pct );
|
|
|
|
//
|
|
// Reset the TTL if this is the anonymous user so items in the
|
|
// cache don't get invalidated (token handle used as a
|
|
// discriminator)
|
|
|
|
pct->_TTL = 2;
|
|
|
|
UnlockTokenCache();
|
|
return pct;
|
|
}
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
return NULL;
|
|
} // FastFindAnonymousToken
|
|
|
|
|
|
|
|
BOOL
|
|
AddTokenToCache(
|
|
IN const CHAR * pszUser,
|
|
IN const CHAR * pszDomain,
|
|
IN const CHAR * pszPwd,
|
|
IN BOOL fUseSubAuth,
|
|
IN HANDLE hToken,
|
|
IN DWORD dwLogonMethod,
|
|
OUT CACHED_TOKEN * * ppct,
|
|
BOOL fCheckAlreadyExist,
|
|
LPBOOL pfExist
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Adds the specified token to the cache and converts the token handle
|
|
to a cached token object
|
|
|
|
Arguments:
|
|
|
|
pszUser - User name attempting to logon
|
|
pszDomain - Domain the user belongs to
|
|
pszPwd - Cast sensitive password
|
|
fUseSubAuth - TRUE if subauth to be used
|
|
phToken - Contains the token handle that was just logged on
|
|
dwLogonMethod - Logon Method
|
|
ppct - Receives cached token object
|
|
fCheckAlreadyExist - check if entry with same name already exist
|
|
pfExist - updated with TRUE if acct already exists
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if the entry couldn't be found
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
CACHED_TOKEN * pctF;
|
|
CACHED_TOKEN * pct;
|
|
DWORD dwAcctDescLen;
|
|
BOOL fFound = FALSE;
|
|
CHAR achAcctDesc[MAX_ACCT_DESC_LEN];
|
|
|
|
DBG_ASSERT( pszUser != NULL );
|
|
|
|
if( ( strlen( pszUser ) >= UNLEN ) ||
|
|
( strlen( pszDomain ) >= IIS_DNLEN ) )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !BuildAcctDesc( pszUser, pszDomain, pszPwd, fUseSubAuth, achAcctDesc, &dwAcctDescLen) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pct = new CACHED_TOKEN;
|
|
|
|
if ( !pct )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
pct->_hToken = hToken;
|
|
pct->m_hImpersonationToken = NULL; // initialize to invalid value
|
|
CopyMemory( pct->_achAcctDesc, achAcctDesc, dwAcctDescLen );
|
|
pct->m_dwAcctDescLen = dwAcctDescLen;
|
|
pct->m_dwLogonMethod = dwLogonMethod;
|
|
|
|
strcpy( pct->m_achUserName, pszUser );
|
|
strcpy( pct->m_achDomainName, pszDomain );
|
|
|
|
//
|
|
// Add the token to the list, we check for duplicates at callers's request
|
|
//
|
|
|
|
LockTokenCache();
|
|
|
|
if ( fCheckAlreadyExist )
|
|
{
|
|
for ( pEntry = TokenCacheList.Flink;
|
|
pEntry != &TokenCacheList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pctF = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
|
|
|
|
if ( pctF->m_dwAcctDescLen == dwAcctDescLen &&
|
|
!memcmp( pctF->_achAcctDesc, pct->_achAcctDesc, dwAcctDescLen ) &&
|
|
pctF->m_dwLogonMethod == dwLogonMethod )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pfExist = fFound;
|
|
|
|
if ( !fFound )
|
|
{
|
|
InsertHeadList( &TokenCacheList, &pct->_ListEntry );
|
|
}
|
|
else
|
|
{
|
|
// delete cache item ( was not yet on list )
|
|
|
|
CACHED_TOKEN::Dereference( pct );
|
|
|
|
pct = pctF;
|
|
}
|
|
|
|
CACHED_TOKEN::Reference( pct );
|
|
|
|
UnlockTokenCache();
|
|
|
|
*ppct = pct;
|
|
|
|
return TRUE;
|
|
} // AddTokenToCache
|
|
|
|
|
|
|
|
|
|
VOID
|
|
RemoveTokenFromCache( IN CACHED_TOKEN * pct)
|
|
{
|
|
DBG_ASSERT( pct != NULL);
|
|
LockTokenCache();
|
|
|
|
//
|
|
// Remove from the list
|
|
//
|
|
|
|
if ( pct->_ListEntry.Flink )
|
|
{
|
|
RemoveEntryList( &pct->_ListEntry );
|
|
pct->_ListEntry.Flink = NULL;
|
|
|
|
//
|
|
// Free any handles this user may still have open
|
|
//
|
|
|
|
TsCacheFlushUser( pct->_hToken, FALSE );
|
|
|
|
CACHED_TOKEN::Dereference( pct );
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
|
|
return;
|
|
} // RemoveTokenFromCache()
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
TokenCacheScavenger(
|
|
IN VOID * /* pContext */
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Decrements TTLs and removes tokens that have timed out
|
|
|
|
Arguments:
|
|
|
|
pContext - Not used
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
LIST_ENTRY * pEntryNext;
|
|
CACHED_TOKEN * pct;
|
|
|
|
|
|
LockTokenCache();
|
|
|
|
for ( pEntry = TokenCacheList.Flink;
|
|
pEntry != &TokenCacheList; )
|
|
{
|
|
pEntryNext = pEntry->Flink;
|
|
|
|
pct = CONTAINING_RECORD( pEntry, CACHED_TOKEN, _ListEntry );
|
|
|
|
if ( !(--pct->_TTL) )
|
|
{
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[TokenCacheScavenger] Timing out token for %s\n",
|
|
pct->_achAcctDesc ));
|
|
}
|
|
|
|
//
|
|
// This item has timed out, remove from the list
|
|
//
|
|
|
|
RemoveEntryList( &pct->_ListEntry );
|
|
pct->_ListEntry.Flink = NULL;
|
|
|
|
//
|
|
// Free any handles this user may still have open
|
|
//
|
|
|
|
TsCacheFlushUser( pct->_hToken, FALSE );
|
|
|
|
CACHED_TOKEN::Dereference( pct );
|
|
}
|
|
|
|
pEntry = pEntryNext;
|
|
}
|
|
|
|
UnlockTokenCache();
|
|
|
|
} // TokenCacheScavenger
|
|
|
|
|
|
BOOL
|
|
TsGetSecretW(
|
|
WCHAR * pszSecretName,
|
|
BUFFER * pbufSecret
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Retrieves the specified unicode secret
|
|
|
|
Arguments:
|
|
|
|
pszSecretName - LSA Secret to retrieve
|
|
pbufSecret - Receives found secret
|
|
|
|
Returns:
|
|
TRUE on success and FALSE if any failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fResult;
|
|
NTSTATUS ntStatus;
|
|
PUNICODE_STRING punicodePassword = NULL;
|
|
UNICODE_STRING unicodeSecret;
|
|
LSA_HANDLE hPolicy;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
|
|
if ( pfnLsaOpenPolicy == NULL ) {
|
|
DBGPRINTF((DBG_CONTEXT,"LsaOpenPolicy does not exist on win95\n"));
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Open a policy to the remote LSA
|
|
//
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
NULL,
|
|
0L,
|
|
NULL,
|
|
NULL );
|
|
|
|
ntStatus = pfnLsaOpenPolicy( NULL,
|
|
&ObjectAttributes,
|
|
POLICY_ALL_ACCESS,
|
|
&hPolicy );
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) )
|
|
{
|
|
SetLastError( pfnLsaNtStatusToWinError( ntStatus ) );
|
|
return FALSE;
|
|
}
|
|
|
|
InitUnicodeString( &unicodeSecret, pszSecretName );
|
|
|
|
//
|
|
// Query the secret value.
|
|
//
|
|
|
|
ntStatus = pfnLsaRetrievePrivateData( hPolicy,
|
|
&unicodeSecret,
|
|
&punicodePassword );
|
|
|
|
if( NT_SUCCESS(ntStatus) )
|
|
{
|
|
DWORD cbNeeded;
|
|
|
|
cbNeeded = punicodePassword->Length + sizeof(WCHAR);
|
|
|
|
if ( !pbufSecret->Resize( cbNeeded ) )
|
|
{
|
|
ntStatus = STATUS_NO_MEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
CopyMemory(
|
|
pbufSecret->QueryPtr(),
|
|
punicodePassword->Buffer,
|
|
punicodePassword->Length
|
|
);
|
|
|
|
*((WCHAR *) pbufSecret->QueryPtr() +
|
|
punicodePassword->Length / sizeof(WCHAR)) = L'\0';
|
|
|
|
ZeroMemory( punicodePassword->Buffer,
|
|
punicodePassword->MaximumLength );
|
|
|
|
}
|
|
|
|
Failure:
|
|
|
|
fResult = NT_SUCCESS(ntStatus);
|
|
|
|
//
|
|
// Cleanup & exit.
|
|
//
|
|
|
|
if( punicodePassword != NULL )
|
|
{
|
|
pfnLsaFreeMemory( (PVOID)punicodePassword );
|
|
}
|
|
|
|
pfnLsaClose( hPolicy );
|
|
|
|
if ( !fResult )
|
|
{
|
|
SetLastError( pfnLsaNtStatusToWinError( ntStatus ));
|
|
}
|
|
|
|
return fResult;
|
|
} // TsGetSecretW
|
|
|
|
|
|
DWORD
|
|
TsSetSecretW(
|
|
IN LPWSTR SecretName,
|
|
IN LPWSTR pSecret,
|
|
IN DWORD cbSecret
|
|
)
|
|
/*++
|
|
|
|
Description
|
|
|
|
Sets the specified LSA secret
|
|
|
|
Arguments:
|
|
|
|
SecretName - Name of the LSA secret
|
|
pSecret - Pointer to secret memory
|
|
cbSecret - Size of pSecret memory block
|
|
|
|
Note:
|
|
|
|
--*/
|
|
{
|
|
LSA_HANDLE hPolicy;
|
|
UNICODE_STRING unicodePassword;
|
|
UNICODE_STRING unicodeServer;
|
|
NTSTATUS ntStatus;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING unicodeSecret;
|
|
|
|
|
|
InitUnicodeString( &unicodeServer,
|
|
L"" );
|
|
|
|
//
|
|
// Initialize the unicode string by hand so we can handle '\0' in the
|
|
// string
|
|
//
|
|
|
|
unicodePassword.Buffer = pSecret;
|
|
unicodePassword.Length = (USHORT) cbSecret;
|
|
unicodePassword.MaximumLength = (USHORT) cbSecret;
|
|
|
|
//
|
|
// Open a policy to the remote LSA
|
|
//
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
NULL,
|
|
0L,
|
|
NULL,
|
|
NULL );
|
|
|
|
ntStatus = pfnLsaOpenPolicy( &unicodeServer,
|
|
&ObjectAttributes,
|
|
POLICY_ALL_ACCESS,
|
|
&hPolicy );
|
|
|
|
if ( !NT_SUCCESS( ntStatus ) )
|
|
return pfnLsaNtStatusToWinError( ntStatus );
|
|
|
|
//
|
|
// Create or open the LSA secret
|
|
//
|
|
|
|
InitUnicodeString( &unicodeSecret,
|
|
SecretName );
|
|
|
|
ntStatus = pfnLsaStorePrivateData( hPolicy,
|
|
&unicodeSecret,
|
|
&unicodePassword );
|
|
|
|
pfnLsaClose( hPolicy );
|
|
|
|
if ( !NT_SUCCESS( ntStatus ))
|
|
{
|
|
return pfnLsaNtStatusToWinError( ntStatus );
|
|
}
|
|
|
|
return NO_ERROR;
|
|
} // TsSetSecretW()
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ApiAccessCheck
|
|
|
|
SYNOPSIS: Impersonate the RPC client, then check for valid
|
|
access against our server security object.
|
|
|
|
ENTRY: maskDesiredAccess - Specifies the desired access mask.
|
|
This mask must not contain generic accesses.
|
|
|
|
RETURNS: DWORD - NO_ERROR if access granted, ERROR_ACCESS_DENIED
|
|
if access denied, other Win32 errors if something
|
|
tragic happened.
|
|
|
|
HISTORY:
|
|
KeithMo 26-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
DWORD TsApiAccessCheck( ACCESS_MASK maskDesiredAccess )
|
|
{
|
|
DWORD err;
|
|
BOOL fRet;
|
|
|
|
if ( maskDesiredAccess == TCP_QUERY_STATISTICS) {
|
|
|
|
//
|
|
// Statistics query should be allowed without authentication.
|
|
// Any body can bring up perfmon and request statistics.
|
|
//
|
|
|
|
return ( NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// Impersonate the RPC client.
|
|
//
|
|
|
|
err = (DWORD)RpcImpersonateClient( NULL );
|
|
|
|
if( err != NO_ERROR )
|
|
{
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot impersonate rpc client, error %lu\n",
|
|
err ));
|
|
}
|
|
|
|
} else {
|
|
|
|
BOOL fAccessStatus;
|
|
BOOL fGenerateOnClose;
|
|
ACCESS_MASK maskAccessGranted = 0;
|
|
|
|
//
|
|
// Validate access.
|
|
//
|
|
|
|
if ( g_fUseSingleToken )
|
|
{
|
|
HANDLE hAccToken;
|
|
BYTE Set[256];
|
|
DWORD dwSet = sizeof(Set);
|
|
|
|
if ( OpenThreadToken( GetCurrentThread(), TOKEN_READ, TRUE, &hAccToken ) )
|
|
{
|
|
fRet = AccessCheck( sdApiObject,
|
|
hAccToken,
|
|
maskDesiredAccess,
|
|
&TCPApiObjectMapping,
|
|
(PPRIVILEGE_SET)&Set,
|
|
&dwSet,
|
|
&maskAccessGranted,
|
|
&fAccessStatus );
|
|
|
|
CloseHandle( hAccToken );
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fRet = AccessCheckAndAuditAlarmW( SUBSYSTEM_NAME,
|
|
NULL,
|
|
OBJECTTYPE_NAME,
|
|
OBJECT_NAME,
|
|
sdApiObject,
|
|
maskDesiredAccess,
|
|
&TCPApiObjectMapping,
|
|
FALSE,
|
|
&maskAccessGranted,
|
|
&fAccessStatus,
|
|
&fGenerateOnClose );
|
|
}
|
|
|
|
if ( !fRet ) {
|
|
|
|
err = GetLastError();
|
|
}
|
|
|
|
//
|
|
// Revert to our former self.
|
|
//
|
|
|
|
DBG_REQUIRE( !RpcRevertToSelf() );
|
|
|
|
//
|
|
// Check the results.
|
|
//
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
IF_DEBUG( DLL_SECURITY ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot check access, error %lu\n",
|
|
err ));
|
|
}
|
|
} else if( !fAccessStatus ) {
|
|
|
|
err = ERROR_ACCESS_DENIED;
|
|
|
|
IF_DEBUG( DLL_SECURITY ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"bad access status, error %lu\n",
|
|
err ));
|
|
}
|
|
}
|
|
}
|
|
|
|
return (err);
|
|
|
|
} // ApiAccessCheck
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: CreateWellKnownSids
|
|
|
|
SYNOPSIS: Create some well-known SIDs used to create a security
|
|
descriptor for the API security object.
|
|
|
|
RETURNS: NTSTATUS - An NT Status code.
|
|
|
|
HISTORY:
|
|
KeithMo 26-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
|
|
DWORD CreateWellKnownSids( HINSTANCE hDll )
|
|
{
|
|
DWORD error = NO_ERROR;
|
|
SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
|
|
BOOL fRet;
|
|
|
|
fRet = AllocateAndInitializeSid( &siaWorld,
|
|
1,
|
|
SECURITY_WORLD_RID,
|
|
0,0,0,0,0,0,0,
|
|
&psidWorld );
|
|
|
|
if( fRet )
|
|
{
|
|
fRet = AllocateAndInitializeSid( &siaNt,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0,0,0,0,0,0,0,
|
|
&psidLocalSystem );
|
|
}
|
|
|
|
if( fRet )
|
|
{
|
|
fRet = AllocateAndInitializeSid( &siaNt,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0,
|
|
&psidAdmins );
|
|
}
|
|
|
|
if( fRet )
|
|
{
|
|
fRet = AllocateAndInitializeSid( &siaNt,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_SYSTEM_OPS,
|
|
0,0,0,0,0,0,
|
|
&psidServerOps );
|
|
}
|
|
|
|
if( fRet )
|
|
{
|
|
fRet = AllocateAndInitializeSid( &siaNt,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_POWER_USERS,
|
|
0,0,0,0,0,0,
|
|
&psidPowerUsers );
|
|
}
|
|
|
|
if( fRet )
|
|
{
|
|
USER_MODALS_INFO_2 * pUsrModals2 = NULL;
|
|
HINSTANCE hInstance = NULL;
|
|
NET_USER_MODALS_GET_FN pfnNetUserModalsGet = NULL;
|
|
NET_API_BUFFER_FREE_FN pfnNetApiBufferFree = NULL;
|
|
|
|
//
|
|
// Construct well-known-sid for Guest User on the local computer
|
|
//
|
|
// 1) Obtain the sid for the local machine's domain
|
|
// 2) copy domain sid to guest user sid
|
|
// 3) append DOMAIN_USER_RID_GUEST to the domain sid in GuestUser sid.
|
|
//
|
|
|
|
g_psidGuestUser = (PSID ) g_GuestUserSidBuffer;
|
|
|
|
hInstance = LoadLibrary("netapi32.dll");
|
|
if ( hInstance != NULL ) {
|
|
|
|
pfnNetUserModalsGet = (NET_USER_MODALS_GET_FN)
|
|
GetProcAddress(hInstance,"NetUserModalsGet");
|
|
pfnNetApiBufferFree = (NET_API_BUFFER_FREE_FN)
|
|
GetProcAddress(hInstance,"NetApiBufferFree");
|
|
}
|
|
|
|
if ( (pfnNetUserModalsGet != NULL) &&
|
|
(pfnNetApiBufferFree != NULL) ) {
|
|
|
|
fRet = ( (pfnNetUserModalsGet(NULL, // local computer
|
|
2, // get level 2 information
|
|
(LPBYTE *) &pUsrModals2
|
|
) == 0)
|
|
&&
|
|
CopySid(GUEST_USER_SID_BUFFER_LEN - 4,// Buffer len
|
|
g_psidGuestUser, // psidDestination
|
|
pUsrModals2->usrmod2_domain_id // obtain domain sid.
|
|
)
|
|
);
|
|
} else {
|
|
|
|
DBGPRINTF((DBG_CONTEXT,"Unable to get netapi32 entrypoints\n"));
|
|
fRet = FALSE;
|
|
}
|
|
|
|
//
|
|
// if successful append the DOMAIN_USER_RID_GUEST.
|
|
//
|
|
|
|
if ( fRet) {
|
|
|
|
DWORD lenSid = GetLengthSid( g_psidGuestUser);
|
|
CHAR nSubAuth;
|
|
|
|
//
|
|
// There is no Win32 way to set a SID value.
|
|
// We will munge around on our own.
|
|
// Pretty dangerous thing to do :-(
|
|
//
|
|
|
|
// increment the number of sub authorities
|
|
nSubAuth = *((UCHAR *) ((UCHAR *) g_psidGuestUser + 1));
|
|
nSubAuth++;
|
|
*((UCHAR *) ((UCHAR *) g_psidGuestUser + 1)) = nSubAuth;
|
|
|
|
// Store the new sub authority (Domain User Rid for Guest).
|
|
*((ULONG *) ((BYTE *) g_psidGuestUser + lenSid)) =
|
|
DOMAIN_USER_RID_GUEST;
|
|
} else {
|
|
|
|
g_psidGuestUser = NULL;
|
|
}
|
|
|
|
if ( pUsrModals2 != NULL) {
|
|
|
|
NET_API_STATUS ns = pfnNetApiBufferFree( (LPVOID )pUsrModals2);
|
|
pUsrModals2 = NULL;
|
|
}
|
|
|
|
if ( hInstance != NULL ) {
|
|
FreeLibrary(hInstance);
|
|
}
|
|
}
|
|
|
|
if ( fRet && g_fUseSingleToken )
|
|
{
|
|
BYTE abInfo[256];
|
|
DWORD dwInfo;
|
|
|
|
if ( GetTokenInformation( g_hProcessPrimaryToken,
|
|
TokenUser,
|
|
abInfo,
|
|
sizeof(abInfo),
|
|
&dwInfo ) )
|
|
{
|
|
if ( !(g_psidProcessUser = (PSID)LocalAlloc( LMEM_FIXED,
|
|
GetLengthSid(((TOKEN_USER*)abInfo)->User.Sid))) )
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
memcpy ( g_psidProcessUser,
|
|
((TOKEN_USER*)abInfo)->User.Sid,
|
|
GetLengthSid(((TOKEN_USER*)abInfo)->User.Sid) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !fRet ) {
|
|
error = GetLastError( );
|
|
IF_DEBUG( DLL_SECURITY ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot create well-known sids\n" ));
|
|
}
|
|
}
|
|
|
|
return error;
|
|
|
|
} // CreateWellKnownSids
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: FreeWellKnownSids
|
|
|
|
SYNOPSIS: Frees the SIDs created with CreateWellKnownSids.
|
|
|
|
HISTORY:
|
|
KeithMo 26-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
VOID FreeWellKnownSids( VOID )
|
|
{
|
|
|
|
if( psidWorld != NULL )
|
|
{
|
|
FreeSid( psidWorld );
|
|
psidWorld = NULL;
|
|
}
|
|
|
|
if( psidLocalSystem != NULL )
|
|
{
|
|
FreeSid( psidLocalSystem );
|
|
psidLocalSystem = NULL;
|
|
}
|
|
|
|
if( psidAdmins != NULL )
|
|
{
|
|
FreeSid( psidAdmins );
|
|
psidAdmins = NULL;
|
|
}
|
|
|
|
if( psidServerOps != NULL )
|
|
{
|
|
FreeSid( psidServerOps );
|
|
psidServerOps = NULL;
|
|
}
|
|
|
|
if( psidPowerUsers != NULL )
|
|
{
|
|
FreeSid( psidPowerUsers );
|
|
psidPowerUsers = NULL;
|
|
}
|
|
|
|
if( g_psidProcessUser != NULL )
|
|
{
|
|
LocalFree( g_psidProcessUser );
|
|
g_psidProcessUser = NULL;
|
|
}
|
|
|
|
} // FreeWellKnownSids
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: CreateApiSecurityObject
|
|
|
|
SYNOPSIS: Create an abstract security object used for validating
|
|
user access to the TCP Server APIs.
|
|
|
|
RETURNS: NTSTATUS - An NT Status code.
|
|
|
|
HISTORY:
|
|
KeithMo 26-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
DWORD CreateApiSecurityObject( VOID )
|
|
{
|
|
DWORD err;
|
|
ACE_DATA aces[] =
|
|
{
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_ALL_ACCESS,
|
|
&psidLocalSystem
|
|
},
|
|
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_ALL_ACCESS,
|
|
&psidAdmins
|
|
},
|
|
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_ALL_ACCESS,
|
|
&psidServerOps
|
|
},
|
|
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_ALL_ACCESS,
|
|
&psidPowerUsers
|
|
},
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_GENERIC_EXECUTE,
|
|
&psidWorld
|
|
},
|
|
{
|
|
ACCESS_ALLOWED_ACE_TYPE,
|
|
0,
|
|
0,
|
|
TCP_GENERIC_EXECUTE,
|
|
&g_psidProcessUser
|
|
},
|
|
|
|
};
|
|
|
|
#define NUM_ACES (sizeof(aces) / sizeof(RTL_ACE_DATA))
|
|
|
|
err = INetCreateSecurityObject( aces,
|
|
(ULONG)(g_fUseSingleToken ? NUM_ACES : NUM_ACES-1),
|
|
NULL,
|
|
NULL,
|
|
&TCPApiObjectMapping,
|
|
&sdApiObject );
|
|
|
|
|
|
IF_DEBUG( DLL_SECURITY )
|
|
{
|
|
if( err )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"cannot create api security object, error %d\n",
|
|
err ));
|
|
}
|
|
}
|
|
|
|
return err;
|
|
|
|
} // CreateApiSecurityObject
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: DeleteApiSecurityObject
|
|
|
|
SYNOPSIS: Frees the security descriptor created with
|
|
CreateApiSecurityObject.
|
|
|
|
HISTORY:
|
|
KeithMo 26-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
VOID DeleteApiSecurityObject( VOID )
|
|
{
|
|
INetDeleteSecurityObject( &sdApiObject );
|
|
|
|
} // DeleteApiSecurityObject
|
|
|
|
|
|
|
|
//
|
|
// Short routine to enable the TcbPrivilege for testing services running
|
|
// as an executable (rather then a service). Note that the account
|
|
// running the .exe must be added in User Manager's User Right's dialog
|
|
// under "Act as part of the OS"
|
|
//
|
|
|
|
VOID EnableTcbPrivilege(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE ProcessHandle = NULL;
|
|
HANDLE TokenHandle = NULL;
|
|
BOOL Result;
|
|
LUID TcbValue;
|
|
LUID AuditValue;
|
|
TOKEN_PRIVILEGES * TokenPrivileges;
|
|
CHAR buf[ 5 * sizeof(TOKEN_PRIVILEGES) ];
|
|
|
|
ProcessHandle = OpenProcess(
|
|
PROCESS_QUERY_INFORMATION,
|
|
FALSE,
|
|
GetCurrentProcessId()
|
|
);
|
|
|
|
if ( ProcessHandle == NULL ) {
|
|
|
|
//
|
|
// This should not happen
|
|
//
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Result = OpenProcessToken (
|
|
ProcessHandle,
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&TokenHandle
|
|
);
|
|
|
|
if ( !Result ) {
|
|
|
|
//
|
|
// This should not happen
|
|
//
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Find out the value of TakeOwnershipPrivilege
|
|
//
|
|
|
|
|
|
Result = LookupPrivilegeValue(
|
|
NULL,
|
|
"SeTcbPrivilege",
|
|
&TcbValue
|
|
);
|
|
|
|
if ( !Result ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Need this for RPC impersonation (calls NtAccessCheckAndAuditAlarm)
|
|
//
|
|
|
|
Result = LookupPrivilegeValue(
|
|
NULL,
|
|
"SeAuditPrivilege",
|
|
&AuditValue
|
|
);
|
|
|
|
if ( !Result ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set up the privilege set we will need
|
|
//
|
|
|
|
TokenPrivileges = (TOKEN_PRIVILEGES *) buf;
|
|
|
|
TokenPrivileges->PrivilegeCount = 2;
|
|
TokenPrivileges->Privileges[0].Luid = TcbValue;
|
|
TokenPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
TokenPrivileges->Privileges[1].Luid = AuditValue;
|
|
TokenPrivileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
(VOID) AdjustTokenPrivileges (
|
|
TokenHandle,
|
|
FALSE,
|
|
TokenPrivileges,
|
|
sizeof(buf),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if ( TokenHandle )
|
|
{
|
|
CloseHandle( TokenHandle );
|
|
}
|
|
|
|
if ( ProcessHandle )
|
|
{
|
|
CloseHandle( ProcessHandle );
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CrackUserAndDomain(
|
|
CHAR * pszDomainAndUser,
|
|
CHAR * * ppszUser,
|
|
CHAR * * ppszDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a user name potentially in the form domain\user, zero terminates
|
|
the domain name and returns pointers to the domain name and the user name
|
|
|
|
Arguments:
|
|
|
|
pszDomainAndUser - Pointer to user name or domain and user name
|
|
ppszUser - Receives pointer to user portion of name
|
|
ppszDomain - Receives pointer to domain portion of name
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE otherwise (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
static CHAR szDefaultDomain[MAX_COMPUTERNAME_LENGTH+1];
|
|
// BUGBUG: how come this does not screw up multi domain per multi site???
|
|
|
|
//
|
|
// Crack the name into domain/user components.
|
|
//
|
|
|
|
*ppszDomain = pszDomainAndUser;
|
|
*ppszUser = (PCHAR)_mbspbrk( (PUCHAR)pszDomainAndUser, (PUCHAR)"/\\" );
|
|
|
|
if( *ppszUser == NULL )
|
|
{
|
|
//
|
|
// No domain name specified, just the username so we assume the
|
|
// user is on the local machine
|
|
//
|
|
|
|
if ( !*szDefaultDomain )
|
|
{
|
|
if ( !pfnGetDefaultDomainName( szDefaultDomain,
|
|
sizeof(szDefaultDomain)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*ppszDomain = szDefaultDomain;
|
|
*ppszUser = pszDomainAndUser;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Both domain & user specified, skip delimiter.
|
|
//
|
|
|
|
**ppszUser = '\0';
|
|
(*ppszUser)++;
|
|
|
|
if( ( **ppszUser == '\0' ) ||
|
|
( **ppszUser == '\\' ) ||
|
|
( **ppszUser == '/' ) ||
|
|
( *pszDomainAndUser == '\0' ) )
|
|
{
|
|
//
|
|
// Name is of one of the following (invalid) forms:
|
|
//
|
|
// "domain\"
|
|
// "domain\\..."
|
|
// "domain/..."
|
|
// "\username"
|
|
// "/username"
|
|
//
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LONG
|
|
WINAPI
|
|
NullReferenceMapper(
|
|
IN HMAPPER *pMap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Increment reference count to mapper
|
|
|
|
Arguments:
|
|
|
|
pMap - ptr to mapper struct
|
|
|
|
Returns:
|
|
|
|
Ref count
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
return pfnInterlockedExchangeAdd( &((IisMapper*)pMap)->lRefCount, 1 ) + 1;
|
|
}
|
|
|
|
|
|
LONG
|
|
WINAPI
|
|
NullDeReferenceMapper(
|
|
IN HMAPPER *pMap
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement reference count to mapper
|
|
|
|
Arguments:
|
|
|
|
pMap - ptr to mapper struct
|
|
|
|
Returns:
|
|
|
|
Ref count
|
|
|
|
--*/
|
|
{
|
|
LONG l;
|
|
|
|
DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
if ( !(l = pfnInterlockedExchangeAdd( &((IisMapper*)pMap)->lRefCount, -1 ) - 1 ) )
|
|
{
|
|
LocalFree( pMap );
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullGetIssuerList(
|
|
HMAPPER *phMapper, // in
|
|
VOID * Reserved, // in
|
|
BYTE * pIssuerList, // out
|
|
DWORD * pcbIssuerList // out
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to retrieve the list of preferred cert issuers
|
|
|
|
Arguments:
|
|
|
|
ppIssuer -- updated with ptr buffer of issuers
|
|
pdwIssuer -- updated with issuers buffer size
|
|
|
|
Returns:
|
|
|
|
TRUE if success, FALSE if error
|
|
|
|
--*/
|
|
{
|
|
return SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullGetChallenge(
|
|
HMAPPER *pMap, // in
|
|
BYTE * pAuthenticatorId, // in
|
|
DWORD cbAuthenticatorId, // in
|
|
BYTE * pChallenge, // out
|
|
DWORD * pcbChallenge // out
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get challenge for auth sequence
|
|
|
|
Arguments:
|
|
|
|
Not used
|
|
|
|
Returns:
|
|
|
|
FALSE ( not supported )
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
return SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullMapCredential(
|
|
HMAPPER * phMapper,
|
|
DWORD dwCredentialType,
|
|
const VOID* pCredential, // in
|
|
const VOID* pAuthority, // in
|
|
HLOCATOR * phToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to map a certificate to a NT account
|
|
|
|
Arguments:
|
|
|
|
phMapper - ptr to mapper descriptor
|
|
dwCredentialType -- type of credential
|
|
pCredential - ptr to PCERT_CONTEXT for client cert
|
|
pAuthority - ptr to PCERT_CONTEXT for Certifying authority
|
|
phToken -- updated with impersonation access token
|
|
|
|
Returns:
|
|
|
|
FALSE ( mapping always fail )
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( ((IisMapper*)phMapper)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
return SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullCloseLocator(
|
|
HMAPPER *pMap,
|
|
HLOCATOR hLocator //in
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to close a HLOCATOR returned by MapCredential
|
|
|
|
Arguments:
|
|
|
|
tokenhandle -- HLOCATOR
|
|
|
|
Returns:
|
|
|
|
TRUE if success, FALSE if error
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
if (hLocator == 1) {
|
|
return SEC_E_OK;
|
|
}
|
|
else {
|
|
if (CloseHandle( (HANDLE)hLocator )) {\
|
|
return SEC_E_OK;
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
return hLocator == 1 ? TRUE : CloseHandle( (HANDLE)hLocator );
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullGetAccessToken(
|
|
HMAPPER *pMap,
|
|
HLOCATOR tokenhandle,
|
|
HANDLE * phToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to retrieve an access token from a mapping
|
|
|
|
Arguments:
|
|
|
|
tokenhandle -- HLOCATOR returned by MapCredential
|
|
phToken -- updated with potentially new token
|
|
|
|
Returns:
|
|
|
|
TRUE if success, FALSE if error
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( ((IisMapper*)pMap)->dwSignature == IIS_MAPPER_SIGNATURE );
|
|
|
|
if ( tokenhandle == 1 )
|
|
{
|
|
*phToken = (HANDLE)tokenhandle;
|
|
}
|
|
|
|
else if ( !pfnDuplicateTokenEx( (HANDLE)tokenhandle,
|
|
TOKEN_ALL_ACCESS,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
phToken ))
|
|
{
|
|
return SEC_E_UNSUPPORTED_FUNCTION;
|
|
}
|
|
|
|
return SEC_E_OK;
|
|
}
|
|
|
|
|
|
DWORD WINAPI NullQueryMappedCredentialAttributes(
|
|
HMAPPER *phMapper, // in
|
|
HLOCATOR hLocator, // in
|
|
ULONG ulAttribute, // in
|
|
PVOID pBuffer, //out
|
|
DWORD *pcbBuffer // in out
|
|
)
|
|
{
|
|
return ( SEC_E_NOT_SUPPORTED );
|
|
}
|
|
|
|
|
|
QuerySingleAccessToken(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query status of single access token mode
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
TRUE if single access token mode used, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
return g_fUseSingleToken;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CACHED_CREDENTIAL::GetCredential(
|
|
LPSTR pszPackage,
|
|
PIIS_SERVER_INSTANCE psi,
|
|
PTCP_AUTHENT_INFO pTAI,
|
|
CredHandle* prcred,
|
|
ULONG* pcbMaxToken
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get SSPI credential handle from cache
|
|
|
|
Arguments:
|
|
|
|
pszPackage - SSPI package name, e.g NTLM
|
|
psi - pointer to server instance
|
|
pTAI - pointer to authent info, only DomainName used
|
|
prcred - updated with CredHandle from cache
|
|
pcbMaxToken - updated with max token size used by this package
|
|
|
|
Returns:
|
|
|
|
TRUE if success, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
CACHED_CREDENTIAL * pcred;
|
|
SEC_WINNT_AUTH_IDENTITY AuthIdentity;
|
|
SEC_WINNT_AUTH_IDENTITY * pAuthIdentity;
|
|
SecPkgInfo * pspkg;
|
|
TimeStamp Lifetime;
|
|
STACK_STR ( strDefaultLogonDomain, IIS_DNLEN+1 );
|
|
SECURITY_STATUS ss;
|
|
|
|
|
|
DBG_ASSERT( pszPackage != NULL );
|
|
DBG_ASSERT( pTAI != NULL );
|
|
|
|
EnterCriticalSection( &csCredentialCacheLock );
|
|
|
|
for ( pEntry = CredentialCacheList.Flink;
|
|
pEntry != &CredentialCacheList;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pcred = CONTAINING_RECORD( pEntry, CACHED_CREDENTIAL, _ListEntry );
|
|
|
|
if ( !strcmp( pszPackage, pcred->_PackageName.QueryStr() ) &&
|
|
!strcmp( pTAI->strDefaultLogonDomain.QueryStr(), pcred->_DefaultDomain.QueryStr() ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ( (pcred = new CACHED_CREDENTIAL) == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
goto Exit;
|
|
}
|
|
|
|
if ( !pcred->_PackageName.Copy( pszPackage ) ||
|
|
!pcred->_DefaultDomain.Copy( pTAI->strDefaultLogonDomain ) )
|
|
{
|
|
delete pcred;
|
|
pcred = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// provide default logon domain
|
|
//
|
|
|
|
if ( psi == NULL )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
pAuthIdentity = &AuthIdentity;
|
|
|
|
memset( &AuthIdentity,
|
|
0,
|
|
sizeof( AuthIdentity ));
|
|
|
|
if ( pTAI->strDefaultLogonDomain.QueryCCH() <= IIS_DNLEN )
|
|
{
|
|
strDefaultLogonDomain.Copy( pTAI->strDefaultLogonDomain );
|
|
AuthIdentity.Domain = (LPBYTE)strDefaultLogonDomain.QueryStr();
|
|
}
|
|
if ( AuthIdentity.Domain != NULL )
|
|
{
|
|
if ( AuthIdentity.DomainLength =
|
|
strlen( (LPCTSTR)AuthIdentity.Domain ) )
|
|
{
|
|
// remove trailing '\\' if present
|
|
|
|
if ( AuthIdentity.Domain[AuthIdentity.DomainLength-1]
|
|
== '\\' )
|
|
{
|
|
--AuthIdentity.DomainLength;
|
|
}
|
|
}
|
|
}
|
|
if ( AuthIdentity.DomainLength == 0 )
|
|
{
|
|
pAuthIdentity = NULL;
|
|
}
|
|
else
|
|
{
|
|
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
|
|
}
|
|
}
|
|
|
|
ss = pfnAcquireCredentialsHandle( NULL, // New principal
|
|
pszPackage, // Package name
|
|
SECPKG_CRED_INBOUND,
|
|
NULL, // Logon ID
|
|
pAuthIdentity, // Auth Data
|
|
NULL, // Get key func
|
|
NULL, // Get key arg
|
|
&pcred->_hcred,
|
|
&Lifetime );
|
|
|
|
//
|
|
// Need to determine the max token size for this package
|
|
//
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
pcred->_fHaveCredHandle = TRUE;
|
|
ss = pfnQuerySecurityPackageInfo( (char *) pszPackage,
|
|
&pspkg );
|
|
}
|
|
|
|
if ( ss == STATUS_SUCCESS )
|
|
{
|
|
pcred->_cbMaxToken = pspkg->cbMaxToken;
|
|
DBG_ASSERT( pspkg->fCapabilities & SECPKG_FLAG_CONNECTION );
|
|
pfnFreeContextBuffer( pspkg );
|
|
}
|
|
|
|
if ( ss != STATUS_SUCCESS )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[GetCredential] AcquireCredentialsHandle or QuerySecurityPackageInfo failed, error %d\n",
|
|
ss ));
|
|
|
|
SetLastError( ss );
|
|
|
|
delete pcred;
|
|
pcred = NULL;
|
|
}
|
|
else
|
|
{
|
|
InsertHeadList( &CredentialCacheList, &pcred->_ListEntry );
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( pcred )
|
|
{
|
|
*pcbMaxToken = pcred->_cbMaxToken;
|
|
*prcred = pcred->_hcred;
|
|
}
|
|
|
|
LeaveCriticalSection( &csCredentialCacheLock );
|
|
|
|
return pcred ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
CACHED_CREDENTIAL::~CACHED_CREDENTIAL(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SSPI Credential cache entry destructor
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
if ( _fHaveCredHandle )
|
|
{
|
|
pfnFreeCredentialsHandle( &_hcred );
|
|
}
|
|
}
|
|
|