Windows-Server-2003/base/win32/winnls/security.c

868 lines
25 KiB
C

/*++
Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
Module Name:
security.c
Abstract:
This file handles the management of the NLS per-thread and process cache.
The cache is only established when hitting an API that needs it. The process
NLS cache is used when accessing NLS info for a process NOT running in the
context of the interactive logged on user. The per-thread NLS cache is used
when accssing NLS info and the thread is doing a user impersonation.
External Routines found in this file:
NlsFlushProcessCache
NlsGetCurrentUserNlsInfo
NlsIsInteractiveUserProcess
NlsCheckForInteractiveUser
NlsGetUserLocale
Revision History:
03-29-1999 SamerA Created.
--*/
//
// Include Files.
//
#include "nls.h"
#include "nlssafe.h"
//
// Global Variables.
//
//
// Process Nls Cache.
//
PNLS_LOCAL_CACHE gpNlsProcessCache;
//
// Whether the current running process is the same as the
// interactive logged on user.
//
BOOL gInteractiveLogonUserProcess = (BOOL)-1;
//
// Forward Declarations.
//
NTSTATUS FASTCALL
NlsGetCacheBuffer(
PNLS_USER_INFO pNlsUserInfo,
LCTYPE LCType,
PWSTR *ppCache);
void FASTCALL
NlsInvalidateCache(
PNLS_USER_INFO pNlsUserInfo);
//-------------------------------------------------------------------------//
// EXTERNAL ROUTINES //
//-------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////
//
// NlsFlushProcessCache
//
// Invalidates an entry in the NLS process cache.
//
// 05-22-99 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsFlushProcessCache(
LCTYPE LCType)
{
PWSTR pOutputCache;
NTSTATUS NtStatus = STATUS_SUCCESS;
//
// If there is no thread impersonation, then flush the
// process entry cache.
//
if (NtCurrentTeb()->IsImpersonating != 0)
{
return (NtStatus);
}
if (gpNlsProcessCache)
{
NtStatus = NlsGetCacheBuffer( &gpNlsProcessCache->NlsInfo,
LCType,
&pOutputCache );
if (NT_SUCCESS(NtStatus))
{
RtlEnterCriticalSection(&gcsNlsProcessCache);
pOutputCache[0] = NLS_INVALID_INFO_CHAR;
RtlLeaveCriticalSection(&gcsNlsProcessCache);
}
}
return (NtStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsGetCurrentUserNlsInfo
//
// Retreive the NLS info correponding to the current security context.
//
// 03-29-99 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsGetCurrentUserNlsInfo(
LCID Locale,
LCTYPE LCType,
PWSTR RegistryValue,
PWSTR pOutputBuffer,
size_t cchOutputBuffer,
BOOL IgnoreLocaleValue)
{
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
PNLS_LOCAL_CACHE pNlsThreadCache;
PWSTR pOutputCache;
//
// Possible NtCurrentTeb()->IsImpersonating values :
//
// 0 : Thread isn't impersonating any user.
//
// 1 : Thread has just started to do impersonation.
// Per thread cache needs to be allocated now.
//
// 2 : Thread is calling the NLS apis while its
// a context other than the interactive logged on user.
//
switch (NtCurrentTeb()->IsImpersonating)
{
case ( 0 ) :
{
//
// Thread is NOT impersonating any user. We check if the process
// belongs to the interactive user, then we retreive the info from
// the NLS cache in CSR. Otherwise if the process is running in
// the context of a different user, then we retreive the NLS info
// from the process cache.
//
if (gInteractiveLogonUserProcess == (BOOL)-1)
{
NlsIsInteractiveUserProcess();
}
if (gInteractiveLogonUserProcess == FALSE)
{
if ((IgnoreLocaleValue) ||
(GetUserDefaultLCID() == Locale))
{
if (!gpNlsProcessCache)
{
//
// Allocate and invalidate the NLS process cache.
//
RtlEnterCriticalSection(&gcsNlsProcessCache);
if (!gpNlsProcessCache)
{
gpNlsProcessCache = RtlAllocateHeap(
RtlProcessHeap(),
0,
sizeof(NLS_LOCAL_CACHE) );
if (gpNlsProcessCache)
{
NlsInvalidateCache(&gpNlsProcessCache->NlsInfo);
gpNlsProcessCache->CurrentUserKeyHandle = NULL;
}
}
RtlLeaveCriticalSection(&gcsNlsProcessCache);
}
if (gpNlsProcessCache)
{
NtStatus = NlsGetCacheBuffer( &gpNlsProcessCache->NlsInfo,
LCType,
&pOutputCache);
if (NT_SUCCESS(NtStatus))
{
//
// See if it is a valid cache.
//
if (pOutputCache[0] == NLS_INVALID_INFO_CHAR)
{
RtlEnterCriticalSection(&gcsNlsProcessCache);
if (GetUserInfoFromRegistry( RegistryValue,
pOutputCache,
MAX_REG_VAL_SIZE, 0 ) == FALSE)
{
NtStatus = STATUS_UNSUCCESSFUL;
pOutputCache[0] = NLS_INVALID_INFO_CHAR;
}
RtlLeaveCriticalSection(&gcsNlsProcessCache);
}
if (NT_SUCCESS(NtStatus))
{
if(FAILED((StringCchCopyW(pOutputBuffer, cchOutputBuffer, pOutputCache))))
{
NtStatus = STATUS_UNSUCCESSFUL;
}
}
}
}
}
}
break;
}
case ( 1 ) :
{
//
// Thread started to do impersonation.
//
pNlsThreadCache = NtCurrentTeb()->NlsCache;
if (!pNlsThreadCache)
{
pNlsThreadCache = RtlAllocateHeap( RtlProcessHeap(),
0,
sizeof(NLS_LOCAL_CACHE) );
if (pNlsThreadCache)
{
pNlsThreadCache->CurrentUserKeyHandle = NULL;
}
NtCurrentTeb()->NlsCache = (PVOID) pNlsThreadCache;
}
if (pNlsThreadCache)
{
NlsInvalidateCache(&pNlsThreadCache->NlsInfo);
}
NtCurrentTeb()->IsImpersonating = 2;
//
// Fall Thru...
//
}
case ( 2 ) :
{
//
// Thread is impersonating a particular user.
//
pNlsThreadCache = NtCurrentTeb()->NlsCache;
if (pNlsThreadCache)
{
if ((IgnoreLocaleValue) ||
(GetUserDefaultLCID() == Locale))
{
NtStatus = NlsGetCacheBuffer( &pNlsThreadCache->NlsInfo,
LCType,
&pOutputCache );
if (NT_SUCCESS(NtStatus))
{
if (pOutputCache[0] == NLS_INVALID_INFO_CHAR)
{
//
// Don't cache key handles - this will break
// profile unload.
//
OPEN_CPANEL_INTL_KEY( pNlsThreadCache->CurrentUserKeyHandle,
STATUS_UNSUCCESSFUL,
KEY_READ );
NtStatus = NlsQueryCurrentUserInfo( pNlsThreadCache,
RegistryValue,
pOutputCache,
MAX_REG_VAL_SIZE );
CLOSE_REG_KEY(pNlsThreadCache->CurrentUserKeyHandle);
if (!NT_SUCCESS(NtStatus))
{
pOutputCache[0] = NLS_INVALID_INFO_CHAR;
}
}
if (NT_SUCCESS(NtStatus))
{
if(FAILED((StringCchCopyW(pOutputBuffer, cchOutputBuffer, pOutputCache))))
{
NtStatus = STATUS_UNSUCCESSFUL;
}
}
}
}
}
break;
}
}
return (NtStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsIsInteractiveUserProcess
//
// Read the process's authetication id out of its access token object and
// cache it since it never changes.
//
// 12-27-98 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsIsInteractiveUserProcess()
{
NTSTATUS NtStatus;
TOKEN_STATISTICS TokenInformation;
HANDLE TokenHandle;
ULONG BytesRequired;
BOOL IsInteractiveProcess = TRUE;
//
// Get the process access token.
//
NtStatus = NtOpenProcessToken( NtCurrentProcess(),
TOKEN_QUERY,
&TokenHandle );
if (NT_SUCCESS(NtStatus))
{
//
// Get the LUID.
//
NtStatus = NtQueryInformationToken( TokenHandle,
TokenStatistics,
&TokenInformation,
sizeof(TokenInformation),
&BytesRequired );
if (NT_SUCCESS(NtStatus))
{
if (RtlEqualLuid( &pNlsUserInfo->InteractiveUserLuid,
&TokenInformation.AuthenticationId ) == FALSE)
{
IsInteractiveProcess = FALSE;
}
}
NtClose(TokenHandle);
}
gInteractiveLogonUserProcess = IsInteractiveProcess;
return (NtStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsCheckForInteractiveUser
//
// This function makes sure that the current thread isn't impersonating
// anybody, but the interactive. It compares the authentication-id of the
// interactive user -cached in CSRSS at logon time- with the
// authentication-id of the current thread or process. It returns failure
// ONLY if the current security context -session- isn't the same as the
// interactive logged-on user.
//
// 12-16-98 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsCheckForInteractiveUser()
{
NTSTATUS NtStatus, ReturnStatus = STATUS_SUCCESS;
TOKEN_STATISTICS TokenInformation;
HANDLE TokenHandle;
ULONG BytesRequired;
PLUID InteractiveUserLuid = &pNlsUserInfo->InteractiveUserLuid;
//
// Get the Token Handle.
// Fast optimization to detect if a thread hasn't started to do any
// impersonation, which is the case for most GUI user apps.
//
if (NtCurrentTeb()->IsImpersonating == 0)
{
NtStatus = STATUS_NO_TOKEN;
}
else
{
NtStatus = NtOpenThreadToken( NtCurrentThread(),
TOKEN_QUERY,
FALSE,
&TokenHandle );
}
if (!NT_SUCCESS(NtStatus))
{
if (NtStatus != STATUS_NO_TOKEN)
{
KdPrint(("NLSAPI: Couldn't retreive thread token - %lx.\n", NtStatus));
return (STATUS_SUCCESS);
}
//
// Get the process access token.
//
if (gInteractiveLogonUserProcess == (BOOL)-1)
{
NtStatus = NlsIsInteractiveUserProcess();
if (!NT_SUCCESS(NtStatus))
{
KdPrint(("NLSAPI: Couldn't retreive process token - %lx\n", NtStatus));
return (STATUS_SUCCESS);
}
}
if (gInteractiveLogonUserProcess == FALSE)
{
ReturnStatus = STATUS_UNSUCCESSFUL;
}
}
else
{
//
// Get the AuthenticationId of the current thread's security context.
//
NtStatus = NtQueryInformationToken( TokenHandle,
TokenStatistics,
&TokenInformation,
sizeof(TokenInformation),
&BytesRequired );
//
// Close the thread token here.
//
NtClose(TokenHandle);
if (NT_SUCCESS(NtStatus))
{
if (RtlEqualLuid( InteractiveUserLuid,
&TokenInformation.AuthenticationId ) == FALSE)
{
ReturnStatus = STATUS_UNSUCCESSFUL;
}
}
}
return (ReturnStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsGetUserLocale
//
// Retreives the user locale from the registry of the current security
// context. It is called ONLY when the running security context is
// different from the interactive logged-on security context-(user).
//
// 12-16-98 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsGetUserLocale(
LCID *Lcid)
{
NTSTATUS NtStatus;
WCHAR wszLocale[MAX_REG_VAL_SIZE];
UNICODE_STRING ObLocaleString;
PNLS_LOCAL_CACHE pNlsCache = NtCurrentTeb()->NlsCache;
//
// Get the current user locale.
//
NtStatus = NlsGetCurrentUserNlsInfo( LOCALE_USER_DEFAULT,
(LCTYPE)LOCALE_SLOCALE,
L"Locale",
wszLocale,
ARRAYSIZE(wszLocale),
TRUE );
if ((NT_SUCCESS(NtStatus)) ||
(GetUserInfoFromRegistry(L"Locale", wszLocale, ARRAYSIZE(wszLocale), 0)))
{
RtlInitUnicodeString(&ObLocaleString, wszLocale);
NtStatus = RtlUnicodeStringToInteger( &ObLocaleString,
16,
(PULONG)Lcid);
}
return (NtStatus);
}
//-------------------------------------------------------------------------//
// INTERNAL ROUTINES //
//-------------------------------------------------------------------------//
////////////////////////////////////////////////////////////////////////////
//
// NlsGetCacheBuffer
//
// Get a buffer pointer inside the cache for this LCTYPE.
//
// 03-29-99 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS FASTCALL NlsGetCacheBuffer(
PNLS_USER_INFO pNlsUserInfo,
LCTYPE LCType,
PWSTR *ppCache)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
switch (LCType)
{
case ( LOCALE_SLANGUAGE ) :
{
*ppCache = pNlsUserInfo->sAbbrevLangName;
break;
}
case ( LOCALE_ICOUNTRY ) :
{
*ppCache = pNlsUserInfo->iCountry;
break;
}
case ( LOCALE_SCOUNTRY ) :
{
*ppCache = pNlsUserInfo->sCountry;
break;
}
case ( LOCALE_SLIST ) :
{
*ppCache = pNlsUserInfo->sList;
break;
}
case ( LOCALE_IMEASURE ) :
{
*ppCache = pNlsUserInfo->iMeasure;
break;
}
case ( LOCALE_IPAPERSIZE ) :
{
*ppCache = pNlsUserInfo->iPaperSize;
break;
}
case ( LOCALE_SDECIMAL ) :
{
*ppCache = pNlsUserInfo->sDecimal;
break;
}
case ( LOCALE_STHOUSAND ) :
{
*ppCache = pNlsUserInfo->sThousand;
break;
}
case ( LOCALE_SGROUPING ) :
{
*ppCache = pNlsUserInfo->sGrouping;
break;
}
case ( LOCALE_IDIGITS ) :
{
*ppCache = pNlsUserInfo->iDigits;
break;
}
case ( LOCALE_ILZERO ) :
{
*ppCache = pNlsUserInfo->iLZero;
break;
}
case ( LOCALE_INEGNUMBER ) :
{
*ppCache = pNlsUserInfo->iNegNumber;
break;
}
case ( LOCALE_SNATIVEDIGITS ) :
{
*ppCache = pNlsUserInfo->sNativeDigits;
break;
}
case ( LOCALE_IDIGITSUBSTITUTION ) :
{
*ppCache = pNlsUserInfo->iDigitSubstitution;
break;
}
case ( LOCALE_SCURRENCY ) :
{
*ppCache = pNlsUserInfo->sCurrency;
break;
}
case ( LOCALE_SMONDECIMALSEP ) :
{
*ppCache = pNlsUserInfo->sMonDecSep;
break;
}
case ( LOCALE_SMONTHOUSANDSEP ) :
{
*ppCache = pNlsUserInfo->sMonThouSep;
break;
}
case ( LOCALE_SMONGROUPING ) :
{
*ppCache = pNlsUserInfo->sMonGrouping;
break;
}
case ( LOCALE_ICURRDIGITS ) :
{
*ppCache = pNlsUserInfo->iCurrDigits;
break;
}
case ( LOCALE_ICURRENCY ) :
{
*ppCache = pNlsUserInfo->iCurrency;
break;
}
case ( LOCALE_INEGCURR ) :
{
*ppCache = pNlsUserInfo->iNegCurr;
break;
}
case ( LOCALE_SPOSITIVESIGN ) :
{
*ppCache = pNlsUserInfo->sPosSign;
break;
}
case ( LOCALE_SNEGATIVESIGN ) :
{
*ppCache = pNlsUserInfo->sNegSign;
break;
}
case ( LOCALE_STIMEFORMAT ) :
{
*ppCache = pNlsUserInfo->sTimeFormat;
break;
}
case ( LOCALE_STIME ) :
{
*ppCache = pNlsUserInfo->sTime;
break;
}
case ( LOCALE_ITIME ) :
{
*ppCache = pNlsUserInfo->iTime;
break;
}
case ( LOCALE_ITLZERO ) :
{
*ppCache = pNlsUserInfo->iTLZero;
break;
}
case ( LOCALE_ITIMEMARKPOSN ) :
{
*ppCache = pNlsUserInfo->iTimeMarkPosn;
break;
}
case ( LOCALE_S1159 ) :
{
*ppCache = pNlsUserInfo->s1159;
break;
}
case ( LOCALE_S2359 ) :
{
*ppCache = pNlsUserInfo->s2359;
break;
}
case ( LOCALE_SSHORTDATE ) :
{
*ppCache = pNlsUserInfo->sShortDate;
break;
}
case ( LOCALE_SDATE ) :
{
*ppCache = pNlsUserInfo->sDate;
break;
}
case ( LOCALE_IDATE ) :
{
*ppCache = pNlsUserInfo->iDate;
break;
}
case ( LOCALE_SYEARMONTH ) :
{
*ppCache = pNlsUserInfo->sYearMonth;
break;
}
case ( LOCALE_SLONGDATE ) :
{
*ppCache = pNlsUserInfo->sLongDate;
break;
}
case ( LOCALE_ICALENDARTYPE ) :
{
*ppCache = pNlsUserInfo->iCalType;
break;
}
case ( LOCALE_IFIRSTDAYOFWEEK ) :
{
*ppCache = pNlsUserInfo->iFirstDay;
break;
}
case ( LOCALE_IFIRSTWEEKOFYEAR ) :
{
*ppCache = pNlsUserInfo->iFirstWeek;
break;
}
case ( LOCALE_SLOCALE ) :
{
*ppCache = pNlsUserInfo->sLocale;
break;
}
default :
{
NtStatus = STATUS_UNSUCCESSFUL;
break;
}
}
return (NtStatus);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsQueryCurrentUserInfo
//
// Retreive the NLS info from the registry using a cached key.
//
// 04-07-99 SamerA Created.
////////////////////////////////////////////////////////////////////////////
NTSTATUS NlsQueryCurrentUserInfo(
PNLS_LOCAL_CACHE pNlsCache,
LPWSTR pValue,
LPWSTR pOutput,
size_t cchOutput)
{
PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info
BYTE pStatic[MAX_KEY_VALUE_FULLINFO]; // ptr to static buffer
ULONG rc;
//
// Initialize the output string.
//
*pOutput = 0;
//
// Query the registry value.
//
pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)pStatic;
rc = QueryRegValue( pNlsCache->CurrentUserKeyHandle,
pValue,
&pKeyValueFull,
MAX_KEY_VALUE_FULLINFO,
NULL );
//
// If the query failed or if the output buffer is not large enough,
// then return failure.
//
if ((rc != NO_ERROR) ||
(pKeyValueFull->DataLength > (MAX_REG_VAL_SIZE * sizeof(WCHAR))))
{
return (STATUS_UNSUCCESSFUL);
}
//
// Save the string in pOutput.
//
if(FAILED(StringCchCopyW(pOutput, MAX_REG_VAL_SIZE, GET_VALUE_DATA_PTR(pKeyValueFull))))
{
return (STATUS_UNSUCCESSFUL);
}
//
// Return success.
//
return (STATUS_SUCCESS);
}
////////////////////////////////////////////////////////////////////////////
//
// NlsInvalidateCache
//
// Invalidate an NLS Cache.
//
// 03-29-99 SamerA Created.
////////////////////////////////////////////////////////////////////////////
void FASTCALL NlsInvalidateCache(
PNLS_USER_INFO pNlsUserInfo)
{
pNlsUserInfo->sAbbrevLangName[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iCountry[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sCountry[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sList[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iMeasure[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iPaperSize[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sDecimal[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sThousand[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sGrouping[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iDigits[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iLZero[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iNegNumber[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sNativeDigits[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iDigitSubstitution[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sCurrency[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sMonDecSep[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sMonThouSep[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sMonGrouping[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iCurrDigits[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iCurrency[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iNegCurr[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sPosSign[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sNegSign[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sTimeFormat[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sTime[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iTime[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iTLZero[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iTimeMarkPosn[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->s1159[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->s2359[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sShortDate[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sDate[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iDate[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sYearMonth[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sLongDate[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iCalType[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iFirstDay[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->iFirstWeek[0] = NLS_INVALID_INFO_CHAR;
pNlsUserInfo->sLocale[0] = NLS_INVALID_INFO_CHAR;
return;
}