//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 2001 // // File: admin.cpp // // Authors; // Jeff Saathoff (jeffreys) // // Notes; // Support for Administratively pinned folders //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "strings.h" #include "registry.h" DWORD WINAPI _PinAdminFoldersThread(LPVOID); //************************************************************* // // ApplyAdminFolderPolicy // // Purpose: Pin the admin folder list // // Parameters: none // // Return: none // // Notes: // //************************************************************* void ApplyAdminFolderPolicy(void) { BOOL bNoNet = FALSE; CSCIsServerOffline(NULL, &bNoNet); if (!bNoNet) { SHCreateThread(_PinAdminFoldersThread, NULL, CTF_COINIT | CTF_FREELIBANDEXIT, NULL); } } // // Does a particular path exist in the DPA of path strings? // BOOL ExistsAPF( HDPA hdpa, LPCTSTR pszPath ) { const int cItems = DPA_GetPtrCount(hdpa); for (int i = 0; i < cItems; i++) { LPCTSTR pszItem = (LPCTSTR)DPA_GetPtr(hdpa, i); if (pszItem && (0 == lstrcmpi(pszItem, pszPath))) return TRUE; } return FALSE; } BOOL ReadAPFFromRegistry(HDPA hdpaFiles) { const HKEY rghkeyRoots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; for (int i = 0; i < ARRAYSIZE(rghkeyRoots); i++) { HKEY hKey; // Read in the Administratively pinned folder list. if (ERROR_SUCCESS == RegOpenKeyEx(rghkeyRoots[i], c_szRegKeyAPF, 0, KEY_QUERY_VALUE, &hKey)) { TCHAR szName[MAX_PATH]; DWORD dwIndex = 0, dwSize = ARRAYSIZE(szName); while (ERROR_SUCCESS == _RegEnumValueExp(hKey, dwIndex, szName, &dwSize, NULL, NULL, NULL, NULL)) { if (!ExistsAPF(hdpaFiles, szName)) { LPTSTR pszDup; if (LocalAllocString(&pszDup, szName)) { if (-1 == DPA_AppendPtr(hdpaFiles, pszDup)) { LocalFreeString(&pszDup); } } } dwSize = ARRAYSIZE(szName); dwIndex++; } RegCloseKey(hKey); } } return TRUE; } BOOL BuildFRList(HDPA hdpaFiles) { HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"), 0, KEY_QUERY_VALUE, &hKey)) { TCHAR szName[MAX_PATH]; DWORD cchName = ARRAYSIZE(szName); TCHAR szValue[MAX_PATH]; DWORD cbValue = sizeof(szValue); DWORD dwIndex = 0; while (ERROR_SUCCESS == RegEnumValue(hKey, dwIndex, szName, &cchName, NULL, NULL, (LPBYTE)szValue, &cbValue)) { LPTSTR pszUNC = NULL; GetRemotePath(szValue, &pszUNC); if (pszUNC) { if (-1 == DPA_AppendPtr(hdpaFiles, pszUNC)) { LocalFreeString(&pszUNC); } } cchName = ARRAYSIZE(szName); cbValue = sizeof(szValue); dwIndex++; } RegCloseKey(hKey); } return TRUE; } BOOL ReconcileAPF(HDPA hdpaPin, HDPA hdpaUnpin) { HKEY hKey; int cItems; int i; // // First, try to convert everything to UNC // cItems = DPA_GetPtrCount(hdpaPin); for (i = 0; i < cItems; i++) { LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaPin, i); if (!PathIsUNC(pszItem)) { LPTSTR pszUNC = NULL; GetRemotePath(pszItem, &pszUNC); if (pszUNC) { DPA_SetPtr(hdpaPin, i, pszUNC); LocalFree(pszItem); } } } // Read in the previous Administratively pinned folder list for this user. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, c_szRegKeyAPFResult, 0, KEY_QUERY_VALUE, &hKey)) { TCHAR szName[MAX_PATH]; DWORD dwIndex = 0, dwSize = ARRAYSIZE(szName); while (ERROR_SUCCESS == _RegEnumValueExp(hKey, dwIndex, szName, &dwSize, NULL, NULL, NULL, NULL)) { if (!ExistsAPF(hdpaPin, szName)) { LPTSTR pszDup = NULL; // This one is not in the new list, save it in the Unpin list if (LocalAllocString(&pszDup, szName)) { if (-1 == DPA_AppendPtr(hdpaUnpin, pszDup)) { LocalFreeString(&pszDup); } } } dwSize = ARRAYSIZE(szName); dwIndex++; } RegCloseKey(hKey); } // Save out the new admin pin list for this user if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, c_szRegKeyAPFResult, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL)) { // Add reg entries from the Pin list cItems = DPA_GetPtrCount(hdpaPin); for (i = 0; i < cItems; i++) { DWORD dwValue = 0; RegSetValueEx(hKey, (LPCTSTR)DPA_GetPtr(hdpaPin, i), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue)); } // Remove reg entries from the Unpin list cItems = DPA_GetPtrCount(hdpaUnpin); for (i = 0; i < cItems; i++) { RegDeleteValue(hKey, (LPCTSTR)DPA_GetPtr(hdpaUnpin, i)); } RegCloseKey(hKey); } return TRUE; } DWORD WINAPI _AdminFillCallback(LPCTSTR /*pszName*/, DWORD /*dwStatus*/, DWORD /*dwHintFlags*/, DWORD /*dwPinCount*/, LPWIN32_FIND_DATA /*pFind32*/, DWORD /*dwReason*/, DWORD /*dwParam1*/, DWORD /*dwParam2*/, DWORD_PTR /*dwContext*/) { if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0)) return CSCPROC_RETURN_ABORT; return CSCPROC_RETURN_CONTINUE; } void _DoAdminPin(LPCTSTR pszItem, LPWIN32_FIND_DATA pFind32) { DWORD dwHintFlags = 0; TraceEnter(TRACE_ADMINPIN, "_DoAdminPin"); if (!pszItem || !*pszItem) TraceLeaveVoid(); TraceAssert(PathIsUNC(pszItem)); // This may fail, for example if the file is not in the cache CSCQueryFileStatus(pszItem, NULL, NULL, &dwHintFlags); // Is the admin flag already turned on? if (!(dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN)) { // // Pin the item // if (CSCPinFile(pszItem, dwHintFlags | FLAG_CSC_HINT_PIN_ADMIN, NULL, NULL, &dwHintFlags)) { ShellChangeNotify(pszItem, pFind32, FALSE); } } // // Make sure files are filled. // // Yes, this takes longer, and isn't necessary if you stay logged // on, since the CSC agent fills everything in the background. // // However, JDP's are using this with laptop pools, and for // people who logon just to get the latest stuff, then immediately // disconnect their laptop and hit the road. They need to have // everything filled right away. // if (!pFind32 || !(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { CSCFillSparseFiles(pszItem, FALSE, _AdminFillCallback, 0); } Trace((TEXT("AdminPin %s"), pszItem)); TraceLeaveVoid(); } void _PinLinkTarget(LPCTSTR pszLink) { LPTSTR pszTarget = NULL; TraceEnter(TRACE_ADMINPIN, "_PinLinkTarget"); TraceAssert(pszLink); // We only want to pin a link target if it's a file (not a directory). // GetLinkTarget does this check and only returns files. GetLinkTarget(pszLink, &pszTarget, NULL); if (pszTarget) { WIN32_FIND_DATA fd = {0}; LPCTSTR pszT = PathFindFileName(pszTarget); fd.dwFileAttributes = 0; lstrcpyn(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName)); // Pin the target _DoAdminPin(pszTarget, &fd); LocalFree(pszTarget); } TraceLeaveVoid(); } // export this from shell32.dll BOOL PathIsShortcut(LPCTSTR pszItem, DWORD dwAttributes) { BOOL bIsShortcut = FALSE; SHFILEINFO sfi; sfi.dwAttributes = SFGAO_LINK; if (SHGetFileInfo(pszItem, dwAttributes, &sfi, sizeof(sfi), SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED | SHGFI_USEFILEATTRIBUTES)) { bIsShortcut = (sfi.dwAttributes & SFGAO_LINK); } return bIsShortcut; } DWORD WINAPI _PinAdminFolderCallback(LPCTSTR pszItem, ENUM_REASON eReason, LPWIN32_FIND_DATA pFind32, LPARAM /*lpContext*/) { TraceEnter(TRACE_ADMINPIN, "_PinAdminFolderCallback"); TraceAssert(pszItem); if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0)) TraceLeaveValue(CSCPROC_RETURN_ABORT); if (!pszItem || !*pszItem) TraceLeaveValue(CSCPROC_RETURN_SKIP); if (eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN) { // If it's a link, pin the target if (PathIsShortcut(pszItem, pFind32 ? pFind32->dwFileAttributes : 0)) _PinLinkTarget(pszItem); // Pin the item if (PathIsUNC(pszItem)) _DoAdminPin(pszItem, pFind32); } TraceLeaveValue(CSCPROC_RETURN_CONTINUE); } void _UnpinLinkTarget(LPCTSTR pszLink) { LPTSTR pszTarget = NULL; TraceEnter(TRACE_ADMINPIN, "_UnpinLinkTarget"); TraceAssert(pszLink); // We only want to unpin a link target if it's a file (not a directory). // GetLinkTarget does this check and only returns files. GetLinkTarget(pszLink, &pszTarget, NULL); if (pszTarget) { DWORD dwStatus = 0; DWORD dwPinCount = 0; DWORD dwHintFlags = 0; if (CSCQueryFileStatus(pszTarget, &dwStatus, &dwPinCount, &dwHintFlags) && (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN)) { // Unpin the target CSCUnpinFile(pszTarget, FLAG_CSC_HINT_PIN_ADMIN, &dwStatus, &dwPinCount, &dwHintFlags); if (0 == dwPinCount && 0 == dwHintFlags && !(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY)) { WIN32_FIND_DATA fd = {0}; LPCTSTR pszT = PathFindFileName(pszTarget); fd.dwFileAttributes = 0; lstrcpyn(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName)); CscDelete(pszTarget); ShellChangeNotify(pszTarget, &fd, FALSE); } } LocalFree(pszTarget); } TraceLeaveVoid(); } DWORD WINAPI _UnpinAdminFolderCallback(LPCTSTR pszItem, ENUM_REASON eReason, DWORD dwStatus, DWORD dwHintFlags, DWORD dwPinCount, LPWIN32_FIND_DATA pFind32, LPARAM /*dwContext*/) { BOOL bDeleteItem = FALSE; TraceEnter(TRACE_ADMINPIN, "_UnpinAdminFolderCallback"); if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0)) TraceLeaveValue(CSCPROC_RETURN_ABORT); if (!pszItem || !*pszItem) TraceLeaveValue(CSCPROC_RETURN_SKIP); TraceAssert(PathIsUNC(pszItem)); if (eReason == ENUM_REASON_FILE) { if (PathIsShortcut(pszItem, pFind32 ? pFind32->dwFileAttributes : 0)) { _UnpinLinkTarget(pszItem); } } if ((eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN) && (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN)) { // Unpin the item CSCUnpinFile(pszItem, FLAG_CSC_HINT_PIN_ADMIN, &dwStatus, &dwPinCount, &dwHintFlags); // // If it's a file, delete it below on this pass // bDeleteItem = (ENUM_REASON_FILE == eReason); Trace((TEXT("AdminUnpin %s"), pszItem)); } else if (ENUM_REASON_FOLDER_END == eReason) { // // Delete any unused folders in the post-order part of the traversal. // // Note that dwPinCount and dwHintFlags are always 0 in the // post-order part of the traversal, so fetch them here. // bDeleteItem = CSCQueryFileStatus(pszItem, &dwStatus, &dwPinCount, &dwHintFlags); } // // Delete items that are no longer pinned and have no offline changes // if (bDeleteItem && 0 == dwPinCount && 0 == dwHintFlags && !(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY)) { CscDelete(pszItem); ShellChangeNotify(pszItem, pFind32, FALSE); } TraceLeaveValue(CSCPROC_RETURN_CONTINUE); } // // Determines if a path is a "special" file pinned by the folder // redirection code. // BOOL _IsSpecialRedirectedFile( LPCTSTR pszPath, HDPA hdpaFRList ) { TraceAssert(NULL != pszPath); TraceAssert(!IsBadStringPtr(pszPath, MAX_PATH)); if (hdpaFRList) { const int cchPath = lstrlen(pszPath); int i; for (i = 0; i < DPA_GetPtrCount(hdpaFRList); i++) { LPCTSTR pszThis = (LPCTSTR)DPA_GetPtr(hdpaFRList, i); int cchThis = lstrlen(pszThis); if (cchPath >= cchThis) { // // Path being examined is the same length or longer than // current path from the table. Possible match. // if (0 == StrCmpNI(pszPath, pszThis, cchThis)) { // // Path being examined is either the same as, // or a child of, the current path from the table. // if (TEXT('\0') == *(pszPath + cchThis)) { // // Path is same as this path from the table. // return TRUE; } else if (0 == lstrcmpi(pszPath + cchThis + 1, L"desktop.ini")) { // // Path is for a desktop.ini file that exists in the // root of one of our special folders. // return TRUE; } } } } } return FALSE; } DWORD WINAPI _ResetPinCountsCallback(LPCTSTR pszItem, ENUM_REASON eReason, DWORD dwStatus, DWORD dwHintFlags, DWORD dwPinCount, LPWIN32_FIND_DATA /*pFind32*/, LPARAM dwContext) { TraceEnter(TRACE_ADMINPIN, "_ResetPinCountsCallback"); if (WAIT_OBJECT_0 == WaitForSingleObject(g_heventTerminate, 0)) TraceLeaveValue(CSCPROC_RETURN_ABORT); if (!pszItem || !*pszItem) TraceLeaveValue(CSCPROC_RETURN_SKIP); TraceAssert(PathIsUNC(pszItem)); if (eReason == ENUM_REASON_FILE || eReason == ENUM_REASON_FOLDER_BEGIN) { DWORD dwCurrentPinCount = dwPinCount; DWORD dwDesiredPinCount = _IsSpecialRedirectedFile(pszItem, (HDPA)dwContext) ? 1 : 0; while (dwCurrentPinCount-- > dwDesiredPinCount) { CSCUnpinFile(pszItem, FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT, &dwStatus, &dwPinCount, &dwHintFlags); } } TraceLeaveValue(CSCPROC_RETURN_CONTINUE); } int CALLBACK _LocalFreeCallback(LPVOID p, LPVOID) { // OK to pass NULL to LocalFree LocalFree(p); return 1; } DWORD WINAPI _PinAdminFoldersThread(LPVOID) { TraceEnter(TRACE_ADMINPIN, "_PinAdminFoldersThread"); TraceAssert(IsCSCEnabled()); HANDLE rghSyncObj[] = { g_heventTerminate, g_hmutexAdminPin }; UINT wmAdminPin = RegisterWindowMessage(c_szAPFMessage); // // Wait until we either own the "admin pin" mutex OR the // "terminate" event is set. // TraceMsg("Waiting for 'admin-pin' mutex or 'terminate' event..."); DWORD dwWait = WaitForMultipleObjects(ARRAYSIZE(rghSyncObj), rghSyncObj, FALSE, INFINITE); if (1 == (dwWait - WAIT_OBJECT_0)) { HKEY hkCSC = NULL; FILETIME ft = {0}; RegCreateKeyEx(HKEY_CURRENT_USER, c_szCSCKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkCSC, NULL); if (hkCSC) { GetSystemTimeAsFileTime(&ft); RegSetValueEx(hkCSC, c_szAPFStart, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft)); RegDeleteValue(hkCSC, c_szAPFEnd); } if (wmAdminPin) SendNotifyMessage(HWND_BROADCAST, wmAdminPin, 0, 0); TraceMsg("Thread now owns 'admin-pin' mutex."); // // We own the "admin pin" mutex. OK to perform admin pin. // SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); // // Get the Admin Folders list from the registry // HDPA hdpaFiles = DPA_Create(10); HDPA hdpaUnpin = DPA_Create(4); if (NULL != hdpaFiles && NULL != hdpaUnpin) { DWORD dwResult = CSCPROC_RETURN_CONTINUE; int cFiles; int i; // // NTRAID#NTBUG9-376185-2001/04/24-jeffreys // NTRAID#NTBUG9-379736-2001/04/24-jeffreys // // Unless directed by policy, pin all redirected special folders. // if (!CConfig::GetSingleton().NoAdminPinSpecialFolders()) { BuildFRList(hdpaFiles); } ReadAPFFromRegistry(hdpaFiles); ReconcileAPF(hdpaFiles, hdpaUnpin); // // Iterate through the unpin list and unpin the items // // cFiles = DPA_GetPtrCount(hdpaUnpin); for (i = 0; i < cFiles; i++) { LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaUnpin, i); DWORD dwStatus = 0; DWORD dwPinCount = 0; DWORD dwHintFlags = 0; // If this fails, then it's not cached and there's nothing to do if (CSCPROC_RETURN_CONTINUE == dwResult && CSCQueryFileStatus(pszItem, &dwStatus, &dwPinCount, &dwHintFlags)) { // Unpin this item dwResult = _UnpinAdminFolderCallback(pszItem, ENUM_REASON_FILE, dwStatus, dwHintFlags, dwPinCount, NULL, 0); if (CSCPROC_RETURN_CONTINUE == dwResult && PathIsUNC(pszItem)) { // Unpin everything under this folder (if it's a folder) dwResult = _CSCEnumDatabase(pszItem, TRUE, _UnpinAdminFolderCallback, 0); // Delete this item if it's no longer used (won't cause any // harm if it's not a folder). _UnpinAdminFolderCallback(pszItem, ENUM_REASON_FOLDER_END, 0, 0, 0, NULL, 0); } } if (CSCPROC_RETURN_ABORT == dwResult) { // We failed to clean this one up completely, so remember it for next time SHSetValue(HKEY_CURRENT_USER, c_szRegKeyAPFResult, pszItem, REG_DWORD, &dwResult, sizeof(dwResult)); } } // // Iterate through the list and pin the items // cFiles = DPA_GetPtrCount(hdpaFiles); for (i = 0; i < cFiles && CSCPROC_RETURN_CONTINUE == dwResult; i++) { LPTSTR pszItem = (LPTSTR)DPA_GetPtr(hdpaFiles, i); // Pin this item dwResult = _PinAdminFolderCallback(pszItem, ENUM_REASON_FILE, NULL, 0); // Pin everything under this folder (if it's a folder) if (CSCPROC_RETURN_CONTINUE == dwResult && PathIsUNC(pszItem)) { dwResult = _Win32EnumFolder(pszItem, TRUE, _PinAdminFolderCallback, 0); } } } if (NULL != hdpaFiles) { DPA_DestroyCallback(hdpaFiles, _LocalFreeCallback, 0); } if (NULL != hdpaUnpin) { DPA_DestroyCallback(hdpaUnpin, _LocalFreeCallback, 0); } // // Reduce pin counts on everything since we don't use them anymore. // This is a one time (per user) cleanup. // DWORD dwCleanupDone = 0; DWORD dwSize = sizeof(dwCleanupDone); if (hkCSC) { RegQueryValueEx(hkCSC, c_szPinCountsReset, 0, NULL, (LPBYTE)&dwCleanupDone, &dwSize); } if (0 == dwCleanupDone) { HDPA hdpaFRList = DPA_Create(4); if (hdpaFRList) { BuildFRList(hdpaFRList); } TraceMsg("Doing pin count cleanup."); if (CSCPROC_RETURN_ABORT != _CSCEnumDatabase(NULL, TRUE, _ResetPinCountsCallback, (LPARAM)hdpaFRList) && hkCSC) { dwCleanupDone = 1; RegSetValueEx(hkCSC, c_szPinCountsReset, 0, REG_DWORD, (LPBYTE)&dwCleanupDone, sizeof(dwCleanupDone)); } if (hdpaFRList) { DPA_DestroyCallback(hdpaFRList, _LocalFreeCallback, 0); } } if (hkCSC) { GetSystemTimeAsFileTime(&ft); RegSetValueEx(hkCSC, c_szAPFEnd, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft)); RegCloseKey(hkCSC); } if (wmAdminPin) SendNotifyMessage(HWND_BROADCAST, wmAdminPin, 1, 0); TraceMsg("Thread releasing 'admin-pin' mutex."); ReleaseMutex(g_hmutexAdminPin); } TraceMsg("_PinAdminFoldersThread exiting"); TraceLeaveValue(0); }