Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

407 lines
15 KiB

  1. //***********************************************************************************
  2. //
  3. // Copyright (c) 2002 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: EnsureACLs.cpp
  6. // Module: util.lib
  7. //
  8. //***********************************************************************************
  9. #pragma once
  10. #ifndef _WIN32_WINNT
  11. #define _WIN32_WINNT 0x0500 // Win2000 and later
  12. #endif
  13. #include <windows.h>
  14. #include <tchar.h>
  15. #include <safefunc.h>
  16. #include <shlobj.h>
  17. #include <sddl.h>
  18. #include <Aclapi.h>
  19. #include <fileutil.h>
  20. #include <logging.h>
  21. #include <wusafefn.h>
  22. #include <mistsafe.h>
  23. #if defined(UNICODE) || defined (_UNICODE)
  24. typedef DWORD (*TREERESETSECURITY)(
  25. LPTSTR pObjectName,
  26. SE_OBJECT_TYPE ObjectType,
  27. SECURITY_INFORMATION SecurityInfo,
  28. PSID pOwner OPTIONAL,
  29. PSID pGroup OPTIONAL,
  30. PACL pDacl OPTIONAL,
  31. PACL pSacl OPTIONAL,
  32. BOOL KeepExplicit,
  33. FN_PROGRESS fnProgress OPTIONAL,
  34. PROG_INVOKE_SETTING ProgressInvokeSetting,
  35. PVOID Args OPTIONAL);
  36. //Function to enable or disable a particular privelege for the current process
  37. //Last parameter is optional, will return the previous state of the privelege
  38. DWORD EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable, BOOL *pfWasEnabled);
  39. /******************************************************************************
  40. //Function to recursively set ACLS on the specified folder.
  41. //Currently we set the following ACL's
  42. // * Allow SYSTEM full control
  43. // * Allow Admins full control
  44. // * Allow Owners full control
  45. // * Allow Power Users R/W/X control
  46. ******************************************************************************/
  47. HRESULT SetDirPermissions(LPCTSTR lpszDir);
  48. #endif
  49. //Rename the 'WindowsUpdate' file to 'WindowsUpdate.TickCount'; if rename fails we try to delete it
  50. //Note that we wont revert the ownerhip of the file
  51. BOOL RenameWUFile(LPCTSTR lpszFilePath);
  52. /*****************************************************************************
  53. //Function to set ACL's on Windows Update directories, optionally creates the
  54. //directory if it doesnt already exists
  55. //This function will:
  56. // * Take ownership of the directory and it's children
  57. // * Set all the children to inherit ACL's from parent
  58. // * Set the specified directory to NOT inherit properties from it's parent
  59. // * Set the required ACL's on the specified directory
  60. // * Replace the ACL's on the children (i.e. propogate own ACL's and remove
  61. // those ACL's which were explicitly set
  62. //
  63. // Input:
  64. // lpszDirectory: Path to the directory to ACL, If it is NULL we use the
  65. path to the WindowsUpdate directory
  66. fCreateAlways: Flag to indicate creation of new directory if it doesnt
  67. already exist
  68. ******************************************************************************/
  69. HRESULT CreateDirectoryAndSetACLs(LPCTSTR lpszDirectory, BOOL fCreateAlways)
  70. {
  71. LOG_Block("CreateDirectoryAndSetACLs");
  72. BOOL fIsDirectory = FALSE;
  73. LPTSTR lpszWUDirPath = NULL;
  74. LPTSTR lpszDirPath = NULL;
  75. #if defined(UNICODE) || defined (_UNICODE)
  76. BOOL fChangedPriv = FALSE;
  77. BOOL fPrevPrivEnabled = FALSE;
  78. #endif
  79. HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE);
  80. if(NULL == lpszDirectory && !fCreateAlways)
  81. {
  82. hr = E_INVALIDARG;
  83. goto done;
  84. }
  85. //Use WU directory if input parameter is NULL
  86. if(NULL == lpszDirectory)
  87. {
  88. lpszWUDirPath = (LPTSTR)malloc(sizeof(TCHAR)*(MAX_PATH+1));
  89. if(NULL == lpszWUDirPath)
  90. {
  91. hr = E_OUTOFMEMORY;
  92. goto done;
  93. }
  94. //Get the path to the Windows Update directory
  95. if(!GetWUDirectory(lpszWUDirPath, MAX_PATH+1))
  96. {
  97. goto done;
  98. }
  99. lpszDirPath = lpszWUDirPath;
  100. }
  101. // else use the passed in parameter
  102. else
  103. {
  104. lpszDirPath = (LPTSTR)lpszDirectory;
  105. }
  106. //if dir (or file) does not exist
  107. if (!fFileExists(lpszDirPath, &fIsDirectory))
  108. {
  109. if(!fCreateAlways) //no need to create a new one
  110. {
  111. goto done;
  112. }
  113. if(!(fIsDirectory = CreateNestedDirectory(lpszDirPath)))
  114. {
  115. goto done;
  116. }
  117. }
  118. //Since these apis are only available for win2k and above, dont compile for ansii (we dont care about NT4)
  119. #if defined(UNICODE) || defined (_UNICODE)
  120. //Enable privelege to 'take ownership' , we will continue even if we failed for some reason
  121. fChangedPriv = (ERROR_SUCCESS == EnablePrivilege(SE_TAKE_OWNERSHIP_NAME, TRUE, &fPrevPrivEnabled));
  122. //Take ownership and apply correct ACL's, we dont care if we fail
  123. SetDirPermissions(lpszDirPath);
  124. #endif
  125. //Check for a file name-squatting on the specified directory
  126. if (!fIsDirectory)
  127. {
  128. if( !RenameWUFile(lpszDirPath) || //Rename or delete the existing file
  129. !CreateNestedDirectory(lpszDirPath)) //Create a new directory
  130. {
  131. goto done;
  132. }
  133. #if defined(UNICODE) || defined (_UNICODE)
  134. //Take ownership and apply correct ACL's, we dont care if we fail
  135. SetDirPermissions(lpszDirPath);
  136. #endif
  137. }
  138. hr = S_OK;
  139. done:
  140. #if defined(UNICODE) || defined (_UNICODE)
  141. //Restore previous privelege
  142. if(fChangedPriv)
  143. {
  144. EnablePrivilege(SE_TAKE_OWNERSHIP_NAME, fPrevPrivEnabled, NULL);
  145. }
  146. #endif
  147. SafeFreeNULL(lpszWUDirPath);
  148. return hr;
  149. }
  150. /********************************************************************************
  151. //Get the path to the WindowsUpdate Directory (without the backslash at the end)
  152. *********************************************************************************/
  153. BOOL GetWUDirectory(LPTSTR lpszDirPath, DWORD chCount, BOOL fGetV4Path)
  154. {
  155. LOG_Block("GetWUDirectory");
  156. const TCHAR szWUDir[] = _T("\\WindowsUpdate");
  157. const TCHAR szV4[] = _T("\\V4");
  158. BOOL fRet = FALSE;
  159. if(NULL == lpszDirPath)
  160. {
  161. return FALSE;
  162. }
  163. //Get the path to the Program Files directory
  164. if (S_OK != SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, 0, lpszDirPath))
  165. {
  166. goto done;
  167. }
  168. //Append the WU Directory
  169. if (FAILED(StringCchCatEx(lpszDirPath, chCount, szWUDir, NULL, NULL, MISTSAFE_STRING_FLAGS)))
  170. {
  171. goto done;
  172. }
  173. if(fGetV4Path && FAILED(StringCchCatEx(lpszDirPath, chCount, szV4, NULL, NULL, MISTSAFE_STRING_FLAGS)))
  174. {
  175. goto done;
  176. }
  177. fRet = TRUE;
  178. done:
  179. return fRet;
  180. }
  181. #if defined(UNICODE) || defined (_UNICODE)
  182. /********************************************************************************
  183. //Function to enable or disable a particular privelege
  184. //Last parameter is optional, will return the previous state of the privelege
  185. ********************************************************************************/
  186. DWORD EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable, BOOL *pfWasEnabled)
  187. {
  188. LOG_Block("EnablePrivilege");
  189. DWORD dwError = ERROR_SUCCESS;
  190. HANDLE hToken = 0;
  191. DWORD dwSize = 0;
  192. TOKEN_PRIVILEGES privNew;
  193. TOKEN_PRIVILEGES privOld;
  194. if(!OpenProcessToken(
  195. GetCurrentProcess(),
  196. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  197. &hToken))
  198. {
  199. dwError = GetLastError();
  200. goto Cleanup;
  201. }
  202. if(!LookupPrivilegeValue(
  203. 0,
  204. pszPrivName,
  205. &privNew.Privileges[0].Luid))
  206. {
  207. dwError = GetLastError();
  208. goto Cleanup;
  209. }
  210. privNew.PrivilegeCount = 1;
  211. privNew.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
  212. AdjustTokenPrivileges(
  213. hToken,
  214. FALSE,
  215. &privNew,
  216. sizeof(privOld),
  217. &privOld,
  218. &dwSize);
  219. //Always call GetLastError, even when we succeed (as per msdn)
  220. dwError = GetLastError();
  221. if(dwError != ERROR_SUCCESS)
  222. {
  223. goto Cleanup;
  224. }
  225. if (pfWasEnabled)
  226. {
  227. *pfWasEnabled = (privOld.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? TRUE : FALSE;
  228. }
  229. Cleanup:
  230. SafeCloseHandle(hToken);
  231. return dwError;
  232. }
  233. /********************************************************************************
  234. //Apply appropriate ACL's to the specified directory
  235. ********************************************************************************/
  236. HRESULT SetDirPermissions(LPCTSTR lpszDir)
  237. {
  238. LOG_Block("SetDirPermissions");
  239. DWORD dwErr = ERROR_SUCCESS;
  240. PSECURITY_DESCRIPTOR pAdminSD = NULL;
  241. PSECURITY_DESCRIPTOR pSD = NULL;
  242. PACL pDacl = NULL;
  243. PSID pOwner = NULL;
  244. BOOL fIsDefault = FALSE;
  245. HMODULE hModule = NULL;
  246. TREERESETSECURITY pfnTreeResetSec = NULL;
  247. //Admin Security Descriptor String
  248. LPCTSTR pszAdminSD = _T("O:BAG:BAD:(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)");
  249. //Security Descriptor String with correct ACLs for the WindowsUpdate Directory
  250. LPCTSTR pszSD = _T("D:") // DACL
  251. _T("(A;OICI;GA;;;SY)") // Allow SYSTEM full control
  252. _T("(A;OICI;GA;;;BA)") // Allow Admins full control
  253. _T("(A;OICI;GA;;;CO)") // Allow Owners full control
  254. _T("(A;OICI;GRGWGX;;;PU)"); // Allow Power Users R/W/X control
  255. if(NULL == lpszDir)
  256. {
  257. return E_INVALIDARG;
  258. }
  259. //Create an admin SD from admin SD string
  260. if(!ConvertStringSecurityDescriptorToSecurityDescriptor(pszAdminSD, SDDL_REVISION_1, &pAdminSD, NULL))
  261. {
  262. dwErr = GetLastError();
  263. goto done;
  264. }
  265. //Get the owner SID from the Admin SD
  266. if(!GetSecurityDescriptorOwner(pAdminSD, &pOwner, &fIsDefault))
  267. {
  268. dwErr = GetLastError();
  269. goto done;
  270. }
  271. //Generate the Security Descriptor from the SD String with custom ACL's
  272. if(!ConvertStringSecurityDescriptorToSecurityDescriptor(pszSD, SDDL_REVISION_1, &pSD, NULL))
  273. {
  274. dwErr = GetLastError();
  275. goto done;
  276. }
  277. //Exctract the DACL from the Security Descriptor
  278. BOOL fIsDaclPresent = FALSE;
  279. if(!GetSecurityDescriptorDacl(
  280. pSD, // SD
  281. &fIsDaclPresent, // DACL presence
  282. &pDacl, // ACL
  283. &fIsDefault)) // default DACL
  284. {
  285. dwErr = GetLastError();
  286. goto done;
  287. }
  288. //If for some reason no DACL was present, we have an invalid SD
  289. if(!fIsDaclPresent)
  290. {
  291. dwErr = ERROR_INVALID_SECURITY_DESCR;
  292. goto done;
  293. }
  294. //Load Advapi32.dll
  295. if ((NULL == (hModule = LoadLibraryFromSystemDir(_T("advapi32.dll")))) ||
  296. (NULL == (pfnTreeResetSec = (TREERESETSECURITY)::GetProcAddress(hModule, "TreeResetNamedSecurityInfo"))))
  297. {
  298. if(ERROR_SUCCESS != (dwErr = SetNamedSecurityInfo(
  299. (LPTSTR)lpszDir,
  300. SE_FILE_OBJECT,
  301. DACL_SECURITY_INFORMATION |
  302. PROTECTED_DACL_SECURITY_INFORMATION |
  303. OWNER_SECURITY_INFORMATION,
  304. pOwner,
  305. NULL,
  306. pDacl,
  307. NULL)))
  308. {
  309. goto done;
  310. }
  311. }
  312. else
  313. {
  314. //Recursively apply the ownership and the ACL's on the tree
  315. if(ERROR_SUCCESS != (dwErr = pfnTreeResetSec(
  316. (LPTSTR)lpszDir, //Directory
  317. SE_FILE_OBJECT, //object type
  318. DACL_SECURITY_INFORMATION | //Set DACL
  319. PROTECTED_DACL_SECURITY_INFORMATION | //Do not inherit
  320. OWNER_SECURITY_INFORMATION, //Set owner
  321. pOwner, //Owner SID
  322. NULL, //pGroup - null
  323. pDacl, //Dacl to set
  324. NULL, //pSacl - null
  325. FALSE, //Retain explicitly added ACL's to children
  326. NULL, //Callback function --- we dont need one
  327. ProgressInvokeNever, //Since we dont have a callback
  328. NULL))) //Other args
  329. {
  330. goto done;
  331. }
  332. }
  333. done:
  334. if ( NULL != hModule )
  335. {
  336. FreeLibrary(hModule);
  337. }
  338. SafeLocalFree(pSD);
  339. SafeLocalFree(pAdminSD);
  340. return HRESULT_FROM_WIN32(dwErr);
  341. }
  342. #endif
  343. /*************************************************************************************************
  344. //Rename the 'WindowsUpdate' file to 'WindowsUpdate.TickCount'; if rename fails we try to delete it
  345. //Note that we wont revert the ownerhip of the file
  346. **************************************************************************************************/
  347. BOOL RenameWUFile(LPCTSTR lpszFilePath)
  348. {
  349. LOG_Block("RenameWUFile");
  350. TCHAR szNewFilePath[MAX_PATH+1];
  351. DWORD dwTickCount = GetTickCount();
  352. LPTSTR szFormat = _T("%s.%lu");
  353. //Generate path to new file, should never fail
  354. if(SUCCEEDED(StringCchPrintfEx(szNewFilePath, ARRAYSIZE(szNewFilePath), NULL, NULL, MISTSAFE_STRING_FLAGS, szFormat, lpszFilePath, dwTickCount)) &&
  355. MoveFile(lpszFilePath, szNewFilePath) ||
  356. DeleteFile(lpszFilePath))
  357. {
  358. return TRUE;
  359. }
  360. return FALSE;
  361. }