|
|
//*************************************************************
//
// Copyright (c) Microsoft Corporation 1998
// All rights reserved
//
// filedb.cxx
//
//*************************************************************
#include "fdeploy.hxx"
#define SAVED_SETTINGS_FILE L"{25537BA6-77A8-11D2-9B6C-0000F8080861}.ini"
HRESULT RsopSidsFromToken(PRSOPTOKEN pRsopToken, PTOKEN_GROUPS* ppGroups);
FOLDERINFO gUserShellFolders[] = { {CSIDL_APPDATA, 17, L"Application Data\\"}, // {CSIDL_COOKIES, 8, L"Cookies\\"},
{CSIDL_DESKTOPDIRECTORY, 8, L"Desktop\\"}, {CSIDL_FAVORITES, 10, L"Favorites\\"}, // {CSIDL_HISTORY, 8, L"History\\"},
// {0, 15, L"Local Settings\\"}, Has no reg key, no CSIDL
{CSIDL_MYPICTURES, 25, L"My Documents\\My Pictures\\"}, {CSIDL_PERSONAL, 13, L"My Documents\\"}, {CSIDL_NETHOOD, 8, L"NetHood\\"}, {CSIDL_PRINTHOOD, 10, L"PrintHood\\"}, // {CSIDL_RECENT, 7, L"Recent\\"},
{CSIDL_SENDTO, 7, L"SendTo\\"}, {CSIDL_STARTUP, 28, L"Start Menu\\Programs\\Startup\\"}, {CSIDL_PROGRAMS, 20, L"Start Menu\\Programs\\"}, {CSIDL_STARTMENU, 11, L"Start Menu\\"}, {CSIDL_TEMPLATES, 10, L"Templates\\"}, // {CSIDL_INTERNET_CACHE, 25, L"Temporary Internet Files\\"},
{0, 0, NULL } };
FOLDERINFO gMachineShellFolders[] = { {CSIDL_COMMON_APPDATA, 17, L"Application Data\\"}, {CSIDL_COMMON_DESKTOPDIRECTORY, 8, L"Desktop\\"}, // {0, 10, L"Documents\\"}, No shell support
{CSIDL_COMMON_STARTUP, 28, L"Start Menu\\Programs\\Startup\\"}, {CSIDL_COMMON_PROGRAMS, 20, L"Start Menu\\Programs\\"}, {CSIDL_COMMON_STARTMENU, 11, L"Start Menu\\"}, {0, 0, NULL} };
static DWORD gSchema = 1;
CFileDB::CFileDB() { _hUserToken = 0; _hkRoot = 0; _pEnvBlock = 0; _pGroups = 0; _pwszGPTPath = 0; _GPTPathLen = 0; _pwszIniFilePath = 0; _IniFileLen = 0; _pwszGPOName = 0; _pwszGPOUniqueName = 0; _pwszGPOSOMPath = 0; _pRsopContext = 0; }
CFileDB::~CFileDB() { if ( _pEnvBlock ) DestroyEnvironmentBlock( _pEnvBlock ); if (_pwszGPTPath) delete [] _pwszGPTPath; if (_pwszIniFilePath) delete [] _pwszIniFilePath;
//
// Note that _pGroups must be freed with LocalFree --
// we are able to free it with delete because we
// have redefined delete to be LocalFree
//
if (_pGroups) delete [] (BYTE*) _pGroups;
// _pwszGPOName is not allocated
// _pwszGPOUniqueName is not allocated
// _pwszGPODSPath is not allocated
}
DWORD CFileDB::Initialize ( HANDLE hUserToken, HKEY hkRoot, CRsopContext* pRsopContext) { BOOL bStatus; DWORD Status = ERROR_SUCCESS; ULONG Size; int CSidl; WCHAR * pwszSlash; HRESULT hr; HANDLE hFind; WIN32_FIND_DATA FindData;
//set the token
_hUserToken = hUserToken;
//set the root key
_hkRoot = hkRoot;
// set the rsop logging context
_pRsopContext = pRsopContext;
//create an environment block for the user. we need this for expanding
//variables.
if (! _pEnvBlock) { if (!CreateEnvironmentBlock ( &_pEnvBlock, _hUserToken, FALSE)) { Status = GetLastError(); goto InitializeEnd; } }
//get the list of group to which the user belongs
_pGroups = 0; Size = 0;
//
// We may only use the Nt security subsystem api below
// to retrieve groups when we are not in planning mode
//
for (; ! _pRsopContext->IsPlanningModeEnabled() ;) { Status = NtQueryInformationToken( _hUserToken, TokenGroups, _pGroups, Size, &Size );
if ( (STATUS_BUFFER_TOO_SMALL == Status) || (ERROR_OUTOFMEMORY == Status) ) { _pGroups = (PTOKEN_GROUPS) new BYTE [ Size ]; if ( ! _pGroups ) { Status = ERROR_OUTOFMEMORY; break; }
continue; }
if ( Status != STATUS_SUCCESS ) { if (_pGroups) delete [] ((BYTE*) _pGroups); _pGroups = 0; }
break; }
//
// In planning mode, we get our security groups from
// the policy engine's simulated token, not from a real token
//
if ( _pRsopContext->IsPlanningModeEnabled() && ! _pRsopContext->IsReportingModeEnabled() ) { DWORD cbSize; PRSOP_TARGET pRsopTarget;
pRsopTarget = _pRsopContext->_pRsopTarget;
//
// The call below uses RSoP's planning mode "simulated"
// security subsystem to retrieve the sids from the simulated
// token. The function allocates memory in _pGroups that
// must be freed with LocalFree.
//
hr = RsopSidsFromToken(pRsopTarget->pRsopToken, &_pGroups);
Status = HRESULT_CODE(hr); }
if (ERROR_SUCCESS != Status) goto InitializeEnd;
//
// Retrieve the local path -- note that we do not need this in planning mode
//
if ( ! _pRsopContext->IsPlanningModeEnabled() ) { //get the path to our directory under Local Settings.
CSidl = CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE;
hr = SHGetFolderPath( NULL, CSidl, _hUserToken, 0, _pwszLocalPath );
if ( hr != S_OK ) { //try to get the last error.
if (FACILITY_WIN32 == HRESULT_FACILITY (hr)) { Status = HRESULT_CODE(hr); } else { Status = GetLastError(); if (ERROR_SUCCESS == Status) { //an error had occurred but nobody called SetLastError
//should not be mistaken as a success.
Status = (DWORD) hr; } } DebugMsg((DM_WARNING, IDS_NO_LOCALAPPDATA, Status)); goto InitializeEnd; }
pwszSlash = _pwszLocalPath + wcslen( _pwszLocalPath ); hr = StringCchCat( _pwszLocalPath, MAX_PATH, L"\\Microsoft\\Windows\\File Deployment" ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto InitializeEnd; }
Status = ERROR_SUCCESS; //now create directories as necessary
// Quick check to see if we have necessary local dirs.
hFind = FindFirstFile( _pwszLocalPath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind ) { do { pwszSlash = wcschr( &pwszSlash[1], L'\\' );
if ( pwszSlash ) *pwszSlash = 0;
bStatus = CreateDirectory( _pwszLocalPath, NULL ); if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) ) { Status = GetLastError(); break; }
if ( pwszSlash ) *pwszSlash = L'\\'; } while ( pwszSlash ); } else { FindClose( hFind ); } }
InitializeEnd: return Status; }
DWORD CFileDB::Process( PGROUP_POLICY_OBJECT pGPO, BOOL bRemove ) { WCHAR * pwszGPTIniFilePath = NULL; DWORD Length = 0; DWORD Status; DWORD ProcessStatus; BOOL bStatus; BOOL bPolicyApplied; HANDLE hFind; DWORD dwAttr = INVALID_FILE_ATTRIBUTES; WIN32_FIND_DATA FindData; HRESULT hr = S_OK;
if ( bRemove && _pRsopContext->IsPlanningModeEnabled() ) { return ERROR_INVALID_PARAMETER; }
bPolicyApplied = FALSE; Status = ERROR_SUCCESS;
//first initialize the variables that vary with policies
_bRemove = bRemove; if ( ! _bRemove ) { Length = wcslen(pGPO->lpFileSysPath) + wcslen(GPT_SUBDIR) + 1; if (Length > _GPTPathLen) { //we need more memory than has been allocated.
//so get that before proceeding
if (_pwszGPTPath) delete _pwszGPTPath; _GPTPathLen = 0; //make sure that this always reflects the correct value
_pwszGPTPath = new WCHAR [Length]; if ( ! _pwszGPTPath ) { Status = ERROR_OUTOFMEMORY; goto ProcessEnd; } _GPTPathLen = Length; //make sure that this always reflects the correct value
}
hr = StringCchCopy( _pwszGPTPath, _GPTPathLen, pGPO->lpFileSysPath ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } hr = StringCchCat( _pwszGPTPath, _GPTPathLen, GPT_SUBDIR ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; }
Length += wcslen( INIFILE_NAME ); pwszGPTIniFilePath = new WCHAR[ Length ]; if ( ! pwszGPTIniFilePath ) { Status = ERROR_OUTOFMEMORY; goto ProcessEnd; }
hr = StringCchCopy( pwszGPTIniFilePath, Length, _pwszGPTPath ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } hr = StringCchCat( pwszGPTIniFilePath, Length, INIFILE_NAME ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; }
//
// Do a quick check to see if we have any file deployment
// for this policy.
//
hFind = FindFirstFile( pwszGPTIniFilePath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind ) { Status = GetLastError(); goto ProcessEnd; } else { bPolicyApplied = TRUE; DebugMsg((DM_VERBOSE, IDS_HASADD_POLICY, pGPO->lpDisplayName)); FindClose( hFind ); } }
Status = ERROR_SUCCESS;
if ( _pRsopContext->IsPlanningModeEnabled() ) { Length = wcslen( pwszGPTIniFilePath ) + 1; } else { Length = wcslen( _pwszLocalPath ) + wcslen( pGPO->szGPOName ) + 6; }
if (Length > _IniFileLen) { //we need more memory than has been allocated
if (_pwszIniFilePath) delete [] _pwszIniFilePath; _IniFileLen = 0; //make sure that this always reflects the current value
_pwszIniFilePath = new WCHAR[Length]; if ( ! _pwszIniFilePath ) { Status = ERROR_OUTOFMEMORY; goto ProcessEnd; } _IniFileLen = Length; //make sure that this always reflects the current value
}
if ( _pRsopContext->IsPlanningModeEnabled() ) { hr = StringCchCopy( _pwszIniFilePath, _IniFileLen, pwszGPTIniFilePath ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } } else { hr = StringCchCopy( _pwszIniFilePath, _IniFileLen, _pwszLocalPath ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } hr = StringCchCat( _pwszIniFilePath, _IniFileLen, L"\\" ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } hr = StringCchCat( _pwszIniFilePath, _IniFileLen, pGPO->szGPOName ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } hr = StringCchCat( _pwszIniFilePath, _IniFileLen, L".ini" ); if ( FAILED(hr) ) { Status = HRESULT_CODE(hr); goto ProcessEnd; } }
if ( _bRemove ) { hFind = FindFirstFile( _pwszIniFilePath, &FindData );
if ( INVALID_HANDLE_VALUE == hFind ) { //this error should be ignored since there is nothing we can do.
//the policy has been deleted and the local settings are missing
//so we have no choice but to treat these as if the settings were
//to orphan the folder upon policy removal.
goto ProcessEnd; } else { bPolicyApplied = TRUE; DebugMsg((DM_VERBOSE, IDS_HASREMOVE_POLICY, pGPO->lpDisplayName)); FindClose( hFind ); } } else if ( ! _pRsopContext->IsPlanningModeEnabled() ) { //
// Cache the ini file locally. Note that if a local copy of the GPO already
// exists and it has hidden/read-only/system attributes, then the CopyFile
// API will fail with ERROR_ACCESS_DENIED unless the source (on the sysvol)
// has the same attributes.
// Therefore we first slap on normal attributes on the local copy if any
// and restore the original attributes if we fail in the CopyFile.
//
dwAttr = GetFileAttributes (_pwszIniFilePath); //
// Don't set the file attributes to normal unless GetFileAttributes
// succeeds
//
if (INVALID_FILE_ATTRIBUTES != dwAttr) SetFileAttributes (_pwszIniFilePath, FILE_ATTRIBUTE_NORMAL); bStatus = CopyFile( pwszGPTIniFilePath, _pwszIniFilePath, FALSE ); if ( ! bStatus ) { Status = GetLastError(); //
// Restore the local cache's attributes (if we had changed them)
// since our copy operation has failed.
//
if (INVALID_FILE_ATTRIBUTES != dwAttr) SetFileAttributes (_pwszIniFilePath, dwAttr); } }
if ( Status != ERROR_SUCCESS ) goto ProcessEnd;
_pwszGPOName = (WCHAR *) pGPO->lpDisplayName; _pwszGPOUniqueName = (WCHAR *) pGPO->szGPOName; _pwszGPOSOMPath = ( WCHAR *) pGPO->lpLink; _pwszGPODSPath = ( WCHAR *) pGPO->lpDSPath;
ProcessStatus = ProcessRedirects();
if ( (ProcessStatus != ERROR_SUCCESS) && (ERROR_SUCCESS == Status) ) Status = ProcessStatus;
ProcessEnd:
if ( Status != ERROR_SUCCESS ) { gpEvents->Report ( EVENT_FDEPLOY_POLICYPROCESS_FAIL, 2, pGPO->lpDisplayName, StatusToString (Status) ); } else if ( bPolicyApplied ) { DebugMsg((DM_VERBOSE, IDS_PROCESS_GATHER_OK, pGPO->lpDisplayName)); }
delete [] pwszGPTIniFilePath;
return Status; }
DWORD CFileDB::ProcessRedirects(void) { WCHAR * pwszString = 0; WCHAR * pwszSectionStrings = 0; WCHAR * pwszRedirection = 0; WCHAR wszFolderName[80]; UNICODE_STRING String; DWORD Flags; DWORD RedirectStatus; DWORD Status; BOOL bStatus; REDIRECTABLE index; DWORD RedirStatus; CRedirectInfo riPolicy [(int) EndRedirectable]; //the redirection info. for this policy
DWORD i;
//first load the localized folder names
for (i = 0, Status = ERROR_SUCCESS; i < (DWORD)EndRedirectable; i++) { Status = riPolicy[i].LoadLocalizedNames(); if (ERROR_SUCCESS != Status) return Status; //bail out if the resource names cannot be loaded.
}
pwszSectionStrings = 0;
bStatus = ReadIniSection( L"FolderStatus", &pwszSectionStrings );
if ( ! bStatus ) { Status = ERROR_OUTOFMEMORY; goto ProcessRedirectsEnd; }
Status = ERROR_SUCCESS;
for ( pwszString = pwszSectionStrings; *pwszString != 0; pwszString += lstrlen(pwszString) + 1 ) { //
// The syntax for each line is :
// foldername=FLAGS
//
//
// First extract the foldername.
//
pwszRedirection = wcschr( pwszString, L'=' ); if (!pwszRedirection) { Status = ERROR_FILE_CORRUPT; goto ProcessRedirectsEnd; } *pwszRedirection = 0; Status = HRESULT_CODE(StringCbCopy( wszFolderName, sizeof(wszFolderName), pwszString )); if ( Status != ERROR_SUCCESS ) { goto ProcessRedirectsEnd; } *pwszRedirection++ = L'=';
//
// Now grab the hex FLAGS.
//
String.Buffer = pwszRedirection;
pwszRedirection = wcschr( pwszRedirection, L' ' ); if ( pwszRedirection ) *pwszRedirection = 0;
String.Length = wcslen( String.Buffer ) * sizeof(WCHAR); String.MaximumLength = String.Length + sizeof(WCHAR);
RtlUnicodeStringToInteger( &String, 16, &Flags );
//just gather the information here.
//actual redirections are performed in ProcessGPO after all the policies
//have been processed.
if (EndRedirectable == (index = CRedirectInfo::GetFolderIndex (wszFolderName))) { //redirection of this folder is not supported
DebugMsg ((DM_VERBOSE, IDS_REDIR_NOTSUPPORTED, wszFolderName)); } else { //if this is a policy that has been removed and it was decided to
//orphan the contents, we don't even look at it
if (!_bRemove || (Flags & (REDIR_RELOCATEONREMOVE | REDIR_FOLLOW_PARENT))) { //if there is a problem in gathering redirection info. for a folder
//quit immediately, or we might end up computing an incorrect
//resultant policy
if (ERROR_SUCCESS != (Status = riPolicy [(int) index].GatherRedirectionInfo (this, Flags, _bRemove))) goto ProcessRedirectsEnd; } } } //now update the data stored in the descendant objects
//this is required because if the descendants follow the parent
//then the settings need to be obtained from the parent
//note that we do not call UpdateDescendant for MyPics here, but in fdeploy.cxx
//for details on why we do this, look at comments in operator= in redir.cxx
Status = riPolicy[(int) Programs].UpdateDescendant();
if ( ERROR_SUCCESS == Status ) { Status = riPolicy[(int) Startup].UpdateDescendant(); //this call must be made after Programs has been updated
}
if ( ERROR_SUCCESS != Status ) { goto ProcessRedirectsEnd; }
//merge info into the global redirection store
for (i = 0; i < (DWORD) EndRedirectable; i++) { if (_bRemove) gDeletedPolicyResultant[i] = riPolicy[i]; else gAddedPolicyResultant[i] = riPolicy[i]; }
ProcessRedirectsEnd: delete pwszSectionStrings;
if ( ERROR_SUCCESS != Status ) { DebugMsg((DM_VERBOSE, IDS_PROCESSREDIRECTS, Status)); } else { if ( ! _bRemove && _pRsopContext->IsRsopEnabled() ) { (void) AddRedirectionPolicies( this, riPolicy); } }
return Status; }
BOOL CFileDB::ReadIniSection( WCHAR * pwszSectionName, WCHAR ** ppwszStrings, DWORD * pcchLen ) { DWORD Length; DWORD ReturnLength;
*ppwszStrings = 0; Length = 256;
for (;;) { // prevent a corrupt Ini file from eating up all available memory.
if (MaxIniSectionSize < Length) return FALSE;
delete *ppwszStrings; *ppwszStrings = new WCHAR[Length];
if ( ! *ppwszStrings ) return FALSE;
ReturnLength = GetPrivateProfileSection( pwszSectionName, *ppwszStrings, Length, _pwszIniFilePath );
if ( ReturnLength != (Length - 2) ) { if (pcchLen) { *pcchLen = ReturnLength; }
return TRUE; }
Length *= 2; } }
DWORD CFileDB::GetLocalFilePath( WCHAR * pwszFolderPath, WCHAR * wszFullPath ) { int CSidl; DWORD Status; HRESULT hr; WCHAR * pwszFolderName;
CSidl = CSIDL_FLAG_MASK; //use a value that is not a valid CSIDL value for any folder.
for (DWORD n = 0; gUserShellFolders[n].FolderName; n++) { if (0 == _wcsicmp (pwszFolderPath, gUserShellFolders[n].FolderName)) { pwszFolderName = gUserShellFolders[n].FolderName; CSidl = gUserShellFolders[n].CSidl; break; } }
if ( CSIDL_FLAG_MASK != CSidl ) { hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY, _hUserToken, 0, wszFullPath ); Status = GetWin32ErrFromHResult (hr);
if ( ERROR_SUCCESS != Status ) { DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, Status)); return Status; } } else return ERROR_INVALID_NAME;
return ERROR_SUCCESS; }
DWORD CFileDB::GetPathFromFolderName( WCHAR * pwszFolderName, WCHAR * wszFullPath ) { int CSidl; DWORD Status; HRESULT hr;
CSidl = CSIDL_FLAG_MASK; //use a csidl value that is not valid for any folder
for (DWORD n = 0; gUserShellFolders[n].FolderName; n++) { //we subtract 1 from the length because one of the paths is \ terminated
//and the other is not.
if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 ) { CSidl = gUserShellFolders[n].CSidl; break; } }
if ( CSIDL_FLAG_MASK != CSidl ) { hr = SHGetFolderPath( 0, CSidl | CSIDL_FLAG_DONT_VERIFY, _hUserToken, 0, wszFullPath );
if ( S_OK != hr ) { DebugMsg((DM_WARNING, IDS_FOLDERPATH_FAIL, pwszFolderName, hr)); return (DWORD) hr; } } else return ERROR_INVALID_NAME;
return ERROR_SUCCESS; }
const FOLDERINFO* CFileDB::FolderInfoFromFolderName( WCHAR * pwszFolderName ) { //
// This method returns the index into global array.
//
if ( _hUserToken ) { for ( DWORD n = 0; gUserShellFolders[n].FolderName; n++ ) { if ( _wcsnicmp( pwszFolderName, gUserShellFolders[n].FolderName, gUserShellFolders[n].FolderNameLength - 1 ) == 0 ) return &gUserShellFolders[n]; } } else { for ( DWORD n = 0; gMachineShellFolders[n].FolderName; n++ ) { if ( _wcsnicmp( pwszFolderName, gMachineShellFolders[n].FolderName, gMachineShellFolders[n].FolderNameLength - 1 ) == 0 ) return &gMachineShellFolders[n]; } }
return NULL; }
int CFileDB::RegValueCSIDLFromFolderName( WCHAR * pwszFolderName ) { const FOLDERINFO *pFI;
pFI = FolderInfoFromFolderName(pwszFolderName); if (pFI != NULL) return pFI->CSidl; else return -1; // invalid CSIDL
}
const WCHAR * CFileDB::GetLocalStoragePath () { return (LPCWSTR) _pwszLocalPath; }
DWORD CFileDB::CopyFileTree( WCHAR * pwszExistingPath, WCHAR * pwszNewPath, WCHAR * pwszIgnoredSubdir, SHARESTATUS StatusFrom, SHARESTATUS StatusTo, BOOL bAllowRdrTimeout, CCopyFailData * pCopyFailure ) { HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA FindData; WIN32_FILE_ATTRIBUTE_DATA SourceAttr; WIN32_FILE_ATTRIBUTE_DATA DestAttr; WCHAR * wszSource = NULL; WCHAR * pwszSourceEnd = 0; WCHAR * wszDest = NULL; WCHAR * pwszDestEnd = 0; WCHAR * pwszTempFilename = 0; DWORD FileAttributes; DWORD Status; BOOL bStatus; BOOL bReuseTempName = FALSE; int lenSource; int lenDest; DWORD StatusCSCDel = ERROR_SUCCESS; DWORD dwAttr = INVALID_FILE_ATTRIBUTES; size_t sourceRemaining; size_t destRemaining;
if (! pwszExistingPath || ! pwszNewPath) return ERROR_PATH_NOT_FOUND;
lenSource = wcslen (pwszExistingPath); lenDest = wcslen (pwszNewPath);
if (! lenSource || ! lenDest) return ERROR_PATH_NOT_FOUND;
// Create source string
sourceRemaining = lenSource + MAX_PATH + 2; wszSource = new WCHAR[ sourceRemaining ]; if (NULL == wszSource) { return ERROR_OUTOFMEMORY; }
Status = HRESULT_CODE(StringCchCopyEx( wszSource, sourceRemaining, pwszExistingPath, &pwszSourceEnd, &sourceRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto CopyFileTree_End; } if (L'\\' != pwszSourceEnd[-1]) { Status = HRESULT_CODE(StringCchCopyEx( pwszSourceEnd, sourceRemaining, L"\\", &pwszSourceEnd, &sourceRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto CopyFileTree_End; } } Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, L"*" )); if ( Status != ERROR_SUCCESS ) { goto CopyFileTree_End; }
// Create destination string
destRemaining = lenDest + MAX_PATH + 2; wszDest = new WCHAR[ destRemaining ]; if (NULL == wszDest) { Status = ERROR_OUTOFMEMORY; goto CopyFileTree_End; }
Status = HRESULT_CODE(StringCchCopyEx( wszDest, destRemaining, pwszNewPath, &pwszDestEnd, &destRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto CopyFileTree_End; }
if (L'\\' != pwszDestEnd[-1]) { Status = HRESULT_CODE(StringCchCopyEx( pwszDestEnd, destRemaining, L"\\", &pwszDestEnd, &destRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto CopyFileTree_End; } }
// Now find the relevant file(s)
hFind = FindFirstFile( wszSource, &FindData );
Status = ERROR_SUCCESS; if ( INVALID_HANDLE_VALUE == hFind ) { goto CopyFileTree_End; }
do { Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, FindData.cFileName )); if ( Status != ERROR_SUCCESS ) { break; } Status = HRESULT_CODE(StringCchCopy( pwszDestEnd, destRemaining, FindData.cFileName )); if ( Status != ERROR_SUCCESS ) { break; }
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( lstrcmp( FindData.cFileName, L"." ) == 0 || lstrcmp( FindData.cFileName, L".." ) == 0 || (pwszIgnoredSubdir && lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) == 0) ) continue;
Status = FullDirCopyW (wszSource, wszDest, FALSE);
if ( ERROR_SUCCESS == Status ) { if (ERROR_SUCCESS == StatusCSCDel) { Status = CopyFileTree( wszSource, wszDest, NULL, StatusFrom, StatusTo, bAllowRdrTimeout, pCopyFailure ); } else { //no point delaying CSCDeletes anymore since we have already failed once.
Status = CopyFileTree (wszSource, wszDest, NULL, StatusFrom, StatusTo, FALSE, pCopyFailure); } //copy over the pin info. too
if (ERROR_SUCCESS == Status) MergePinInfo (wszSource, wszDest, StatusFrom, StatusTo); } else { pCopyFailure->RegisterFailure (wszSource, wszDest); DebugMsg((DM_VERBOSE, IDS_DIRCREATE_FAIL, wszDest, Status)); } } else { Status = ERROR_SUCCESS; // First check if it is necessary to copy the file over.
bStatus = GetFileAttributesEx( wszSource, GetFileExInfoStandard, &SourceAttr ); if ( bStatus ) { bStatus = GetFileAttributesEx( wszDest, GetFileExInfoStandard, &DestAttr ); if (bStatus) { if (CompareFileTime( &SourceAttr.ftLastWriteTime, &DestAttr.ftLastWriteTime ) <= 0) { // The destination is newer or at least as old as the source.
// There is no need to copy. However, we should delete
// the locally cached copy of the file if any since the
// destination is newer.
DebugMsg((DM_VERBOSE, IDS_SKIP_FILE, wszDest)); if (ERROR_SUCCESS == StatusCSCDel) StatusCSCDel = DeleteCSCFile ( wszSource, bAllowRdrTimeout); else DeleteCSCFile ( wszSource, FALSE); continue; } } else { Status = GetLastError(); if (ERROR_PATH_NOT_FOUND == Status || ERROR_FILE_NOT_FOUND == Status) { // The destination was not found. So we must proceed with the copy.
bStatus = TRUE; Status = ERROR_SUCCESS; } } } else { // We failed to get the attributes of the source file.
Status = GetLastError(); }
if (ERROR_SUCCESS == Status) { //
// If we are here, we need to copy the file over.
// In order to avoid loss of data, we must first copy the file
// over to a temporary file at the destination and then rename
// the file at the destination. This is because if the network
// connection gets dropped during the filecopy operation, then
// we are left with an incomplete file at the destination.
// If we use the real name on the destination directly, then
// the file will be skipped at the next redirection attempt
// because of our last writer wins algorithm. As a result, when
// the redirection succeeds subsequently, we end up with a loss
// of user data. Copying to a temp name and then renaming the
// file prevents this problem from happening because the rename
// operation is atomic.
//
// First check if we need to generate a new temporary filename
// Note: We try to minimize the number of calls to GetTempFilename
// because it can be a very expensive call as it can result in
// multiple CreateFile calls over the network which can be
// especially slow for EFS shares.
//
bReuseTempName = FALSE; if (NULL != pwszTempFilename && L'\0' != *pwszTempFilename) { dwAttr = GetFileAttributes (pwszTempFilename); if (INVALID_FILE_ATTRIBUTES == dwAttr) { Status = GetLastError(); if (ERROR_PATH_NOT_FOUND == Status || ERROR_FILE_NOT_FOUND == Status) { Status = ERROR_SUCCESS; bReuseTempName = TRUE; } } } if (ERROR_SUCCESS == Status && FALSE == bReuseTempName) { // We need to generate a new temporary filename.
if (NULL == pwszTempFilename) { pwszTempFilename = new WCHAR [MAX_PATH + 1]; if (NULL == pwszTempFilename) Status = ERROR_OUTOFMEMORY; } if (ERROR_SUCCESS == Status) { *pwszTempFilename = 0; *pwszDestEnd = 0; bStatus = GetTempFileName(wszDest, TEXT("frd"), 0, pwszTempFilename); *pwszDestEnd = FindData.cFileName[0]; if (!bStatus) { Status = GetLastError(); DebugMsg((DM_VERBOSE, IDS_NO_TEMPNAME, wszDest, Status)); } } } if (ERROR_SUCCESS == Status) { // Now we have a temp. filename and we are ready to copy.
Status = FullFileCopyW (wszSource, pwszTempFilename, FALSE);
if (ERROR_SUCCESS == Status) { // Now we rename the file at the destination in one atomic
// step. Note however that if the destination file exists
// and has readonly/hidden/system attributes, the MoveFileEx
// API will fail with ERROR_ACCESS_DENIED. So we slap on normal
// attributes on the file before doing the Move. If we fail,
// we restore the attributes.
dwAttr = GetFileAttributes (wszDest); // Change attributes only if we managed to figure out the
// actual attributes.
if (INVALID_FILE_ATTRIBUTES != dwAttr) SetFileAttributes (wszDest, FILE_ATTRIBUTE_NORMAL); if (!MoveFileEx(pwszTempFilename, wszDest, MOVEFILE_REPLACE_EXISTING)) { Status = GetLastError(); DebugMsg((DM_VERBOSE, IDS_RENAME_FAILED, pwszTempFilename, wszDest, Status)); // Restore the attributes of the destination file.
// Provided we changed those in the first place.
if (INVALID_FILE_ATTRIBUTES != dwAttr) SetFileAttributes (wszDest, dwAttr); // Also try to delete the temp file because we might still
// be able to do it. But ignore any failures. We are just
// trying to be nice by removing turds.
DeleteFile(pwszTempFilename); } } else { //
// ignore failure here because we may no longer have network connectivity and
// the delete operation may fail here.
//
DeleteFile(pwszTempFilename); } } } if ( Status != ERROR_SUCCESS ) { pCopyFailure->RegisterFailure (wszSource, wszDest);
switch (Status) { case ERROR_INVALID_SECURITY_DESCR: DebugMsg((DM_VERBOSE, IDS_SETSECURITY_FAIL, wszSource, wszDest)); break; default: DebugMsg((DM_VERBOSE, IDS_FILECOPY_FAIL, wszSource, wszDest, Status)); break; } } }
if ( Status != ERROR_SUCCESS ) break;
} while ( FindNextFile( hFind, &FindData ) ); CopyFileTree_End: // Some final cleanup before we return.
delete [] wszSource; delete [] wszDest; delete [] pwszTempFilename;
if (INVALID_HANDLE_VALUE != hFind) FindClose( hFind ); return Status; }
DWORD CFileDB::DeleteFileTree( WCHAR * pwszPath, WCHAR * pwszIgnoredSubdir ) { HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA FindData; WCHAR * wszSource = NULL; WCHAR * pwszSourceEnd = 0; DWORD Status; BOOL bStatus; int len; size_t sourceRemaining;
if (!pwszPath) return ERROR_PATH_NOT_FOUND;
len = wcslen (pwszPath);
if (!len) return ERROR_PATH_NOT_FOUND;
// Create source string
sourceRemaining = len + MAX_PATH + 2; wszSource = new WCHAR[ sourceRemaining ]; if (NULL == wszSource) { return ERROR_OUTOFMEMORY; } Status = HRESULT_CODE(StringCchCopyEx( wszSource, sourceRemaining, pwszPath, &pwszSourceEnd, &sourceRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto DeleteFileTree_End; } if (L'\\' != pwszSourceEnd[-1]) { Status = HRESULT_CODE(StringCchCopyEx( pwszSourceEnd, sourceRemaining, L"\\", &pwszSourceEnd, &sourceRemaining, 0 )); if ( Status != ERROR_SUCCESS ) { goto DeleteFileTree_End; } } Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, L"*" )); if ( Status != ERROR_SUCCESS ) { goto DeleteFileTree_End; }
// Now find the file(s)
hFind = FindFirstFile( wszSource, &FindData );
Status = ERROR_SUCCESS; if ( INVALID_HANDLE_VALUE == hFind ) { goto DeleteFileTree_End; }
for (;;) { Status = HRESULT_CODE(StringCchCopy( pwszSourceEnd, sourceRemaining, FindData.cFileName )); if ( Status != ERROR_SUCCESS ) { break; }
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( lstrcmp( FindData.cFileName, L"." ) != 0 && lstrcmp( FindData.cFileName, L".." ) != 0 && (! pwszIgnoredSubdir || lstrcmpi( FindData.cFileName, pwszIgnoredSubdir ) != 0) ) { SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL ); Status = DeleteFileTree( wszSource, NULL);
if ( ERROR_SUCCESS == Status ) { if ( ! RemoveDirectory( wszSource ) ) { Status = GetLastError(); DebugMsg((DM_VERBOSE, IDS_DIRDEL_FAIL, wszSource, Status)); } } } } else { SetFileAttributes( wszSource, FILE_ATTRIBUTE_NORMAL ); bStatus = DeleteFile( wszSource ); if ( ! bStatus ) { Status = GetLastError(); DebugMsg((DM_VERBOSE, IDS_FILEDEL_FAIL, wszSource, Status)); } }
if ( Status != ERROR_SUCCESS ) break;
bStatus = FindNextFile( hFind, &FindData );
if ( ! bStatus ) { Status = GetLastError(); if ( ERROR_NO_MORE_FILES == Status ) Status = ERROR_SUCCESS; break; } }
DeleteFileTree_End: if (INVALID_HANDLE_VALUE != hFind) { FindClose( hFind ); }
delete [] wszSource; return Status; }
//note: the bCheckOwner flag: if set to true, then if the directory in question
// already exists, the function Fails with an ERROR_INVALID_OWNER if the
// the owner of the existing directory is not the user.
// bSourceValid indicates if there is a valid path in pwszSource
DWORD CFileDB::CreateRedirectedFolderPath( const WCHAR * pwszSource, const WCHAR * pwszDest, BOOL bSourceValid, BOOL bCheckOwner, BOOL bMoveContents ) { WCHAR * pwszSlash = 0; WCHAR * pwszPath = NULL; DWORD Status = ERROR_SUCCESS; int len; WCHAR * pwszSuccess = NULL; DWORD dwAttributes;
//first make a local copy of the destination path to work with.
//while copying, we actually convert it into an absolute path so
//that we eliminate any problems with weird paths like
//\\server\share\..\hello\..\there
len = wcslen (pwszDest) + 1; pwszPath = new WCHAR[len]; if ( !pwszPath ) { return ERROR_OUTOFMEMORY; } pwszSuccess = _wfullpath (pwszPath, pwszDest, len); if (!pwszSuccess) { Status = ERROR_BAD_PATHNAME; //actually _wfullpath rarely fails
goto CreateRedirectedFolderPath_Cleanup; }
//
// Will only accept drive based or UNC based paths.
//
// A redirect path of just <drive>:\ or \\server\share will be accepted
// even though this would be a strange choice for redirection.
//
// IMPORTANT: also see notes at the beginning of the function
if ( L':' == pwszPath[1] && L'\\' == pwszPath[2] ) { pwszSlash = &pwszPath[2]; } else if ( L'\\' == pwszPath[0] && L'\\' == pwszPath[1] ) { pwszSlash = wcschr( &pwszPath[2], L'\\' ); if ( pwszSlash ) { //watch out for '\' terminated paths
if (L'\0' == pwszSlash[1]) { pwszSlash = 0; } else //it is at least of the form \\server\share
{ pwszSlash = wcschr( &pwszSlash[1], L'\\' ); //if it is of the form \\server\share, then we allow this path
//based depending on the ownership checks if any
if (!pwszSlash) { Status = bCheckOwner ? IsUserOwner(pwszPath) : ERROR_SUCCESS; goto CreateRedirectedFolderPath_Cleanup; } //note: we do not have to watch out for the '\' terminated
//paths here (e.g. \\server\share\) because that will be
//taken care of below : in -> if (!pwszSlash[1])
} } } else { pwszSlash = 0; }
if ( ! pwszSlash ) { Status = ERROR_BAD_PATHNAME; goto CreateRedirectedFolderPath_Cleanup; }
//if it is the root directory of a drive or root of a UNC share
//we succeed based on ownership checks, if any...
//but before that, we also need to make sure that the specified path
//exists or we may end up redirecting to a non-existent location.
if ( !pwszSlash[1]) { if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath))) { //it exists
if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes)) { Status = ERROR_DIRECTORY; } else { //it exists and is a directory
Status = bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS; } } else { Status = GetLastError(); }
goto CreateRedirectedFolderPath_Cleanup; }
//if we are here, it is not the root of a drive or a share.
//so we might have to do create the destination.
//First do a quick check to see if the path exists already. this
//is not only an optimization, but is also necessary for cases
//where an admin. may want to lock down access to certain folders
//by pre-creating the folders and putting highly resitrictive ACLs on them
//in that case, CreateDirectory (later in the code) would fail with
//ACCESS_DENIED rather than ERROR_ALREADY_EXISTS and the redirection code
//will bail out even though it is not necessary to.
if (0xFFFFFFFF != (dwAttributes = GetFileAttributes(pwszPath))) { //it exists
if (! (FILE_ATTRIBUTE_DIRECTORY & dwAttributes)) { Status = ERROR_DIRECTORY; } else { //it exists and is a directory
Status = bCheckOwner ? IsUserOwner (pwszPath) : ERROR_SUCCESS; } goto CreateRedirectedFolderPath_Cleanup; }
//the destination has not been pre-created, so we need to do that
//ourselves
do { pwszSlash = wcschr( &pwszSlash[1], L'\\' );
// Watch out for '\' terminated paths.
if ( pwszSlash && (L'\0' == pwszSlash[1]) ) pwszSlash = 0;
if ( pwszSlash ) { *pwszSlash = 0; CreateDirectory( pwszPath, NULL ); *pwszSlash = L'\\';
//ignore all errors in the intermediate folders not just
//ERROR_ALREADY_EXISTS because of folders like
//\\server\share\dir1\dir2\%username% where the user may not
//have write access in dir1 but might have so in dir2
//if the path is invalid we will either discover it at the last
//dir in the chain or while trying to redirect to the destination
//retaining the code here just in case...
//
/*if ( ! bStatus && (GetLastError() != ERROR_ALREADY_EXISTS) )
return GetLastError();*/ } else { //
// Last dir in the chain. We set security on the last dir in
// the path to only allow the user & system access if
// the directory did not already exist.
//
if (bCheckOwner) { Status = CreateFolderWithUserFileSecurity( pwszPath ); } else { Status = ERROR_SUCCESS; if (!CreateDirectory( pwszPath, NULL )) Status = GetLastError(); }
if ( ERROR_SUCCESS == Status ) { //the extension created the directory, so try to set the user as
//the owner explicitly because if a member of the local administrators
//group creates a directory/file, the Administrators group becomes
//the owner by default. This can cause problems with quota accounting
//and also if the settings on the redirection policy are changed
//at a later date. However, since it is not necessary that the
//we will succeed in setting the owner here, we ignore any failures
SetUserAsOwner (pwszPath); //
// We want to skip the DACL if we want to apply exclusive ACLs
// i.e., bCheckOwner is true. Otherwise, we should just copy
// over all the metadata.
//
if (bSourceValid && bMoveContents) FullDirCopyW (pwszSource, pwszPath, bCheckOwner);
goto CreateRedirectedFolderPath_Cleanup; } else if ( ERROR_ALREADY_EXISTS != Status) { goto CreateRedirectedFolderPath_Cleanup; } else { //the directory already exists
//start anti-spoofing agent
//do the ownership check only on the last dir in chain and only
//if the flags in the ini file tell you to.
if (bCheckOwner && (ERROR_SUCCESS != (Status = IsUserOwner(pwszPath)))) { DebugMsg ((DM_VERBOSE, IDS_ACL_MISMATCH, pwszPath, Status)); goto CreateRedirectedFolderPath_Cleanup; } else { Status = ERROR_SUCCESS; goto CreateRedirectedFolderPath_Cleanup; } } }
} while ( pwszSlash );
Status = ERROR_SUCCESS;
CreateRedirectedFolderPath_Cleanup: delete [] pwszPath;
return Status; }
DWORD CFileDB::SetUserAsOwner( WCHAR * pwszPath ) { SECURITY_DESCRIPTOR SecDesc; PSID pSidUser = 0; PTOKEN_USER pTokenUser = 0; DWORD Size = 0; DWORD Status = ERROR_SUCCESS; BOOL bStatus;
if ( ! _hUserToken ) return ERROR_SUCCESS;
Status = NtQueryInformationToken( _hUserToken, TokenUser, pTokenUser, Size, &Size );
if ( STATUS_BUFFER_TOO_SMALL == Status ) { pTokenUser = (PTOKEN_USER) new BYTE[ Size ]; if ( ! pTokenUser ) { return ERROR_OUTOFMEMORY; }
Status = NtQueryInformationToken( _hUserToken, TokenUser, pTokenUser, Size, &Size ); }
if ( Status != ERROR_SUCCESS ) { goto SetUserAsOwner_End; }
Size = RtlLengthSid( pTokenUser->User.Sid ); pSidUser = (PSID) new BYTE[ Size ]; if ( pSidUser == NULL ) { Status = ERROR_OUTOFMEMORY; goto SetUserAsOwner_End; }
Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid ); if ( Status != ERROR_SUCCESS ) { goto SetUserAsOwner_End; }
bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
if ( bStatus ) { bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0); }
if (bStatus) { bStatus = SetFileSecurity( pwszPath, OWNER_SECURITY_INFORMATION, &SecDesc); }
if ( ! bStatus ) { Status = GetLastError(); }
SetUserAsOwner_End: delete [] (BYTE*) pTokenUser; delete [] (BYTE*) pSidUser; return Status; }
DWORD CFileDB::CreateFolderWithUserFileSecurity( WCHAR * pwszPath ) { SECURITY_DESCRIPTOR SecDesc; SID_IDENTIFIER_AUTHORITY AuthorityNT = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY AuthWorld = SECURITY_WORLD_SID_AUTHORITY; PSID pSidUser = 0; PSID pSidSystem = 0; PACL pAcl = 0; ACE_HEADER * pAceHeader; PTOKEN_USER pTokenUser = 0; PSID pSid = 0; DWORD AclSize; DWORD AceIndex; DWORD Size; DWORD Status; BOOL bStatus;
if ( ! _hUserToken ) return ERROR_SUCCESS;
pSidSystem = 0;
pTokenUser = 0; Size = 0;
Status = NtQueryInformationToken( _hUserToken, TokenUser, pTokenUser, Size, &Size );
if ( STATUS_BUFFER_TOO_SMALL == Status ) { pTokenUser = (PTOKEN_USER) new BYTE[ Size ]; if ( ! pTokenUser ) { return ERROR_OUTOFMEMORY; }
Status = NtQueryInformationToken( _hUserToken, TokenUser, pTokenUser, Size, &Size ); }
if ( Status != ERROR_SUCCESS ) { goto SetUserFileSecurityEnd; }
Size = RtlLengthSid( pTokenUser->User.Sid ); pSidUser = (PSID) new BYTE[ Size ]; if ( pSidUser == NULL ) { Status = ERROR_OUTOFMEMORY; goto SetUserFileSecurityEnd; }
Status = RtlCopySid( Size, pSidUser, pTokenUser->User.Sid );
if ( Status != ERROR_SUCCESS ) { goto SetUserFileSecurityEnd; }
bStatus = AllocateAndInitializeSid( &AuthorityNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSidSystem);
if ( ! bStatus ) { Status = GetLastError(); goto SetUserFileSecurityEnd; }
//
// Allocate space for the ACL
//
AclSize = (GetLengthSid(pSidUser)) + (GetLengthSid(pSidSystem)) + sizeof(ACL) + (2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));
pAcl = (PACL) new BYTE[ AclSize ]; if ( pAcl ) { bStatus = InitializeAcl( pAcl, AclSize, ACL_REVISION ); if ( ! bStatus ) Status = GetLastError(); } else { Status = ERROR_OUTOFMEMORY; }
if ( Status != ERROR_SUCCESS ) { goto SetUserFileSecurityEnd; }
//
// Add Aces for User, System, and Admin. Non-inheritable ACEs first
//
AceIndex = 0; bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidUser);
if ( bStatus ) { bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader); if ( bStatus ) pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE); }
if ( bStatus ) { AceIndex++; bStatus = AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, pSidSystem);
if ( bStatus ) { bStatus = GetAce(pAcl, AceIndex, (void **) &pAceHeader); if ( bStatus ) pAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE); } }
if ( ! bStatus ) { Status = GetLastError(); goto SetUserFileSecurityEnd; }
bStatus = InitializeSecurityDescriptor( &SecDesc, SECURITY_DESCRIPTOR_REVISION );
if (bStatus) { SetSecurityDescriptorControl (&SecDesc, SE_DACL_PROTECTED, SE_DACL_PROTECTED); //SE_DACL_PROTECTED is supported by NTFS5 but not by NTFS4, therefore
//we ignore any failures in SetSecurityDesciptorControl
}
if ( bStatus ) { bStatus = SetSecurityDescriptorDacl( &SecDesc, TRUE, pAcl, FALSE ); }
//set the owner explicitly. This is required because if the user is an
//admin, then when the directory is created, the owner is set to the group
//administrators, rather than the user
if ( bStatus ) { bStatus = SetSecurityDescriptorOwner (&SecDesc, pSidUser, 0); }
if ( bStatus ) { SECURITY_ATTRIBUTES sa; sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = &SecDesc; sa.nLength = sizeof(SECURITY_ATTRIBUTES); bStatus = CreateDirectory(pwszPath, &sa); }
if ( ! bStatus ) { Status = GetLastError(); }
SetUserFileSecurityEnd:
delete [] (BYTE*) pAcl; delete [] (BYTE*) pTokenUser; delete [] (BYTE*) pSidUser;
if (pSidSystem) { FreeSid( pSidSystem ); }
return Status; }
//+--------------------------------------------------------------------------
//
// Member: CFileDB::IsUserOwner
//
// Synopsis: given a path. this function determines if the user is the
// the owner of the file/folder
//
// Arguments: [in] pwszPath
//
// Returns: ERROR_SUCCESS if user is owner
// otherwise an error code
//
// History: 10/6/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CFileDB::IsUserOwner (const WCHAR * pwszPath) { BOOL bStatus; DWORD Status; BOOL bIsMember; DWORD dwLengthNeeded; PSECURITY_DESCRIPTOR pSecDesc = 0; PSID pOwnerSid = 0; BOOL bDaclDefaulted;
//first check if we are on FAT. if we are on FAT, then we simply let
//succeed since FAT cannot have any ACLs anyway
Status = IsOnNTFS (pwszPath); if (ERROR_NO_SECURITY_ON_OBJECT == Status) { Status = ERROR_SUCCESS; //we are on FAT
goto UserOwnerEnd; } else if (ERROR_SUCCESS != Status) goto UserOwnerEnd; //there was some other error
//else we are on NTFS and ready to rumble!
//get the owner sid from the folder.
dwLengthNeeded = 0; if ( !GetFileSecurity (pwszPath, OWNER_SECURITY_INFORMATION, pSecDesc, dwLengthNeeded, &dwLengthNeeded) ) { //GetFileSecurity failed if we are here
Status = GetLastError(); if (ERROR_INSUFFICIENT_BUFFER != Status) goto UserOwnerEnd;
//GetFileSecurity failed due to insufficient memory if we are here.
pSecDesc = (PSECURITY_DESCRIPTOR) new BYTE[dwLengthNeeded]; if (NULL == pSecDesc) { Status = ERROR_OUTOFMEMORY; goto UserOwnerEnd; }
if ( !GetFileSecurity (pwszPath, OWNER_SECURITY_INFORMATION, pSecDesc, dwLengthNeeded, &dwLengthNeeded) ) { Status = GetLastError(); goto UserOwnerEnd; } }
//now get the owner sid
bStatus = GetSecurityDescriptorOwner ( pSecDesc, &pOwnerSid, &bDaclDefaulted); if (!bStatus) { Status = GetLastError(); } else { if (!pOwnerSid) { Status = ERROR_INVALID_OWNER; } else { bStatus = CheckTokenMembership (_hUserToken, pOwnerSid, &bIsMember); if (!bStatus) Status = GetLastError(); else { if (bIsMember) Status = ERROR_SUCCESS; else Status = ERROR_INVALID_OWNER; } } }
UserOwnerEnd: delete [] (BYTE*) pSecDesc;
return Status; }
//+--------------------------------------------------------------------------
//
// Member: CFileDB::GetEnvBlock
//
// Synopsis: gets a pointer to the user's environment block
//
// Arguments: none.
//
// Returns: pointer to the user's environment block.
// NULL if it is not created yet
//
// History: 9/20/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
PVOID CFileDB::GetEnvBlock (void) { return _pEnvBlock; }
//+--------------------------------------------------------------------------
//
// Member: CFileDB::GetRsopContext
//
// Synopsis: gets a pointer to the rsop logging context
//
// Arguments: none.
//
// Returns: pointer to the rsop logging context -- never null
//
//
// History: 12/8/1999 adamed created
//
// Notes:
//
//---------------------------------------------------------------------------
CRsopContext* CFileDB::GetRsopContext() { return _pRsopContext; }
//member functions and data for class CSavedSettings
//initialize the class's static variables.
int CSavedSettings::m_idConstructor = 0; CFileDB * CSavedSettings::m_pFileDB = NULL; WCHAR * CSavedSettings::m_szSavedSettingsPath = NULL;
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::ResetStaticMembers
//
// Synopsis: resets the static members to their default values.
//
// Arguments: none
//
// Returns: nothing
//
// History: 12/17/2000 RahulTh created
//
// Notes: static members of a class, like other global variables are
// initialized only when the dll is loaded. So if this dll
// stays loaded across logons, then the fact that the globals
// have been initialized based on a previous logon can cause
// problems. Therefore, this function is used to reinitialize
// the globals.
//
//---------------------------------------------------------------------------
void CSavedSettings::ResetStaticMembers(void) { //
// No need to delete it. This usually points to a local variable in
// ProcessGroupPolicyInternal. So the actual object gets deleted when
// after each processing. We just need to make sure that it is reflected
// here.
//
m_pFileDB = NULL; if (m_szSavedSettingsPath) { delete [] m_szSavedSettingsPath; m_szSavedSettingsPath = NULL; } // No need to do anything about m_idConstructor
}
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings
//
// Synopsis: default constructor for the class.
//
// Arguments:
//
// Returns:
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CSavedSettings::CSavedSettings () { m_rID = (REDIRECTABLE) m_idConstructor; m_idConstructor = (m_idConstructor + 1) % ((int) EndRedirectable); m_szLastRedirectedPath = NULL; m_szCurrentPath = NULL; m_szLastUserName = NULL; m_szLastHomedir = NULL; m_bIsHomedirRedir = FALSE; m_bHomedirChanged = FALSE; m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
m_psid = 0; m_bValidGPO = FALSE; m_bUserNameChanged = FALSE; m_szGPOName[0] = L'\0'; }
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::ResetMembers
//
// Synopsis: Resets the member variables.
//
// Arguments: none.
//
// Returns: nothing.
//
// History: 12/17/2000 RahulTh created
//
// Notes: see ResetStaticMembers.
//
//---------------------------------------------------------------------------
void CSavedSettings::ResetMembers(void) { if (m_szLastRedirectedPath) { delete [] m_szLastRedirectedPath; m_szLastRedirectedPath = NULL; } if (m_szCurrentPath) { delete [] m_szCurrentPath; m_szCurrentPath = NULL; }
if (m_szLastUserName) { delete [] m_szLastUserName; m_szLastUserName = NULL; }
if (m_szLastHomedir) { delete [] m_szLastHomedir; m_szLastHomedir = NULL; }
if (m_psid) { RtlFreeSid (m_psid); m_psid = NULL; } m_bHomedirChanged = FALSE; m_dwFlags = REDIR_DONT_CARE; //this is always a safe default
m_bValidGPO = FALSE; m_bUserNameChanged = FALSE; m_szGPOName[0] = L'\0'; // No need to do anything about m_rID.
}
//+--------------------------------------------------------------------------
//
// Member: ~CSavedSettings
//
// Synopsis: default destructor
//
// Arguments:
//
// Returns:
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
CSavedSettings::~CSavedSettings () { //free static members if necessary
if (m_szSavedSettingsPath) { delete [] m_szSavedSettingsPath; m_szSavedSettingsPath = NULL; //this is necessary so that the
//destructor of the next object does
//not try to free it again
}
//
// Free other memory allocated for members of this object
// and reset them.
//
ResetMembers(); }
//+--------------------------------------------------------------------------
//
// Member: Load
//
// Synopsis: loads the saved settings into the object for its corresponding
// folder
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful. otherwise an error code
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::Load (CFileDB * pFileDB) { DWORD Status; BOOL bStatus; DWORD len;
//first set the FileDB object if it has not already been set
if (!m_pFileDB) m_pFileDB = pFileDB;
ASSERT (m_pFileDB);
//get the name of the file where the last settings have been saved
//if we haven't already done so.
if (!m_szSavedSettingsPath) { len = wcslen (pFileDB->_pwszLocalPath) + wcslen (SAVED_SETTINGS_FILE) + 2;
m_szSavedSettingsPath = (WCHAR *) new WCHAR [len]; if (!m_szSavedSettingsPath) { Status = ERROR_OUTOFMEMORY; goto LoadEnd; } Status = HRESULT_CODE(StringCchCopy(m_szSavedSettingsPath, len, pFileDB->_pwszLocalPath)); if ( Status != ERROR_SUCCESS ) { goto LoadEnd; } Status = HRESULT_CODE(StringCchCat(m_szSavedSettingsPath, len, L"\\")); if ( Status != ERROR_SUCCESS ) { goto LoadEnd; } Status = HRESULT_CODE(StringCchCat(m_szSavedSettingsPath, len, SAVED_SETTINGS_FILE)); if ( Status != ERROR_SUCCESS ) { goto LoadEnd; } }
//do a quick check to see if the file exists
if (0xFFFFFFFF == GetFileAttributes(m_szSavedSettingsPath)) Status = LoadDefaultLocal (); else Status = LoadFromIniFile ();
LoadEnd: return Status; }
//+--------------------------------------------------------------------------
//
// Member: GetCurrentPath
//
// Synopsis: gets the current path of the folder.
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::GetCurrentPath (void) { DWORD Status = ERROR_SUCCESS; WCHAR * pwszValueName = 0; DWORD Size; WCHAR * pwszProcessedPath = NULL;
if (m_szCurrentPath) { delete [] m_szCurrentPath; m_szCurrentPath = NULL; }
m_szCurrentPath = new WCHAR [MAX_PATH]; if (!m_szCurrentPath) { Status = ERROR_OUTOFMEMORY; goto GetCurrentPathEnd; } m_szCurrentPath[0] = L'\0'; Status = m_pFileDB->GetPathFromFolderName ( g_szRelativePathNames[(int)m_rID], m_szCurrentPath ); if (((DWORD) S_OK != Status) && //if SHGetFolderPath failed, use the local userprofile path
ERROR_INVALID_NAME != Status) //the only error from GetPathFromFolderName that is not generated by SHGetFolderPath
{ Status = HRESULT_CODE(StringCchCopy(m_szCurrentPath, MAX_PATH, L"%USERPROFILE%\\")); if ( Status != ERROR_SUCCESS ) { goto GetCurrentPathEnd; } Status = HRESULT_CODE(StringCchCat(m_szCurrentPath, MAX_PATH, g_szRelativePathNames[(int)m_rID])); if ( Status != ERROR_SUCCESS ) { goto GetCurrentPathEnd; } } else { // expand the homedir path if applicable.
if (IsHomedirPath (m_rID, m_szCurrentPath, TRUE)) { Status = ExpandHomeDir (m_rID, m_szCurrentPath, TRUE, &pwszProcessedPath); delete [] m_szCurrentPath; m_szCurrentPath = NULL; if (ERROR_SUCCESS == Status) { m_szCurrentPath = pwszProcessedPath; pwszProcessedPath = NULL; } } }
if (m_szCurrentPath) RemoveEndingSlash( m_szCurrentPath );
GetCurrentPathEnd: return Status; }
//+--------------------------------------------------------------------------
//
// Member: ResetLastUserName
//
// Synopsis: sets the last user name to the current username
//
// Arguments: none.
//
// Returns: ERROR_SUCCESS : if there is no error.
// an error code otherwise.
//
// History: 9/15/1999 RahulTh created
//
// Notes: the most likely cause of failure for this function -- if at all
// it happens, will be an out of memory condition.
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::ResetLastUserName (void) { DWORD Status = ERROR_SUCCESS; size_t lastUserNameLength = 0; if (m_szLastUserName) { delete [] m_szLastUserName; m_szLastUserName = NULL; }
lastUserNameLength = wcslen(gwszUserName); m_szLastUserName = new WCHAR [lastUserNameLength + 1]; if (!m_szLastUserName) { return ERROR_OUTOFMEMORY; }
Status = HRESULT_CODE(StringCchCopy(m_szLastUserName, lastUserNameLength+1, gwszUserName)); if ( Status == ERROR_SUCCESS ) { m_bUserNameChanged = FALSE; }
return Status; }
//+--------------------------------------------------------------------------
//
// Member: LoadDefaultLocal
//
// Synopsis: loads the default local userprofile path and default
// flags into the object
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if successful an error code otherwise
//
// History: 11/18/1998 RahulTh created
//
// Notes: the security group is set to Everyone as you can never
// cease being a member of the group
//
// this function is usually invoked when one can't find
// the ini file that contains the last saved settings or
// if the ini file does not contain the corresponding section
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::LoadDefaultLocal (void) { DWORD Status = ERROR_SUCCESS; DWORD len;
// The default case cannot be homedir redirection.
// Just make sure that our bool has been properly set.
m_bIsHomedirRedir = FALSE;
//set the default flags
m_dwFlags = REDIR_DONT_CARE;
//to be on the safe side -- set the default values for all members.
m_bValidGPO = FALSE; m_szGPOName[0] = L'\0';
//set the last username to be the same as the current username
//since we load the defaults only when we do not have any data about the
//last logon (i.e., no per user per machine FR cache
Status = ResetLastUserName (); if (ERROR_SUCCESS != Status) goto LoadDefaultsEnd;
//allocate the sid
//to be on the safe side, free the sid if it has already been allocated
if (m_psid) { RtlFreeSid (m_psid); m_psid = NULL; } Status = AllocateAndInitSidFromString (L"S-1-1-0", &m_psid);
if (ERROR_SUCCESS != Status) { m_psid = 0; goto LoadDefaultsEnd; }
//get the last redirected path
//again, to be on the safe side, free any used memory first
if (m_szLastRedirectedPath) { delete [] m_szLastRedirectedPath; m_szLastRedirectedPath = NULL; } len = wcslen (L"%USERPROFILE%\\") + wcslen (g_szRelativePathNames[(int)m_rID]) + 1; m_szLastRedirectedPath = new WCHAR [len]; if (!m_szLastRedirectedPath) { Status = ERROR_OUTOFMEMORY; goto LoadDefaultsEnd; } Status = HRESULT_CODE(StringCchCopy(m_szLastRedirectedPath, len, L"%USERPROFILE%\\")); if ( Status != ERROR_SUCCESS ) { goto LoadDefaultsEnd; } Status = HRESULT_CODE(StringCchCat(m_szLastRedirectedPath, len, g_szRelativePathNames[(int)m_rID])); if ( Status != ERROR_SUCCESS ) { goto LoadDefaultsEnd; }
//get the current path
Status = GetCurrentPath();
LoadDefaultsEnd: return Status; }
//+--------------------------------------------------------------------------
//
// Member: LoadFromIniFile
//
// Synopsis: this function loads redirection info. from the ini file
// that contains the last saved settings.
// file.
//
// Arguments: none
//
// Returns: ERROR_SUCCESS if everything is successful. an error code otherwise
//
// History: 11/18/1998 RahulTh created
//
// Notes: if this function cannot find the corresponding section in the
// ini file, it loads the default local path
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::LoadFromIniFile (void) { DWORD Status = ERROR_SUCCESS; WCHAR pwszDefault[] = L"*"; WCHAR * pwszReturnedString = NULL; WCHAR * pwszProcessedPath = NULL; DWORD retVal; DWORD Size; UNICODE_STRING StringW; GUID GPOGuid;
//
// If this object contains the MyDocs settings, get the last value
// of homedir
//
if (MyDocs == m_rID || MyPics == m_rID) { Status = SafeGetPrivateProfileStringW ( g_szDisplayNames [(int) m_rID], L"Homedir", pwszDefault, &m_szLastHomedir, &Size, m_szSavedSettingsPath); if (ERROR_SUCCESS != Status) goto LoadIniEnd;
if (L'*' == *m_szLastHomedir) { // The value of homedir at the last redirection was not found.
delete [] m_szLastHomedir; m_szLastHomedir = NULL; } }
//first try to get the user name when the user had last logged on.
Status = SafeGetPrivateProfileStringW ( g_szDisplayNames [(int) m_rID], L"Username", pwszDefault, &m_szLastUserName, &Size, m_szSavedSettingsPath);
if (ERROR_SUCCESS != Status) goto LoadIniEnd;
if (L'*' == *m_szLastUserName) { //the username field was not found in the ini file. must be an older
//cache. since we do not have the information, we just use the defaults,
//i.e. set the current user name as the last user name
Status = ResetLastUserName(); if (ERROR_SUCCESS != Status) goto LoadIniEnd; } else { //we found a user name in the local cache.
//update the member variable which indicates whether the user name
//has changed since the last logon.
if (0 == _wcsicmp (m_szLastUserName, gwszUserName)) m_bUserNameChanged = FALSE; else m_bUserNameChanged = TRUE; }
//then try to get the path
Status = SafeGetPrivateProfileStringW ( g_szDisplayNames [(int) m_rID], L"Path", pwszDefault, &m_szLastRedirectedPath, &Size, m_szSavedSettingsPath);
if (ERROR_SUCCESS != Status) goto LoadIniEnd;
if (L'*' == *m_szLastRedirectedPath) //we could not find the required data.
{ //so we go with the defaults
Status = LoadDefaultLocal(); goto LoadIniEnd; } else if (IsHomedirPath (m_rID, m_szLastRedirectedPath, TRUE)) { Status = ExpandHomeDir (m_rID, m_szLastRedirectedPath, TRUE, &pwszProcessedPath, m_szLastHomedir ); delete [] m_szLastRedirectedPath; if (ERROR_SUCCESS != Status) { m_szLastRedirectedPath = NULL; goto LoadIniEnd; } else { m_bIsHomedirRedir = TRUE; m_szLastRedirectedPath = pwszProcessedPath; pwszProcessedPath = NULL; } }
//next try to get the security group
Status = SafeGetPrivateProfileStringW ( g_szDisplayNames[(int)m_rID], L"Group", pwszDefault, &pwszReturnedString, &Size, m_szSavedSettingsPath );
if (ERROR_SUCCESS != Status) goto LoadIniEnd;
if (L'*' == *pwszReturnedString) { //data was missing, so go with the defaults
Status = LoadDefaultLocal(); goto LoadIniEnd; }
if (m_psid) { RtlFreeSid (m_psid); m_psid = NULL; }
Status = AllocateAndInitSidFromString (pwszReturnedString, &m_psid);
if (ERROR_SUCCESS != Status) { m_psid = 0; goto LoadIniEnd; }
//now get the flags
Status = SafeGetPrivateProfileStringW ( g_szDisplayNames[(int)m_rID], L"Flags", pwszDefault, &pwszReturnedString, &Size, m_szSavedSettingsPath );
if (ERROR_SUCCESS != Status) goto LoadIniEnd;
if (L'*' == *pwszReturnedString) { Status = LoadDefaultLocal(); goto LoadIniEnd; }
//now grab the hex flags
StringW.Buffer = pwszReturnedString; StringW.Length = wcslen (pwszReturnedString) * sizeof (WCHAR); StringW.MaximumLength = StringW.Length + sizeof(WCHAR); RtlUnicodeStringToInteger( &StringW, 16, &m_dwFlags );
//now get the unique name of the GPO (if any) that was used to redirect the folder
Status = SafeGetPrivateProfileStringW ( g_szDisplayNames[(int) m_rID], L"GPO", pwszDefault, &pwszReturnedString, &Size, m_szSavedSettingsPath ); if (ERROR_SUCCESS != Status) goto LoadIniEnd;
StringW.Length = sizeof (WCHAR) * wcslen (pwszReturnedString); StringW.MaximumLength = (USHORT)(sizeof(WCHAR) * Size); StringW.Buffer = pwszReturnedString;
if ((L'*' == *pwszReturnedString) || //there is no valid GPO info., or
(sizeof(m_szGPOName) <= wcslen(pwszReturnedString)) || //the file has been corrupted. the length of a valid unique name cannot exceed the size of m_szGPO, or
(STATUS_INVALID_PARAMETER == RtlGUIDFromString (&StringW, &GPOGuid)) //it is not a valid GUID
) { //there is no valid GPO info.
m_bValidGPO = FALSE; m_szGPOName[0] = L'\0'; } else { m_bValidGPO = TRUE; (void) StringCbCopy(m_szGPOName, sizeof(m_szGPOName), pwszReturnedString); }
//get the current path.
Status = GetCurrentPath ();
LoadIniEnd: if (pwszReturnedString) delete [] pwszReturnedString; if (pwszProcessedPath) delete [] pwszProcessedPath;
return Status; }
//+--------------------------------------------------------------------------
//
// Member: NeedsProcessing
//
// Synopsis: once the saved settings and the registry values have been
// loaded, this function determines if we need to look at all the
// policies. Note that this assumes that the GPO_NOCHANGES flag
// has already been set by the policy engine
//
// Arguments: none
//
// Returns: TRUE / FALSE
//
// History: 11/18/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
BOOL CSavedSettings::NeedsProcessing (void) { BOOL bStatus; DWORD i; DWORD Status; PTOKEN_GROUPS pGroups; WCHAR wszExpandedPath [TARGETPATHLIMIT + 1]; UNICODE_STRING Path; UNICODE_STRING ExpandedPath; const WCHAR * pwszCurrentHomedir = NULL; WCHAR wszProcessedSource [TARGETPATHLIMIT + 1]; WCHAR wszProcessedDest [TARGETPATHLIMIT + 1]; int len;
//if policy didn't care about the location of the folder at last logon
//then even if the last redirected path is not the same as the current
//path, it should not mean that that we need to reprocess the policy
//when the GPO_NOCHANGES flag has been provided
if (m_dwFlags & REDIR_DONT_CARE) return FALSE;
//if we are here, policy specified the location of the folder at the
//the last logon. make sure that the user name has not changed since
//last logon. If so, we need to process the policies again and move
//the user's folders accordingly.
if (m_bUserNameChanged) return TRUE;
//
// If we are here, the username had not changed, but if the homedir
// changed, that can affect the path.
// Note: Here, we cannot use the IsHomedirPath function to determine
// if this is a homedir redirection since we would have already expanded
// the path in LoadFromIniFile. Therefore, we must use m_bIsHomedirRedir
//
if (m_bIsHomedirRedir) { //
// Note: GetHomeDir is an expensive call since it can result
// in a call to the DS to get the user's home directory. So we try to
// be as lazy as possible about executing it.
//
pwszCurrentHomedir = gUserInfo.GetHomeDir (Status); if (ERROR_SUCCESS != Status || ! pwszCurrentHomedir || ! m_szLastHomedir || 0 != lstrcmpi (m_szLastHomedir, pwszCurrentHomedir)) { m_bHomedirChanged = TRUE; return TRUE; } }
//check if the last redirected path and current path are identical
if (0 != _wcsicmp(m_szLastRedirectedPath, m_szCurrentPath)) { //the paths are different. we need to do processing
//even if the policy engine thinks otherwise
//but sometimes we may have an expanded path in m_szCurrentPath
//e.g. if some User Shell Folder values are missing. So expand
//the last redirected path and compare it with the current path
Path.Length = (wcslen (m_szLastRedirectedPath) + 1) * sizeof (WCHAR); Path.MaximumLength = sizeof (m_szLastRedirectedPath); Path.Buffer = m_szLastRedirectedPath;
ExpandedPath.Length = 0; ExpandedPath.MaximumLength = sizeof (wszExpandedPath); ExpandedPath.Buffer = wszExpandedPath;
Status = RtlExpandEnvironmentStrings_U ( m_pFileDB->_pEnvBlock, &Path, &ExpandedPath, NULL ); if (ERROR_SUCCESS != Status) return TRUE; //that's our best bet in case of failure
//
// Now process the paths so that they do not contain any redundant
// slashes etc.
//
if (NULL == _wfullpath (wszProcessedSource, m_szCurrentPath, MAX_PATH)) { return TRUE; } else { //
// Eliminate any trailing slashes. Note: after going through
// _wfullpath, there can be at the most one trailing slash
//
len = lstrlen (wszProcessedSource); if (L'\\' == wszProcessedSource[len-1]) wszProcessedSource[len - 1] = L'\0'; }
if (NULL == _wfullpath (wszProcessedDest, wszExpandedPath, MAX_PATH)) { return TRUE; } else { //
// Eliminate any trailing slashes. Note: after going through
// _wfullpath, there can be at the most one trailing slash
//
len = lstrlen (wszProcessedDest); if (L'\\' == wszProcessedDest[len-1]) wszProcessedDest[len - 1] = L'\0'; }
// Now that we have the nice compact paths, we compare them.
if (0 != _wcsicmp (wszProcessedSource, wszProcessedDest)) return TRUE; }
return FALSE; }
//+--------------------------------------------------------------------------
//
// Member: Save
//
// Synopsis: saves settings back to the local settings
//
// Arguments: [in] pwszPath : the path to which the folder was redirected
// [in] dwFlags : the flags that were used for redirection
// [in] pSid : the Sid of the group that was used for
// redirection. If this is NULL, then we
// default to the Sid for everyone : S-1-1-0
//
// Returns: ERROR_SUCCESS if everything was successful or an error code.
//
// History: 11/20/1998 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::Save (const WCHAR * pwszPath, DWORD dwFlags, PSID pSid, const WCHAR * pszGPOName) { WCHAR * pwszSection = NULL; UNICODE_STRING StringW; WCHAR pwszFlags [ MAX_PATH ]; size_t len; int homedirInfoLen = 0; WCHAR * pszCurr; WCHAR pwszSid [ MaxSidString ]; DWORD Status; BOOL bStatus; ULONG ulUserNameInfoLen; const WCHAR * wszHomedir = NULL; BOOL bSaveHomedir = FALSE;
//
// First determine if the homedir value needs to be saved.
// We need to do this first so that we can add the necessary value to the
// buffer.
if (IsHomedirPath (m_rID, pwszPath, TRUE) || IsHomedirPolicyPath (m_rID, pwszPath, TRUE)) { wszHomedir = gUserInfo.GetHomeDir (Status); if (ERROR_SUCCESS != Status || ! wszHomedir || L'\0' == *wszHomedir) { Status = (ERROR_SUCCESS == Status) ? ERROR_BAD_PATHNAME : Status; goto SaveSettings_End; }
// If we are here, then we need to save the homedir value
bSaveHomedir = TRUE; homedirInfoLen = wcslen (wszHomedir) + 9; // 8 chars for Homedir=
// + 1 for the terminating NULL
}
//calculate the # of characters required to store UserName=<username>
//including the terminating NULL character.
ulUserNameInfoLen = wcslen (gwszUserName) + 10; //9 chars for Username=
//+ 1 terminating NULL
//don't need to calculate the exact length, this will be more than enough
// no double null termination required since new is 0 initing it.
len = wcslen(pwszPath) + homedirInfoLen + 2 * MAX_PATH + ulUserNameInfoLen + 50; pwszSection = new WCHAR[sizeof(WCHAR) * len];
if (NULL == pwszSection) { Status = ERROR_OUTOFMEMORY; goto SaveSettings_End; }
pwszFlags[0] = L'\0'; StringW.Length = 0; StringW.MaximumLength = sizeof (pwszFlags); StringW.Buffer = pwszFlags;
RtlIntegerToUnicodeString (dwFlags, 16, &StringW);
Status = HRESULT_CODE(StringCchCopy(pwszSection, len, L"Username=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } Status = HRESULT_CODE(StringCchCatEx(pwszSection, len, gwszUserName, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; }
pszCurr++;
// Save the homedir value only if the redirection destination is the homedir.
if (bSaveHomedir) { Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Homedir=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, wszHomedir, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } pszCurr++; }
Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Path=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; }
if (IsHomedirPolicyPath (m_rID, pwszPath, TRUE)) { Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, &pwszPath[2], &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } } else { Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszPath, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } }
pszCurr++;
Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Flags=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszFlags, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; }
pszCurr++;
Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"Group=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; }
//now we set the sid to everyone (that is the safest) if no sid has been
//specified or if we are unable to convert the supplied sid into a string.
Status = ERROR_INVALID_SID; //just some error code
if (pSid) { pwszSid [0] = L'\0'; StringW.Length = 0; StringW.MaximumLength = sizeof (pwszSid); StringW.Buffer = pwszSid;
Status = RtlConvertSidToUnicodeString (&StringW, pSid, FALSE); }
if (ERROR_SUCCESS != Status) { (void) StringCbCopy(pwszSid, sizeof(pwszSid), L"S-1-1-0"); //use the sid for everyone if we can't find anything else
}
Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pwszSid, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; }
pszCurr++;
//add the GPO if there is a valid one.
if (pszGPOName) { Status = HRESULT_CODE(StringCchCopy(pszCurr, len, L"GPO=")); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } Status = HRESULT_CODE(StringCchCatEx(pszCurr, len, pszGPOName, &pszCurr, &len, 0)); if ( Status != ERROR_SUCCESS ) { goto SaveSettings_End; } }
//before writing to the ini file, we must pre-create it in Unicode,
//otherwise, the WritePrivate* APIs will write the file in ANSI, which
//will break folder redirection in international/ML builds.
Status = PrecreateUnicodeIniFile (m_szSavedSettingsPath); if ((Status != ERROR_SUCCESS) && (Status != ERROR_ALREADY_EXISTS)) { goto SaveSettings_End; }
Status = ERROR_SUCCESS; //now we can go ahead and save the section
//first empty the section
bStatus = WritePrivateProfileSection ( g_szDisplayNames[(int) m_rID], NULL, m_szSavedSettingsPath ); //now write the actual section
if (bStatus) bStatus = WritePrivateProfileSection ( g_szDisplayNames[(int) m_rID], pwszSection, m_szSavedSettingsPath );
if (!bStatus) Status = GetLastError();
SaveSettings_End: if (pwszSection) delete [] pwszSection;
return Status; }
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::HandleUserNameChange
//
// Synopsis: this function handles any changes in the user name that
// have occurred since the last logon. in case the username has
// changed since the last logon, this function renames any
// folders redirected earlier using the %username% variable so
// that the path that the redirected folder points to is continues
// to be valid.
//
// Arguments: [in] pFileDB : point to the CFileDB object.
//
// Returns: ERROR_SUCCESS if everything worked properly
// an error code otherwise.
//
// History: 9/20/1999 RahulTh created
//
// Notes: a failed rename is not considered a failure. in this case,
// an event is logged and the code ensures that redirection
// is not attempted if there is a simultaneous change in the
// redirection policies.
//
// in this way a failed rename for one folder does not hold up
// the redirection of other folders which might be independent of
// this particular folder.
//---------------------------------------------------------------------------
DWORD CSavedSettings::HandleUserNameChange (CFileDB * pFileDB, CRedirectInfo * pRedir) { BOOL bStatus; DWORD Status = ERROR_SUCCESS; DWORD RedirStatus = ERROR_SUCCESS; WCHAR wszLastPath [TARGETPATHLIMIT]; WCHAR wszExpandedSource [TARGETPATHLIMIT]; WCHAR wszExpandedDest [TARGETPATHLIMIT]; WCHAR wszRenamePart [TARGETPATHLIMIT]; WCHAR wszExpandedRenameSource [TARGETPATHLIMIT]; WCHAR wszExpandedRenameDest [TARGETPATHLIMIT]; WCHAR * wszTemp = NULL; WCHAR * wszEnd = NULL; SHARESTATUS SourceStatus; SHARESTATUS DestStatus; BOOL bRenamePerformed = FALSE;
if ((! m_bUserNameChanged) || pRedir->WasRedirectionAttempted() || (REDIR_DONT_CARE & m_dwFlags)) { goto HandleUPNChangeEnd;//nothing to do if the username has not changed
//since the last logon or if the rename has
//already been attempted. Even if the attempted
//redirection had failed, it is not fatal.
//similarly, if policy didn't set the location
//of the folder last time, we don't care.
}
if (Programs == m_rID || //since programs and startup always follow the
Startup == m_rID //Start Menu, rename of Start Menu handles these
//folders automatically
) { goto HandleUPNChangeEnd; }
//show additional status messages if the verbose status is on.
DisplayStatusMessage (IDS_REDIR_CALLBACK); DebugMsg ((DM_VERBOSE, IDS_UPN_CHANGE, pRedir->GetLocalizedName(), m_szLastUserName, gwszUserName));
//okay, so there is a change in the user name and policy did care about the
//the location. check if last path contained the username. if not, we have
//nothing more to do.
if (TARGETPATHLIMIT <= wcslen (m_szLastRedirectedPath)) { gpEvents->Report ( EVENT_FDEPLOY_DESTPATH_TOO_LONG, 3, pRedir->GetLocalizedName(), m_szLastRedirectedPath, NumberToString ( TARGETPATHLIMIT ) ); pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL); goto HandleUPNChangeEnd; } (void) StringCchCopy(wszLastPath, TARGETPATHLIMIT, m_szLastRedirectedPath); _wcslwr (wszLastPath);
wszTemp = wcsstr (wszLastPath, L"%username%");
if (NULL == wszTemp) goto HandleUPNChangeEnd; //there is no %username% string, we are
//done. no rename is required.
//get the part that needs to be renamed.
(void) StringCchCopy(wszRenamePart, TARGETPATHLIMIT, wszLastPath); wszEnd = wcschr (wszRenamePart + (wszTemp - wszLastPath), L'\\'); if (wszEnd) *wszEnd = L'\0';
//get expanded versions of the paths -- using the new username and the old
//username
wszTemp = wszLastPath; RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, m_szLastUserName, wszExpandedSource); if (ERROR_SUCCESS == RedirStatus) { RedirStatus = ExpandPathSpecial (pFileDB, wszLastPath, gwszUserName, wszExpandedDest); } if (ERROR_SUCCESS == RedirStatus) { wszTemp = wszRenamePart; RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, m_szLastUserName, wszExpandedRenameSource); } if (ERROR_SUCCESS == RedirStatus) { RedirStatus = ExpandPathSpecial (pFileDB, wszRenamePart, gwszUserName, wszExpandedRenameDest); }
if (STATUS_BUFFER_TOO_SMALL == RedirStatus) { gpEvents->Report ( EVENT_FDEPLOY_DESTPATH_TOO_LONG, 3, pRedir->GetLocalizedName(), wszTemp, NumberToString ( TARGETPATHLIMIT ) ); pRedir->PreventRedirection (STATUS_BUFFER_TOO_SMALL); goto HandleUPNChangeEnd; } else if (ERROR_SUCCESS != RedirStatus) { gpEvents->Report ( EVENT_FDEPLOY_FOLDER_EXPAND_FAIL, 2, pRedir->GetLocalizedName(), StatusToString ( RedirStatus ) ); pRedir->PreventRedirection (RedirStatus); goto HandleUPNChangeEnd; }
//get the online/offline status of the shares.
SourceStatus = GetCSCStatus (wszExpandedRenameSource); DestStatus = GetCSCStatus (wszExpandedRenameDest);
if (ShareOffline == SourceStatus || ShareOffline == DestStatus) { gpEvents->Report ( EVENT_FDEPLOY_FOLDER_OFFLINE, 3, pRedir->GetLocalizedName(), wszExpandedSource, wszExpandedDest ); pRedir->PreventRedirection (ERROR_CSCSHARE_OFFLINE); goto HandleUPNChangeEnd; }
//we are finally ready to rename. first make sure that the source exists.
//sometimes an earlier rename operation might have already renamed this
//folder
RedirStatus = ERROR_SUCCESS; if (0xFFFFFFFF == GetFileAttributes(wszExpandedRenameSource)) { RedirStatus = GetLastError(); } if (ERROR_FILE_NOT_FOUND != RedirStatus) { bStatus = MoveFile (wszExpandedRenameSource, wszExpandedRenameDest); if (!bStatus) { RedirStatus = GetLastError(); gpEvents->Report ( EVENT_FDEPLOY_REDIRECT_FAIL, 4, pRedir->GetLocalizedName(), StatusToString (RedirStatus), wszExpandedSource, wszExpandedDest ); pRedir->PreventRedirection (RedirStatus); goto HandleUPNChangeEnd; } }
//the rename was successful. now rename the CSC cache.
if (ShareOnline == SourceStatus) { MoveDirInCSC (wszExpandedSource, wszExpandedDest, NULL, SourceStatus, DestStatus, TRUE, TRUE); DeleteCSCShareIfEmpty (wszExpandedSource, SourceStatus); } bRenamePerformed = TRUE;
HandleUPNChangeEnd: if (m_bUserNameChanged && ERROR_SUCCESS == pRedir->GetRedirStatus()) { UpdateUserNameInCache(); if (bRenamePerformed) { gpEvents->Report( EVENT_FDEPLOY_FOLDER_REDIRECT, 1, pRedir->GetLocalizedName()); } } return RedirStatus; }
//+--------------------------------------------------------------------------
//
// Member: CSavedSettings::UpdateUserNameInCache
//
// Synopsis: updates the user name in the cache with the new username
//
// Arguments: none.
//
// Returns: ERROR_SUCCESS : if successful.
// an error code otherwise.
//
// History: 9/20/1999 RahulTh created
//
// Notes:
//
//---------------------------------------------------------------------------
DWORD CSavedSettings::UpdateUserNameInCache (void) { BOOL bStatus;
bStatus = WritePrivateProfileString ( g_szDisplayNames[(int) m_rID], L"UserName", gwszUserName, m_szSavedSettingsPath );
if (!bStatus) return GetLastError();
return ERROR_SUCCESS; }
|