Leaked source code of windows server 2003
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

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