Source code of Windows XP (NT5)
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.

5164 lines
150 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. SecurSup.c
  5. Abstract:
  6. This module implements the Ntfs Security Support routines
  7. Author:
  8. Gary Kimura [GaryKi] 27-Dec-1991
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #define Dbg (DEBUG_TRACE_SECURSUP)
  13. #define DbgAcl (DEBUG_TRACE_SECURSUP | DEBUG_TRACE_ACLINDEX)
  14. //
  15. // Define a tag for general pool allocations from this module
  16. //
  17. #undef MODULE_POOL_TAG
  18. #define MODULE_POOL_TAG ('SFtN')
  19. UNICODE_STRING FileString = CONSTANT_UNICODE_STRING( L"File" );
  20. //
  21. // Local procedure prototypes
  22. //
  23. PSHARED_SECURITY
  24. NtfsCacheSharedSecurityByDescriptor (
  25. IN PIRP_CONTEXT IrpContext,
  26. PSECURITY_DESCRIPTOR SecurityDescriptor,
  27. ULONG SecurityDescriptorLength,
  28. IN BOOLEAN RaiseIfInvalid
  29. );
  30. VOID
  31. NtfsStoreSecurityDescriptor (
  32. PIRP_CONTEXT IrpContext,
  33. IN PFCB Fcb,
  34. IN BOOLEAN LogIt
  35. );
  36. PSHARED_SECURITY
  37. FindCachedSharedSecurityByHashUnsafe (
  38. IN PVCB Vcb,
  39. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  40. IN ULONG SecurityDescriptorLength,
  41. IN ULONG Hash
  42. );
  43. VOID
  44. AddCachedSharedSecurityUnsafe (
  45. IN PVCB Vcb,
  46. PSHARED_SECURITY SharedSecurity
  47. );
  48. BOOLEAN
  49. MapSecurityIdToSecurityDescriptorHeaderUnsafe (
  50. IN PIRP_CONTEXT IrpContext,
  51. IN PVCB Vcb,
  52. IN SECURITY_ID SecurityId,
  53. OUT PSECURITY_DESCRIPTOR_HEADER *SecurityDescriptorHeader,
  54. OUT PBCB *Bcb
  55. );
  56. NTSTATUS
  57. NtOfsMatchSecurityHash (
  58. IN PINDEX_ROW IndexRow,
  59. IN OUT PVOID MatchData
  60. );
  61. VOID
  62. NtOfsLookupSecurityDescriptorInIndex (
  63. PIRP_CONTEXT IrpContext,
  64. IN OUT PSHARED_SECURITY SharedSecurity
  65. );
  66. PSHARED_SECURITY
  67. GetSharedSecurityFromDescriptorUnsafe (
  68. IN PIRP_CONTEXT IrpContext,
  69. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  70. IN ULONG SecurityDescriptorLength,
  71. IN BOOLEAN RaiseIfInvalid
  72. );
  73. #ifdef NTFS_CACHE_RIGHTS
  74. //
  75. // Local procedure prototypes for access rights cache
  76. //
  77. VOID
  78. NtfsAddCachedRights (
  79. IN PVCB Vcb,
  80. IN PSHARED_SECURITY SharedSecurity,
  81. IN ACCESS_MASK Rights,
  82. IN PLUID TokenId,
  83. IN PLUID ModifiedId
  84. );
  85. INLINE ACCESS_MASK
  86. NtfsGetCachedRightsWorld (
  87. IN PCACHED_ACCESS_RIGHTS CachedRights
  88. )
  89. {
  90. return CachedRights->EveryoneRights;
  91. }
  92. INLINE VOID
  93. NtfsSetCachedRightsWorld (
  94. IN PSHARED_SECURITY SharedSecurity
  95. )
  96. {
  97. SeGetWorldRights( &SharedSecurity->SecurityDescriptor,
  98. IoGetFileObjectGenericMapping(),
  99. &SharedSecurity->CachedRights.EveryoneRights );
  100. //
  101. // Make certain that MAXIMUM_ALLOWED is not in the rights.
  102. //
  103. ClearFlag( SharedSecurity->CachedRights.EveryoneRights, MAXIMUM_ALLOWED );
  104. return;
  105. }
  106. #endif
  107. #ifdef ALLOC_PRAGMA
  108. #pragma alloc_text(PAGE, NtfsAssignSecurity)
  109. #pragma alloc_text(PAGE, NtfsCacheSharedSecurityByDescriptor)
  110. #pragma alloc_text(PAGE, NtfsModifySecurity)
  111. #pragma alloc_text(PAGE, NtfsQuerySecurity)
  112. #pragma alloc_text(PAGE, NtfsAccessCheck)
  113. #pragma alloc_text(PAGE, NtfsCheckFileForDelete)
  114. #pragma alloc_text(PAGE, NtfsCheckIndexForAddOrDelete)
  115. #pragma alloc_text(PAGE, GetSharedSecurityFromDescriptorUnsafe)
  116. #pragma alloc_text(PAGE, NtfsSetFcbSecurityFromDescriptor)
  117. #pragma alloc_text(PAGE, NtfsNotifyTraverseCheck)
  118. #pragma alloc_text(PAGE, NtfsInitializeSecurity)
  119. #pragma alloc_text(PAGE, NtfsCacheSharedSecurityBySecurityId)
  120. #pragma alloc_text(PAGE, FindCachedSharedSecurityByHashUnsafe)
  121. #pragma alloc_text(PAGE, AddCachedSharedSecurityUnsafe)
  122. #pragma alloc_text(PAGE, NtOfsPurgeSecurityCache)
  123. #pragma alloc_text(PAGE, MapSecurityIdToSecurityDescriptorHeaderUnsafe)
  124. #pragma alloc_text(PAGE, NtfsLoadSecurityDescriptor)
  125. #pragma alloc_text(PAGE, NtOfsMatchSecurityHash)
  126. #pragma alloc_text(PAGE, NtOfsLookupSecurityDescriptorInIndex)
  127. #pragma alloc_text(PAGE, GetSecurityIdFromSecurityDescriptorUnsafe)
  128. #pragma alloc_text(PAGE, NtfsStoreSecurityDescriptor)
  129. #pragma alloc_text(PAGE, NtfsCacheSharedSecurityForCreate)
  130. #pragma alloc_text(PAGE, NtOfsCollateSecurityHash)
  131. #pragma alloc_text(PAGE, NtfsCanAdministerVolume)
  132. #endif
  133. #ifdef NTFS_CACHE_RIGHTS
  134. #ifdef ALLOC_PRAGMA
  135. #pragma alloc_text(PAGE, NtfsGetCachedRightsById)
  136. #pragma alloc_text(PAGE, NtfsGetCachedRights)
  137. #pragma alloc_text(PAGE, NtfsAddCachedRights)
  138. #endif
  139. #endif
  140. VOID
  141. NtfsAssignSecurity (
  142. IN PIRP_CONTEXT IrpContext,
  143. IN PFCB ParentFcb,
  144. IN PIRP Irp,
  145. IN PFCB NewFcb,
  146. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  147. IN PBCB FileRecordBcb,
  148. IN LONGLONG FileOffset,
  149. IN OUT PBOOLEAN LogIt
  150. )
  151. /*++
  152. Routine Description:
  153. LEGACY NOTE - this routine disappears when all volumes go to Cairo.
  154. This routine constructs and assigns a new security descriptor to the
  155. specified file/directory. The new security descriptor is placed both
  156. on the fcb and on the disk.
  157. This will only be called in the context of an open/create operation.
  158. It currently MUST NOT be called to store a security descriptor for
  159. an existing file, because it instructs NtfsStoreSecurityDescriptor
  160. to not log the change.
  161. If this is a large security descriptor then it is possible that
  162. AllocateClusters may be called twice within the call to AddAllocation
  163. when the attribute is created. If so then the second call will always
  164. log the changes. In that case we need to log all of the operations to
  165. create this security attribute and also we must log the current state
  166. of the file record.
  167. It is possible that our caller has already started logging operations against
  168. this log record. In that case we always log the security changes.
  169. Arguments:
  170. ParentFcb - Supplies the directory under which the new fcb exists
  171. Irp - Supplies the Irp being processed
  172. NewFcb - Supplies the fcb that is being assigned a new security descriptor
  173. FileRecord - Supplies the file record for this operation. Used if we
  174. have to log against the file record.
  175. FileRecordBcb - Bcb for the file record above.
  176. FileOffset - File offset in the Mft for this file record.
  177. LogIt - On entry this indicates whether our caller wants this operation
  178. logged. On exit we return TRUE if we logged the security change.
  179. Return Value:
  180. None.
  181. --*/
  182. {
  183. PSECURITY_DESCRIPTOR SecurityDescriptor;
  184. NTSTATUS Status;
  185. BOOLEAN IsDirectory;
  186. PACCESS_STATE AccessState;
  187. PIO_STACK_LOCATION IrpSp;
  188. ULONG SecurityDescLength;
  189. ASSERT_IRP_CONTEXT( IrpContext );
  190. ASSERT_FCB( ParentFcb );
  191. ASSERT_IRP( Irp );
  192. ASSERT_FCB( NewFcb );
  193. PAGED_CODE();
  194. if (NewFcb->Vcb->SecurityDescriptorStream != NULL) {
  195. return;
  196. }
  197. DebugTrace( +1, Dbg, ("NtfsAssignSecurity...\n") );
  198. //
  199. // First decide if we are creating a file or a directory
  200. //
  201. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  202. if (FlagOn(IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE)) {
  203. IsDirectory = TRUE;
  204. } else {
  205. IsDirectory = FALSE;
  206. }
  207. //
  208. // Extract the parts of the Irp that we need to do our assignment
  209. //
  210. AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
  211. //
  212. // Check if we need to load the security descriptor for the parent.
  213. //
  214. if (ParentFcb->SharedSecurity == NULL) {
  215. NtfsLoadSecurityDescriptor( IrpContext, ParentFcb );
  216. }
  217. ASSERT( ParentFcb->SharedSecurity != NULL );
  218. //
  219. // Create a new security descriptor for the file and raise if there is
  220. // an error
  221. //
  222. if (!NT_SUCCESS( Status = SeAssignSecurity( &ParentFcb->SharedSecurity->SecurityDescriptor,
  223. AccessState->SecurityDescriptor,
  224. &SecurityDescriptor,
  225. IsDirectory,
  226. &AccessState->SubjectSecurityContext,
  227. IoGetFileObjectGenericMapping(),
  228. PagedPool ))) {
  229. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  230. }
  231. //
  232. // Load the security descriptor into the Fcb
  233. //
  234. SecurityDescLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
  235. try {
  236. //
  237. // Make sure the length is non-zero.
  238. //
  239. if (SecurityDescLength == 0) {
  240. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  241. }
  242. ASSERT( SeValidSecurityDescriptor( SecurityDescLength, SecurityDescriptor ));
  243. NtfsSetFcbSecurityFromDescriptor(
  244. IrpContext,
  245. NewFcb,
  246. SecurityDescriptor,
  247. SecurityDescLength,
  248. TRUE );
  249. } finally {
  250. //
  251. // Free the security descriptor created by Se
  252. //
  253. SeDeassignSecurity( &SecurityDescriptor );
  254. }
  255. //
  256. // If the security descriptor is large enough that it may cause us to
  257. // start logging in the StoreSecurity call below then make sure everything
  258. // is logged.
  259. //
  260. if (!(*LogIt) &&
  261. (SecurityDescLength > BytesFromClusters( NewFcb->Vcb, MAXIMUM_RUNS_AT_ONCE ))) {
  262. //
  263. // Log the current state of the file record.
  264. //
  265. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  266. NewFcb->Vcb->MftScb,
  267. FileRecordBcb,
  268. InitializeFileRecordSegment,
  269. FileRecord,
  270. FileRecord->FirstFreeByte,
  271. Noop,
  272. NULL,
  273. 0,
  274. FileOffset,
  275. 0,
  276. 0,
  277. NewFcb->Vcb->BytesPerFileRecordSegment );
  278. *LogIt = TRUE;
  279. }
  280. //
  281. // Write out the new security descriptor
  282. //
  283. NtfsStoreSecurityDescriptor( IrpContext, NewFcb, *LogIt );
  284. //
  285. // And return to our caller
  286. //
  287. DebugTrace( -1, Dbg, ("NtfsAssignSecurity -> VOID\n") );
  288. return;
  289. }
  290. PSHARED_SECURITY
  291. NtfsCacheSharedSecurityByDescriptor (
  292. IN PIRP_CONTEXT IrpContext,
  293. PSECURITY_DESCRIPTOR SecurityDescriptor,
  294. ULONG SecurityDescriptorLength,
  295. IN BOOLEAN RaiseIfInvalid
  296. )
  297. /*++
  298. Routine Description:
  299. This routine finds or constructs a security id and SHARED_SECURITY from
  300. a specific file or directory.
  301. Arguments:
  302. IrpContext - Context of the call
  303. SecurityDescriptor - the actual security descriptor being stored
  304. SecurityDescriptorLength - length of security descriptor
  305. RaiseIfInvalid - raise status if sd is invalid
  306. Return Value:
  307. Referenced shared security.
  308. --*/
  309. {
  310. PSHARED_SECURITY SharedSecurity = NULL;
  311. SECURITY_ID SecurityId;
  312. ULONG FcbSecurityAcquired;
  313. ULONG OwnerCount;
  314. ASSERT_IRP_CONTEXT( IrpContext );
  315. PAGED_CODE();
  316. //
  317. // LEGACY NOTE - this goes away when all volumes become NT 5.0
  318. //
  319. if (IrpContext->Vcb->SecurityDescriptorStream == NULL) {
  320. return NULL;
  321. }
  322. DebugTrace( +1, DbgAcl, ("NtfsCacheSharedSecurityByDescriptor...\n") );
  323. //
  324. // Serialize access to the security cache and use a try/finally to make
  325. // sure we release it
  326. //
  327. NtfsAcquireFcbSecurity( IrpContext->Vcb );
  328. FcbSecurityAcquired = TRUE;
  329. //
  330. // Capture our owner count on the mft - so we can release it if we acquired it later on
  331. // growing the file record for the security stream
  332. //
  333. OwnerCount = NtfsIsSharedScb( IrpContext->Vcb->MftScb );
  334. try {
  335. //
  336. // We have a security descriptor. Create a shared security descriptor.
  337. //
  338. SharedSecurity = GetSharedSecurityFromDescriptorUnsafe( IrpContext,
  339. SecurityDescriptor,
  340. SecurityDescriptorLength,
  341. RaiseIfInvalid );
  342. //
  343. // Make sure the shared security doesn't go away
  344. //
  345. SharedSecurity->ReferenceCount += 1;
  346. DebugTrace( 0, DbgAcl, ("NtfsCacheSharedSecurityByDescriptor bumping refcount %08x\n", SharedSecurity ));
  347. //
  348. // If we found a shared security descriptor with no Id assigned, then
  349. // we must assign it. Since it is known that no Id was assigned we
  350. // must also add it into the cache.
  351. //
  352. if (SharedSecurity->Header.HashKey.SecurityId == SECURITY_ID_INVALID) {
  353. //
  354. // Find unique SecurityId for descriptor and set SecurityId in Fcb.
  355. //
  356. SecurityId = GetSecurityIdFromSecurityDescriptorUnsafe( IrpContext,
  357. SharedSecurity );
  358. ASSERT( SharedSecurity->Header.HashKey.SecurityId == SecurityId );
  359. SharedSecurity->Header.HashKey.SecurityId = SecurityId;
  360. DebugTrace( 0, DbgAcl, ("NtfsCacheSharedSecurityByDescriptor setting security Id to new %08x\n", SecurityId ));
  361. //
  362. // We need to drop the FcbSecurity before performing the checkpoint, to avoid
  363. // deadlocks, but this is ok since we have incremented the reference count on
  364. // our SharedSecurity.
  365. //
  366. NtfsReleaseFcbSecurity( IrpContext->Vcb );
  367. FcbSecurityAcquired = FALSE;
  368. //
  369. // Checkpoint the current transaction so that we can safely add this
  370. // shared security to the cache. Once this call is complete, we are
  371. // guaranteed that the security index modifications make it out to
  372. // disk before the newly allocated security ID does.
  373. //
  374. NtfsCheckpointCurrentTransaction( IrpContext );
  375. //
  376. // Release the security descriptor and mft if owned
  377. //
  378. NtfsReleaseExclusiveScbIfOwned( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
  379. //
  380. // Check if the mft has been acquired during the call before releasing it
  381. //
  382. if (NtfsIsSharedScb( IrpContext->Vcb->MftScb ) != OwnerCount) {
  383. NtfsReleaseScb( IrpContext, IrpContext->Vcb->MftScb );
  384. }
  385. //
  386. // Cache this shared security for faster access.
  387. //
  388. NtfsAcquireFcbSecurity( IrpContext->Vcb );
  389. FcbSecurityAcquired = TRUE;
  390. AddCachedSharedSecurityUnsafe( IrpContext->Vcb, SharedSecurity );
  391. }
  392. } finally {
  393. if (AbnormalTermination( )) {
  394. if (SharedSecurity != NULL) {
  395. if (!FcbSecurityAcquired) {
  396. NtfsAcquireFcbSecurity( IrpContext->Vcb );
  397. RemoveReferenceSharedSecurityUnsafe( &SharedSecurity );
  398. FcbSecurityAcquired = TRUE;
  399. }
  400. }
  401. }
  402. if (FcbSecurityAcquired) {
  403. NtfsReleaseFcbSecurity( IrpContext->Vcb );
  404. }
  405. }
  406. //
  407. // And return to our caller
  408. //
  409. DebugTrace( -1, DbgAcl, ( "NtfsCacheSharedSecurityByDescriptor -> %08x\n", SharedSecurity ) );
  410. return SharedSecurity;
  411. }
  412. NTSTATUS
  413. NtfsModifySecurity (
  414. IN PIRP_CONTEXT IrpContext,
  415. IN PFCB Fcb,
  416. IN PSECURITY_INFORMATION SecurityInformation,
  417. OUT PSECURITY_DESCRIPTOR SecurityDescriptor
  418. )
  419. /*++
  420. Routine Description:
  421. This routine modifies an existing security descriptor for a file/directory.
  422. Arguments:
  423. Fcb - Supplies the Fcb whose security is being modified
  424. SecurityInformation - Supplies the security information structure passed to
  425. the file system by the I/O system.
  426. SecurityDescriptor - Supplies the security information structure passed to
  427. the file system by the I/O system.
  428. Return Value:
  429. NTSTATUS - Returns an appropriate status value for the function results
  430. --*/
  431. {
  432. NTSTATUS Status;
  433. PSECURITY_DESCRIPTOR DescriptorPtr;
  434. ULONG DescriptorLength;
  435. PSCB Scb;
  436. ASSERT_IRP_CONTEXT( IrpContext );
  437. ASSERT_FCB( Fcb );
  438. PAGED_CODE();
  439. DebugTrace( +1, DbgAcl, ("NtfsModifySecurity...\n") );
  440. //
  441. // First check if we need to load the security descriptor for the file
  442. //
  443. if (Fcb->SharedSecurity == NULL) {
  444. NtfsLoadSecurityDescriptor( IrpContext, Fcb );
  445. }
  446. ASSERT( Fcb->SharedSecurity != NULL);
  447. DescriptorPtr = &Fcb->SharedSecurity->SecurityDescriptor;
  448. //
  449. // Do the modify operation. SeSetSecurityDescriptorInfo no longer
  450. // frees the passed security descriptor.
  451. //
  452. if (!NT_SUCCESS( Status = SeSetSecurityDescriptorInfo( NULL,
  453. SecurityInformation,
  454. SecurityDescriptor,
  455. &DescriptorPtr,
  456. PagedPool,
  457. IoGetFileObjectGenericMapping() ))) {
  458. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  459. }
  460. DescriptorLength = RtlLengthSecurityDescriptor( DescriptorPtr );
  461. try {
  462. //
  463. // Check for a zero length.
  464. //
  465. if (DescriptorLength == 0) {
  466. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  467. }
  468. //
  469. // LEGACY NOTE - remove this test when all volumes go to NT 5
  470. //
  471. if (Fcb->Vcb->SecurityDescriptorStream != NULL) {
  472. PSHARED_SECURITY SharedSecurity;
  473. PSHARED_SECURITY OldSharedSecurity = NULL;
  474. SECURITY_ID OldSecurityId;
  475. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  476. //
  477. // Cache security descriptor
  478. //
  479. //
  480. // After the SeSetSecurityDescriptorInfo we should have a valid sd
  481. //
  482. ASSERT( SeValidSecurityDescriptor( DescriptorLength, DescriptorPtr ));
  483. SharedSecurity = NtfsCacheSharedSecurityByDescriptor( IrpContext, DescriptorPtr, DescriptorLength, TRUE );
  484. NtfsInitializeAttributeContext( &AttributeContext );
  485. try {
  486. //
  487. // Move Quota to new owner as described in descriptor.
  488. //
  489. NtfsMoveQuotaOwner( IrpContext, Fcb, DescriptorPtr );
  490. //
  491. // Set in new shared security
  492. //
  493. OldSharedSecurity = Fcb->SharedSecurity;
  494. OldSecurityId = Fcb->SecurityId;
  495. Fcb->SharedSecurity = SharedSecurity;
  496. Fcb->SecurityId = SharedSecurity->Header.HashKey.SecurityId;
  497. DebugTrace( 0, DbgAcl, ("NtfsModifySecurity setting Fcb securityId to %08x\n", Fcb->SecurityId ));
  498. //
  499. // We are called to replace an existing security descriptor. In the
  500. // event that we have a downlevel $STANDARD_INFORMATION attribute, we
  501. // must convert it to large form so that the security ID is stored.
  502. //
  503. if (!FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO) ) {
  504. DebugTrace( 0, DbgAcl, ("Growing standard information\n") );
  505. NtfsGrowStandardInformation( IrpContext, Fcb );
  506. }
  507. //
  508. // Despite having a large $STANDARD_INFORMATION, we may have
  509. // a security descriptor present. This occurs if the SecurityId
  510. // is invalid
  511. //
  512. if (OldSecurityId == SECURITY_ID_INVALID) {
  513. //
  514. // Read in the security descriptor attribute. If it
  515. // doesn't exist then we're done, otherwise simply delete the
  516. // attribute
  517. //
  518. if (NtfsLookupAttributeByCode( IrpContext,
  519. Fcb,
  520. &Fcb->FileReference,
  521. $SECURITY_DESCRIPTOR,
  522. &AttributeContext )) {
  523. UNICODE_STRING NoName = CONSTANT_UNICODE_STRING( L"" );
  524. DebugTrace( 0, DbgAcl, ("Delete existing Security Descriptor\n") );
  525. NtfsDeleteAttributeRecord( IrpContext,
  526. Fcb,
  527. DELETE_LOG_OPERATION |
  528. DELETE_RELEASE_FILE_RECORD |
  529. DELETE_RELEASE_ALLOCATION,
  530. &AttributeContext );
  531. //
  532. // If the $SECURITY_DESCRIPTOR was non resident, the above
  533. // delete call created one for us under the covers. We
  534. // need to mark it as deleted otherwise, we detect the
  535. // volume as being corrupt.
  536. //
  537. Scb = NtfsCreateScb( IrpContext,
  538. Fcb,
  539. $SECURITY_DESCRIPTOR,
  540. &NoName,
  541. TRUE,
  542. NULL );
  543. if (Scb != NULL) {
  544. ASSERT_EXCLUSIVE_SCB( Scb );
  545. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  546. }
  547. }
  548. }
  549. //
  550. // The security descriptor in the FCB is now changed and may not
  551. // reflect what is $STANDARD_INFORMATION. The caller is responsible
  552. // for making this update.
  553. //
  554. } finally {
  555. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  556. if (AbnormalTermination()) {
  557. if (OldSharedSecurity != NULL) {
  558. //
  559. // Put back the security the way we found it
  560. //
  561. Fcb->SharedSecurity = OldSharedSecurity;
  562. Fcb->SecurityId = OldSecurityId;
  563. DebugTrace( 0, DbgAcl, ("NtfsModifySecurity resetting Fcb->SecurityId to %08x\n", Fcb->SecurityId ));
  564. }
  565. OldSharedSecurity = SharedSecurity;
  566. }
  567. //
  568. // release old security descriptor (or new one if
  569. // NtfsMoveQuotaOwner raises
  570. //
  571. ASSERT( OldSharedSecurity != NULL );
  572. NtfsAcquireFcbSecurity( Fcb->Vcb );
  573. RemoveReferenceSharedSecurityUnsafe( &OldSharedSecurity );
  574. NtfsReleaseFcbSecurity( Fcb->Vcb );
  575. }
  576. } else {
  577. // LEGACY NOTE - delete this clause when all volumes go to NT 5
  578. //
  579. // Update the move the quota to the new owner if necessary.
  580. //
  581. NtfsMoveQuotaOwner( IrpContext, Fcb, DescriptorPtr );
  582. //
  583. // Load the security descriptor into the Fcb
  584. //
  585. NtfsAcquireFcbSecurity( Fcb->Vcb );
  586. RemoveReferenceSharedSecurityUnsafe( &Fcb->SharedSecurity );
  587. NtfsReleaseFcbSecurity( Fcb->Vcb );
  588. NtfsSetFcbSecurityFromDescriptor(
  589. IrpContext,
  590. Fcb,
  591. DescriptorPtr,
  592. DescriptorLength,
  593. TRUE );
  594. //
  595. // Now we need to store the new security descriptor on disk
  596. //
  597. NtfsStoreSecurityDescriptor( IrpContext, Fcb, TRUE );
  598. }
  599. } finally {
  600. SeDeassignSecurity( &DescriptorPtr );
  601. }
  602. //
  603. // Remember that we modified the security on the file.
  604. //
  605. SetFlag( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY );
  606. //
  607. // And return to our caller
  608. //
  609. DebugTrace( -1, DbgAcl, ("NtfsModifySecurity -> %08lx\n", Status) );
  610. return Status;
  611. }
  612. NTSTATUS
  613. NtfsQuerySecurity (
  614. IN PIRP_CONTEXT IrpContext,
  615. IN PFCB Fcb,
  616. IN PSECURITY_INFORMATION SecurityInformation,
  617. OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
  618. IN OUT PULONG SecurityDescriptorLength
  619. )
  620. /*++
  621. Routine Description:
  622. This routine is used to query the contents of an existing security descriptor for
  623. a file/directory.
  624. Arguments:
  625. Fcb - Supplies the file/directory being queried
  626. SecurityInformation - Supplies the security information structure passed to
  627. the file system by the I/O system.
  628. SecurityDescriptor - Supplies the security information structure passed to
  629. the file system by the I/O system.
  630. SecurityDescriptorLength - Supplies the length of the input security descriptor
  631. buffer in bytes.
  632. Return Value:
  633. NTSTATUS - Returns an appropriate status value for the function results
  634. --*/
  635. {
  636. NTSTATUS Status;
  637. PSECURITY_DESCRIPTOR LocalPointer;
  638. ASSERT_IRP_CONTEXT( IrpContext );
  639. ASSERT_FCB( Fcb );
  640. PAGED_CODE();
  641. DebugTrace( +1, Dbg, ("NtfsQuerySecurity...\n") );
  642. //
  643. // First check if we need to load the security descriptor for the file
  644. //
  645. if (Fcb->SharedSecurity == NULL) {
  646. NtfsLoadSecurityDescriptor( IrpContext, Fcb );
  647. }
  648. LocalPointer = &Fcb->SharedSecurity->SecurityDescriptor;
  649. //
  650. // Now with the security descriptor loaded do the query operation but
  651. // protect ourselves with a exception handler just in case the caller's
  652. // buffer isn't valid
  653. //
  654. try {
  655. Status = SeQuerySecurityDescriptorInfo( SecurityInformation,
  656. SecurityDescriptor,
  657. SecurityDescriptorLength,
  658. &LocalPointer );
  659. } except(EXCEPTION_EXECUTE_HANDLER) {
  660. ExRaiseStatus( STATUS_INVALID_USER_BUFFER );
  661. }
  662. //
  663. // And return to our caller
  664. //
  665. DebugTrace( -1, Dbg, ("NtfsQuerySecurity -> %08lx\n", Status) );
  666. return Status;
  667. }
  668. #define NTFS_SE_CONTROL (((SE_DACL_PRESENT | SE_SELF_RELATIVE) << 16) | SECURITY_DESCRIPTOR_REVISION1)
  669. #define NTFS_DEFAULT_ACCESS_MASK 0x001f01ff
  670. ULONG NtfsWorldAclFile[] = {
  671. 0x00000000, // Null Sacl
  672. 0x00000014, // Dacl
  673. 0x001c0002, // Acl header
  674. 0x00000001, // One ACE
  675. 0x00140000, // ACE Header
  676. NTFS_DEFAULT_ACCESS_MASK,
  677. 0x00000101, // World Sid
  678. 0x01000000,
  679. 0x00000000
  680. };
  681. ULONG NtfsWorldAclDir[] = {
  682. 0x00000000, // Null Sacl
  683. 0x00000014, // Dacl
  684. 0x00300002, // Acl header
  685. 0x00000002, // Two ACEs
  686. 0x00140000, // ACE Header
  687. NTFS_DEFAULT_ACCESS_MASK,
  688. 0x00000101, // World Sid
  689. 0x01000000,
  690. 0x00000000,
  691. 0x00140b00, // ACE Header
  692. NTFS_DEFAULT_ACCESS_MASK,
  693. 0x00000101, // World Sid
  694. 0x01000000,
  695. 0x00000000
  696. };
  697. VOID
  698. NtfsAccessCheck (
  699. PIRP_CONTEXT IrpContext,
  700. IN PFCB Fcb,
  701. IN PFCB ParentFcb OPTIONAL,
  702. IN PIRP Irp,
  703. IN ACCESS_MASK DesiredAccess,
  704. IN BOOLEAN CheckOnly
  705. )
  706. /*++
  707. Routine Description:
  708. This routine does a general access check for the indicated desired access.
  709. This will only be called in the context of an open/create operation.
  710. If access is granted then control is returned to the caller
  711. otherwise this function will do the proper Nt security calls to log
  712. the attempt and then raise an access denied status.
  713. Arguments:
  714. Fcb - Supplies the file/directory being examined
  715. ParentFcb - Optionally supplies the parent of the Fcb being examined
  716. Irp - Supplies the Irp being processed
  717. DesiredAccess - Supplies a mask of the access being requested
  718. CheckOnly - Indicates if this operation is to check the desired access
  719. only and not accumulate the access granted here. In this case we
  720. are guaranteed that we have passed in a hard-wired desired access
  721. and MAXIMUM_ALLOWED will not be one of them.
  722. Return Value:
  723. None.
  724. --*/
  725. {
  726. NTSTATUS Status;
  727. NTSTATUS AccessStatus;
  728. NTSTATUS AccessStatusError;
  729. PACCESS_STATE AccessState;
  730. PIO_STACK_LOCATION IrpSp;
  731. #ifdef NTFS_CACHE_RIGHTS
  732. ACCESS_MASK TmpDesiredAccess;
  733. #endif
  734. KPROCESSOR_MODE EffectiveMode;
  735. BOOLEAN AccessGranted;
  736. ACCESS_MASK GrantedAccess;
  737. PISECURITY_DESCRIPTOR SecurityDescriptor;
  738. PPRIVILEGE_SET Privileges;
  739. PUNICODE_STRING FileName;
  740. PUNICODE_STRING RelatedFileName;
  741. PUNICODE_STRING PartialFileName;
  742. UNICODE_STRING FullFileName;
  743. PUNICODE_STRING DeviceObjectName;
  744. USHORT DeviceObjectNameLength;
  745. ULONG FullFileNameLength;
  746. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  747. BOOLEAN CleanupAttrContext = FALSE;
  748. BOOLEAN LeadingSlash;
  749. BOOLEAN RelatedFileNamePresent;
  750. BOOLEAN PartialFileNamePresent;
  751. BOOLEAN MaximumRequested;
  752. BOOLEAN MaximumDeleteAcquired;
  753. BOOLEAN MaximumReadAttrAcquired;
  754. BOOLEAN PerformAccessValidation;
  755. BOOLEAN PerformDeleteAudit;
  756. ASSERT_IRP_CONTEXT( IrpContext );
  757. ASSERT_FCB( Fcb );
  758. ASSERT_IRP( Irp );
  759. PAGED_CODE();
  760. DebugTrace( +1, Dbg, ("NtfsAccessCheck...\n") );
  761. //
  762. // First extract the parts of the Irp that we need to do our checking
  763. //
  764. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  765. AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
  766. //
  767. // Check if we need to load the security descriptor for the file
  768. //
  769. if (Fcb->SharedSecurity == NULL) {
  770. NtfsLoadSecurityDescriptor( IrpContext, Fcb );
  771. }
  772. ASSERT( Fcb->SharedSecurity != NULL );
  773. SecurityDescriptor = (PISECURITY_DESCRIPTOR) Fcb->SharedSecurity->SecurityDescriptor;
  774. //
  775. // Check to see if auditing is enabled and if this is the default world ACL.
  776. //
  777. if ((*((PULONG) SecurityDescriptor) == NTFS_SE_CONTROL) &&
  778. !SeAuditingFileEvents( TRUE, SecurityDescriptor )) {
  779. //
  780. // Directories and files have different default ACLs.
  781. //
  782. if (((Fcb->Info.FileAttributes & DUP_FILE_NAME_INDEX_PRESENT) &&
  783. RtlEqualMemory( &SecurityDescriptor->Sacl,
  784. NtfsWorldAclDir,
  785. sizeof( NtfsWorldAclDir ))) ||
  786. RtlEqualMemory( &SecurityDescriptor->Sacl,
  787. NtfsWorldAclFile,
  788. sizeof(NtfsWorldAclFile))) {
  789. if (FlagOn( DesiredAccess, MAXIMUM_ALLOWED )) {
  790. GrantedAccess = NTFS_DEFAULT_ACCESS_MASK;
  791. } else {
  792. GrantedAccess = DesiredAccess & NTFS_DEFAULT_ACCESS_MASK;
  793. }
  794. if (!CheckOnly) {
  795. SetFlag( AccessState->PreviouslyGrantedAccess, GrantedAccess );
  796. ClearFlag( AccessState->RemainingDesiredAccess, GrantedAccess | MAXIMUM_ALLOWED );
  797. }
  798. DebugTrace( -1, Dbg, ("NtfsAccessCheck -> DefaultWorldAcl\n") );
  799. return;
  800. }
  801. }
  802. Privileges = NULL;
  803. FileName = NULL;
  804. RelatedFileName = NULL;
  805. PartialFileName = NULL;
  806. DeviceObjectName = NULL;
  807. MaximumRequested = FALSE;
  808. MaximumDeleteAcquired = FALSE;
  809. MaximumReadAttrAcquired = FALSE;
  810. PerformAccessValidation = TRUE;
  811. PerformDeleteAudit = FALSE;
  812. //
  813. // Check to see if we need to perform access validation
  814. //
  815. ClearFlag( DesiredAccess, AccessState->PreviouslyGrantedAccess );
  816. #ifdef NTFS_CACHE_RIGHTS
  817. //
  818. // Get any cached knowledge about rights that all callers are known to
  819. // have for this security descriptor.
  820. //
  821. GrantedAccess = NtfsGetCachedRightsWorld( &Fcb->SharedSecurity->CachedRights );
  822. if (!CheckOnly) {
  823. SetFlag( AccessState->PreviouslyGrantedAccess,
  824. FlagOn( DesiredAccess, GrantedAccess ));
  825. }
  826. ClearFlag( DesiredAccess, GrantedAccess );
  827. #endif
  828. if (DesiredAccess == 0) {
  829. //
  830. // Nothing to check, skip AVR and go straight to auditing
  831. //
  832. PerformAccessValidation = FALSE;
  833. AccessGranted = TRUE;
  834. }
  835. //
  836. // Remember the case where MAXIMUM_ALLOWED was requested.
  837. //
  838. if (FlagOn( DesiredAccess, MAXIMUM_ALLOWED )) {
  839. MaximumRequested = TRUE;
  840. }
  841. if (FlagOn(IrpSp->Parameters.Create.SecurityContext->FullCreateOptions,FILE_DELETE_ON_CLOSE)) {
  842. PerformDeleteAudit = TRUE;
  843. }
  844. //
  845. // SL_FORCE_ACCESS_CHECK causes us to use an effective RequestorMode
  846. // of UserMode.
  847. //
  848. EffectiveMode = (KPROCESSOR_MODE)(FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
  849. UserMode :
  850. Irp->RequestorMode);
  851. //
  852. // Lock the user context, do the access check and then unlock the context
  853. //
  854. SeLockSubjectContext( &AccessState->SubjectSecurityContext );
  855. //
  856. // Use a try-finally to facilitate cleanup.
  857. //
  858. try {
  859. if (PerformAccessValidation) {
  860. #ifdef NTFS_CACHE_RIGHTS
  861. BOOLEAN EntryCached = FALSE;
  862. //
  863. // Check the cached information only if the effective
  864. // RequestorMode is UserMode.
  865. if (EffectiveMode == UserMode) {
  866. //
  867. // Add in any cached knowledge about rights that this caller
  868. // is known to have for this security descriptor.
  869. //
  870. (VOID)NtfsGetCachedRights( Fcb->Vcb,
  871. &AccessState->SubjectSecurityContext,
  872. Fcb->SharedSecurity,
  873. &GrantedAccess,
  874. &EntryCached,
  875. NULL,
  876. NULL );
  877. //
  878. // Make certain that GrantedAccess has no rights not
  879. // originally requested.
  880. //
  881. ClearFlag( GrantedAccess, ~DesiredAccess );
  882. TmpDesiredAccess = DesiredAccess;
  883. ClearFlag( TmpDesiredAccess, GrantedAccess );
  884. if (EntryCached) {
  885. ClearFlag( TmpDesiredAccess, MAXIMUM_ALLOWED );
  886. }
  887. //
  888. // If all rights are available, then access is granted.
  889. //
  890. if (TmpDesiredAccess == 0) {
  891. AccessGranted = TRUE;
  892. AccessStatus = STATUS_SUCCESS;
  893. //
  894. // Otherwise, we don't know.
  895. //
  896. } else {
  897. AccessGranted = FALSE;
  898. }
  899. } else {
  900. AccessGranted = FALSE;
  901. }
  902. #endif
  903. //
  904. // We need to take the slow path.
  905. //
  906. #ifdef NTFS_CACHE_RIGHTS
  907. if (!AccessGranted) {
  908. #endif
  909. //
  910. // Get the rights information.
  911. //
  912. AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
  913. &AccessState->SubjectSecurityContext,
  914. TRUE, // Tokens are locked
  915. DesiredAccess,
  916. 0,
  917. &Privileges,
  918. IoGetFileObjectGenericMapping(),
  919. EffectiveMode,
  920. &GrantedAccess,
  921. &AccessStatus );
  922. if (Privileges != NULL) {
  923. Status = SeAppendPrivileges( AccessState, Privileges );
  924. SeFreePrivileges( Privileges );
  925. Privileges = NULL;
  926. }
  927. #ifdef NTFS_CACHE_RIGHTS
  928. }
  929. #endif
  930. if (AccessGranted) {
  931. ClearFlag( DesiredAccess, GrantedAccess | MAXIMUM_ALLOWED );
  932. if (!CheckOnly) {
  933. SetFlag( AccessState->PreviouslyGrantedAccess, GrantedAccess );
  934. //
  935. // Remember the case where MAXIMUM_ALLOWED was requested and we
  936. // got everything requested from the file.
  937. //
  938. if (MaximumRequested) {
  939. //
  940. // Check whether we got DELETE and READ_ATTRIBUTES. Otherwise
  941. // we will query the parent.
  942. //
  943. if (FlagOn( AccessState->PreviouslyGrantedAccess, DELETE )) {
  944. MaximumDeleteAcquired = TRUE;
  945. }
  946. if (FlagOn( AccessState->PreviouslyGrantedAccess, FILE_READ_ATTRIBUTES )) {
  947. MaximumReadAttrAcquired = TRUE;
  948. }
  949. }
  950. ClearFlag( AccessState->RemainingDesiredAccess, (GrantedAccess | MAXIMUM_ALLOWED) );
  951. }
  952. } else {
  953. AccessStatusError = AccessStatus;
  954. }
  955. //
  956. // Check if the access is not granted and if we were given a parent fcb, and
  957. // if the desired access was asking for delete or file read attributes. If so
  958. // then we need to do some extra work to decide if the caller does get access
  959. // based on the parent directories security descriptor. We also do the same
  960. // work if MAXIMUM_ALLOWED was requested and we didn't get DELETE or
  961. // FILE_READ_ATTRIBUTES.
  962. //
  963. if ((ParentFcb != NULL)
  964. && ((!AccessGranted && FlagOn( DesiredAccess, DELETE | FILE_READ_ATTRIBUTES ))
  965. || (MaximumRequested
  966. && (!MaximumDeleteAcquired || !MaximumReadAttrAcquired)))) {
  967. BOOLEAN DeleteAccessGranted = TRUE;
  968. BOOLEAN ReadAttributesAccessGranted = TRUE;
  969. ACCESS_MASK DeleteChildGrantedAccess = 0;
  970. ACCESS_MASK ListDirectoryGrantedAccess = 0;
  971. //
  972. // Before we proceed load in the parent security descriptor.
  973. // Acquire the parent shared while doing this to protect the
  974. // security descriptor.
  975. //
  976. SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
  977. NtfsAcquireResourceShared( IrpContext, ParentFcb, TRUE );
  978. SeLockSubjectContext( &AccessState->SubjectSecurityContext );
  979. try {
  980. if (ParentFcb->SharedSecurity == NULL) {
  981. NtfsLoadSecurityDescriptor( IrpContext, ParentFcb );
  982. }
  983. ASSERT( ParentFcb->SharedSecurity != NULL);
  984. //
  985. // Now if the user is asking for delete access then check if the parent
  986. // will granted delete access to the child, and if so then we munge the
  987. // desired access
  988. //
  989. #ifdef NTFS_CACHE_RIGHTS
  990. //
  991. // Check the cached information only if the effective
  992. // RequestorMode is UserMode.
  993. //
  994. if (EffectiveMode == UserMode) {
  995. //
  996. // Acquire in any cached knowledge about rights that
  997. // this caller is known to have for this security
  998. // descriptor.
  999. //
  1000. (VOID)NtfsGetCachedRights( ParentFcb->Vcb,
  1001. &AccessState->SubjectSecurityContext,
  1002. ParentFcb->SharedSecurity,
  1003. &GrantedAccess,
  1004. NULL,
  1005. NULL,
  1006. NULL );
  1007. //
  1008. // Add in the results of the parent directory access.
  1009. //
  1010. if (FlagOn( GrantedAccess, FILE_DELETE_CHILD) ) {
  1011. SetFlag( DeleteChildGrantedAccess, DELETE );
  1012. ClearFlag( DesiredAccess, DELETE );
  1013. MaximumDeleteAcquired = TRUE;
  1014. }
  1015. if (FlagOn( GrantedAccess, FILE_LIST_DIRECTORY) ) {
  1016. SetFlag( ListDirectoryGrantedAccess, FILE_READ_ATTRIBUTES );
  1017. ClearFlag( DesiredAccess, FILE_READ_ATTRIBUTES );
  1018. MaximumReadAttrAcquired = TRUE;
  1019. }
  1020. }
  1021. #endif
  1022. if (FlagOn( DesiredAccess, DELETE ) ||
  1023. (MaximumRequested && !MaximumDeleteAcquired)) {
  1024. DeleteAccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
  1025. &AccessState->SubjectSecurityContext,
  1026. TRUE, // Tokens are locked
  1027. FILE_DELETE_CHILD,
  1028. 0,
  1029. &Privileges,
  1030. IoGetFileObjectGenericMapping(),
  1031. EffectiveMode,
  1032. &DeleteChildGrantedAccess,
  1033. &AccessStatus );
  1034. if (Privileges != NULL) {
  1035. SeFreePrivileges( Privileges );
  1036. Privileges = NULL;
  1037. }
  1038. if (DeleteAccessGranted) {
  1039. SetFlag( DeleteChildGrantedAccess, DELETE );
  1040. ClearFlag( DeleteChildGrantedAccess, FILE_DELETE_CHILD );
  1041. ClearFlag( DesiredAccess, DELETE );
  1042. } else {
  1043. AccessStatusError = AccessStatus;
  1044. }
  1045. }
  1046. //
  1047. // Do the same test for read attributes and munge the desired access
  1048. // as appropriate
  1049. //
  1050. if (FlagOn(DesiredAccess, FILE_READ_ATTRIBUTES)
  1051. || (MaximumRequested && !MaximumReadAttrAcquired)) {
  1052. ReadAttributesAccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
  1053. &AccessState->SubjectSecurityContext,
  1054. TRUE, // Tokens are locked
  1055. FILE_LIST_DIRECTORY,
  1056. 0,
  1057. &Privileges,
  1058. IoGetFileObjectGenericMapping(),
  1059. EffectiveMode,
  1060. &ListDirectoryGrantedAccess,
  1061. &AccessStatus );
  1062. if (Privileges != NULL) {
  1063. SeFreePrivileges( Privileges );
  1064. Privileges = NULL;
  1065. }
  1066. if (ReadAttributesAccessGranted) {
  1067. SetFlag( ListDirectoryGrantedAccess, FILE_READ_ATTRIBUTES );
  1068. ClearFlag( ListDirectoryGrantedAccess, FILE_LIST_DIRECTORY );
  1069. ClearFlag( DesiredAccess, FILE_READ_ATTRIBUTES );
  1070. } else {
  1071. AccessStatusError = AccessStatus;
  1072. }
  1073. }
  1074. } finally {
  1075. NtfsReleaseResource( IrpContext, ParentFcb );
  1076. }
  1077. if (DesiredAccess == 0) {
  1078. //
  1079. // If we got either the delete or list directory access then
  1080. // grant access.
  1081. //
  1082. if (ListDirectoryGrantedAccess != 0 ||
  1083. DeleteChildGrantedAccess != 0) {
  1084. AccessGranted = TRUE;
  1085. }
  1086. } else {
  1087. //
  1088. // Now the desired access has been munged by removing everything the parent
  1089. // has granted so now do the check on the child again
  1090. //
  1091. AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
  1092. &AccessState->SubjectSecurityContext,
  1093. TRUE, // Tokens are locked
  1094. DesiredAccess,
  1095. 0,
  1096. &Privileges,
  1097. IoGetFileObjectGenericMapping(),
  1098. EffectiveMode,
  1099. &GrantedAccess,
  1100. &AccessStatus );
  1101. if (Privileges != NULL) {
  1102. Status = SeAppendPrivileges( AccessState, Privileges );
  1103. SeFreePrivileges( Privileges );
  1104. Privileges = NULL;
  1105. }
  1106. //
  1107. // Suppose that we asked for MAXIMUM_ALLOWED and no access was allowed
  1108. // on the file. In that case the call above would fail. It's possible
  1109. // that we were given DELETE or READ_ATTR permission from the
  1110. // parent directory. If we have granted any access and the only remaining
  1111. // desired access is MAXIMUM_ALLOWED then grant this access.
  1112. //
  1113. if (!AccessGranted) {
  1114. AccessStatusError = AccessStatus;
  1115. if (DesiredAccess == MAXIMUM_ALLOWED &&
  1116. (ListDirectoryGrantedAccess != 0 ||
  1117. DeleteChildGrantedAccess != 0)) {
  1118. GrantedAccess = 0;
  1119. AccessGranted = TRUE;
  1120. }
  1121. }
  1122. }
  1123. //
  1124. // If we are given access this time then by definition one of the earlier
  1125. // parent checks had to have succeeded, otherwise we would have failed again
  1126. // and we can update the access state
  1127. //
  1128. if (!CheckOnly && AccessGranted) {
  1129. SetFlag( AccessState->PreviouslyGrantedAccess,
  1130. (GrantedAccess | DeleteChildGrantedAccess | ListDirectoryGrantedAccess) );
  1131. ClearFlag( AccessState->RemainingDesiredAccess,
  1132. (GrantedAccess | MAXIMUM_ALLOWED | DeleteChildGrantedAccess | ListDirectoryGrantedAccess) );
  1133. }
  1134. }
  1135. }
  1136. //
  1137. // Now call a routine that will do the proper open audit/alarm work
  1138. //
  1139. // **** We need to expand the audit alarm code to deal with
  1140. // create and traverse alarms.
  1141. //
  1142. //
  1143. // First we take a shortcut and see if we should bother setting up
  1144. // and making the audit call.
  1145. //
  1146. //
  1147. // NOTE: Calling SeAuditingFileEvents below disables per-user auditing functionality.
  1148. // To make per-user auditing work again, it is necessary to change the call below to
  1149. // be SeAuditingFileOrGlobalEvents, which also takes the subject context.
  1150. //
  1151. // The reason for calling SeAuditingFileEvents here is because per-user auditing is
  1152. // not currently exposed to users, and this routine imposes less of a performance
  1153. // penalty than does calling SeAuditingFileOrGlobalEvents.
  1154. //
  1155. if (SeAuditingFileEvents( AccessGranted, &Fcb->SharedSecurity->SecurityDescriptor )) {
  1156. BOOLEAN Found;
  1157. PFILE_NAME FileNameAttr;
  1158. UNICODE_STRING FileRecordName;
  1159. NtfsInitializeAttributeContext( &Context );
  1160. CleanupAttrContext = TRUE;
  1161. //
  1162. // Construct the file name. The file name
  1163. // consists of:
  1164. //
  1165. // The device name out of the Vcb +
  1166. //
  1167. // The contents of the filename in the File Object +
  1168. //
  1169. // The contents of the Related File Object if it
  1170. // is present and the name in the File Object
  1171. // does not start with a '\'
  1172. //
  1173. //
  1174. // Obtain the file name.
  1175. //
  1176. PartialFileName = &IrpSp->FileObject->FileName;
  1177. PartialFileNamePresent = (PartialFileName->Length != 0);
  1178. if (!PartialFileNamePresent &&
  1179. FlagOn(IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID) ||
  1180. (IrpSp->FileObject->RelatedFileObject != NULL &&
  1181. IrpSp->FileObject->RelatedFileObject->FsContext2 != NULL &&
  1182. FlagOn(((PCCB) IrpSp->FileObject->RelatedFileObject->FsContext2)->Flags,
  1183. CCB_FLAG_OPEN_BY_FILE_ID))) {
  1184. //
  1185. // If this file is open by id or the relative file object is
  1186. // then get the first file name out of the file record.
  1187. //
  1188. Found = NtfsLookupAttributeByCode( IrpContext,
  1189. Fcb,
  1190. &Fcb->FileReference,
  1191. $FILE_NAME,
  1192. &Context );
  1193. while (Found) {
  1194. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
  1195. if (FileNameAttr->Flags != FILE_NAME_DOS) {
  1196. FileRecordName.Length = FileNameAttr->FileNameLength *
  1197. sizeof(WCHAR);
  1198. FileRecordName.MaximumLength = FileRecordName.Length;
  1199. FileRecordName.Buffer = FileNameAttr->FileName;
  1200. PartialFileNamePresent = TRUE;
  1201. PartialFileName = &FileRecordName;
  1202. break;
  1203. }
  1204. Found = NtfsLookupNextAttributeByCode( IrpContext,
  1205. Fcb,
  1206. $FILE_NAME,
  1207. &Context );
  1208. }
  1209. }
  1210. //
  1211. // Obtain the device name.
  1212. //
  1213. DeviceObjectName = &Fcb->Vcb->DeviceName;
  1214. DeviceObjectNameLength = DeviceObjectName->Length;
  1215. //
  1216. // Compute how much space we need for the final name string
  1217. //
  1218. FullFileNameLength = (ULONG)DeviceObjectNameLength +
  1219. PartialFileName->Length +
  1220. sizeof( UNICODE_NULL ) +
  1221. sizeof((WCHAR)'\\');
  1222. if ((FullFileNameLength & 0xffff0000L) != 0) {
  1223. NtfsRaiseStatus( IrpContext, STATUS_OBJECT_NAME_INVALID, NULL, NULL );
  1224. }
  1225. FullFileName.MaximumLength = DeviceObjectNameLength +
  1226. PartialFileName->Length +
  1227. sizeof( UNICODE_NULL ) +
  1228. sizeof((WCHAR)'\\');
  1229. //
  1230. // If the partial file name starts with a '\', then don't use
  1231. // whatever may be in the related file name.
  1232. //
  1233. if (PartialFileNamePresent &&
  1234. ((WCHAR)(PartialFileName->Buffer[0]) == L'\\' ||
  1235. PartialFileName == &FileRecordName)) {
  1236. LeadingSlash = TRUE;
  1237. } else {
  1238. //
  1239. // Since PartialFileName either doesn't exist or doesn't
  1240. // start with a '\', examine the RelatedFileName to see
  1241. // if it exists.
  1242. //
  1243. LeadingSlash = FALSE;
  1244. if (IrpSp->FileObject->RelatedFileObject != NULL) {
  1245. RelatedFileName = &IrpSp->FileObject->RelatedFileObject->FileName;
  1246. }
  1247. if (RelatedFileNamePresent = ((RelatedFileName != NULL) && (RelatedFileName->Length != 0))) {
  1248. FullFileName.MaximumLength += RelatedFileName->Length;
  1249. }
  1250. }
  1251. FullFileName.Buffer = NtfsAllocatePool(PagedPool, FullFileName.MaximumLength );
  1252. NtfsCleanupAttributeContext( IrpContext, &Context );
  1253. CleanupAttrContext = FALSE;
  1254. RtlCopyUnicodeString( &FullFileName, DeviceObjectName );
  1255. //
  1256. // RelatedFileNamePresent is not initialized if LeadingSlash == TRUE,
  1257. // but in that case we won't even examine it.
  1258. //
  1259. if (!LeadingSlash && RelatedFileNamePresent) {
  1260. Status = RtlAppendUnicodeStringToString( &FullFileName, RelatedFileName );
  1261. ASSERTMSG("RtlAppendUnicodeStringToString of RelatedFileName", NT_SUCCESS( Status ));
  1262. //
  1263. // RelatedFileName may simply be '\'. Don't append another
  1264. // '\' in this case.
  1265. //
  1266. if (RelatedFileName->Length != sizeof( WCHAR )) {
  1267. FullFileName.Buffer[ (FullFileName.Length / sizeof( WCHAR )) ] = L'\\';
  1268. FullFileName.Length += sizeof(WCHAR);
  1269. }
  1270. }
  1271. if (PartialFileNamePresent) {
  1272. Status = RtlAppendUnicodeStringToString( &FullFileName, PartialFileName );
  1273. //
  1274. // This should not fail
  1275. //
  1276. ASSERTMSG("RtlAppendUnicodeStringToString of PartialFileName failed", NT_SUCCESS( Status ));
  1277. }
  1278. if (PerformDeleteAudit) {
  1279. SeOpenObjectForDeleteAuditAlarm( &FileString,
  1280. NULL,
  1281. &FullFileName,
  1282. &Fcb->SharedSecurity->SecurityDescriptor,
  1283. AccessState,
  1284. FALSE,
  1285. AccessGranted,
  1286. EffectiveMode,
  1287. &AccessState->GenerateOnClose );
  1288. } else {
  1289. SeOpenObjectAuditAlarm( &FileString,
  1290. NULL,
  1291. &FullFileName,
  1292. &Fcb->SharedSecurity->SecurityDescriptor,
  1293. AccessState,
  1294. FALSE,
  1295. AccessGranted,
  1296. EffectiveMode,
  1297. &AccessState->GenerateOnClose );
  1298. }
  1299. NtfsFreePool( FullFileName.Buffer );
  1300. }
  1301. } finally {
  1302. if (CleanupAttrContext) {
  1303. NtfsCleanupAttributeContext( IrpContext, &Context );
  1304. }
  1305. SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
  1306. }
  1307. //
  1308. // If access is not granted then we will raise
  1309. //
  1310. if (!AccessGranted) {
  1311. DebugTrace( -1, Dbg, ("NtfsAccessCheck -> Access Denied\n") );
  1312. NtfsRaiseStatus( IrpContext, AccessStatusError, NULL, NULL );
  1313. }
  1314. //
  1315. // And return to our caller
  1316. //
  1317. DebugTrace( -1, Dbg, ("NtfsAccessCheck -> VOID\n") );
  1318. return;
  1319. }
  1320. NTSTATUS
  1321. NtfsCheckFileForDelete (
  1322. IN PIRP_CONTEXT IrpContext,
  1323. IN PSCB ParentScb,
  1324. IN PFCB ThisFcb,
  1325. IN BOOLEAN FcbExisted,
  1326. IN PINDEX_ENTRY IndexEntry
  1327. )
  1328. /*++
  1329. Routine Description:
  1330. This routine checks that the caller has permission to delete the target
  1331. file of a rename or set link operation.
  1332. Arguments:
  1333. ParentScb - This is the parent directory for this file.
  1334. ThisFcb - This is the Fcb for the link being removed.
  1335. FcbExisted - Indicates if this Fcb was just created.
  1336. IndexEntry - This is the index entry on the disk for this file.
  1337. Return Value:
  1338. NTSTATUS - Indicating whether access was granted or the reason access
  1339. was denied.
  1340. --*/
  1341. {
  1342. UNICODE_STRING LastComponentFileName;
  1343. PFILE_NAME IndexFileName;
  1344. PLCB ThisLcb;
  1345. PFCB ParentFcb = ParentScb->Fcb;
  1346. PSCB NextScb = NULL;
  1347. BOOLEAN LcbExisted = FALSE;
  1348. BOOLEAN AccessGranted;
  1349. ACCESS_MASK GrantedAccess;
  1350. NTSTATUS Status = STATUS_SUCCESS;
  1351. BOOLEAN UnlockSubjectContext = FALSE;
  1352. PPRIVILEGE_SET Privileges = NULL;
  1353. PAGED_CODE();
  1354. DebugTrace( +1, Dbg, ("NtfsCheckFileForDelete: Entered\n") );
  1355. ThisLcb = NULL;
  1356. IndexFileName = (PFILE_NAME) NtfsFoundIndexEntry( IndexEntry );
  1357. //
  1358. // If the unclean count is non-zero, we exit with an error.
  1359. //
  1360. if (ThisFcb->CleanupCount != 0) {
  1361. DebugTrace( 0, Dbg, ("Cleanup count of target is non-zero\n") );
  1362. return STATUS_ACCESS_DENIED;
  1363. }
  1364. //
  1365. // We look at the index entry to see if the file is either a directory
  1366. // or a read-only file. We can't delete this for a target directory open.
  1367. //
  1368. if (IsDirectory( &ThisFcb->Info )
  1369. || IsReadOnly( &ThisFcb->Info )) {
  1370. DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Read only or directory\n") );
  1371. return STATUS_ACCESS_DENIED;
  1372. }
  1373. //
  1374. // We want to scan through all of the Scb for data streams on this file
  1375. // and look for image sections. We must be able to remove the image section
  1376. // in order to delete the file. Otherwise we can get the case where an
  1377. // active image (with no handle) could be deleted and subsequent faults
  1378. // through the image section will return zeroes.
  1379. //
  1380. if (ThisFcb->LinkCount == 1) {
  1381. BOOLEAN DecrementScb = FALSE;
  1382. //
  1383. // We will increment the Scb count to prevent this Scb from going away
  1384. // if the flush call below generates a close. Use a try-finally to
  1385. // restore the count.
  1386. //
  1387. try {
  1388. while ((NextScb = NtfsGetNextChildScb( ThisFcb, NextScb )) != NULL) {
  1389. InterlockedIncrement( &NextScb->CloseCount );
  1390. DecrementScb = TRUE;
  1391. if (NtfsIsTypeCodeUserData( NextScb->AttributeTypeCode ) &&
  1392. !FlagOn( NextScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) &&
  1393. (NextScb->NonpagedScb->SegmentObject.ImageSectionObject != NULL)) {
  1394. if (!MmFlushImageSection( &NextScb->NonpagedScb->SegmentObject,
  1395. MmFlushForDelete )) {
  1396. Status = STATUS_ACCESS_DENIED;
  1397. leave;
  1398. }
  1399. }
  1400. InterlockedDecrement( &NextScb->CloseCount );
  1401. DecrementScb = FALSE;
  1402. }
  1403. } finally {
  1404. if (DecrementScb) {
  1405. InterlockedDecrement( &NextScb->CloseCount );
  1406. }
  1407. }
  1408. if (Status != STATUS_SUCCESS) {
  1409. return Status;
  1410. }
  1411. }
  1412. //
  1413. // We need to check if the link to this file has been deleted. We
  1414. // first check if we definitely know if the link is deleted by
  1415. // looking at the file name flags and the Fcb flags.
  1416. // If that result is uncertain, we need to create an Lcb and
  1417. // check the Lcb flags.
  1418. //
  1419. if (FcbExisted) {
  1420. if (FlagOn( IndexFileName->Flags, FILE_NAME_NTFS | FILE_NAME_DOS )) {
  1421. if (FlagOn( ThisFcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED )) {
  1422. DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Link is going away\n") );
  1423. return STATUS_DELETE_PENDING;
  1424. }
  1425. //
  1426. // This is a Posix link. We need to create the link to test it
  1427. // for deletion.
  1428. //
  1429. } else {
  1430. LastComponentFileName.MaximumLength =
  1431. LastComponentFileName.Length = IndexFileName->FileNameLength * sizeof( WCHAR );
  1432. LastComponentFileName.Buffer = (PWCHAR) IndexFileName->FileName;
  1433. ThisLcb = NtfsCreateLcb( IrpContext,
  1434. ParentScb,
  1435. ThisFcb,
  1436. LastComponentFileName,
  1437. IndexFileName->Flags,
  1438. &LcbExisted );
  1439. //
  1440. // If no Lcb was returned, there's no way that the Lcb has been
  1441. // marked for deletion already.
  1442. //
  1443. if ((ThisLcb != NULL) &&
  1444. (FlagOn( ThisLcb->LcbState, LCB_STATE_DELETE_ON_CLOSE ))) {
  1445. DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Link is going away\n") );
  1446. return STATUS_DELETE_PENDING;
  1447. }
  1448. }
  1449. }
  1450. //
  1451. // Finally call the security package to check for delete access.
  1452. // We check for delete access on the target Fcb. If this succeeds, we
  1453. // are done. Otherwise we will check for delete child access on the
  1454. // the parent. Either is sufficient to perform the delete.
  1455. //
  1456. //
  1457. // Check if we need to load the security descriptor for the file
  1458. //
  1459. if (ThisFcb->SharedSecurity == NULL) {
  1460. NtfsLoadSecurityDescriptor( IrpContext, ThisFcb );
  1461. }
  1462. ASSERT( ThisFcb->SharedSecurity != NULL );
  1463. #ifdef NTFS_CACHE_RIGHTS
  1464. //
  1465. // Get any cached knowledge about rights that all callers are known to
  1466. // have for this security descriptor.
  1467. //
  1468. GrantedAccess = NtfsGetCachedRightsWorld( &ThisFcb->SharedSecurity->CachedRights );
  1469. if (FlagOn( GrantedAccess, DELETE )) {
  1470. return STATUS_SUCCESS;
  1471. }
  1472. #endif
  1473. //
  1474. // Use a try-finally to facilitate cleanup.
  1475. //
  1476. try {
  1477. //
  1478. // Lock the user context, do the access check and then unlock the context
  1479. //
  1480. SeLockSubjectContext( IrpContext->Union.SubjectContext );
  1481. UnlockSubjectContext = TRUE;
  1482. #ifdef NTFS_CACHE_RIGHTS
  1483. //
  1484. // Acquire any cached knowledge about rights that this caller
  1485. // is known to have for this security descriptor.
  1486. //
  1487. (VOID)NtfsGetCachedRights( ThisFcb->Vcb,
  1488. IrpContext->Union.SubjectContext,
  1489. ThisFcb->SharedSecurity,
  1490. &GrantedAccess,
  1491. NULL,
  1492. NULL,
  1493. NULL );
  1494. if (FlagOn( GrantedAccess, DELETE )) {
  1495. AccessGranted = TRUE;
  1496. Status = STATUS_SUCCESS;
  1497. } else {
  1498. #endif
  1499. AccessGranted = SeAccessCheck( &ThisFcb->SharedSecurity->SecurityDescriptor,
  1500. IrpContext->Union.SubjectContext,
  1501. TRUE, // Tokens are locked
  1502. DELETE,
  1503. 0,
  1504. &Privileges,
  1505. IoGetFileObjectGenericMapping(),
  1506. UserMode,
  1507. &GrantedAccess,
  1508. &Status );
  1509. #ifdef NTFS_CACHE_RIGHTS
  1510. }
  1511. #endif
  1512. //
  1513. // Check if the access is not granted and if we were given a parent fcb, and
  1514. // if the desired access was asking for delete or file read attributes. If so
  1515. // then we need to do some extra work to decide if the caller does get access
  1516. // based on the parent directories security descriptor
  1517. //
  1518. if (!AccessGranted) {
  1519. //
  1520. // Before we proceed load in the parent security descriptor
  1521. //
  1522. if (ParentFcb->SharedSecurity == NULL) {
  1523. NtfsLoadSecurityDescriptor( IrpContext, ParentFcb );
  1524. }
  1525. ASSERT( ParentFcb->SharedSecurity != NULL);
  1526. //
  1527. // Now if the user is asking for delete access then check if the parent
  1528. // will granted delete access to the child, and if so then we munge the
  1529. // desired access
  1530. //
  1531. #ifdef NTFS_CACHE_RIGHTS
  1532. //
  1533. // Add in any cached knowledge about rights that this caller
  1534. // is known to have for this security descriptor.
  1535. //
  1536. (VOID)NtfsGetCachedRights( ParentFcb->Vcb,
  1537. IrpContext->Union.SubjectContext,
  1538. ParentFcb->SharedSecurity,
  1539. &GrantedAccess,
  1540. NULL,
  1541. NULL,
  1542. NULL );
  1543. if (FlagOn( GrantedAccess, FILE_DELETE_CHILD )) {
  1544. AccessGranted = TRUE;
  1545. Status = STATUS_SUCCESS;
  1546. } else {
  1547. #endif
  1548. AccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
  1549. IrpContext->Union.SubjectContext,
  1550. TRUE, // Tokens are locked
  1551. FILE_DELETE_CHILD,
  1552. 0,
  1553. &Privileges,
  1554. IoGetFileObjectGenericMapping(),
  1555. UserMode,
  1556. &GrantedAccess,
  1557. &Status );
  1558. #ifdef NTFS_CACHE_RIGHTS
  1559. }
  1560. #endif
  1561. }
  1562. } finally {
  1563. DebugUnwind( NtfsCheckFileForDelete );
  1564. if (UnlockSubjectContext) {
  1565. SeUnlockSubjectContext( IrpContext->Union.SubjectContext );
  1566. }
  1567. DebugTrace( -1, Dbg, ("NtfsCheckFileForDelete: Exit\n") );
  1568. }
  1569. return Status;
  1570. }
  1571. VOID
  1572. NtfsCheckIndexForAddOrDelete (
  1573. IN PIRP_CONTEXT IrpContext,
  1574. IN PFCB ParentFcb,
  1575. IN ACCESS_MASK DesiredAccess,
  1576. IN ULONG CreatePrivileges
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. This routine checks if a caller has permission to remove or add a link
  1581. within a directory.
  1582. Arguments:
  1583. ParentFcb - This is the parent directory for the add or delete operation.
  1584. DesiredAccess - Indicates the type of operation. We could be adding or
  1585. removing and entry in the index.
  1586. CreatePriveleges - Backup and restore priveleges captured at create time.
  1587. Return Value:
  1588. None - This routine raises on error.
  1589. --*/
  1590. {
  1591. BOOLEAN AccessGranted;
  1592. ACCESS_MASK GrantedAccess;
  1593. NTSTATUS Status;
  1594. BOOLEAN UnlockSubjectContext = FALSE;
  1595. PPRIVILEGE_SET Privileges = NULL;
  1596. PAGED_CODE();
  1597. DebugTrace( +1, Dbg, ("NtfsCheckIndexForAddOrDelete: Entered\n") );
  1598. //
  1599. // Use a try-finally to facilitate cleanup.
  1600. //
  1601. try {
  1602. //
  1603. // If we have restore privelege then we can add either a file or directory.
  1604. //
  1605. if (FlagOn( CreatePrivileges, TOKEN_HAS_RESTORE_PRIVILEGE )) {
  1606. ClearFlag( DesiredAccess,
  1607. DELETE | FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE );
  1608. }
  1609. //
  1610. // Do a security check if there is more being asked for.
  1611. //
  1612. if (DesiredAccess != 0) {
  1613. //
  1614. // Finally call the security package to check for delete access.
  1615. // We check for delete access on the target Fcb. If this succeeds, we
  1616. // are done. Otherwise we will check for delete child access on the
  1617. // the parent. Either is sufficient to perform the delete.
  1618. //
  1619. //
  1620. // Check if we need to load the security descriptor for the file
  1621. //
  1622. if (ParentFcb->SharedSecurity == NULL) {
  1623. NtfsLoadSecurityDescriptor( IrpContext, ParentFcb );
  1624. }
  1625. ASSERT( ParentFcb->SharedSecurity != NULL );
  1626. #ifdef NTFS_CACHE_RIGHTS
  1627. //
  1628. // Get any cached knowledge about rights that all callers are known to
  1629. // have for this security descriptor.
  1630. //
  1631. GrantedAccess = NtfsGetCachedRightsWorld( &ParentFcb->SharedSecurity->CachedRights );
  1632. ClearFlag( DesiredAccess, GrantedAccess );
  1633. }
  1634. if (DesiredAccess != 0) {
  1635. //
  1636. // Finally call the security package to check for delete access.
  1637. // We check for delete access on the target Fcb. If this succeeds, we
  1638. // are done. Otherwise we will check for delete child access on the
  1639. // the parent. Either is sufficient to perform the delete.
  1640. //
  1641. #endif
  1642. //
  1643. // Capture and lock the user context, do the access check and then unlock the context
  1644. //
  1645. SeLockSubjectContext( IrpContext->Union.SubjectContext );
  1646. UnlockSubjectContext = TRUE;
  1647. #ifdef NTFS_CACHE_RIGHTS
  1648. //
  1649. // Acquire any cached knowledge about rights that this caller
  1650. // is known to have for this security descriptor.
  1651. //
  1652. (VOID)NtfsGetCachedRights( ParentFcb->Vcb,
  1653. IrpContext->Union.SubjectContext,
  1654. ParentFcb->SharedSecurity,
  1655. &GrantedAccess,
  1656. NULL,
  1657. NULL,
  1658. NULL );
  1659. if (FlagOn( GrantedAccess, DELETE )) {
  1660. AccessGranted = TRUE;
  1661. Status = STATUS_SUCCESS;
  1662. } else {
  1663. #endif
  1664. AccessGranted = SeAccessCheck( &ParentFcb->SharedSecurity->SecurityDescriptor,
  1665. IrpContext->Union.SubjectContext,
  1666. TRUE, // Tokens are locked
  1667. DesiredAccess,
  1668. 0,
  1669. &Privileges,
  1670. IoGetFileObjectGenericMapping(),
  1671. UserMode,
  1672. &GrantedAccess,
  1673. &Status );
  1674. //
  1675. // If access is not granted then we will raise
  1676. //
  1677. if (!AccessGranted) {
  1678. DebugTrace( 0, Dbg, ("Access Denied\n") );
  1679. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  1680. }
  1681. #ifdef NTFS_CACHE_RIGHTS
  1682. }
  1683. #endif
  1684. }
  1685. } finally {
  1686. DebugUnwind( NtfsCheckIndexForAddOrDelete );
  1687. if (UnlockSubjectContext) {
  1688. SeUnlockSubjectContext( IrpContext->Union.SubjectContext );
  1689. }
  1690. DebugTrace( -1, Dbg, ("NtfsCheckIndexForAddOrDelete: Exit\n") );
  1691. }
  1692. return;
  1693. }
  1694. PSHARED_SECURITY
  1695. GetSharedSecurityFromDescriptorUnsafe (
  1696. IN PIRP_CONTEXT IrpContext,
  1697. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  1698. IN ULONG SecurityDescriptorLength,
  1699. IN BOOLEAN RaiseIfInvalid
  1700. )
  1701. /*++
  1702. Routine Description:
  1703. This routine is called to create or find a shared security structure
  1704. given an security descriptor. We check the parent if present to determine
  1705. if we have a matching security descriptor and reference the existing one if
  1706. so. This routine must be called while holding the Vcb so we can
  1707. safely access the parent structure.
  1708. Arguments:
  1709. IrpContext - context of call
  1710. SecurityId - Id (if known) of security descriptor.
  1711. SecurityDescriptor - Security Descriptor for this file.
  1712. SecurityDescriptorLength - Length of security descriptor for this file
  1713. RaiseIfInvalid - raise if the sd is invalid rather than supplying a default
  1714. used during a create as opposed to an open
  1715. Return Value:
  1716. PSHARED_SECURITY if found, NULL otherwise.
  1717. --*/
  1718. {
  1719. ULONG Hash = 0;
  1720. PSHARED_SECURITY SharedSecurity;
  1721. PAGED_CODE();
  1722. //
  1723. // Make sure the security descriptor we just read in is valid
  1724. //
  1725. if ((SecurityDescriptorLength == 0) ||
  1726. !SeValidSecurityDescriptor( SecurityDescriptorLength, SecurityDescriptor )) {
  1727. if (RaiseIfInvalid) {
  1728. NtfsRaiseStatus( IrpContext, STATUS_INVALID_SECURITY_DESCR, NULL, NULL );
  1729. }
  1730. SecurityDescriptor = NtfsData.DefaultDescriptor;
  1731. SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
  1732. if (!SeValidSecurityDescriptor( SecurityDescriptorLength, SecurityDescriptor )) {
  1733. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  1734. }
  1735. }
  1736. //
  1737. // Hash security descriptor. This hash must be position independent to
  1738. // allow for multiple instances of the same descriptor. It is assumed
  1739. // that the bits within the security descriptor are all position
  1740. // independent, i.e, no pointers, all offsets.
  1741. //
  1742. // For speed in the hash, we consider the security descriptor as an array
  1743. // of ULONGs. The fragment at the end that is ignored should not affect
  1744. // the collision nature of this hash.
  1745. //
  1746. {
  1747. PULONG Rover = (PULONG)SecurityDescriptor;
  1748. ULONG Count = SecurityDescriptorLength / 4;
  1749. while (Count--) {
  1750. Hash = ((Hash << 3) | (Hash >> (32-3))) + *Rover++;
  1751. }
  1752. }
  1753. DebugTrace( 0, DbgAcl, ("Hash is %08x\n", Hash) );
  1754. //
  1755. // try to find it by hash
  1756. //
  1757. SharedSecurity = FindCachedSharedSecurityByHashUnsafe( IrpContext->Vcb,
  1758. SecurityDescriptor,
  1759. SecurityDescriptorLength,
  1760. Hash );
  1761. //
  1762. // If we can't find an existing descriptor allocate new pool and copy
  1763. // security descriptor into it.
  1764. //
  1765. if (SharedSecurity == NULL) {
  1766. SharedSecurity = NtfsAllocatePool( PagedPool,
  1767. FIELD_OFFSET( SHARED_SECURITY, SecurityDescriptor )
  1768. + SecurityDescriptorLength );
  1769. SharedSecurity->ReferenceCount = 0;
  1770. //
  1771. // Initialize security index data in shared security
  1772. //
  1773. //
  1774. // Set the security id in the shared structure. If it is not
  1775. // invalid, also cache this shared security structure
  1776. //
  1777. SharedSecurity->Header.HashKey.SecurityId = SECURITY_ID_INVALID;
  1778. SharedSecurity->Header.HashKey.Hash = Hash;
  1779. SetSharedSecurityLength(SharedSecurity, SecurityDescriptorLength);
  1780. SharedSecurity->Header.Offset = (ULONGLONG) 0xFFFFFFFFFFFFFFFFi64;
  1781. RtlCopyMemory( &SharedSecurity->SecurityDescriptor,
  1782. SecurityDescriptor,
  1783. SecurityDescriptorLength );
  1784. #ifdef NTFS_CACHE_RIGHTS
  1785. //
  1786. // Initialize the cached rights.
  1787. //
  1788. RtlZeroMemory( &SharedSecurity->CachedRights,
  1789. sizeof( CACHED_ACCESS_RIGHTS ));
  1790. #endif
  1791. }
  1792. DebugTrace( 0, DbgAcl, ("GetSharedSecurityFromDescriptorUnsafe found %08x with Id %08x\n",
  1793. SharedSecurity, SharedSecurity->Header.HashKey.SecurityId ));
  1794. return SharedSecurity;
  1795. }
  1796. VOID
  1797. NtfsSetFcbSecurityFromDescriptor (
  1798. IN PIRP_CONTEXT IrpContext,
  1799. IN OUT PFCB Fcb,
  1800. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  1801. IN ULONG SecurityDescriptorLength,
  1802. IN BOOLEAN RaiseIfInvalid
  1803. )
  1804. /*++
  1805. Routine Description:
  1806. This routine is called to fill in the shared security structure in
  1807. an Fcb. We check the parent if present to determine if we have
  1808. a matching security descriptor and reference the existing one if
  1809. so. This routine must be called while holding the Vcb so we can
  1810. safely access the parent structure.
  1811. Arguments:
  1812. IrpContext - context of call
  1813. Fcb - Supplies the fcb for the file being operated on
  1814. SecurityDescriptor - Security Descriptor for this file.
  1815. SecurityDescriptorLength - Length of security descriptor for this file
  1816. Return Value:
  1817. None.
  1818. --*/
  1819. {
  1820. PSHARED_SECURITY SharedSecurity;
  1821. PAGED_CODE( );
  1822. NtfsAcquireFcbSecurity( Fcb->Vcb );
  1823. try {
  1824. SharedSecurity = GetSharedSecurityFromDescriptorUnsafe( IrpContext,
  1825. SecurityDescriptor,
  1826. SecurityDescriptorLength,
  1827. RaiseIfInvalid );
  1828. SharedSecurity->ReferenceCount += 1;
  1829. DebugTrace( +1, DbgAcl, ("NtfsSetFcbSecurityFromDescriptor bumping refcount %08x\n", SharedSecurity ));
  1830. ASSERT( Fcb->SharedSecurity == NULL );
  1831. Fcb->SharedSecurity = SharedSecurity;
  1832. AddCachedSharedSecurityUnsafe( IrpContext->Vcb, SharedSecurity );
  1833. } finally {
  1834. NtfsReleaseFcbSecurity( Fcb->Vcb );
  1835. }
  1836. return;
  1837. }
  1838. BOOLEAN
  1839. NtfsNotifyTraverseCheck (
  1840. IN PCCB Ccb,
  1841. IN PFCB Fcb,
  1842. IN PSECURITY_SUBJECT_CONTEXT SubjectContext
  1843. )
  1844. /*++
  1845. Routine Description:
  1846. This routine is the callback routine provided to the dir notify package
  1847. to check that a caller who is watching a tree has traverse access to
  1848. the directory which has the change. This routine is only called
  1849. when traverse access checking was turned on for the open used to
  1850. perform the watch.
  1851. Arguments:
  1852. Ccb - This is the Ccb associated with the directory which is being
  1853. watched.
  1854. Fcb - This is the Fcb for the directory which contains the file being
  1855. modified. We want to walk up the tree from this point and check
  1856. that the caller has traverse access across that directory.
  1857. If not specified then there is no work to do.
  1858. SubjectContext - This is the subject context captured at the time the
  1859. dir notify call was made.
  1860. Return Value:
  1861. BOOLEAN - TRUE if the caller has traverse access to the file which was
  1862. changed. FALSE otherwise.
  1863. --*/
  1864. {
  1865. TOP_LEVEL_CONTEXT TopLevelContext;
  1866. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  1867. PFCB TopFcb;
  1868. IRP_CONTEXT LocalIrpContext;
  1869. IRP LocalIrp;
  1870. PIRP_CONTEXT IrpContext;
  1871. BOOLEAN AccessGranted;
  1872. ACCESS_MASK GrantedAccess;
  1873. NTSTATUS Status = STATUS_SUCCESS;
  1874. #ifdef NTFS_CACHE_RIGHTS
  1875. NTSTATUS TokenInfoStatus = STATUS_UNSUCCESSFUL;
  1876. #endif
  1877. PPRIVILEGE_SET Privileges = NULL;
  1878. PAGED_CODE();
  1879. //
  1880. // If we have no Fcb then we can return immediately.
  1881. //
  1882. if (Fcb == NULL) {
  1883. return TRUE;
  1884. }
  1885. IrpContext = &LocalIrpContext;
  1886. NtfsInitializeIrpContext( NULL, TRUE, &IrpContext );
  1887. IrpContext->OriginatingIrp = &LocalIrp;
  1888. IrpContext->Vcb = Fcb->Vcb;
  1889. //
  1890. // Make sure we don't get any pop-ups
  1891. //
  1892. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, FALSE );
  1893. ASSERT( ThreadTopLevelContext == &TopLevelContext );
  1894. NtfsUpdateIrpContextWithTopLevel( IrpContext, &TopLevelContext );
  1895. TopFcb = Ccb->Lcb->Fcb;
  1896. //
  1897. // Use a try-except to catch all of the errors.
  1898. //
  1899. try {
  1900. //
  1901. // Always lock the subject context.
  1902. //
  1903. SeLockSubjectContext( SubjectContext );
  1904. //
  1905. // Use a try-finally to perform local cleanup.
  1906. //
  1907. try {
  1908. //
  1909. // We look while walking up the tree.
  1910. //
  1911. do {
  1912. #ifdef NTFS_CACHE_RIGHTS
  1913. LUID ModifiedId;
  1914. LUID TokenId;
  1915. #endif
  1916. PLCB ParentLcb;
  1917. //
  1918. // Since this is a directory it can have only one parent. So
  1919. // we can use any Lcb to walk upwards.
  1920. //
  1921. ParentLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
  1922. LCB,
  1923. FcbLinks );
  1924. Fcb = ParentLcb->Scb->Fcb;
  1925. //
  1926. // Check if we need to load the security descriptor for the file
  1927. //
  1928. if (Fcb->SharedSecurity == NULL) {
  1929. NtfsLoadSecurityDescriptor( IrpContext, Fcb );
  1930. }
  1931. #ifdef NTFS_CACHE_RIGHTS
  1932. //
  1933. // Acquire any cached knowledge about rights that this caller
  1934. // is known to have for this security descriptor.
  1935. //
  1936. // Note that we can trust that TokenId and ModifiedId won't
  1937. // change inside this block of code because we have locked
  1938. // the subject context above.
  1939. //
  1940. if (TokenInfoStatus != STATUS_SUCCESS) {
  1941. //
  1942. // We have not previously acquired the Id information.
  1943. //
  1944. TokenInfoStatus = NtfsGetCachedRights( Fcb->Vcb,
  1945. SubjectContext,
  1946. Fcb->SharedSecurity,
  1947. &GrantedAccess,
  1948. NULL,
  1949. &TokenId,
  1950. &ModifiedId );
  1951. } else {
  1952. NtfsGetCachedRightsById( Fcb->Vcb,
  1953. &TokenId,
  1954. &ModifiedId,
  1955. SubjectContext,
  1956. Fcb->SharedSecurity,
  1957. NULL,
  1958. &GrantedAccess );
  1959. }
  1960. if (FlagOn( GrantedAccess, FILE_TRAVERSE )) {
  1961. AccessGranted = TRUE;
  1962. } else {
  1963. #endif
  1964. AccessGranted = SeAccessCheck( &Fcb->SharedSecurity->SecurityDescriptor,
  1965. SubjectContext,
  1966. TRUE, // Tokens are locked
  1967. FILE_TRAVERSE,
  1968. 0,
  1969. &Privileges,
  1970. IoGetFileObjectGenericMapping(),
  1971. UserMode,
  1972. &GrantedAccess,
  1973. &Status );
  1974. #ifdef NTFS_CACHE_RIGHTS
  1975. }
  1976. #endif
  1977. } while (AccessGranted && (Fcb != TopFcb));
  1978. } finally {
  1979. SeUnlockSubjectContext( SubjectContext );
  1980. }
  1981. } except (NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  1982. NOTHING;
  1983. }
  1984. NtfsCleanupIrpContext( IrpContext, TRUE );
  1985. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  1986. return AccessGranted;
  1987. }
  1988. VOID
  1989. NtfsInitializeSecurity (
  1990. IN PIRP_CONTEXT IrpContext,
  1991. IN PVCB Vcb,
  1992. IN PFCB Fcb
  1993. )
  1994. /*++
  1995. Routine Description:
  1996. This routine is called to initialize the security indexes and descriptor
  1997. stream.
  1998. Arguments:
  1999. IrpContext - context of call
  2000. Vcb - Supplies the volume being initialized
  2001. Fcb - Supplies the file containing the seurity indexes and descriptor
  2002. stream.
  2003. Return Value:
  2004. None.
  2005. --*/
  2006. {
  2007. UNICODE_STRING SecurityIdIndexName = CONSTANT_UNICODE_STRING( L"$SII" );
  2008. UNICODE_STRING SecurityDescriptorHashIndexName = CONSTANT_UNICODE_STRING( L"$SDH" );
  2009. UNICODE_STRING SecurityDescriptorStreamName = CONSTANT_UNICODE_STRING( L"$SDS" );
  2010. MAP_HANDLE Map;
  2011. NTSTATUS Status;
  2012. PAGED_CODE( );
  2013. //
  2014. // Open/Create the security descriptor stream
  2015. //
  2016. NtOfsCreateAttribute( IrpContext,
  2017. Fcb,
  2018. SecurityDescriptorStreamName,
  2019. CREATE_OR_OPEN,
  2020. TRUE,
  2021. &Vcb->SecurityDescriptorStream );
  2022. NtfsAcquireSharedScb( IrpContext, Vcb->SecurityDescriptorStream );
  2023. //
  2024. // Load the run information for the Security data stream.
  2025. // Note this call must be done after the stream is nonresident.
  2026. //
  2027. if (!FlagOn( Vcb->SecurityDescriptorStream->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  2028. NtfsPreloadAllocation( IrpContext,
  2029. Vcb->SecurityDescriptorStream,
  2030. 0,
  2031. MAXLONGLONG );
  2032. }
  2033. //
  2034. // Open the Security descriptor indexes and storage.
  2035. //
  2036. NtOfsCreateIndex( IrpContext,
  2037. Fcb,
  2038. SecurityIdIndexName,
  2039. CREATE_OR_OPEN,
  2040. 0,
  2041. COLLATION_NTOFS_ULONG,
  2042. NtOfsCollateUlong,
  2043. NULL,
  2044. &Vcb->SecurityIdIndex );
  2045. NtOfsCreateIndex( IrpContext,
  2046. Fcb,
  2047. SecurityDescriptorHashIndexName,
  2048. CREATE_OR_OPEN,
  2049. 0,
  2050. COLLATION_NTOFS_SECURITY_HASH,
  2051. NtOfsCollateSecurityHash,
  2052. NULL,
  2053. &Vcb->SecurityDescriptorHashIndex );
  2054. //
  2055. // Retrieve the next security Id to allocate
  2056. //
  2057. try {
  2058. SECURITY_ID LastSecurityId = 0xFFFFFFFF;
  2059. INDEX_KEY LastKey;
  2060. INDEX_ROW LastRow;
  2061. LastKey.KeyLength = sizeof( SECURITY_ID );
  2062. LastKey.Key = &LastSecurityId;
  2063. Map.Bcb = NULL;
  2064. Status = NtOfsFindLastRecord( IrpContext,
  2065. Vcb->SecurityIdIndex,
  2066. &LastKey,
  2067. &LastRow,
  2068. &Map );
  2069. //
  2070. // If we've found the last key, set the next Id to allocate to be
  2071. // one greater than this last key.
  2072. //
  2073. if (Status == STATUS_SUCCESS) {
  2074. ASSERT( LastRow.KeyPart.KeyLength == sizeof( SECURITY_ID ) );
  2075. if (LastRow.KeyPart.KeyLength != sizeof( SECURITY_ID )) {
  2076. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  2077. }
  2078. DebugTrace( 0, DbgAcl, ("Found last security Id in index\n") );
  2079. Vcb->NextSecurityId = *(SECURITY_ID *)LastRow.KeyPart.Key + 1;
  2080. //
  2081. // If the index is empty, then set the next Id to be the beginning of the
  2082. // user range.
  2083. //
  2084. } else if (Status == STATUS_NO_MATCH) {
  2085. DebugTrace( 0, DbgAcl, ("Security Id index is empty\n") );
  2086. Vcb->NextSecurityId = SECURITY_ID_FIRST;
  2087. } else {
  2088. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  2089. }
  2090. DebugTrace( 0, DbgAcl, ("NextSecurityId is %x\n", Vcb->NextSecurityId) );
  2091. } finally {
  2092. NtOfsReleaseMap( IrpContext, &Map );
  2093. }
  2094. }
  2095. PSHARED_SECURITY
  2096. NtfsCacheSharedSecurityBySecurityId (
  2097. IN PIRP_CONTEXT IrpContext,
  2098. IN PVCB Vcb,
  2099. IN SECURITY_ID SecurityId
  2100. )
  2101. /*++
  2102. Routine Description:
  2103. This routine maps looks up a shared security structure given the security Id by
  2104. looking in the per-Vcb cache or loads it if not present.
  2105. Arguments:
  2106. IrpContext - Context of call
  2107. Vcb - Volume where security Id is cached
  2108. SecurityId - security Id for descriptor that is being retrieved
  2109. Return Value:
  2110. Referenced PSHARED_SECURITY of found descriptor.
  2111. --*/
  2112. {
  2113. PSHARED_SECURITY *Bucket;
  2114. PSHARED_SECURITY SharedSecurity;
  2115. PBCB Bcb;
  2116. PSECURITY_DESCRIPTOR_HEADER SecurityDescriptorHeader;
  2117. PAGED_CODE( );
  2118. NtfsAcquireFcbSecurity( Vcb );
  2119. //
  2120. // Probe the cache by Id
  2121. //
  2122. Bucket = Vcb->SecurityCacheById[SecurityId % VCB_SECURITY_CACHE_BY_ID_SIZE];
  2123. //
  2124. // We get a match under the following conditions.
  2125. //
  2126. // - There is a corresponding entry in the SecurityID array
  2127. // - This entry points to an entry in the SecurityHash array
  2128. // - The entry in the SecurityHash array has the correct SecurityID
  2129. //
  2130. if ((Bucket != NULL) &&
  2131. ((SharedSecurity = *Bucket) != NULL) &&
  2132. (SharedSecurity->Header.HashKey.SecurityId == SecurityId)) {
  2133. DebugTrace( 0, DbgAcl, ("Found cached security descriptor %x %x\n",
  2134. SharedSecurity, SharedSecurity->Header.HashKey.SecurityId) );
  2135. DebugTrace( 0, DbgAcl, ("NtfsCacheSharedSecurityBySecurityId bumping refcount %08x\n", SharedSecurity ));
  2136. //
  2137. // We found the correct shared security. Make sure it cannot go
  2138. // away on us.
  2139. //
  2140. SharedSecurity->ReferenceCount += 1;
  2141. NtfsReleaseFcbSecurity( Vcb );
  2142. return SharedSecurity;
  2143. }
  2144. //
  2145. // If we get here we didn't find a matching descriptor. Throw away
  2146. // the incorrect security descriptor we may have found through the
  2147. // SecurityID array.
  2148. //
  2149. SharedSecurity = NULL;
  2150. NtfsReleaseFcbSecurity( Vcb );
  2151. //
  2152. // If we don't have a security index, then return the default security descriptor.
  2153. // This should only happen in cases of corrupted volumes or security indices.
  2154. //
  2155. if (Vcb->SecurityDescriptorStream == NULL) {
  2156. DebugTrace( 0, 0, ("No security index present in Vcb, using default descriptor\n") );
  2157. return NULL;
  2158. }
  2159. //
  2160. // We don't have the descriptor in the cache and have to load it from disk.
  2161. //
  2162. Bcb = NULL;
  2163. DebugTrace( 0, DbgAcl, ("Looking up security descriptor %x\n", SecurityId) );
  2164. //
  2165. // Lock down the security stream
  2166. //
  2167. NtfsAcquireSharedScb( IrpContext, Vcb->SecurityDescriptorStream );
  2168. //
  2169. // Reacquire the security mutex
  2170. //
  2171. NtfsAcquireFcbSecurity( Vcb );
  2172. try {
  2173. //
  2174. // Consult the Vcb index to map to the security descriptor
  2175. //
  2176. if (!MapSecurityIdToSecurityDescriptorHeaderUnsafe( IrpContext,
  2177. Vcb,
  2178. SecurityId,
  2179. &SecurityDescriptorHeader,
  2180. &Bcb )) {
  2181. //
  2182. // We couldn't find the Id. We generate a security descriptor from
  2183. // the default one.
  2184. //
  2185. leave;
  2186. }
  2187. DebugTrace( 0, DbgAcl, ("Found it at %16I64X\n", SecurityDescriptorHeader->Offset) );
  2188. //
  2189. // Look up the security descriptor by hash (just in case)
  2190. //
  2191. SharedSecurity = FindCachedSharedSecurityByHashUnsafe( Vcb,
  2192. (PSECURITY_DESCRIPTOR) ( SecurityDescriptorHeader + 1 ),
  2193. GETSECURITYDESCRIPTORLENGTH( SecurityDescriptorHeader ),
  2194. SecurityDescriptorHeader->HashKey.Hash );
  2195. //
  2196. // If not found
  2197. //
  2198. if (SharedSecurity == NULL) {
  2199. DebugTrace( 0, DbgAcl, ("Not in hash table, creating new SHARED SECURITY\n") );
  2200. SharedSecurity = NtfsAllocatePool( PagedPool,
  2201. FIELD_OFFSET( SHARED_SECURITY, Header ) + SecurityDescriptorHeader->Length );
  2202. SharedSecurity->ReferenceCount = 0;
  2203. RtlCopyMemory( &SharedSecurity->Header,
  2204. SecurityDescriptorHeader,
  2205. SecurityDescriptorHeader->Length );
  2206. #ifdef NTFS_CACHE_RIGHTS
  2207. //
  2208. // Initialize the cached rights.
  2209. //
  2210. RtlZeroMemory( &SharedSecurity->CachedRights,
  2211. sizeof( CACHED_ACCESS_RIGHTS ));
  2212. #endif
  2213. } else {
  2214. DebugTrace( 0, DbgAcl, ("Found in hash table %x, promoting header\n", SharedSecurity) );
  2215. //
  2216. // We found the descriptor by hash. Perform some consistency checks
  2217. //
  2218. #if DBG
  2219. if (SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID &&
  2220. SharedSecurity->Header.HashKey.SecurityId != SecurityId )
  2221. DebugTrace( 0, 0, ("Duplicate hash entry found %x %x\n", SecurityId,
  2222. SharedSecurity->Header.HashKey.SecurityId ));
  2223. #endif
  2224. SharedSecurity->Header = *SecurityDescriptorHeader;
  2225. }
  2226. //
  2227. // reference the security descriptor
  2228. //
  2229. SharedSecurity->ReferenceCount += 1;
  2230. //
  2231. // Regardless of whether we found it by hash (since the earlier Id probe missed)
  2232. // or created it anew. Let's put it back into the cache
  2233. //
  2234. AddCachedSharedSecurityUnsafe( Vcb, SharedSecurity );
  2235. } finally {
  2236. NtfsUnpinBcb( IrpContext, &Bcb );
  2237. NtfsReleaseScb( IrpContext, Vcb->SecurityDescriptorStream );
  2238. //
  2239. // Release access to security cache
  2240. //
  2241. NtfsReleaseFcbSecurity( Vcb );
  2242. }
  2243. //
  2244. // if we did not generate a shared security, then build one from
  2245. // the default security descriptor
  2246. //
  2247. if (SharedSecurity == NULL) {
  2248. DebugTrace( 0, 0, ("Security Id %x not found, using default\n", SecurityId) );
  2249. SharedSecurity = NtfsCacheSharedSecurityByDescriptor( IrpContext,
  2250. NtfsData.DefaultDescriptor,
  2251. NtfsData.DefaultDescriptorLength,
  2252. FALSE );
  2253. }
  2254. return SharedSecurity;
  2255. }
  2256. //
  2257. // Local Support routine
  2258. //
  2259. PSHARED_SECURITY
  2260. FindCachedSharedSecurityByHashUnsafe (
  2261. IN PVCB Vcb,
  2262. IN PSECURITY_DESCRIPTOR SecurityDescriptor,
  2263. IN ULONG SecurityDescriptorLength,
  2264. IN ULONG Hash
  2265. )
  2266. /*++
  2267. Routine Description:
  2268. This routine maps looks up a shared security structure given the Hash by
  2269. looking in the per-Vcb cache. This routine assumes exclusive access to the
  2270. security cache.
  2271. Arguments:
  2272. Vcb - Volume where security Id is cached
  2273. SecurityDescriptor - Security descriptor being retrieved
  2274. SecurityDescriptorLength - length of descriptor.
  2275. Hash - Hash for descriptor that is being retrieved
  2276. Return Value:
  2277. PSHARED_SECURITY of found shared descriptor. Otherwise, NULL is returned.
  2278. --*/
  2279. {
  2280. PSHARED_SECURITY SharedSecurity;
  2281. PAGED_CODE( );
  2282. //
  2283. // Hash the hash into the per-volume table
  2284. SharedSecurity = Vcb->SecurityCacheByHash[Hash % VCB_SECURITY_CACHE_BY_HASH_SIZE];
  2285. //
  2286. // If there is no shared descriptor there, then no match
  2287. //
  2288. if (SharedSecurity == NULL) {
  2289. return NULL;
  2290. }
  2291. //
  2292. // if the hash doesn't match then no descriptor found
  2293. //
  2294. if (SharedSecurity->Header.HashKey.Hash != Hash) {
  2295. return NULL;
  2296. }
  2297. //
  2298. // If the lengths don't match then no descriptor found
  2299. //
  2300. if (GetSharedSecurityLength( SharedSecurity ) != SecurityDescriptorLength) {
  2301. return NULL;
  2302. }
  2303. //
  2304. // If the security descriptor bits don't compare then no match
  2305. //
  2306. if (!RtlEqualMemory( SharedSecurity->SecurityDescriptor,
  2307. SecurityDescriptor,
  2308. SecurityDescriptorLength) ) {
  2309. return NULL;
  2310. }
  2311. //
  2312. // The shared security was found
  2313. //
  2314. return SharedSecurity;
  2315. }
  2316. //
  2317. // Local Support routine
  2318. //
  2319. VOID
  2320. AddCachedSharedSecurityUnsafe (
  2321. IN PVCB Vcb,
  2322. PSHARED_SECURITY SharedSecurity
  2323. )
  2324. /*++
  2325. Routine Description:
  2326. This routine adds shared security to the Vcb Cache. This routine assumes
  2327. exclusive access to the security cache. The shared security being added
  2328. may have a ref count of one and may already be in the table.
  2329. Arguments:
  2330. Vcb - Volume where security Id is cached
  2331. SharedSecurity - descriptor to be added to the cache
  2332. Return Value:
  2333. None.
  2334. --*/
  2335. {
  2336. PSHARED_SECURITY *Bucket;
  2337. PSHARED_SECURITY Old;
  2338. PAGED_CODE( );
  2339. //
  2340. // Is there an item already in the hash bucket?
  2341. //
  2342. Bucket = &Vcb->SecurityCacheByHash[SharedSecurity->Header.HashKey.Hash % VCB_SECURITY_CACHE_BY_HASH_SIZE];
  2343. Old = *Bucket;
  2344. //
  2345. // Place it into the bucket and reference it
  2346. //
  2347. *Bucket = SharedSecurity;
  2348. SharedSecurity->ReferenceCount += 1;
  2349. DebugTrace( 0, DbgAcl, ("AddCachedSharedSecurityUnsafe bumping refcount %08x\n", SharedSecurity ));
  2350. //
  2351. // Set up hash to point to bucket
  2352. //
  2353. Vcb->SecurityCacheById[SharedSecurity->Header.HashKey.SecurityId % VCB_SECURITY_CACHE_BY_ID_SIZE] = Bucket;
  2354. //
  2355. // Handle removing the old value from the bucket. We do this after advancing
  2356. // the ReferenceCount above in the case where the item is already in the bucket.
  2357. //
  2358. if (Old != NULL) {
  2359. //
  2360. // Remove and dereference the item in the bucket
  2361. //
  2362. RemoveReferenceSharedSecurityUnsafe( &Old );
  2363. }
  2364. return;
  2365. }
  2366. VOID
  2367. NtOfsPurgeSecurityCache (
  2368. IN PVCB Vcb
  2369. )
  2370. /*++
  2371. Routine Description:
  2372. This routine removes all shared security from the per-Vcb cache.
  2373. Arguments:
  2374. Vcb - Volume where descriptors are cached
  2375. Return Value:
  2376. None.
  2377. --*/
  2378. {
  2379. ULONG i;
  2380. PAGED_CODE( );
  2381. //
  2382. // Serialize access to the security cache
  2383. //
  2384. NtfsAcquireFcbSecurity( Vcb );
  2385. //
  2386. // Walk through the cache looking for cached security
  2387. //
  2388. for (i = 0; i < VCB_SECURITY_CACHE_BY_ID_SIZE; i++)
  2389. {
  2390. if (Vcb->SecurityCacheByHash[i] != NULL) {
  2391. //
  2392. // Remove the reference to the security
  2393. //
  2394. PSHARED_SECURITY SharedSecurity = Vcb->SecurityCacheByHash[i];
  2395. Vcb->SecurityCacheByHash[i] = NULL;
  2396. RemoveReferenceSharedSecurityUnsafe( &SharedSecurity );
  2397. }
  2398. }
  2399. //
  2400. // Release access to the cache
  2401. //
  2402. NtfsReleaseFcbSecurity( Vcb );
  2403. }
  2404. //
  2405. // Local Support routine
  2406. //
  2407. BOOLEAN
  2408. MapSecurityIdToSecurityDescriptorHeaderUnsafe (
  2409. IN PIRP_CONTEXT IrpContext,
  2410. IN PVCB Vcb,
  2411. IN SECURITY_ID SecurityId,
  2412. OUT PSECURITY_DESCRIPTOR_HEADER *SecurityDescriptorHeader,
  2413. OUT PBCB *Bcb
  2414. )
  2415. /*++
  2416. Routine Description:
  2417. This routine maps from a security Id to the descriptor bits stored in the
  2418. security descriptor stream using the security Id index.
  2419. Arguments:
  2420. IrpContext - Context of the call
  2421. Vcb - Volume where descriptor is stored
  2422. SecurityId - security Id for descriptor that is being retrieved
  2423. SecurityDescriptorHeader - returned security descriptor pointer
  2424. Bcb - returned mapping control structure
  2425. Return Value:
  2426. True if the descriptor header was successfully mapped.
  2427. --*/
  2428. {
  2429. SECURITY_DESCRIPTOR_HEADER Header;
  2430. NTSTATUS Status;
  2431. MAP_HANDLE Map;
  2432. INDEX_ROW Row;
  2433. INDEX_KEY Key;
  2434. PSECURITY_DESCRIPTOR SecurityDescriptor;
  2435. PAGED_CODE( );
  2436. DebugTrace( 0, DbgAcl, ("Mapping security ID %08x\n", SecurityId) );
  2437. //
  2438. // Lookup descriptor stream position information.
  2439. // The format of the key is simply the ULONG SecurityId
  2440. //
  2441. Key.KeyLength = sizeof( SecurityId );
  2442. Key.Key = &SecurityId;
  2443. Status = NtOfsFindRecord( IrpContext,
  2444. Vcb->SecurityIdIndex,
  2445. &Key,
  2446. &Row,
  2447. &Map,
  2448. NULL );
  2449. DebugTrace( 0, DbgAcl, ("Security Id lookup status = %08x\n", Status) );
  2450. //
  2451. // If the security Id is not found, we let the called decide if the volume
  2452. // needs fixing or whether a default descriptor should be used.
  2453. //
  2454. if (Status == STATUS_NO_MATCH) {
  2455. return FALSE;
  2456. }
  2457. //
  2458. // Save security descriptor offset and length information
  2459. //
  2460. Header = *(PSECURITY_DESCRIPTOR_HEADER)Row.DataPart.Data;
  2461. ASSERT( Header.HashKey.SecurityId == SecurityId );
  2462. //
  2463. // Release mapping information
  2464. //
  2465. NtOfsReleaseMap( IrpContext, &Map );
  2466. //
  2467. // Make sure that the data is the correct size. This is a true failure case
  2468. // where we must fix the disk up. We can just return false because caller
  2469. // will then use a default sd and chkdsk will replace with the same default
  2470. // when it next verifies the disk
  2471. //
  2472. ASSERT( Row.DataPart.DataLength == sizeof( SECURITY_DESCRIPTOR_HEADER ) );
  2473. if (Row.DataPart.DataLength != sizeof( SECURITY_DESCRIPTOR_HEADER )) {
  2474. DebugTrace( 0, DbgAcl, ("SecurityId data doesn't have the correct length\n") );
  2475. return FALSE;
  2476. }
  2477. //
  2478. // Don't try to map clearly invalid sections of the sds stream
  2479. //
  2480. if (Header.Offset > (ULONGLONG)(Vcb->SecurityDescriptorStream->Header.FileSize.QuadPart) ||
  2481. Header.Offset + Header.Length > (ULONGLONG)(Vcb->SecurityDescriptorStream->Header.FileSize.QuadPart)) {
  2482. DebugTrace( 0, DbgAcl, ("SecurityId data doesn't have a correct position\n") );
  2483. return FALSE;
  2484. }
  2485. //
  2486. // Map security descriptor
  2487. //
  2488. DebugTrace( 0, DbgAcl, ("Mapping security descriptor stream at %I64x, len %x\n",
  2489. Header.Offset, Header.Length) );
  2490. NtfsMapStream(
  2491. IrpContext,
  2492. Vcb->SecurityDescriptorStream,
  2493. Header.Offset,
  2494. Header.Length,
  2495. Bcb,
  2496. SecurityDescriptorHeader );
  2497. //
  2498. // Sanity check the found descriptor
  2499. //
  2500. if (RtlCompareMemory( &Header, *SecurityDescriptorHeader, sizeof( Header )) != sizeof( Header )) {
  2501. DebugTrace( 0, DbgAcl, ("Index data does not match stream header\n") );
  2502. return FALSE;
  2503. }
  2504. //
  2505. // Now actually verify the descriptor is valid. If length is too small (even 0)
  2506. // SeValidSecurityDescriptor will safely return false so we don't need to test this
  2507. // before calling
  2508. //
  2509. SecurityDescriptor = (PSECURITY_DESCRIPTOR) Add2Ptr( (*SecurityDescriptorHeader), sizeof( SECURITY_DESCRIPTOR_HEADER ) );
  2510. if (!SeValidSecurityDescriptor( GETSECURITYDESCRIPTORLENGTH( *SecurityDescriptorHeader ), SecurityDescriptor )) {
  2511. DebugTrace( 0, DbgAcl, ("SecurityId data is not valid\n") );
  2512. return FALSE;
  2513. }
  2514. #if DBG
  2515. {
  2516. ULONG SecurityDescLength;
  2517. SecurityDescLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
  2518. ASSERT( SecurityDescLength == GETSECURITYDESCRIPTORLENGTH( *SecurityDescriptorHeader ) );
  2519. }
  2520. #endif
  2521. return TRUE;
  2522. }
  2523. VOID
  2524. NtfsLoadSecurityDescriptor (
  2525. IN PIRP_CONTEXT IrpContext,
  2526. IN PFCB Fcb
  2527. )
  2528. /*++
  2529. Routine Description:
  2530. This routine loads the shared security descriptor into the fcb for the
  2531. file from disk using either the SecurityId or the $Security_Descriptor
  2532. Arguments:
  2533. Fcb - Supplies the fcb for the file being operated on
  2534. Return Value:
  2535. None.
  2536. --*/
  2537. {
  2538. PAGED_CODE();
  2539. ASSERTMSG("Must only be called with a null value here", Fcb->SharedSecurity == NULL);
  2540. DebugTrace( +1, DbgAcl, ("NtfsLoadSecurityDescriptor...\n") );
  2541. //
  2542. // If the file has a valid SecurityId then retrieve the security descriptor
  2543. // from the security descriptor index
  2544. //
  2545. if ((Fcb->SecurityId != SECURITY_ID_INVALID) &&
  2546. (Fcb->Vcb->SecurityDescriptorStream != NULL)) {
  2547. ASSERT( Fcb->SharedSecurity == NULL );
  2548. Fcb->SharedSecurity = NtfsCacheSharedSecurityBySecurityId( IrpContext,
  2549. Fcb->Vcb,
  2550. Fcb->SecurityId );
  2551. ASSERT( Fcb->SharedSecurity != NULL );
  2552. } else {
  2553. PBCB Bcb = NULL;
  2554. PSECURITY_DESCRIPTOR SecurityDescriptor;
  2555. ULONG SecurityDescriptorLength;
  2556. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  2557. PATTRIBUTE_RECORD_HEADER Attribute;
  2558. try {
  2559. //
  2560. // Read in the security descriptor attribute, and if it is not present
  2561. // then there then the file is not protected. In that case we will
  2562. // use the default descriptor.
  2563. //
  2564. NtfsInitializeAttributeContext( &AttributeContext );
  2565. if (!NtfsLookupAttributeByCode( IrpContext,
  2566. Fcb,
  2567. &Fcb->FileReference,
  2568. $SECURITY_DESCRIPTOR,
  2569. &AttributeContext )) {
  2570. DebugTrace( 0, DbgAcl, ("Security Descriptor attribute does not exist\n") );
  2571. SecurityDescriptor = NtfsData.DefaultDescriptor;
  2572. SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
  2573. } else {
  2574. //
  2575. // There must be a security descriptor with a non-zero length; only
  2576. // applies for non-resident descriptors with valid data length.
  2577. //
  2578. Attribute = NtfsFoundAttribute( &AttributeContext );
  2579. if (NtfsIsAttributeResident( Attribute ) ?
  2580. (Attribute->Form.Resident.ValueLength == 0) :
  2581. (Attribute->Form.Nonresident.ValidDataLength == 0)) {
  2582. SecurityDescriptor = NtfsData.DefaultDescriptor;
  2583. SecurityDescriptorLength = NtfsData.DefaultDescriptorLength;
  2584. } else {
  2585. NtfsMapAttributeValue( IrpContext,
  2586. Fcb,
  2587. (PVOID *)&SecurityDescriptor,
  2588. &SecurityDescriptorLength,
  2589. &Bcb,
  2590. &AttributeContext );
  2591. }
  2592. }
  2593. NtfsSetFcbSecurityFromDescriptor(
  2594. IrpContext,
  2595. Fcb,
  2596. SecurityDescriptor,
  2597. SecurityDescriptorLength,
  2598. FALSE );
  2599. } finally {
  2600. DebugUnwind( NtfsLoadSecurityDescriptor );
  2601. //
  2602. // Cleanup our attribute enumeration context and the Bcb
  2603. //
  2604. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  2605. NtfsUnpinBcb( IrpContext, &Bcb );
  2606. }
  2607. }
  2608. //
  2609. // And return to our caller
  2610. //
  2611. DebugTrace( -1, DbgAcl, ("NtfsLoadSecurityDescriptor -> VOID\n") );
  2612. return;
  2613. }
  2614. //
  2615. // Local Support routine
  2616. //
  2617. NTSTATUS
  2618. NtOfsMatchSecurityHash (
  2619. IN PINDEX_ROW IndexRow,
  2620. IN OUT PVOID MatchData
  2621. )
  2622. /*++
  2623. Routine Description:
  2624. Test whether an index row is worthy of returning based on its contents as
  2625. a row in the SecurityDescriptorHashIndex.
  2626. Arguments:
  2627. IndexRow - row that is being tested
  2628. MatchData - a PVOID that is the hash function we look for.
  2629. Returns:
  2630. STATUS_SUCCESS if the IndexRow matches
  2631. STATUS_NO_MATCH if the IndexRow does not match, but the enumeration should
  2632. continue
  2633. STATUS_NO_MORE_MATCHES if the IndexRow does not match, and the enumeration
  2634. should terminate
  2635. --*/
  2636. {
  2637. ASSERT(IndexRow->KeyPart.KeyLength == sizeof( SECURITY_HASH_KEY ) );
  2638. PAGED_CODE( );
  2639. if (((PSECURITY_HASH_KEY)IndexRow->KeyPart.Key)->Hash == (ULONG)((ULONG_PTR) MatchData)) {
  2640. return STATUS_SUCCESS;
  2641. } else {
  2642. return STATUS_NO_MORE_MATCHES;
  2643. }
  2644. }
  2645. //
  2646. // Local Support routine
  2647. //
  2648. VOID
  2649. NtOfsLookupSecurityDescriptorInIndex (
  2650. PIRP_CONTEXT IrpContext,
  2651. IN OUT PSHARED_SECURITY SharedSecurity
  2652. )
  2653. /*++
  2654. Routine Description:
  2655. Look up the security descriptor in the index. If found, return the
  2656. security ID.
  2657. Arguments:
  2658. IrpContext - context of the call
  2659. SharedSecurity - shared security for a file
  2660. Return Value:
  2661. None.
  2662. --*/
  2663. {
  2664. PAGED_CODE( );
  2665. DebugTrace( +1, DbgAcl, ("NtOfsLookupSecurityDescriptorInIndex...\n") );
  2666. //
  2667. // For each matching hash record in the index, see if the actual security
  2668. // security descriptor matches.
  2669. //
  2670. {
  2671. INDEX_KEY IndexKey;
  2672. INDEX_ROW FoundRow;
  2673. PSECURITY_DESCRIPTOR_HEADER Header;
  2674. UCHAR HashDescriptorHeader[2 * (sizeof( SECURITY_DESCRIPTOR_HEADER ) + sizeof( ULONG ))];
  2675. PINDEX_KEY Key = &IndexKey;
  2676. PREAD_CONTEXT ReadContext = NULL;
  2677. ULONG FoundCount = 0;
  2678. PBCB Bcb = NULL;
  2679. IndexKey.KeyLength = sizeof( SharedSecurity->Header.HashKey );
  2680. IndexKey.Key = &SharedSecurity->Header.HashKey.Hash;
  2681. try {
  2682. //
  2683. // We keep reading hash records until we find a hash.
  2684. //
  2685. while (SharedSecurity->Header.HashKey.SecurityId == SECURITY_ID_INVALID) {
  2686. //
  2687. // Read next matching SecurityHashIndex record
  2688. //
  2689. FoundCount = 1;
  2690. NtOfsReadRecords( IrpContext,
  2691. IrpContext->Vcb->SecurityDescriptorHashIndex,
  2692. &ReadContext,
  2693. Key,
  2694. NtOfsMatchSecurityHash,
  2695. ULongToPtr( SharedSecurity->Header.HashKey.Hash ),
  2696. &FoundCount,
  2697. &FoundRow,
  2698. sizeof( HashDescriptorHeader ),
  2699. &HashDescriptorHeader[0]);
  2700. //
  2701. // Set next read to read sequentially rather than explicitly
  2702. // seek.
  2703. //
  2704. Key = NULL;
  2705. //
  2706. // If there were no more records found, then go and establish a
  2707. // a new security Id.
  2708. //
  2709. if (FoundCount == 0) {
  2710. break;
  2711. }
  2712. //
  2713. // Examine the row to see if the descriptors are
  2714. // the same. Verify the cache contents.
  2715. //
  2716. ASSERT( FoundRow.DataPart.DataLength == sizeof( SECURITY_DESCRIPTOR_HEADER ) );
  2717. if (FoundRow.DataPart.DataLength != sizeof( SECURITY_DESCRIPTOR_HEADER )) {
  2718. DebugTrace( 0, DbgAcl, ("Found row has a bad size\n") );
  2719. NtfsRaiseStatus( IrpContext,
  2720. STATUS_DISK_CORRUPT_ERROR,
  2721. NULL, NULL );
  2722. }
  2723. Header = (PSECURITY_DESCRIPTOR_HEADER)FoundRow.DataPart.Data;
  2724. //
  2725. // If the length of the security descriptor in the stream is NOT
  2726. // the same as the current security descriptor, then a match is
  2727. // not possible
  2728. //
  2729. if (SharedSecurity->Header.Length != Header->Length) {
  2730. DebugTrace( 0, DbgAcl, ("Descriptor has wrong length\n") );
  2731. continue;
  2732. }
  2733. //
  2734. // Map security descriptor given descriptor stream position.
  2735. //
  2736. try {
  2737. PSECURITY_DESCRIPTOR_HEADER TestHeader;
  2738. NtfsMapStream( IrpContext,
  2739. IrpContext->Vcb->SecurityDescriptorStream,
  2740. Header->Offset,
  2741. Header->Length,
  2742. &Bcb,
  2743. &TestHeader );
  2744. //
  2745. // Make sure index data matches stream data
  2746. //
  2747. ASSERT( (TestHeader->HashKey.Hash == Header->HashKey.Hash) &&
  2748. (TestHeader->HashKey.SecurityId == Header->HashKey.SecurityId) &&
  2749. (TestHeader->Length == Header->Length) );
  2750. //
  2751. // Compare byte-for-byte the security descriptors. We do not
  2752. // perform any rearranging of descriptors into canonical forms.
  2753. //
  2754. if (RtlEqualMemory( SharedSecurity->SecurityDescriptor,
  2755. TestHeader + 1,
  2756. GetSharedSecurityLength( SharedSecurity )) ) {
  2757. //
  2758. // We have a match. Save the found header
  2759. //
  2760. SharedSecurity->Header = *TestHeader;
  2761. DebugTrace( 0, DbgAcl, ("Reusing indexed security Id %x\n",
  2762. TestHeader->HashKey.SecurityId) );
  2763. leave;
  2764. }
  2765. DebugTrace( 0, 0, ("Descriptors different in bits %x\n", TestHeader->HashKey.SecurityId));
  2766. } finally {
  2767. NtfsUnpinBcb( IrpContext, &Bcb );
  2768. }
  2769. }
  2770. } finally {
  2771. if (ReadContext != NULL) {
  2772. NtOfsFreeReadContext( ReadContext );
  2773. }
  2774. }
  2775. }
  2776. DebugTrace( -1, DbgAcl, ("NtOfsLookupSecurityDescriptorInIndex...Done\n") );
  2777. return;
  2778. }
  2779. //
  2780. // Local Support routine
  2781. //
  2782. SECURITY_ID
  2783. GetSecurityIdFromSecurityDescriptorUnsafe (
  2784. PIRP_CONTEXT IrpContext,
  2785. IN OUT PSHARED_SECURITY SharedSecurity
  2786. )
  2787. /*++
  2788. Routine Description:
  2789. Return the security Id associated with a given security descriptor. If
  2790. there is an existing Id, return it. If no Id exists, create one. This assumes
  2791. security mutex is already acquired
  2792. Arguments:
  2793. IrpContext - context of the call
  2794. SharedSecurity - Shared security used by file
  2795. Return Value:
  2796. SECURITY_ID corresponding to the unique instantiation of the security
  2797. descriptor on the volume.
  2798. --*/
  2799. {
  2800. SECURITY_ID SavedSecurityId;
  2801. LONGLONG NextDescriptorOffset, PaddedNextDescriptorOffset;
  2802. PAGED_CODE( );
  2803. DebugTrace( +1, DbgAcl, ("GetSecurityIdFromSecurityDescriptorUnsafe...\n") );
  2804. //
  2805. // Drop the security mutex since we are going to acquire / extend the descriptor stream
  2806. // and the mutex is basically an end resource. Inc ref. count to keep
  2807. // shared security around
  2808. //
  2809. SharedSecurity->ReferenceCount += 1;
  2810. NtfsReleaseFcbSecurity( IrpContext->Vcb );
  2811. //
  2812. // Find descriptor in indexes/stream
  2813. //
  2814. try {
  2815. //
  2816. // Make sure the data structures don't change underneath us
  2817. //
  2818. NtfsAcquireSharedScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
  2819. //
  2820. // Save next Security Id. This is used if we fail to find the security
  2821. // descriptor in the descriptor stream.
  2822. //
  2823. SavedSecurityId = IrpContext->Vcb->NextSecurityId;
  2824. NtOfsLookupSecurityDescriptorInIndex( IrpContext, SharedSecurity );
  2825. //
  2826. // If we've found the security descriptor in the stream we're done.
  2827. //
  2828. if (SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID) {
  2829. leave;
  2830. }
  2831. //
  2832. // The security descriptor is not found. Reacquire the security
  2833. // stream exclusive since we are about to modify it.
  2834. //
  2835. NtfsReleaseScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
  2836. NtfsAcquireExclusiveScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
  2837. //
  2838. // During the short interval above, we did not own the security stream.
  2839. // It is possible that another thread has gotten in and created this
  2840. // descriptor. Therefore, we must probe the indexes again.
  2841. //
  2842. // Rather than perform this expensive test *always*, we saved the next
  2843. // security id to be allocated above. Now that we've obtained the stream
  2844. // exclusive we can check to see if the saved one is the same as the next
  2845. // one. If so, then we need to probe the indexes. Otherwise
  2846. // we know that no modifications have taken place.
  2847. //
  2848. if (SavedSecurityId != IrpContext->Vcb->NextSecurityId) {
  2849. DebugTrace( 0, DbgAcl, ("SecurityId changed, rescanning\n") );
  2850. //
  2851. // The descriptor cache has been edited. We must search again
  2852. //
  2853. NtOfsLookupSecurityDescriptorInIndex( IrpContext, SharedSecurity );
  2854. //
  2855. // If the Id was found this time, simply return it
  2856. //
  2857. if (SharedSecurity->Header.HashKey.SecurityId != SECURITY_ID_INVALID) {
  2858. leave;
  2859. }
  2860. }
  2861. //
  2862. // Allocate security id. This does not need to be logged since we only
  2863. // increment this and initialize this from the max key in the index at
  2864. // mount time.
  2865. //
  2866. SharedSecurity->Header.HashKey.SecurityId = IrpContext->Vcb->NextSecurityId++;
  2867. //
  2868. // Determine allocation location in descriptor stream. The alignment
  2869. // requirements for security descriptors within the stream are:
  2870. //
  2871. // DWORD alignment
  2872. // Not spanning a VACB_MAPPING_GRANULARITY boundary
  2873. //
  2874. //
  2875. // Get current EOF for descriptor stream. This includes the replicated
  2876. // region. Remove the replicated region (& ~VACB_MAPPING_GRANULARITY)
  2877. //
  2878. #if DBG
  2879. {
  2880. LONGLONG Tmp = NtOfsQueryLength( IrpContext->Vcb->SecurityDescriptorStream );
  2881. ASSERT( Tmp == 0 || (Tmp & VACB_MAPPING_GRANULARITY) );
  2882. }
  2883. #endif
  2884. NextDescriptorOffset = NtOfsQueryLength( IrpContext->Vcb->SecurityDescriptorStream ) & ~VACB_MAPPING_GRANULARITY;
  2885. //
  2886. // Align to 16 byte boundary.
  2887. //
  2888. PaddedNextDescriptorOffset =
  2889. SharedSecurity->Header.Offset = (NextDescriptorOffset + 0xF) & 0xFFFFFFFFFFFFFFF0i64;
  2890. DebugTrace( 0,
  2891. DbgAcl,
  2892. ("Allocating SecurityId %x at %016I64x\n",
  2893. SharedSecurity->Header.HashKey.SecurityId,
  2894. SharedSecurity->Header.Offset) );
  2895. //
  2896. // Make sure we don't span a VACB_MAPPING_GRANULARITY boundary and
  2897. // have enough room for a completely-zero header.
  2898. //
  2899. if (
  2900. //
  2901. // Offset in window
  2902. //
  2903. (SharedSecurity->Header.Offset & (VACB_MAPPING_GRANULARITY - 1))
  2904. //
  2905. // Plus size security stream entry
  2906. //
  2907. + SharedSecurity->Header.Length
  2908. //
  2909. // Plus size of one empty header
  2910. //
  2911. + sizeof( SharedSecurity->Header )
  2912. //
  2913. // goes into the next block
  2914. //
  2915. > VACB_MAPPING_GRANULARITY) {
  2916. //
  2917. // We are about to span the mapping granularity of the cache manager
  2918. // so we want to place this into the next cache window. However,
  2919. // the following window is where the replicated descriptors are
  2920. // stored. We must advance to the window beyond that.
  2921. //
  2922. SharedSecurity->Header.Offset =
  2923. //
  2924. // Round down to previous VACB_MAPPING GRANULARITY
  2925. //
  2926. (SharedSecurity->Header.Offset & ~(VACB_MAPPING_GRANULARITY - 1))
  2927. //
  2928. // Move past this window and replicated window
  2929. //
  2930. + 2 * VACB_MAPPING_GRANULARITY;
  2931. //
  2932. // The next descriptor offset is used for zeroing out the padding
  2933. //
  2934. PaddedNextDescriptorOffset = SharedSecurity->Header.Offset - VACB_MAPPING_GRANULARITY;
  2935. }
  2936. //
  2937. // Grow security stream to make room for new descriptor and header. This
  2938. // takes into account the replicated copy of the descriptor.
  2939. //
  2940. NtOfsSetLength( IrpContext,
  2941. IrpContext->Vcb->SecurityDescriptorStream,
  2942. (SharedSecurity->Header.Offset +
  2943. SharedSecurity->Header.Length +
  2944. VACB_MAPPING_GRANULARITY) );
  2945. //
  2946. // Zero out any alignment padding since Chkdsk verifies the replication by
  2947. // doing 256K memcmp's.
  2948. //
  2949. NtOfsPutData( IrpContext,
  2950. IrpContext->Vcb->SecurityDescriptorStream,
  2951. NextDescriptorOffset + VACB_MAPPING_GRANULARITY,
  2952. (ULONG)(PaddedNextDescriptorOffset - NextDescriptorOffset),
  2953. NULL );
  2954. NtOfsPutData( IrpContext,
  2955. IrpContext->Vcb->SecurityDescriptorStream,
  2956. NextDescriptorOffset,
  2957. (ULONG)(PaddedNextDescriptorOffset - NextDescriptorOffset),
  2958. NULL );
  2959. //
  2960. // Put the new descriptor into the stream in both the "normal"
  2961. // place and in the replicated place.
  2962. //
  2963. NtOfsPutData( IrpContext,
  2964. IrpContext->Vcb->SecurityDescriptorStream,
  2965. SharedSecurity->Header.Offset,
  2966. SharedSecurity->Header.Length,
  2967. &SharedSecurity->Header );
  2968. NtOfsPutData( IrpContext,
  2969. IrpContext->Vcb->SecurityDescriptorStream,
  2970. SharedSecurity->Header.Offset + VACB_MAPPING_GRANULARITY,
  2971. SharedSecurity->Header.Length,
  2972. &SharedSecurity->Header );
  2973. //
  2974. // add id->data map
  2975. //
  2976. {
  2977. INDEX_ROW Row;
  2978. Row.KeyPart.KeyLength = sizeof( SharedSecurity->Header.HashKey.SecurityId );
  2979. Row.KeyPart.Key = &SharedSecurity->Header.HashKey.SecurityId;
  2980. Row.DataPart.DataLength = sizeof( SharedSecurity->Header );
  2981. Row.DataPart.Data = &SharedSecurity->Header;
  2982. NtOfsAddRecords( IrpContext,
  2983. IrpContext->Vcb->SecurityIdIndex,
  2984. 1,
  2985. &Row,
  2986. FALSE );
  2987. }
  2988. //
  2989. // add hash|id->data map
  2990. //
  2991. {
  2992. INDEX_ROW Row;
  2993. Row.KeyPart.KeyLength =
  2994. sizeof( SharedSecurity->Header.HashKey );
  2995. Row.KeyPart.Key = &SharedSecurity->Header.HashKey;
  2996. Row.DataPart.DataLength = sizeof( SharedSecurity->Header );
  2997. Row.DataPart.Data = &SharedSecurity->Header;
  2998. NtOfsAddRecords( IrpContext,
  2999. IrpContext->Vcb->SecurityDescriptorHashIndex,
  3000. 1,
  3001. &Row,
  3002. FALSE );
  3003. }
  3004. } finally {
  3005. NtfsReleaseScb( IrpContext, IrpContext->Vcb->SecurityDescriptorStream );
  3006. //
  3007. // Reacquire fcb security mutex and deref count
  3008. //
  3009. NtfsAcquireFcbSecurity( IrpContext->Vcb );
  3010. SharedSecurity->ReferenceCount -= 1;
  3011. }
  3012. DebugTrace( -1,
  3013. DbgAcl,
  3014. ("GetSecurityIdFromSecurityDescriptorUnsafe returns %08x\n",
  3015. SharedSecurity->Header.HashKey.SecurityId) );
  3016. return SharedSecurity->Header.HashKey.SecurityId;
  3017. }
  3018. VOID
  3019. NtfsStoreSecurityDescriptor (
  3020. PIRP_CONTEXT IrpContext,
  3021. IN PFCB Fcb,
  3022. IN BOOLEAN LogIt
  3023. )
  3024. /*++
  3025. Routine Description:
  3026. LEGACY NOTE - this routine disappears when all volumes become NT 5
  3027. This routine stores a new security descriptor already stored in the fcb
  3028. from memory onto the disk.
  3029. Arguments:
  3030. Fcb - Supplies the fcb for the file being operated on
  3031. LogIt - Supplies whether or not the creation of a new security descriptor
  3032. should/ be logged or not. Modifications are always logged. This
  3033. parameter must only be specified as FALSE for a file which is currently
  3034. being created.
  3035. Return Value:
  3036. None.
  3037. Note:
  3038. This will dirty the standard information in the FCB but will not update it on
  3039. disk. The caller needs to bring these into sync.
  3040. --*/
  3041. {
  3042. ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
  3043. ATTRIBUTE_ENUMERATION_CONTEXT StdInfoContext;
  3044. BOOLEAN CleanupStdInfoContext = FALSE;
  3045. PAGED_CODE();
  3046. DebugTrace( +1, Dbg, ("NtfsStoreSecurityDescriptor...\n") );
  3047. ASSERT_EXCLUSIVE_FCB( Fcb );
  3048. //
  3049. // Initialize the attribute and find the security attribute
  3050. //
  3051. NtfsInitializeAttributeContext( &AttributeContext );
  3052. try {
  3053. ASSERT( Fcb->Vcb->SecurityDescriptorStream == NULL);
  3054. //
  3055. // Check if the attribute is first being modified or deleted, a null
  3056. // value means that we are deleting the security descriptor
  3057. //
  3058. if (Fcb->SharedSecurity == NULL) {
  3059. DebugTrace( 0, Dbg, ("Security Descriptor is null\n") );
  3060. //
  3061. // If it already doesn't exist then we're done, otherwise simply
  3062. // delete the attribute
  3063. //
  3064. if (NtfsLookupAttributeByCode( IrpContext,
  3065. Fcb,
  3066. &Fcb->FileReference,
  3067. $SECURITY_DESCRIPTOR,
  3068. &AttributeContext )) {
  3069. DebugTrace( 0, Dbg, ("Delete existing Security Descriptor\n") );
  3070. NtfsDeleteAttributeRecord( IrpContext,
  3071. Fcb,
  3072. DELETE_LOG_OPERATION |
  3073. DELETE_RELEASE_FILE_RECORD |
  3074. DELETE_RELEASE_ALLOCATION,
  3075. &AttributeContext );
  3076. }
  3077. leave;
  3078. }
  3079. //
  3080. // At this point we are modifying the security descriptor so read in the
  3081. // security descriptor, if it does not exist then we will need to create
  3082. // one.
  3083. //
  3084. if (!NtfsLookupAttributeByCode( IrpContext,
  3085. Fcb,
  3086. &Fcb->FileReference,
  3087. $SECURITY_DESCRIPTOR,
  3088. &AttributeContext )) {
  3089. DebugTrace( 0, Dbg, ("Create a new Security Descriptor\n") );
  3090. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  3091. NtfsInitializeAttributeContext( &AttributeContext );
  3092. NtfsCreateAttributeWithValue( IrpContext,
  3093. Fcb,
  3094. $SECURITY_DESCRIPTOR,
  3095. NULL, // attribute name
  3096. &Fcb->SharedSecurity->SecurityDescriptor,
  3097. GetSharedSecurityLength(Fcb->SharedSecurity),
  3098. 0, // attribute flags
  3099. NULL, // where indexed
  3100. LogIt, // logit
  3101. &AttributeContext );
  3102. //
  3103. // We may be modifying the security descriptor of an NT 5.0 volume.
  3104. // We want to store a SecurityID in the standard information field so
  3105. // that if we reboot on 5.0 NTFS will know where to find the most
  3106. // recent security descriptor.
  3107. //
  3108. if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO )) {
  3109. LARGE_STANDARD_INFORMATION StandardInformation;
  3110. //
  3111. // Initialize the context structure.
  3112. //
  3113. NtfsInitializeAttributeContext( &StdInfoContext );
  3114. CleanupStdInfoContext = TRUE;
  3115. //
  3116. // Locate the standard information, it must be there.
  3117. //
  3118. if (!NtfsLookupAttributeByCode( IrpContext,
  3119. Fcb,
  3120. &Fcb->FileReference,
  3121. $STANDARD_INFORMATION,
  3122. &StdInfoContext )) {
  3123. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  3124. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3125. }
  3126. ASSERT( NtfsFoundAttribute( &StdInfoContext )->Form.Resident.ValueLength >= sizeof( LARGE_STANDARD_INFORMATION ));
  3127. //
  3128. // Copy the existing standard information to our buffer.
  3129. //
  3130. RtlCopyMemory( &StandardInformation,
  3131. NtfsAttributeValue( NtfsFoundAttribute( &StdInfoContext )),
  3132. sizeof( LARGE_STANDARD_INFORMATION ));
  3133. StandardInformation.SecurityId = SECURITY_ID_INVALID;
  3134. StandardInformation.OwnerId = 0;
  3135. //
  3136. // Call to change the attribute value.
  3137. //
  3138. NtfsChangeAttributeValue( IrpContext,
  3139. Fcb,
  3140. 0,
  3141. &StandardInformation,
  3142. sizeof( LARGE_STANDARD_INFORMATION ),
  3143. FALSE,
  3144. FALSE,
  3145. FALSE,
  3146. FALSE,
  3147. &StdInfoContext );
  3148. }
  3149. } else {
  3150. DebugTrace( 0, Dbg, ("Change an existing Security Descriptor\n") );
  3151. NtfsChangeAttributeValue( IrpContext,
  3152. Fcb,
  3153. 0, // Value offset
  3154. &Fcb->SharedSecurity->SecurityDescriptor,
  3155. GetSharedSecurityLength( Fcb->SharedSecurity ),
  3156. TRUE, // logit
  3157. TRUE,
  3158. FALSE,
  3159. FALSE,
  3160. &AttributeContext );
  3161. }
  3162. } finally {
  3163. DebugUnwind( NtfsStoreSecurityDescriptor );
  3164. //
  3165. // Cleanup our attribute enumeration context
  3166. //
  3167. NtfsCleanupAttributeContext( IrpContext, &AttributeContext );
  3168. if (CleanupStdInfoContext) {
  3169. NtfsCleanupAttributeContext( IrpContext, &StdInfoContext );
  3170. }
  3171. }
  3172. //
  3173. // And return to our caller
  3174. //
  3175. DebugTrace( -1, Dbg, ("NtfsStoreSecurityDescriptor -> VOID\n") );
  3176. return;
  3177. }
  3178. PSHARED_SECURITY
  3179. NtfsCacheSharedSecurityForCreate (
  3180. IN PIRP_CONTEXT IrpContext,
  3181. IN PFCB ParentFcb
  3182. )
  3183. /*++
  3184. Routine Description:
  3185. This routine finds or constructs a security id and SHARED_SECURITY from
  3186. a specific file or directory.
  3187. Arguments:
  3188. IrpContext - Context of the call
  3189. ParentFcb - Supplies the directory under which the new fcb exists
  3190. Return Value:
  3191. Referenced shared security.
  3192. --*/
  3193. {
  3194. PSECURITY_DESCRIPTOR SecurityDescriptor;
  3195. PSHARED_SECURITY SharedSecurity;
  3196. NTSTATUS Status;
  3197. BOOLEAN IsDirectory;
  3198. PACCESS_STATE AccessState;
  3199. PIO_STACK_LOCATION IrpSp;
  3200. ULONG SecurityDescLength;
  3201. ASSERT_IRP_CONTEXT( IrpContext );
  3202. ASSERT_FCB( ParentFcb );
  3203. PAGED_CODE();
  3204. DebugTrace( +1, DbgAcl, ("NtfsCacheSharedSecurityForCreate...\n") );
  3205. //
  3206. // First decide if we are creating a file or a directory
  3207. //
  3208. IrpSp = IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp);
  3209. if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
  3210. IsDirectory = TRUE;
  3211. } else {
  3212. IsDirectory = FALSE;
  3213. }
  3214. //
  3215. // Extract the parts of the Irp that we need to do our assignment
  3216. //
  3217. AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
  3218. //
  3219. // Check if we need to load the security descriptor for the parent.
  3220. //
  3221. if (ParentFcb->SharedSecurity == NULL) {
  3222. NtfsLoadSecurityDescriptor( IrpContext, ParentFcb );
  3223. }
  3224. ASSERT( ParentFcb->SharedSecurity != NULL );
  3225. //
  3226. // Create a new security descriptor for the file and raise if there is
  3227. // an error
  3228. //
  3229. if (!NT_SUCCESS( Status = SeAssignSecurity( &ParentFcb->SharedSecurity->SecurityDescriptor,
  3230. AccessState->SecurityDescriptor,
  3231. &SecurityDescriptor,
  3232. IsDirectory,
  3233. &AccessState->SubjectSecurityContext,
  3234. IoGetFileObjectGenericMapping(),
  3235. PagedPool ))) {
  3236. NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
  3237. }
  3238. SecurityDescLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
  3239. ASSERT( SeValidSecurityDescriptor( SecurityDescLength, SecurityDescriptor ));
  3240. try {
  3241. //
  3242. // Make sure the length is non-zero.
  3243. //
  3244. if (SecurityDescLength == 0) {
  3245. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  3246. }
  3247. //
  3248. // We have a security descriptor. Create a shared security descriptor.
  3249. //
  3250. SharedSecurity = NtfsCacheSharedSecurityByDescriptor( IrpContext,
  3251. SecurityDescriptor,
  3252. SecurityDescLength,
  3253. TRUE );
  3254. } finally {
  3255. //
  3256. // Free the security descriptor created by Se
  3257. //
  3258. SeDeassignSecurity( &SecurityDescriptor );
  3259. }
  3260. //
  3261. // And return to our caller
  3262. //
  3263. DebugTrace( -1, DbgAcl, ("NtfsCacheSharedSecurityForCreate -> VOID\n") );
  3264. return SharedSecurity;
  3265. }
  3266. /*++
  3267. Routine Descriptions:
  3268. Collation routines for security hash index. Collation occurs by Hash first,
  3269. then security Id
  3270. Arguments:
  3271. Key1 - First key to compare.
  3272. Key2 - Second key to compare.
  3273. CollationData - Optional data to support the collation.
  3274. Return Value:
  3275. LessThan, EqualTo, or Greater than, for how Key1 compares
  3276. with Key2.
  3277. --*/
  3278. FSRTL_COMPARISON_RESULT
  3279. NtOfsCollateSecurityHash (
  3280. IN PINDEX_KEY Key1,
  3281. IN PINDEX_KEY Key2,
  3282. IN PVOID CollationData
  3283. )
  3284. {
  3285. PSECURITY_HASH_KEY HashKey1 = (PSECURITY_HASH_KEY) Key1->Key;
  3286. PSECURITY_HASH_KEY HashKey2 = (PSECURITY_HASH_KEY) Key2->Key;
  3287. UNREFERENCED_PARAMETER(CollationData);
  3288. PAGED_CODE( );
  3289. ASSERT( Key1->KeyLength == sizeof( SECURITY_HASH_KEY ) );
  3290. ASSERT( Key2->KeyLength == sizeof( SECURITY_HASH_KEY ) );
  3291. if (HashKey1->Hash < HashKey2->Hash) {
  3292. return LessThan;
  3293. } else if (HashKey1->Hash > HashKey2->Hash) {
  3294. return GreaterThan;
  3295. } else if (HashKey1->SecurityId < HashKey2->SecurityId) {
  3296. return LessThan;
  3297. } else if (HashKey1->SecurityId > HashKey2->SecurityId) {
  3298. return GreaterThan;
  3299. } else {
  3300. return EqualTo;
  3301. }
  3302. }
  3303. BOOLEAN
  3304. NtfsCanAdministerVolume (
  3305. IN PIRP_CONTEXT IrpContext,
  3306. IN PIRP Irp,
  3307. IN PFCB Fcb,
  3308. IN PSECURITY_DESCRIPTOR TestSecurityDescriptor OPTIONAL,
  3309. IN PULONG TestDesiredAccess OPTIONAL
  3310. )
  3311. /*++
  3312. Routine Descriptions:
  3313. For volume open irps test if the user has enough access to administer the volume
  3314. This means retesting the original requested access
  3315. Arguments:
  3316. Irp - The create irp
  3317. Fcb - The fcb to be tested - this should always be the volumedasd fcb
  3318. TestSecurityDescriptor - If specified then use then apply this descriptor for the
  3319. test.
  3320. TestDesiredAccess - If specified then this is the access to apply.
  3321. Return Value:
  3322. TRUE if the user can administer the volume
  3323. --*/
  3324. {
  3325. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  3326. BOOLEAN ManageAccessGranted;
  3327. ULONG ManageDesiredAccess;
  3328. ULONG ManageGrantedAccess;
  3329. NTSTATUS ManageAccessStatus;
  3330. PPRIVILEGE_SET Privileges = NULL;
  3331. PACCESS_STATE AccessState;
  3332. KPROCESSOR_MODE EffectiveMode;
  3333. PAGED_CODE();
  3334. ASSERT( IrpContext->MajorFunction == IRP_MJ_CREATE );
  3335. ASSERT( Fcb == Fcb->Vcb->VolumeDasdScb->Fcb );
  3336. AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState;
  3337. ManageDesiredAccess = AccessState->OriginalDesiredAccess;
  3338. if (ARGUMENT_PRESENT( TestDesiredAccess )) {
  3339. ManageDesiredAccess = *TestDesiredAccess;
  3340. }
  3341. //
  3342. // SL_FORCE_ACCESS_CHECK causes us to use an effective RequestorMode
  3343. // of UserMode.
  3344. //
  3345. EffectiveMode = (KPROCESSOR_MODE)(FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ?
  3346. UserMode :
  3347. Irp->RequestorMode);
  3348. //
  3349. // Lock the user context, do the access check and then unlock the context
  3350. //
  3351. SeLockSubjectContext( &AccessState->SubjectSecurityContext );
  3352. try {
  3353. ManageAccessGranted = SeAccessCheck( (ARGUMENT_PRESENT( TestSecurityDescriptor ) ?
  3354. TestSecurityDescriptor :
  3355. &Fcb->SharedSecurity->SecurityDescriptor),
  3356. &AccessState->SubjectSecurityContext,
  3357. TRUE, // Tokens are locked
  3358. ManageDesiredAccess,
  3359. 0,
  3360. &Privileges,
  3361. IoGetFileObjectGenericMapping(),
  3362. EffectiveMode,
  3363. &ManageGrantedAccess,
  3364. &ManageAccessStatus );
  3365. } finally {
  3366. SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );
  3367. }
  3368. if (Privileges != NULL) {
  3369. SeFreePrivileges( Privileges );
  3370. }
  3371. return ManageAccessGranted;
  3372. UNREFERENCED_PARAMETER( IrpContext );
  3373. }
  3374. #ifdef NTFS_CACHE_RIGHTS
  3375. VOID
  3376. NtfsGetCachedRightsById (
  3377. IN PVCB Vcb,
  3378. IN PLUID TokenId,
  3379. IN PLUID ModifiedId,
  3380. IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
  3381. IN PSHARED_SECURITY SharedSecurity,
  3382. OUT PBOOLEAN EntryCached OPTIONAL,
  3383. OUT PACCESS_MASK Rights
  3384. )
  3385. /*++
  3386. Routine Descriptions:
  3387. This call returns the access rights held by the effective
  3388. ACCESS_TOKEN for the given security information, if available.
  3389. Arguments:
  3390. Vcb - Volume where security Id is cached
  3391. TokenId - The effective token's id.
  3392. ModifiedId - The effective token's modification id.
  3393. SubjectSecurityContext - A pointer to the subject's captured and locked
  3394. security context
  3395. SharedSecurity - Shared security used by file
  3396. EntryCached - If the token-specific rights are cached at all, TRUE is
  3397. optionally returned here, otherwise FALSE is returned.
  3398. Rights - The access rights are returned here. If an entry is not found
  3399. in the cache for the effective token, only the world rights are
  3400. returned.
  3401. Return Value:
  3402. None.
  3403. --*/
  3404. {
  3405. UCHAR Index;
  3406. BOOLEAN AccessGranted;
  3407. BOOLEAN LockHeld = FALSE;
  3408. BOOLEAN IsCached = FALSE;
  3409. NTSTATUS AccessStatus = STATUS_UNSUCCESSFUL;
  3410. ACCESS_MASK GrantedAccess;
  3411. PCACHED_ACCESS_RIGHTS CachedRights;
  3412. PAGED_CODE( );
  3413. NtfsAcquireFcbSecurity( Vcb );
  3414. LockHeld = TRUE;
  3415. try {
  3416. CachedRights = &SharedSecurity->CachedRights;
  3417. *Rights = CachedRights->EveryoneRights;
  3418. //
  3419. // Search the list for the given TokenId.
  3420. // It is assumed that a specific TokenId will only appear
  3421. // once in the cache.
  3422. //
  3423. for (Index = 0;
  3424. Index < CachedRights->Count;
  3425. Index += 1) {
  3426. //
  3427. // Check for a match on TokenId and ModifiedId.
  3428. //
  3429. if (RtlEqualLuid( &CachedRights->TokenRights[Index].TokenId,
  3430. TokenId )) {
  3431. if (RtlEqualLuid( &CachedRights->TokenRights[Index].ModifiedId,
  3432. ModifiedId )) {
  3433. //
  3434. // We have a match.
  3435. //
  3436. SetFlag( *Rights, CachedRights->TokenRights[Index].Rights );
  3437. IsCached = TRUE;
  3438. }
  3439. break;
  3440. }
  3441. }
  3442. //
  3443. // If the entry is not cached, get the maximum rights.
  3444. // Note that it is assumed that this call will not return
  3445. // rights that require privileges, even if they are currently
  3446. // enabled. This is the behavior when only MAXIMUM_ALLOWED
  3447. // is requested.
  3448. //
  3449. if (!IsCached) {
  3450. //
  3451. // Drop our lock across this call.
  3452. //
  3453. NtfsReleaseFcbSecurity( Vcb );
  3454. LockHeld = FALSE;
  3455. AccessGranted = SeAccessCheck( &SharedSecurity->SecurityDescriptor,
  3456. SubjectSecurityContext,
  3457. TRUE, // Tokens are locked
  3458. MAXIMUM_ALLOWED,
  3459. 0,
  3460. NULL,
  3461. IoGetFileObjectGenericMapping(),
  3462. UserMode,
  3463. &GrantedAccess,
  3464. &AccessStatus );
  3465. if (AccessGranted) {
  3466. //
  3467. // Update the cached knowledge about rights that this
  3468. // caller is known to have for this security descriptor.
  3469. //
  3470. NtfsAddCachedRights( Vcb,
  3471. SharedSecurity,
  3472. GrantedAccess,
  3473. TokenId,
  3474. ModifiedId );
  3475. IsCached = TRUE;
  3476. }
  3477. }
  3478. } finally {
  3479. if (LockHeld) {
  3480. NtfsReleaseFcbSecurity( Vcb );
  3481. }
  3482. }
  3483. if (ARGUMENT_PRESENT( EntryCached )) {
  3484. *EntryCached = IsCached;
  3485. }
  3486. return;
  3487. }
  3488. NTSTATUS
  3489. NtfsGetCachedRights (
  3490. IN PVCB Vcb,
  3491. IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
  3492. IN PSHARED_SECURITY SharedSecurity,
  3493. OUT PACCESS_MASK Rights,
  3494. OUT PBOOLEAN EntryCached OPTIONAL,
  3495. OUT PLUID TokenId OPTIONAL,
  3496. OUT PLUID ModifiedId OPTIONAL
  3497. )
  3498. /*++
  3499. Routine Descriptions:
  3500. This call returns the access rights known to be held by the effective
  3501. ACCESS_TOKEN for the given security information. It is assumed that
  3502. the subject context is locked.
  3503. Arguments:
  3504. Vcb - Volume where security Id is cached
  3505. SubjectSecurityContext - A pointer to the subject's captured and locked
  3506. security context
  3507. SharedSecurity - Shared security used by file
  3508. Rights - The access rights are returned here. If an entry is not found
  3509. in the cache for the effective token, only the world rights are
  3510. returned.
  3511. EntryCached - If the token-specific rights are cached at all, TRUE is
  3512. optionally returned here, otherwise FALSE is returned.
  3513. TokenId - The effective token's id is optionally returned here.
  3514. ModifiedId - The effective token's modification id is optionally
  3515. returned here.
  3516. Return Value:
  3517. NTSTATUS - Returns STATUS_SUCCESS if and only if we have obtained at
  3518. least the TokenId and ModifiedId information.
  3519. --*/
  3520. {
  3521. NTSTATUS Status;
  3522. PACCESS_TOKEN EToken;
  3523. PTOKEN_STATISTICS Info = NULL;
  3524. PAGED_CODE( );
  3525. DebugTrace( +1, Dbg, ("NtfsGetCachedRights...\n") );
  3526. //
  3527. // First obtain the effective token's id and modification id.
  3528. //
  3529. EToken = SeQuerySubjectContextToken( SubjectSecurityContext );
  3530. Status = SeQueryInformationToken( EToken, TokenStatistics, &Info );
  3531. //
  3532. // If we have the TokenId and ModifiedId, get the cached rights.
  3533. //
  3534. if (Status == STATUS_SUCCESS) {
  3535. NtfsGetCachedRightsById( Vcb,
  3536. &Info->TokenId,
  3537. &Info->ModifiedId,
  3538. SubjectSecurityContext,
  3539. SharedSecurity,
  3540. EntryCached,
  3541. Rights );
  3542. //
  3543. // Return the Tokenid and ModifiedId to the caller.
  3544. //
  3545. if (ARGUMENT_PRESENT( TokenId )) {
  3546. RtlCopyLuid( TokenId, &Info->TokenId );
  3547. }
  3548. if (ARGUMENT_PRESENT( ModifiedId )) {
  3549. RtlCopyLuid( ModifiedId, &Info->ModifiedId );
  3550. }
  3551. } else {
  3552. //
  3553. // Just return the rights everyone is known to have.
  3554. //
  3555. *Rights = SharedSecurity->CachedRights.EveryoneRights;
  3556. if (ARGUMENT_PRESENT( EntryCached )) {
  3557. *EntryCached = FALSE;
  3558. }
  3559. }
  3560. if (Info != NULL) {
  3561. ExFreePool( Info );
  3562. }
  3563. DebugTrace( -1, Dbg, ("NtfsGetCachedRights -> %08lx, Rights=%08lx\n", Status, *Rights) );
  3564. return Status;
  3565. }
  3566. VOID
  3567. NtfsAddCachedRights (
  3568. IN PVCB Vcb,
  3569. IN PSHARED_SECURITY SharedSecurity,
  3570. IN ACCESS_MASK Rights,
  3571. IN PLUID TokenId,
  3572. IN PLUID ModifiedId
  3573. )
  3574. /*++
  3575. Routine Descriptions:
  3576. This call caches the access rights held by the effective ACCESS_TOKEN
  3577. for the given security information. It is assumed that the subject
  3578. context is locked.
  3579. Arguments:
  3580. Vcb - Volume where security Id is cached
  3581. SharedSecurity - Shared security used by file
  3582. Rights - The access rights.
  3583. TokenId - The effective token's id.
  3584. ModifiedId - The effective token's modification id.
  3585. Return Value:
  3586. None.
  3587. --*/
  3588. {
  3589. BOOLEAN GetEveryoneRights = FALSE;
  3590. UCHAR Index;
  3591. PCACHED_ACCESS_RIGHTS CachedRights;
  3592. PAGED_CODE( );
  3593. DebugTrace( +1, Dbg, ("NtfsAddCachedRights...\n") );
  3594. //
  3595. // Make certain that MAXIMUM_ALLOWED is not in the rights.
  3596. //
  3597. ClearFlag( Rights, MAXIMUM_ALLOWED );
  3598. //
  3599. // Acquire the security mutex
  3600. //
  3601. NtfsAcquireFcbSecurity( Vcb );
  3602. try {
  3603. //
  3604. // Search the list for the given TokenId.
  3605. // It is assumed that a specific TokenId will only appear
  3606. // once in the cache.
  3607. //
  3608. for (Index = 0, CachedRights = &SharedSecurity->CachedRights;
  3609. Index < CachedRights->Count;
  3610. Index += 1) {
  3611. //
  3612. // Check for a match on TokenId and ModifiedId.
  3613. //
  3614. if (RtlEqualLuid( &CachedRights->TokenRights[Index].TokenId,
  3615. TokenId )) {
  3616. //
  3617. // Replace ModifiedId if it doesn't match. That will
  3618. // happen when the token's enabled groups or privileges
  3619. // have changed since the last time we cached information
  3620. // for it.
  3621. //
  3622. if (!RtlEqualLuid( &CachedRights->TokenRights[Index].ModifiedId,
  3623. ModifiedId )) {
  3624. RtlCopyLuid( &CachedRights->TokenRights[Index].ModifiedId,
  3625. ModifiedId );
  3626. }
  3627. //
  3628. // We have a match. Set the rights.
  3629. //
  3630. CachedRights->TokenRights[Index].Rights = Rights;
  3631. //
  3632. // Remember the next entry to use.
  3633. //
  3634. CachedRights->NextInsert = Index + 1;
  3635. break;
  3636. }
  3637. }
  3638. //
  3639. // If the entry was not found above, add the new entry into the cache.
  3640. //
  3641. if (Index == CachedRights->Count) {
  3642. if ((CachedRights->Count >= 1) &&
  3643. !CachedRights->HaveEveryoneRights) {
  3644. //
  3645. // Once we add the second TokenId to the cache, we have a
  3646. // good indicator that having the world rights could be
  3647. // useful.
  3648. //
  3649. GetEveryoneRights = TRUE;
  3650. //
  3651. // Set the indicator that we have the rights now so that
  3652. // there is no need in the acquisition routine to acquire
  3653. // the security mutex. This will prevent multiple threads
  3654. // from attempting to acquire the everyone rights.
  3655. //
  3656. // Note that until we actually acquire the rights information
  3657. // caller will assume that the rights are 0 and go through
  3658. // the normal per-token access check path.
  3659. //
  3660. CachedRights->HaveEveryoneRights = TRUE;
  3661. }
  3662. Index = CachedRights->NextInsert;
  3663. //
  3664. // We will just replace the first entry in the list.
  3665. //
  3666. if (Index == NTFS_MAX_CACHED_RIGHTS) {
  3667. Index = 0;
  3668. }
  3669. ASSERT( Index < NTFS_MAX_CACHED_RIGHTS );
  3670. //
  3671. // Copy in the information.
  3672. //
  3673. CachedRights->TokenRights[Index].Rights = Rights;
  3674. RtlCopyLuid( &CachedRights->TokenRights[Index].TokenId,
  3675. TokenId );
  3676. RtlCopyLuid( &CachedRights->TokenRights[Index].ModifiedId,
  3677. ModifiedId );
  3678. if (Index == CachedRights->Count) {
  3679. //
  3680. // Bump the count of entries.
  3681. //
  3682. CachedRights->Count += 1;
  3683. }
  3684. //
  3685. // Remember the next entry to use.
  3686. //
  3687. CachedRights->NextInsert = Index + 1;
  3688. }
  3689. } finally {
  3690. NtfsReleaseFcbSecurity( Vcb );
  3691. }
  3692. if (GetEveryoneRights) {
  3693. NtfsSetCachedRightsWorld( SharedSecurity );
  3694. }
  3695. DebugTrace( -1, Dbg, ("NtfsAddCachedRights -> VOID\n") );
  3696. return;
  3697. }
  3698. #endif