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