1068 lines
30 KiB
C
1068 lines
30 KiB
C
|
|
|||
|
/*************************************************************************
|
|||
|
*
|
|||
|
* inipath.c
|
|||
|
*
|
|||
|
* Routines to manage per user mapping of Ini file paths
|
|||
|
*
|
|||
|
* copyright notice: Copyright 1998, Microsoft Corporation
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
*************************************************************************/
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
|
|||
|
//*** Instance data
|
|||
|
ULONG ulWinDirFlags = 0; // State of user's Windows directory
|
|||
|
|
|||
|
#define WINDIR_FLAGS_VALID 0x01 // The flags are initialized
|
|||
|
#define WINDIR_USER_WINDIR_OK 0x02 // User's Windows dir exists
|
|||
|
|
|||
|
#define WINDOWS_DIR L"WINDOWS"
|
|||
|
UNICODE_STRING WindowsDir = { sizeof(WINDOWS_DIR) - sizeof(UNICODE_NULL) , sizeof(WINDOWS_DIR) + sizeof(UNICODE_NULL), WINDOWS_DIR };
|
|||
|
|
|||
|
WCHAR gpwszDefaultUserName[MAX_PATH+1];
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* TermsrvPerUserWinDirMapping
|
|||
|
*
|
|||
|
*
|
|||
|
/******************************************************************************/
|
|||
|
BOOLEAN TermsrvPerUserWinDirMapping() {
|
|||
|
|
|||
|
#ifdef PERUSERBYREQUEST
|
|||
|
PRTL_USER_PROCESS_PARAMETERS pUserParam;
|
|||
|
PWCHAR pwch, pwchext;
|
|||
|
WCHAR pwcAppName[MAX_PATH+1];
|
|||
|
ULONG ulCompat=0, ulAppType=0;
|
|||
|
|
|||
|
// Get the path of the executable name
|
|||
|
pUserParam = NtCurrentPeb()->ProcessParameters;
|
|||
|
|
|||
|
// Get the executable name, if there's no \ just use the name as it is
|
|||
|
pwch = wcsrchr(pUserParam->ImagePathName.Buffer, L'\\');
|
|||
|
if (pwch) {
|
|||
|
pwch++;
|
|||
|
} else {
|
|||
|
pwch = pUserParam->ImagePathName.Buffer;
|
|||
|
}
|
|||
|
wcscpy(pwcAppName, pwch);
|
|||
|
pwch = pwcAppName;
|
|||
|
|
|||
|
if (_wcsicmp(pwch, L"ntvdm.exe")) {
|
|||
|
|
|||
|
// If not a 16 bit app
|
|||
|
// Check if we should return the per user windows dir for this app
|
|||
|
GetCtxAppCompatFlags(&ulCompat, &ulAppType);
|
|||
|
if (!(ulCompat & TERMSRV_COMPAT_PERUSERWINDIR) ||
|
|||
|
!(ulCompat & ulAppType)) {
|
|||
|
//
|
|||
|
// Let the standard GetWindowsDirectory call return the actual path
|
|||
|
//
|
|||
|
return FALSE;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
#else
|
|||
|
ULONG ulCompat=0, ulAppType = 0;
|
|||
|
|
|||
|
|
|||
|
// Check if we should return the system windows dir for this app
|
|||
|
GetCtxAppCompatFlags(&ulCompat, &ulAppType);
|
|||
|
if ((ulCompat & CITRIX_COMPAT_SYSWINDIR) &&
|
|||
|
(ulCompat & ulAppType)) {
|
|||
|
return FALSE;
|
|||
|
} else {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* TermsrvBuildIniFileName
|
|||
|
*
|
|||
|
* Build the INI file name based on the INIPATH or HOMEPATH (if no INIPATH)
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* pFQName (output)
|
|||
|
* Buffer to place fully qualified INI file name
|
|||
|
* pBaseFileName (input)
|
|||
|
* pointer to buffer containing base INI file name
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* NTSTATUS
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
NTSTATUS
|
|||
|
TermsrvBuildIniFileName(
|
|||
|
OUT PUNICODE_STRING pFQName,
|
|||
|
IN PUNICODE_STRING pBaseFileName
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
USHORT indexLastWChar;
|
|||
|
ULONG ulCompat, ulAppType=0;
|
|||
|
|
|||
|
|
|||
|
//Added By SalimC
|
|||
|
/*
|
|||
|
* If in install mode, use the base windows directory
|
|||
|
* like a stock NT.
|
|||
|
*/
|
|||
|
if( IsSystemLUID() || TermsrvAppInstallMode() ) {
|
|||
|
|
|||
|
return( STATUS_UNSUCCESSFUL );
|
|||
|
}
|
|||
|
//END SalimC
|
|||
|
|
|||
|
if (!TermsrvPerUserWinDirMapping()) {
|
|||
|
return( STATUS_UNSUCCESSFUL );
|
|||
|
}
|
|||
|
#if 0
|
|||
|
GetCtxAppCompatFlags(&ulCompat, &ulAppType);
|
|||
|
if (((ulCompat & TERMSRV_COMPAT_SYSWINDIR) && (ulCompat & ulAppType))) {
|
|||
|
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
Status = GetPerUserWindowsDirectory( pFQName );
|
|||
|
if ( NT_SUCCESS( Status ) ) {
|
|||
|
/*
|
|||
|
* Add a '\' if one's not already there
|
|||
|
*/
|
|||
|
if ( indexLastWChar = pFQName->Length / sizeof( WCHAR ) ) {
|
|||
|
if ( pFQName->Buffer[--indexLastWChar] != L'\\' ) {
|
|||
|
Status = RtlAppendUnicodeToString( pFQName, L"\\" );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Append the base file name to the fully qualified directory name
|
|||
|
*/
|
|||
|
if ( NT_SUCCESS( Status ) ) {
|
|||
|
Status = RtlAppendUnicodeStringToString( pFQName, pBaseFileName );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* GetPerUserWindowsDirectory
|
|||
|
*
|
|||
|
* Get the user's INI file directory
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* pFQName (output)
|
|||
|
* Buffer to place fully qualified INI file name
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* NTSTATUS
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
NTSTATUS
|
|||
|
GetPerUserWindowsDirectory(
|
|||
|
OUT PUNICODE_STRING pFQName
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
int indexLastWChar;
|
|||
|
USHORT Length;
|
|||
|
#if 0 //Bug fix #340691: Inherit the security
|
|||
|
PSECURITY_ATTRIBUTES psa = NULL;
|
|||
|
#endif //Bug fix #340691: Inherit the security
|
|||
|
UNICODE_STRING UserProfilePath;
|
|||
|
WCHAR* pwszFQProfileName;
|
|||
|
#if DBG
|
|||
|
char pszFile[MAX_PATH+1];
|
|||
|
#endif
|
|||
|
|
|||
|
UNICODE_STRING BaseHomePathVariableName, BaseHomeDriveVariableName;
|
|||
|
|
|||
|
/*
|
|||
|
* If in install mode, use the base windows directory
|
|||
|
* like a stock NT.
|
|||
|
*/
|
|||
|
if( IsSystemLUID() || TermsrvAppInstallMode() ) {
|
|||
|
//Status = GetEnvPath( pFQName, NULL, &BaseWindowsDirectory );
|
|||
|
return( STATUS_UNSUCCESSFUL );
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Check for HOMEDRIVE and HOMEPATH
|
|||
|
*/
|
|||
|
RtlInitUnicodeString(&BaseHomeDriveVariableName,L"HOMEDRIVE");
|
|||
|
RtlInitUnicodeString(&BaseHomePathVariableName,L"HOMEPATH");
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status = GetEnvPath( pFQName, &BaseHomeDriveVariableName,
|
|||
|
&BaseHomePathVariableName ))){
|
|||
|
|
|||
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|||
|
|
|||
|
// Need 2 bytes for the "\" character to cat FQN and WindowsDir
|
|||
|
Length = pFQName->Length + sizeof(WCHAR) + WindowsDir.Length;
|
|||
|
|
|||
|
#if DBG
|
|||
|
DbgPrint("pFQName->Length = %u WindowsDir.Length = %u Length = %u\n",
|
|||
|
pFQName->Length, WindowsDir.Length, Length);
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
pFQName->Length = Length;
|
|||
|
#if DBG
|
|||
|
DbgPrint("\nGetEnvPath return STATUS_BUFFER_TOO_SMALL\n");
|
|||
|
#endif
|
|||
|
} else {
|
|||
|
#if DBG
|
|||
|
DbgPrint("GetEnvPath failed with Status %lx\n",Status);
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If the user profile is Default User then use the
|
|||
|
* base windows directory.
|
|||
|
*/
|
|||
|
|
|||
|
if (pwszFQProfileName = wcsrchr( pFQName->Buffer, L'\\' )) {
|
|||
|
|
|||
|
if (_wcsnicmp(pwszFQProfileName+1, gpwszDefaultUserName, MAX_PATH+1) == 0) {
|
|||
|
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Check buffer length
|
|||
|
*/
|
|||
|
Length = pFQName->Length + sizeof(WCHAR) + WindowsDir.Length;
|
|||
|
|
|||
|
// take into account the NULL terminator character
|
|||
|
if (pFQName->MaximumLength < Length + 1) {
|
|||
|
// Need 2 bytes for the NULL terminator
|
|||
|
Length += sizeof(WCHAR);
|
|||
|
pFQName->Length = Length;
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Add a trailing backslash if one's not already there
|
|||
|
*/
|
|||
|
if ( indexLastWChar = pFQName->Length / sizeof( WCHAR ) ) {
|
|||
|
|
|||
|
if ( pFQName->Buffer[--indexLastWChar] != L'\\' ) {
|
|||
|
|
|||
|
if (NT_SUCCESS(RtlAppendUnicodeToString( pFQName, L"\\" ))) {
|
|||
|
|
|||
|
/*
|
|||
|
* Append "WINDOWS" to home dir
|
|||
|
*/
|
|||
|
Status = RtlAppendUnicodeStringToString( pFQName, &WindowsDir );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
Status = RtlAppendUnicodeStringToString( pFQName, &WindowsDir );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
// Check if we've already tried to create the user's windows path
|
|||
|
if (ulWinDirFlags & WINDIR_FLAGS_VALID) {
|
|||
|
if (ulWinDirFlags & WINDIR_USER_WINDIR_OK) {
|
|||
|
goto done;
|
|||
|
} else {
|
|||
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( NT_SUCCESS(Status) ) {
|
|||
|
|
|||
|
WCHAR Buffer[MAX_PATH+1];
|
|||
|
SECURITY_ATTRIBUTES sa;
|
|||
|
BOOL fDirCreated = FALSE;
|
|||
|
|
|||
|
// Mark this process's windows directory flags as valid
|
|||
|
ulWinDirFlags |= WINDIR_FLAGS_VALID;
|
|||
|
#if 0 //Bug fix #340691: Inherit the security
|
|||
|
/*
|
|||
|
* Since creating a security descriptor calls LookupAccountName,
|
|||
|
* which is very time consuming, we only do that if we have to
|
|||
|
* create the directory (which should rarely happen anyway).
|
|||
|
*/
|
|||
|
if ( CreateDirectoryW( (LPCWSTR)pFQName->Buffer, NULL ) &&
|
|||
|
RemoveDirectoryW( (LPCWSTR)pFQName->Buffer ) &&
|
|||
|
CtxCreateSecurityDescriptor( &sa ) ) {
|
|||
|
psa = &sa;
|
|||
|
}
|
|||
|
/*
|
|||
|
* Create windows directory if it doesn't exist
|
|||
|
*/
|
|||
|
if ( !CreateDirectoryW( (LPCWSTR)pFQName->Buffer, psa ) ) {
|
|||
|
#endif //Bug fix #340691: Inherit the security
|
|||
|
if ( !CreateDirectoryW( (LPCWSTR)pFQName->Buffer, NULL ) ) {
|
|||
|
|
|||
|
if ( (Status = GetLastError()) == ERROR_ALREADY_EXISTS ) {
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
else {
|
|||
|
wcstombs( pszFile, pFQName->Buffer, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: Error (%d) creating dir '%s'\n",
|
|||
|
Status, pszFile );
|
|||
|
}
|
|||
|
#endif
|
|||
|
} else {
|
|||
|
fDirCreated = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Create system directory if it doesn't exist
|
|||
|
* (ignore return code)
|
|||
|
*/
|
|||
|
wcscpy( Buffer, pFQName->Buffer );
|
|||
|
wcscat( Buffer, L"\\system" );
|
|||
|
|
|||
|
/*
|
|||
|
* If the user's WINDOWS directory already existed but the
|
|||
|
* WINDOWS\SYSTEM directory didn't, we need to create the
|
|||
|
* security descriptor (this scenario is even rarer).
|
|||
|
*/
|
|||
|
#if 0 //Bug fix #340691: Inherit the security
|
|||
|
if ( !psa && !fDirCreated &&
|
|||
|
CreateDirectoryW( (LPCWSTR)Buffer, NULL ) &&
|
|||
|
RemoveDirectoryW( (LPCWSTR)Buffer ) &&
|
|||
|
CtxCreateSecurityDescriptor( &sa ) ) {
|
|||
|
psa = &sa;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( !CreateDirectoryW( (LPCWSTR)Buffer, psa ) ) {
|
|||
|
#endif
|
|||
|
if ( !CreateDirectoryW( (LPCWSTR)Buffer, NULL ) ) {
|
|||
|
#if DBG
|
|||
|
if ( GetLastError() != ERROR_ALREADY_EXISTS ) {
|
|||
|
wcstombs( pszFile, Buffer, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: Error (%d) creating dir '%s'\n",
|
|||
|
GetLastError(), pszFile );
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
ulWinDirFlags |= WINDIR_USER_WINDIR_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
done:
|
|||
|
#if 0 //Bug fix #340691: Inherit the security
|
|||
|
if ( psa ) {
|
|||
|
CtxFreeSecurityDescriptor( psa );
|
|||
|
}
|
|||
|
#endif //Bug fix #340691: Inherit the security
|
|||
|
#if DDBG
|
|||
|
wcstombs( pszFile, pFQName->Buffer, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: ctxwindir='%s'\n", Status ? "Error" : pszFile );
|
|||
|
#endif
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* GetEnvPath
|
|||
|
*
|
|||
|
* Retrieve a fully qualified path derived from a drive and dir env variable
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* pFQPath (output)
|
|||
|
* Buffer to place fully qualified path name
|
|||
|
* pDriveVariableName (input)
|
|||
|
* pointer to buffer containing env variable name for drive
|
|||
|
* if NULL, pPathVariableName is a FQPath and no env vars are used
|
|||
|
* pPathVariableName (input)
|
|||
|
* pointer to buffer containing env variable name for dir
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* NTSTATUS
|
|||
|
*
|
|||
|
* If NTSTATUS is STATUS_BUFFER_TOO_SMALL, pFQPath->Length will be set
|
|||
|
* to the buffer size needed.
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
NTSTATUS
|
|||
|
GetEnvPath(
|
|||
|
OUT PUNICODE_STRING pFQPath,
|
|||
|
IN PUNICODE_STRING pDriveVariableName,
|
|||
|
IN PUNICODE_STRING pPathVariableName
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
UNICODE_STRING Path;
|
|||
|
USHORT Length;
|
|||
|
|
|||
|
if ( pDriveVariableName ) {
|
|||
|
/*
|
|||
|
* First let's figure out how big the buffer needs to be
|
|||
|
* We need to do this in case the buffer is too small and we
|
|||
|
* need to return the required size
|
|||
|
*/
|
|||
|
RtlInitUnicodeString( &Path, NULL );
|
|||
|
|
|||
|
/*
|
|||
|
* See if an env variable is defined for the drive
|
|||
|
*/
|
|||
|
Status = RtlQueryEnvironmentVariable_U( NULL, pDriveVariableName,
|
|||
|
&Path);
|
|||
|
switch ( Status ) {
|
|||
|
case STATUS_BUFFER_TOO_SMALL:
|
|||
|
Length = Path.Length; // Count how big this the drive spec is
|
|||
|
break;
|
|||
|
case STATUS_SUCCESS:
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND; // Something's wrong!
|
|||
|
default:
|
|||
|
goto done;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* See if an env variable is defined for the directory
|
|||
|
*/
|
|||
|
Path.Length = 0;
|
|||
|
Status = RtlQueryEnvironmentVariable_U( NULL, pPathVariableName,
|
|||
|
&Path);
|
|||
|
switch ( Status ) {
|
|||
|
case STATUS_BUFFER_TOO_SMALL:
|
|||
|
Length += Path.Length; // Count how big this the dir spec is
|
|||
|
break;
|
|||
|
case STATUS_SUCCESS:
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND; // Something's wrong!
|
|||
|
default:
|
|||
|
goto done;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If the buffer is too small, return the max size needed
|
|||
|
*/
|
|||
|
if ( Length + sizeof(WCHAR) > pFQPath->MaximumLength ) {
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
pFQPath->Length = Length + sizeof(WCHAR); // return size
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Get the env variable for the drive - should work if we got this far
|
|||
|
*/
|
|||
|
if ( Status = RtlQueryEnvironmentVariable_U( NULL, pDriveVariableName,
|
|||
|
pFQPath) ) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Setup a receive buffer that points to the proper spot in pFQPath
|
|||
|
*/
|
|||
|
Length = pFQPath->Length; // Save the drive length
|
|||
|
Path.Length = 0;
|
|||
|
Path.MaximumLength = pFQPath->MaximumLength - Length;
|
|||
|
(ULONG_PTR)Path.Buffer = (ULONG_PTR)pFQPath->Buffer + (ULONG)Length;
|
|||
|
|
|||
|
/*
|
|||
|
* Get the env variable for the directory - should work if we got this far
|
|||
|
* Then append it to the end of the drive spec
|
|||
|
*/
|
|||
|
if ( Status = RtlQueryEnvironmentVariable_U( NULL, pPathVariableName,
|
|||
|
&Path) ) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Fix up the structure and we're done
|
|||
|
*/
|
|||
|
pFQPath->Length = Path.Length + Length;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/*
|
|||
|
* pPathVariableName is really the FQ directory name
|
|||
|
*/
|
|||
|
if ( (pPathVariableName->Length + sizeof(WCHAR)) > pFQPath->MaximumLength ) {
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
pFQPath->Length = pPathVariableName->Length + sizeof(WCHAR); // return size
|
|||
|
} else {
|
|||
|
RtlCopyUnicodeString( pFQPath, pPathVariableName );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* TermsrvConvertSysRootToUserDir
|
|||
|
*
|
|||
|
* People who use INI files should never have to fully qualify them, but some
|
|||
|
* people do anyway. What's more, some people do it wrong. For example,
|
|||
|
* Microsoft PowerPoint 4.0 will call GetSystemDir (not GetWindowsDir) and
|
|||
|
* will strip off "\system" to build a fully qualified path.
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* pFQPath (input/output)
|
|||
|
* Buffer containing fully qualified path name
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* NTSTATUS
|
|||
|
*
|
|||
|
* If NTSTATUS is not STATUS_SUCCESS, the directory was not converted
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
NTSTATUS
|
|||
|
TermsrvConvertSysRootToUserDir(
|
|||
|
OUT PUNICODE_STRING pFQPath,
|
|||
|
IN PUNICODE_STRING BaseWindowsDirectory
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|||
|
PWSTR p;
|
|||
|
INT_PTR c;
|
|||
|
WCHAR buffer[MAX_PATH+1];
|
|||
|
UNICODE_STRING BaseFileName;
|
|||
|
#if DDBG
|
|||
|
char pszFile[MAX_PATH+1];
|
|||
|
#endif
|
|||
|
|
|||
|
ULONG ulCompat, ulAppType=0;
|
|||
|
|
|||
|
/*
|
|||
|
* If in install mode, use the base windows directory
|
|||
|
* like a stock NT.
|
|||
|
*/
|
|||
|
if( IsSystemLUID() || TermsrvAppInstallMode() ) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
GetCtxAppCompatFlags(&ulCompat, &ulAppType);
|
|||
|
if (((ulCompat & TERMSRV_COMPAT_SYSWINDIR) && (ulCompat & ulAppType))) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
#endif
|
|||
|
if (!TermsrvPerUserWinDirMapping()) {
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Check for NULL pointers
|
|||
|
*/
|
|||
|
if ( !pFQPath || !pFQPath->Buffer ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Bogus ini path\n" );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Validate and isolate the path
|
|||
|
*/
|
|||
|
if ( !(p = wcsrchr( pFQPath->Buffer, L'\\' ) ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: No backslash in ini path\n" );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
c = (INT_PTR)((ULONG_PTR)p - (ULONG_PTR)pFQPath->Buffer);
|
|||
|
|
|||
|
#if DDBG
|
|||
|
wcstombs( pszFile, BaseWindowsDirectory->Buffer, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: c(%d) c2(%d) BaseWinDir: '%s'\n",
|
|||
|
c, (int)BaseWindowsDirectory->Length, pszFile );
|
|||
|
wcstombs( pszFile, p, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: BaseFileName: '%s'\n", pszFile );
|
|||
|
#endif
|
|||
|
|
|||
|
if ( c != (INT_PTR)BaseWindowsDirectory->Length ) {
|
|||
|
#if DDBG
|
|||
|
DbgPrint( "KERNEL32: Path length diff from BaseWinDir length\n" );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* See if the path is the same as the base windows directory
|
|||
|
*/
|
|||
|
c /= sizeof(WCHAR);
|
|||
|
if ( _wcsnicmp( BaseWindowsDirectory->Buffer, pFQPath->Buffer, (size_t)c ) ) {
|
|||
|
#if DDBG
|
|||
|
DbgPrint( "KERNEL32: Path diff from BaseWinDir\n" );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Use the user's directory instead
|
|||
|
*/
|
|||
|
wcscpy( buffer, ++p );
|
|||
|
RtlInitUnicodeString( &BaseFileName, buffer );
|
|||
|
Status = TermsrvBuildIniFileName( pFQPath, &BaseFileName );
|
|||
|
|
|||
|
done:
|
|||
|
|
|||
|
#if DDBG
|
|||
|
wcstombs( pszFile, pFQPath->Buffer, sizeof(pszFile) );
|
|||
|
DbgPrint( "KERNEL32: Exit(%x) ConvertSystemRootToUserDir: '%s'\n",
|
|||
|
Status, pszFile );
|
|||
|
#endif
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* CtxCreateSecurityDescriptor
|
|||
|
*
|
|||
|
* This routine will create a security descriptor based on the specified
|
|||
|
* generic flags. If this function succeeds, the caller needs to call
|
|||
|
* CtxFreeSecurityDescriptor() when it is done using the descriptor.
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* psa (output)
|
|||
|
* Pointer to uninitialized security attributes structure
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* TRUE if successful, FALSE if error occurred
|
|||
|
*
|
|||
|
* (GetLastError() can be called to retrieve error code)
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
#if 0 //Bug fix #340691: Inherit the security
|
|||
|
BOOL CtxCreateSecurityDescriptor( PSECURITY_ATTRIBUTES psa )
|
|||
|
{
|
|||
|
BOOL fSuccess = FALSE;
|
|||
|
NTSTATUS Status;
|
|||
|
PSID psidAdmin, psidUser;
|
|||
|
UINT cb = sizeof( SECURITY_DESCRIPTOR ) + 2 * sizeof(PSID);
|
|||
|
UINT cbAcl = sizeof(ACL);
|
|||
|
PACL pAcl;
|
|||
|
PSID *ppsidAdmin, *ppsidUser;
|
|||
|
SID_IDENTIFIER_AUTHORITY gSystemSidAuthority = SECURITY_NT_AUTHORITY;
|
|||
|
HANDLE hUserToken;
|
|||
|
PTOKEN_USER pTokenUser = NULL;
|
|||
|
DWORD cbNeeded;
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize pointers to dynamic memory blocks
|
|||
|
*/
|
|||
|
psa->lpSecurityDescriptor = NULL;
|
|||
|
psidAdmin = NULL;
|
|||
|
psidUser = NULL;
|
|||
|
|
|||
|
/*
|
|||
|
* Get the SID of the bult-in Administrators group
|
|||
|
*/
|
|||
|
Status = RtlAllocateAndInitializeSid(
|
|||
|
&gSystemSidAuthority,
|
|||
|
2,
|
|||
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|||
|
DOMAIN_ALIAS_RID_ADMINS,
|
|||
|
0,0,0,0,0,0,
|
|||
|
&psidAdmin);
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("KERNEL32: Couldn't allocate Administrators SID (0x%x)\n", Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Get the SID for the current user from their process token
|
|||
|
*/
|
|||
|
Status = NtOpenThreadToken(
|
|||
|
NtCurrentThread(),
|
|||
|
TOKEN_QUERY,
|
|||
|
TRUE,
|
|||
|
&hUserToken);
|
|||
|
if (Status == STATUS_NO_TOKEN) {
|
|||
|
Status = NtOpenProcessToken(
|
|||
|
NtCurrentProcess(),
|
|||
|
TOKEN_QUERY,
|
|||
|
&hUserToken);
|
|||
|
}
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("KERNEL32: Couldn't access process' token (0x%x)\n", Status );
|
|||
|
#endif
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, psidAdmin );
|
|||
|
goto done;
|
|||
|
}
|
|||
|
Status = NtQueryInformationToken(
|
|||
|
hUserToken,
|
|||
|
TokenUser,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
&cbNeeded );
|
|||
|
if (Status == STATUS_BUFFER_TOO_SMALL) {
|
|||
|
pTokenUser = (PTOKEN_USER)RtlAllocateHeap( RtlProcessHeap(), 0, cbNeeded );
|
|||
|
if (pTokenUser != NULL) {
|
|||
|
Status = NtQueryInformationToken(
|
|||
|
hUserToken,
|
|||
|
TokenUser,
|
|||
|
(LPVOID)pTokenUser,
|
|||
|
cbNeeded,
|
|||
|
&cbNeeded );
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
/*
|
|||
|
* Make a copy of the user's SID
|
|||
|
*/
|
|||
|
psidUser = RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthSid(pTokenUser->User.Sid) );
|
|||
|
if (psidUser != NULL) {
|
|||
|
Status = RtlCopySid( RtlLengthSid(pTokenUser->User.Sid), psidUser, pTokenUser->User.Sid );
|
|||
|
} else {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
Status = STATUS_NO_MEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (pTokenUser != NULL) {
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, pTokenUser );
|
|||
|
}
|
|||
|
NtClose(hUserToken);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("KERNEL32: Couldn't query user's token (0x%x)\n", Status );
|
|||
|
#endif
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, psidAdmin );
|
|||
|
if (psidUser != NULL) {
|
|||
|
RtlFreeHeap( RtlProcessHeap(), 0, psidUser );
|
|||
|
}
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Figure out how much memory we need to allocate for the SD
|
|||
|
*/
|
|||
|
cbAcl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid( psidUser ) - sizeof(DWORD);
|
|||
|
cbAcl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid( psidAdmin ) - sizeof(DWORD);
|
|||
|
|
|||
|
/*
|
|||
|
* Allocate all the memory we need for the security descriptor
|
|||
|
*/
|
|||
|
if ( !(psa->lpSecurityDescriptor =
|
|||
|
(PSECURITY_DESCRIPTOR)LocalAlloc( LPTR, cb + cbAcl ) ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("KERNEL32: No memory to create security descriptor (%d)\n",
|
|||
|
cb + cbAcl);
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Divvy up our memory block to include SIDs and ACLs
|
|||
|
*/
|
|||
|
ppsidAdmin = (PSID*)((ULONG_PTR)psa->lpSecurityDescriptor + sizeof(SECURITY_DESCRIPTOR));
|
|||
|
ppsidUser = (PSID*)((ULONG_PTR)ppsidAdmin + sizeof(PSID));
|
|||
|
pAcl = (PACL)((ULONG_PTR)ppsidUser + sizeof(PSID));
|
|||
|
/*
|
|||
|
* Save the SIDs - the SIDs must not be freed until we're done
|
|||
|
* using the security descriptor
|
|||
|
*/
|
|||
|
*ppsidAdmin = psidAdmin;
|
|||
|
*ppsidUser = psidUser;
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize the rest of the security attributes structure
|
|||
|
*/
|
|||
|
psa->nLength = sizeof( SECURITY_ATTRIBUTES );
|
|||
|
psa->bInheritHandle = FALSE;
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize the security descriptor
|
|||
|
*/
|
|||
|
if ( Status = RtlCreateSecurityDescriptor(
|
|||
|
psa->lpSecurityDescriptor,
|
|||
|
SECURITY_DESCRIPTOR_REVISION ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) initializing security descriptor\n",
|
|||
|
Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Set the owner
|
|||
|
*/
|
|||
|
if ( Status = RtlSetOwnerSecurityDescriptor( psa->lpSecurityDescriptor,
|
|||
|
NULL, FALSE ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) setting security descriptor owner\n",
|
|||
|
Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Set the group
|
|||
|
*/
|
|||
|
if ( Status = RtlSetGroupSecurityDescriptor( psa->lpSecurityDescriptor,
|
|||
|
psidAdmin, FALSE ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) setting security descriptor owner\n",
|
|||
|
Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Initialize the ACL
|
|||
|
*/
|
|||
|
if ( Status = RtlCreateAcl( pAcl, cbAcl, ACL_REVISION ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) initializing ACL\n",
|
|||
|
Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Add user ACE
|
|||
|
*/
|
|||
|
if ( Status = CtxAddAccessAllowedAce( pAcl, ACL_REVISION, GENERIC_ALL, psidUser, 0 ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) adding user ACE\n", Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Add Administrators ACE
|
|||
|
*/
|
|||
|
if ( Status = CtxAddAccessAllowedAce( pAcl, ACL_REVISION, GENERIC_ALL, psidAdmin, 1 ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) adding admin ACE\n", Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Set the discretionary ACL
|
|||
|
*/
|
|||
|
if ( Status = RtlSetDaclSecurityDescriptor( psa->lpSecurityDescriptor,
|
|||
|
TRUE, pAcl, FALSE ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%08X) setting security descriptor owner\n",
|
|||
|
Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
fSuccess = TRUE;
|
|||
|
|
|||
|
done:
|
|||
|
if ( !fSuccess && psa->lpSecurityDescriptor ) {
|
|||
|
CtxFreeSecurityDescriptor( psa );
|
|||
|
}
|
|||
|
return( fSuccess );
|
|||
|
}
|
|||
|
|
|||
|
/******************************************************************************
|
|||
|
*
|
|||
|
* CtxFreeSecurityDescriptor
|
|||
|
*
|
|||
|
* This routine will free resources allocated in a corresponding
|
|||
|
* CtxCreateSecurityDescriptor() call.
|
|||
|
*
|
|||
|
* ENTRY:
|
|||
|
* psa (input)
|
|||
|
* Pointer to security attributes
|
|||
|
*
|
|||
|
* EXIT:
|
|||
|
* TRUE if successful, FALSE if error occurred
|
|||
|
*
|
|||
|
* (GetLastError() can be called to retrieve error code)
|
|||
|
*
|
|||
|
*****************************************************************************/
|
|||
|
BOOL CtxFreeSecurityDescriptor( PSECURITY_ATTRIBUTES psa )
|
|||
|
{
|
|||
|
BOOL fSuccess = TRUE;
|
|||
|
PSID *ppsidAdmin, *ppsidUser;
|
|||
|
|
|||
|
if ( psa->lpSecurityDescriptor ) {
|
|||
|
ppsidAdmin = (PSID*)((ULONG_PTR)psa->lpSecurityDescriptor + sizeof(SECURITY_DESCRIPTOR));
|
|||
|
ppsidUser = (PSID*)((ULONG_PTR)ppsidAdmin + sizeof(PSID));
|
|||
|
if ( *ppsidUser ) {
|
|||
|
CtxFreeSID( *ppsidUser );
|
|||
|
}
|
|||
|
if ( *ppsidAdmin ) {
|
|||
|
CtxFreeSID( *ppsidAdmin );
|
|||
|
}
|
|||
|
fSuccess = !LocalFree( psa->lpSecurityDescriptor );
|
|||
|
#if DDBG
|
|||
|
DbgPrint( "KERNEL32: fSuccess(%d) freeing security descriptor (%08X)\n",
|
|||
|
fSuccess, psa->lpSecurityDescriptor );
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
return( fSuccess );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CtxAddAccessAllowedAce (
|
|||
|
IN OUT PACL Acl,
|
|||
|
IN ULONG AceRevision,
|
|||
|
IN ACCESS_MASK AccessMask,
|
|||
|
IN PSID Sid,
|
|||
|
IN DWORD index
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
ACE_HEADER *pHeader;
|
|||
|
|
|||
|
/*
|
|||
|
* First add the ACL
|
|||
|
*/
|
|||
|
if ( !(Status = RtlAddAccessAllowedAce( Acl, AceRevision,
|
|||
|
AccessMask, Sid ) ) ) {
|
|||
|
/*
|
|||
|
* Get the ACE
|
|||
|
*/
|
|||
|
if ( Status = RtlGetAce( Acl, index, &pHeader ) ) {
|
|||
|
#if DBG
|
|||
|
DbgPrint( "KERNEL32: Error (%X) from RtlGetAce\n", Status );
|
|||
|
#endif
|
|||
|
goto done;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Now set the inheritence bits
|
|||
|
*/
|
|||
|
pHeader->AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|||
|
}
|
|||
|
|
|||
|
done:
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
#endif //Bug fix #340691: Inherit the security
|
|||
|
|
|||
|
// from \nt\private\windows\gina\userenv\globals.h
|
|||
|
#define PROFILE_LIST_PATH L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"
|
|||
|
#define DEFAULT_USER_PROFILE L"DefaultUserProfile"
|
|||
|
#define DEFAULT_USER L"Default User"
|
|||
|
|
|||
|
BOOL GetDefaultUserProfileName(
|
|||
|
LPWSTR lpProfileDir,
|
|||
|
LPDWORD lpcchSize
|
|||
|
)
|
|||
|
{
|
|||
|
WCHAR* pwszProfileName;
|
|||
|
BYTE pKeyValueInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+(MAX_PATH+1)*sizeof(WCHAR)];
|
|||
|
ULONG ulSize;
|
|||
|
DWORD dwLength;
|
|||
|
BOOL bRetVal = FALSE;
|
|||
|
HKEY hKey;
|
|||
|
NTSTATUS Status;
|
|||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|||
|
UNICODE_STRING UnicodeString;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Query for the Default User profile name
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&UnicodeString, PROFILE_LIST_PATH);
|
|||
|
|
|||
|
InitializeObjectAttributes(&ObjectAttributes,
|
|||
|
&UnicodeString,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
|
|||
|
Status = NtOpenKey( &hKey,
|
|||
|
KEY_READ,
|
|||
|
&ObjectAttributes );
|
|||
|
|
|||
|
//lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, PROFILE_LIST_PATH,
|
|||
|
// 0, KEY_READ, &hKey);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
#if DBG
|
|||
|
DbgPrint("TSAppCmp:GetDefaultUserProfileName: Failed to open profile list key with 0x%x.",Status);
|
|||
|
#endif
|
|||
|
SetLastError(RtlNtStatusToDosError(Status));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//lResult = RegQueryValueExW(hKey, DEFAULT_USER_PROFILE, NULL, &dwType,
|
|||
|
// (LPBYTE) wszProfileName, &dwSize);
|
|||
|
|
|||
|
RtlInitUnicodeString(&UnicodeString, DEFAULT_USER_PROFILE);
|
|||
|
|
|||
|
Status = NtQueryValueKey( hKey,
|
|||
|
&UnicodeString,
|
|||
|
KeyValuePartialInformation,
|
|||
|
pKeyValueInfo,
|
|||
|
sizeof(pKeyValueInfo),
|
|||
|
&ulSize);
|
|||
|
|
|||
|
pwszProfileName = (WCHAR*)(((PKEY_VALUE_PARTIAL_INFORMATION)pKeyValueInfo)->Data);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
lstrcpy (pwszProfileName, DEFAULT_USER);
|
|||
|
}
|
|||
|
|
|||
|
NtClose(hKey);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Save the result if possible
|
|||
|
dwLength = lstrlen(pwszProfileName) + 1;
|
|||
|
|
|||
|
if (lpProfileDir) {
|
|||
|
|
|||
|
if (*lpcchSize >= dwLength) {
|
|||
|
lstrcpy (lpProfileDir, pwszProfileName);
|
|||
|
bRetVal = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
*lpcchSize = dwLength;
|
|||
|
|
|||
|
return bRetVal;
|
|||
|
}
|
|||
|
|
|||
|
|