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.

596 lines
20 KiB

  1. /*++
  2. File Description:
  3. This file contains the utility function used
  4. to go scan drives and examine the related
  5. ACLs.
  6. Author:
  7. Matt Holle (matth) Feb 1998
  8. --*/
  9. //
  10. // System header files
  11. //
  12. #include <nt.h>
  13. //
  14. // Disable the DbgPrint for non-debug builds
  15. //
  16. #ifndef DBG
  17. #define _DBGNT_
  18. #endif
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <ntverp.h>
  22. #include <wtypes.h>
  23. //
  24. // CRT header files
  25. //
  26. #include <stdlib.h>
  27. //
  28. // Private header files
  29. //
  30. #include "setupcl.h"
  31. NTSTATUS
  32. DeleteUsnJournal(
  33. PWSTR DrivePath
  34. );
  35. NTSTATUS
  36. ResetACLs(
  37. IN WCHAR *DirName,
  38. ULONG indent
  39. );
  40. NTSTATUS
  41. EnumerateDrives(
  42. VOID
  43. )
  44. /*++
  45. ===============================================================================
  46. Routine Description:
  47. This function will enumerate all drives on the machine. We're looking
  48. for NTFS volumes. For each that we find, we'll go scan the drive and
  49. poke each directory and file for its ACLs.
  50. Arguments:
  51. Return Value:
  52. Status is returned.
  53. ===============================================================================
  54. --*/
  55. {
  56. NTSTATUS Status = STATUS_SUCCESS;
  57. OBJECT_ATTRIBUTES ObjectAttributes;
  58. HANDLE DosDevicesDir;
  59. CHAR DirInfoBuffer[2048],
  60. LinkTargetBuffer[2048];
  61. UNICODE_STRING UnicodeString,
  62. LinkTarget,
  63. DesiredPrefix1,
  64. DesiredPrefix2,
  65. LinkTypeName;
  66. POBJECT_DIRECTORY_INFORMATION DirInfo;
  67. ULONG Context,
  68. Length;
  69. HANDLE Handle;
  70. BOOLEAN b;
  71. //
  72. // Open \DosDevices
  73. //
  74. RtlInitUnicodeString(&UnicodeString,L"\\DosDevices");
  75. InitializeObjectAttributes(
  76. &ObjectAttributes,
  77. &UnicodeString,
  78. OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
  79. NULL,
  80. NULL
  81. );
  82. Status = NtOpenDirectoryObject(&DosDevicesDir,DIRECTORY_QUERY,&ObjectAttributes);
  83. TEST_STATUS_RETURN( "SETUPCL: EnumerateDrives - Failed to open DosDevices." );
  84. LinkTarget.Buffer = (PVOID)LinkTargetBuffer;
  85. RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink");
  86. RtlInitUnicodeString(&DesiredPrefix1,L"\\Device\\Harddisk");
  87. RtlInitUnicodeString(&DesiredPrefix2,L"\\Device\\Volume");
  88. DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer;
  89. b = TRUE;
  90. //
  91. // Query first object in \DosDevices directory
  92. //
  93. Status = NtQueryDirectoryObject( DosDevicesDir,
  94. DirInfo,
  95. sizeof(DirInfoBuffer),
  96. TRUE,
  97. TRUE,
  98. &Context,
  99. &Length );
  100. while(NT_SUCCESS(Status)) {
  101. //
  102. // Terminate these guys just in case...
  103. //
  104. DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
  105. DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
  106. DbgPrint( "SETUPCL: EnumerateDrives - About to examine an object: %ws\n", DirInfo->Name.Buffer );
  107. //
  108. // Make sure he's a symbolic link.
  109. //
  110. // It's possible for two objects to link to the same device. To
  111. // preclude following duplicate links, disallow any objects except
  112. // those that are a drive letter. Crude but effective...
  113. //
  114. if( (DirInfo->Name.Buffer[1] == L':') &&
  115. (RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) ) {
  116. DbgPrint( "\tSETUPCL: EnumerateDrives - Object: %ws is a symbolic link\n", DirInfo->Name.Buffer );
  117. InitializeObjectAttributes(
  118. &ObjectAttributes,
  119. &DirInfo->Name,
  120. OBJ_CASE_INSENSITIVE,
  121. DosDevicesDir,
  122. NULL
  123. );
  124. Status = NtOpenSymbolicLinkObject( &Handle,
  125. SYMBOLIC_LINK_ALL_ACCESS,
  126. &ObjectAttributes );
  127. if(NT_SUCCESS(Status)) {
  128. LinkTarget.Length = 0;
  129. LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
  130. Status = NtQuerySymbolicLinkObject( Handle,
  131. &LinkTarget,
  132. NULL );
  133. LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
  134. DbgPrint( "\tSETUPCL: EnumerateDrives - We queried him and his name is %ws.\n", LinkTarget.Buffer );
  135. NtClose(Handle);
  136. if( NT_SUCCESS(Status) &&
  137. ( RtlPrefixUnicodeString(&DesiredPrefix1,&LinkTarget,TRUE) ||
  138. RtlPrefixUnicodeString(&DesiredPrefix2,&LinkTarget,TRUE) ) ) {
  139. IO_STATUS_BLOCK IoStatusBlock;
  140. UCHAR buffer[4096];
  141. PFILE_FS_ATTRIBUTE_INFORMATION Info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer;
  142. OBJECT_ATTRIBUTES Obja;
  143. //
  144. // OK, this is a symbolic link to a hard drive.
  145. // Make sure it's 0-terminated.
  146. //
  147. LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
  148. DbgPrint( "\tSETUPCL: EnumerateDrives - He's a drive.\n" );
  149. //
  150. // Is he an NTFS drive? Open him and see.
  151. //
  152. InitializeObjectAttributes( &Obja,
  153. &LinkTarget,
  154. OBJ_CASE_INSENSITIVE,
  155. NULL,
  156. NULL );
  157. Status = NtOpenFile( &Handle,
  158. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  159. &Obja,
  160. &IoStatusBlock,
  161. FILE_SHARE_READ | FILE_SHARE_WRITE,
  162. FILE_SYNCHRONOUS_IO_ALERT);
  163. if( NT_SUCCESS(Status) ) {
  164. Status = NtQueryVolumeInformationFile( Handle,
  165. &IoStatusBlock,
  166. buffer,
  167. sizeof(buffer),
  168. FileFsAttributeInformation );
  169. if( NT_SUCCESS(Status) ) {
  170. Info->FileSystemName[Info->FileSystemNameLength/sizeof(WCHAR)] = 0;
  171. DbgPrint( "\tSETUPCL: EnumerateDrives - His file system is: %ws\n", Info->FileSystemName );
  172. if( !_wcsicmp(Info->FileSystemName,L"NTFS") ) {
  173. //
  174. // He's NTFS. Go whack the change journal, then
  175. // scan this drive and fix up the ACLs.
  176. //
  177. DeleteUsnJournal( LinkTarget.Buffer );
  178. //
  179. // ISSUE-2002/02/26-brucegr,jcohen - potential buffer overrun?
  180. //
  181. wcscat( LinkTarget.Buffer, L"\\" );
  182. ResetACLs( LinkTarget.Buffer, 0 );
  183. }
  184. } else {
  185. TEST_STATUS( "SETUPCL: EnumerateDrives - failed call to NtQueryVolumeInformationFile" );
  186. }
  187. } else {
  188. TEST_STATUS( "SETUPCL: EnumerateDrives - Failed NtOpenFile on this drive" );
  189. }
  190. NtClose(Handle);
  191. }
  192. }
  193. }
  194. //
  195. // Query next object in \DosDevices directory
  196. //
  197. Status = NtQueryDirectoryObject( DosDevicesDir,
  198. DirInfo,
  199. sizeof(DirInfoBuffer),
  200. TRUE,
  201. FALSE,
  202. &Context,
  203. &Length );
  204. }
  205. NtClose(DosDevicesDir);
  206. return( STATUS_SUCCESS );
  207. }
  208. NTSTATUS
  209. ResetACLs(
  210. IN WCHAR *ObjectName,
  211. ULONG indent
  212. )
  213. /*++
  214. ===============================================================================
  215. Routine Description:
  216. This function will go search a drive and inspect each file and directory
  217. for an ACL. If found, it will look for, and replace, any ACL that
  218. contains the old SID with the new SID.
  219. Arguments:
  220. Return Value:
  221. Status is returned.
  222. ===============================================================================
  223. --*/
  224. {
  225. NTSTATUS Status = STATUS_SUCCESS;
  226. UNICODE_STRING UnicodeString;
  227. OBJECT_ATTRIBUTES Obja;
  228. IO_STATUS_BLOCK IoStatusBlock;
  229. HANDLE Handle;
  230. PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
  231. PWSTR NewObjectName;
  232. DWORD dwDirectoryInfoSize;
  233. BOOLEAN bStartScan = TRUE,
  234. bContinue = TRUE;
  235. ULONG i;
  236. #if 0
  237. for( i = 0; i < indent; i++ )
  238. DbgPrint( " " );
  239. DbgPrint( "About to operate on a new object: %ws\n", ObjectName );
  240. #endif
  241. DisplayUI();
  242. //
  243. // Open the file/directory and whack his ACL.
  244. //
  245. INIT_OBJA(&Obja, &UnicodeString, ObjectName);
  246. Status = NtOpenFile( &Handle,
  247. READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY,
  248. &Obja,
  249. &IoStatusBlock,
  250. FILE_SHARE_READ | FILE_SHARE_WRITE,
  251. 0 );
  252. TEST_STATUS( "SETUPCL: ResetACLs - Failed to open file/directory." );
  253. Status = TestSetSecurityObject( Handle );
  254. TEST_STATUS( "SETUPCL: ResetACLs - Failed to reset ACL on file/directory." );
  255. NtClose( Handle );
  256. //
  257. // Now list the directory.
  258. //
  259. Status = NtOpenFile( &Handle,
  260. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  261. &Obja,
  262. &IoStatusBlock,
  263. FILE_SHARE_READ | FILE_SHARE_WRITE,
  264. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
  265. //
  266. // Don't report this error because we'll fail if the handle points
  267. // to a file, which is quite possible and valid. Just quietly return.
  268. //
  269. // TEST_STATUS_RETURN( "SETUPCL: ResetACLs - Failed to open file/directory for list access." );
  270. if( !NT_SUCCESS(Status) ) {
  271. return( STATUS_SUCCESS );
  272. }
  273. //
  274. // It is gruesome to have the allocation/deallocation of this inside
  275. // the while loop, but it saves a *lot* of stack space. We aren't after
  276. // speed here.
  277. //
  278. dwDirectoryInfoSize = (MAX_PATH * 2) + sizeof(FILE_BOTH_DIR_INFORMATION);
  279. DirectoryInfo = (PFILE_BOTH_DIR_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
  280. 0,
  281. dwDirectoryInfoSize );
  282. if ( NULL == DirectoryInfo )
  283. {
  284. bContinue = FALSE;
  285. }
  286. while( bContinue )
  287. {
  288. Status = NtQueryDirectoryFile( Handle,
  289. NULL,
  290. NULL,
  291. NULL,
  292. &IoStatusBlock,
  293. DirectoryInfo,
  294. dwDirectoryInfoSize,
  295. FileBothDirectoryInformation,
  296. TRUE,
  297. NULL,
  298. bStartScan );
  299. if ( NT_SUCCESS( Status ) )
  300. {
  301. //
  302. // Make sure the scan doesn't get restarted...
  303. //
  304. bStartScan = FALSE;
  305. //
  306. // Terminate the name, just in case.
  307. //
  308. DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(WCHAR)] = 0;
  309. }
  310. else
  311. {
  312. if ( STATUS_NO_MORE_FILES == Status )
  313. {
  314. Status = STATUS_SUCCESS;
  315. }
  316. else
  317. {
  318. PRINT_STATUS( "SETUPCL: ResetACLs - Failed to query directory." );
  319. }
  320. //
  321. // We want to exit the loop...
  322. //
  323. bContinue = FALSE;
  324. }
  325. //
  326. // We can't really do anything with encrypted files...
  327. //
  328. if ( bContinue &&
  329. ( ( !(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  330. !(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ) ||
  331. //
  332. // Don't recurse into the "." and ".." directories...
  333. //
  334. ( (DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  335. ( (wcscmp( DirectoryInfo->FileName, L"." )) &&
  336. (wcscmp( DirectoryInfo->FileName, L".." )) ) ) ) )
  337. {
  338. //
  339. // Calculate the maximum buffer size we need to allocate...
  340. // We need to factor in 4 things: 1) the current object
  341. // 2) the directory name
  342. // 3) a possible backslash
  343. // 4) the null terminator
  344. //
  345. DWORD dwObjectLength = wcslen(ObjectName),
  346. dwNewObjectLength = dwObjectLength + (DirectoryInfo->FileNameLength / sizeof(WCHAR)) + 2;
  347. //
  348. // Build the name of the new object.
  349. //
  350. NewObjectName = (PWSTR)RtlAllocateHeap( RtlProcessHeap(),
  351. 0,
  352. dwNewObjectLength * sizeof(WCHAR) );
  353. //
  354. // Make sure the allocation succeeded...
  355. //
  356. if ( NewObjectName )
  357. {
  358. memset( NewObjectName, 0, dwNewObjectLength * sizeof(WCHAR) );
  359. wcsncpy( NewObjectName, ObjectName, dwNewObjectLength - 1 );
  360. //
  361. // If there's not an ending backslash, append one...
  362. // Note: we've already accounted for this possible backslash in the buffer allocation...
  363. //
  364. if ( ObjectName[dwObjectLength - 1] != L'\\' )
  365. {
  366. wcscat( NewObjectName, L"\\" );
  367. }
  368. //
  369. // Append the FileName buffer onto our NewObjectName buffer...
  370. // Note: we've already accounted for the filename length in the buffer allocation...
  371. //
  372. wcscat( NewObjectName, DirectoryInfo->FileName );
  373. //
  374. // Call ourselves on the new object.
  375. //
  376. ResetACLs( NewObjectName, indent + 1 );
  377. RtlFreeHeap( RtlProcessHeap(),
  378. 0,
  379. NewObjectName );
  380. }
  381. else
  382. {
  383. PRINT_STATUS( "SETUPCL: ResetACLs - Failed to allocate NewObjectName buffer." );
  384. bContinue = FALSE;
  385. }
  386. }
  387. }
  388. //
  389. // Free the DirectoryInfo pointer...
  390. //
  391. if ( DirectoryInfo )
  392. {
  393. RtlFreeHeap( RtlProcessHeap(),
  394. 0,
  395. DirectoryInfo );
  396. }
  397. NtClose( Handle );
  398. return( Status );
  399. }
  400. NTSTATUS
  401. DeleteUsnJournal(
  402. PWSTR DrivePath
  403. )
  404. /*++
  405. ===============================================================================
  406. Routine Description:
  407. This function will remove the Change Journal on NTFS partitions.
  408. Arguments:
  409. DriveLetter Supplies the drive letter of the partition we'll be
  410. operating on.
  411. Return Value:
  412. Status is returned.
  413. ===============================================================================
  414. --*/
  415. {
  416. NTSTATUS Status = STATUS_SUCCESS;
  417. UNICODE_STRING UnicodeString;
  418. OBJECT_ATTRIBUTES ObjectAttributes;
  419. HANDLE FileHandle;
  420. IO_STATUS_BLOCK IoStatusBlock;
  421. PUSN_JOURNAL_DATA OutputBuffer = NULL;
  422. PDELETE_USN_JOURNAL_DATA InputBuffer = NULL;
  423. ULONG OutputBufferSize, InputBufferSize;
  424. //
  425. // Build the volume name, then open it.
  426. //
  427. INIT_OBJA( &ObjectAttributes,
  428. &UnicodeString,
  429. DrivePath );
  430. Status = NtOpenFile( &FileHandle,
  431. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  432. &ObjectAttributes,
  433. &IoStatusBlock,
  434. FILE_SHARE_READ | FILE_SHARE_WRITE,
  435. FILE_SYNCHRONOUS_IO_ALERT);
  436. TEST_STATUS_RETURN( "SETUPCL: DeleteUsnJournal - Failed to open volume." );
  437. //
  438. // Allocate buffers for the query and delete operation.
  439. //
  440. OutputBufferSize = sizeof(USN_JOURNAL_DATA);
  441. OutputBuffer = (PUSN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(),
  442. 0,
  443. sizeof(USN_JOURNAL_DATA) );
  444. InputBufferSize = sizeof(DELETE_USN_JOURNAL_DATA);
  445. InputBuffer = (PDELETE_USN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(),
  446. 0,
  447. sizeof(DELETE_USN_JOURNAL_DATA) );
  448. if( !(OutputBuffer && InputBuffer) ) {
  449. DbgPrint( "SETUPCL: DeleteUsnJournal - Failed to allocate buffers.\n" );
  450. //
  451. // ISSUE-2002/02/26-brucegr,jcohen - Leaks Input or Output buffers and FileHandle!
  452. //
  453. return( STATUS_UNSUCCESSFUL );
  454. }
  455. Status = NtFsControlFile( FileHandle,
  456. NULL,
  457. NULL,
  458. NULL,
  459. &IoStatusBlock,
  460. FSCTL_QUERY_USN_JOURNAL,
  461. NULL,
  462. 0,
  463. OutputBuffer,
  464. OutputBufferSize );
  465. TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to query journal." );
  466. if( NT_SUCCESS( Status ) ) {
  467. //
  468. // Now delete him.
  469. //
  470. InputBuffer->DeleteFlags = USN_DELETE_FLAG_DELETE;
  471. InputBuffer->UsnJournalID = OutputBuffer->UsnJournalID;
  472. Status = NtFsControlFile( FileHandle,
  473. NULL,
  474. NULL,
  475. NULL,
  476. &IoStatusBlock,
  477. FSCTL_DELETE_USN_JOURNAL,
  478. InputBuffer,
  479. InputBufferSize ,
  480. NULL,
  481. 0 );
  482. TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to delete journal." );
  483. }
  484. NtClose( FileHandle );
  485. RtlFreeHeap( RtlProcessHeap(),
  486. 0,
  487. OutputBuffer );
  488. RtlFreeHeap( RtlProcessHeap(),
  489. 0,
  490. InputBuffer );
  491. return Status;
  492. }