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.

1262 lines
32 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. RegSec.c
  5. Abstract:
  6. This module contains code to apply security to the otherwise unsecured
  7. top level keys, in fashion that will allow existing consumers access to
  8. the keys that they need (print, srvmgr, etc.).
  9. Author:
  10. Richard Ward (richardw) 15 May 1996
  11. Notes:
  12. --*/
  13. #include <rpc.h>
  14. #include <string.h>
  15. #include <wchar.h>
  16. #include "regrpc.h"
  17. #include "localreg.h"
  18. #include "regsec.h"
  19. #define REGSEC_READ 1
  20. #define REGSEC_WRITE 2
  21. WCHAR RemoteRegistryKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\winreg";
  22. WCHAR AllowedPathsKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\SecurePipeServers\\winreg\\AllowedPaths";
  23. WCHAR MachineValue[] = L"Machine";
  24. WCHAR UsersValue[] = L"Users";
  25. PSECURITY_DESCRIPTOR RemoteRegistrySD;
  26. PUNICODE_STRING MachineAllowedPaths;
  27. PUCHAR MachineAllowedPathsBase;
  28. ULONG MachineAllowedPathsCount;
  29. PUNICODE_STRING UsersAllowedPaths;
  30. PUCHAR UsersAllowedPathsBase;
  31. ULONG UsersAllowedPathsCount;
  32. GENERIC_MAPPING RemoteRegistryMappings;
  33. LARGE_INTEGER WinregChange ;
  34. LARGE_INTEGER AllowedPathsChange ;
  35. RTL_RESOURCE RegSecReloadLock ;
  36. NTSTATUS
  37. RegSecReadSDFromRegistry(
  38. IN HANDLE hKey,
  39. OUT PSECURITY_DESCRIPTOR * pSDToUse)
  40. /*++
  41. Routine Description:
  42. This function checks the registry in the magic place to see if an extra
  43. ACL has been defined for the pipe being passed in. If there is one, it
  44. is translated to a NP acl, then returned. If there isn't one, or if
  45. something goes wrong, an NULL acl is returned.
  46. Arguments:
  47. InterfaceName name of the pipe to check for, e.g. winreg, etc.
  48. pSDToUse returned a pointer to the security decriptor to use.
  49. Return Value:
  50. STATUS_SUCCESS,
  51. STATUS_NO_MEMORY,
  52. Possible other errors from registry apis.
  53. --*/
  54. {
  55. NTSTATUS Status ;
  56. PSECURITY_DESCRIPTOR pSD;
  57. ULONG cbNeeded;
  58. ACL_SIZE_INFORMATION AclSize;
  59. ULONG AceIndex;
  60. ACCESS_MASK NewMask;
  61. PACCESS_ALLOWED_ACE pAce;
  62. PACL pAcl;
  63. BOOLEAN DaclPresent;
  64. BOOLEAN DaclDefaulted;
  65. UNICODE_STRING Interface;
  66. UNICODE_STRING Allowed;
  67. ULONG i;
  68. BOOLEAN PipeNameOk;
  69. PSECURITY_DESCRIPTOR pNewSD;
  70. PACL pNewAcl;
  71. PSID pSid;
  72. PSID pSidCopy;
  73. BOOLEAN Defaulted;
  74. PACL Acl;
  75. PSID AdminSid = NULL ;
  76. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY ;
  77. ULONG SizeOfAcl ;
  78. *pSDToUse = NULL;
  79. //
  80. // Son of a gun, someone has established security for this pipe.
  81. //
  82. pSD = NULL;
  83. cbNeeded = 0;
  84. Status = NtQuerySecurityObject(
  85. hKey,
  86. DACL_SECURITY_INFORMATION |
  87. OWNER_SECURITY_INFORMATION |
  88. GROUP_SECURITY_INFORMATION,
  89. NULL,
  90. 0,
  91. &cbNeeded );
  92. if (Status == STATUS_BUFFER_TOO_SMALL)
  93. {
  94. pSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
  95. if (pSD)
  96. {
  97. Status = NtQuerySecurityObject(
  98. hKey,
  99. DACL_SECURITY_INFORMATION |
  100. OWNER_SECURITY_INFORMATION |
  101. GROUP_SECURITY_INFORMATION,
  102. pSD,
  103. cbNeeded,
  104. &cbNeeded );
  105. if (NT_SUCCESS(Status))
  106. {
  107. //
  108. // Now, the tricky part. There is no 1-1 mapping of Key
  109. // permissions to Pipe permissions. So, we do it here.
  110. // We walk the DACL, and examine each ACE. We build a new
  111. // access mask for each ACE, and set the flags as follows:
  112. //
  113. // if (KEY_READ) GENERIC_READ
  114. // if (KEY_WRITE) GENERIC_WRITE
  115. //
  116. Status = RtlGetDaclSecurityDescriptor(
  117. pSD,
  118. &DaclPresent,
  119. &pAcl,
  120. &DaclDefaulted);
  121. //
  122. // If this failed, or there is no DACL present, then
  123. // we're in trouble.
  124. //
  125. if (!NT_SUCCESS(Status) || !DaclPresent || !pAcl)
  126. {
  127. goto GetSDFromKey_BadAcl;
  128. }
  129. Status = RtlQueryInformationAcl(pAcl,
  130. &AclSize,
  131. sizeof(AclSize),
  132. AclSizeInformation);
  133. if (!NT_SUCCESS(Status))
  134. {
  135. goto GetSDFromKey_BadAcl;
  136. }
  137. for (AceIndex = 0; AceIndex < AclSize.AceCount ; AceIndex++ )
  138. {
  139. NewMask = 0;
  140. Status = RtlGetAce( pAcl,
  141. AceIndex,
  142. & pAce);
  143. //
  144. // We don't care what kind of ACE it is, since we
  145. // are just mapping the access types, and the access
  146. // mask is always at a constant position.
  147. //
  148. if (NT_SUCCESS(Status))
  149. {
  150. if ((pAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) &&
  151. (pAce->Header.AceType != ACCESS_DENIED_ACE_TYPE))
  152. {
  153. //
  154. // Must be an audit or random ACE type. Skip it.
  155. //
  156. continue;
  157. }
  158. if (pAce->Mask & KEY_READ)
  159. {
  160. NewMask |= REGSEC_READ;
  161. }
  162. if (pAce->Mask & KEY_WRITE)
  163. {
  164. NewMask |= REGSEC_WRITE;
  165. }
  166. pAce->Mask = NewMask;
  167. }
  168. else
  169. {
  170. //
  171. // Panic: Bad ACL?
  172. //
  173. goto GetSDFromKey_BadAcl;
  174. }
  175. }
  176. //
  177. // RPC does not understand self-relative SDs, so
  178. // we have to turn this into an absolute for them to turn
  179. // back into a self relative.
  180. //
  181. pNewSD = RtlAllocateHeap(RtlProcessHeap(), 0, cbNeeded);
  182. if (!pNewSD)
  183. {
  184. goto GetSDFromKey_BadAcl;
  185. }
  186. InitializeSecurityDescriptor( pNewSD,
  187. SECURITY_DESCRIPTOR_REVISION);
  188. pNewAcl = (PACL) (((PUCHAR) pNewSD) +
  189. sizeof(SECURITY_DESCRIPTOR) );
  190. RtlCopyMemory(pNewAcl, pAcl, AclSize.AclBytesInUse);
  191. SetSecurityDescriptorDacl(pNewSD, TRUE, pNewAcl, FALSE);
  192. Status = RtlGetOwnerSecurityDescriptor( pSD, &pSid, &Defaulted );
  193. if ( NT_SUCCESS( Status ) )
  194. {
  195. pSidCopy = RtlAllocateHeap( RtlProcessHeap(),
  196. 0,
  197. RtlLengthSid( pSid ) );
  198. if ( pSidCopy )
  199. {
  200. RtlCopyMemory( pSidCopy, pSid, RtlLengthSid( pSid ) );
  201. }
  202. RtlSetOwnerSecurityDescriptor( pNewSD, pSidCopy, FALSE );
  203. }
  204. Status = RtlGetGroupSecurityDescriptor( pSD, &pSid, &Defaulted );
  205. if ( NT_SUCCESS( Status ) )
  206. {
  207. pSidCopy = RtlAllocateHeap( RtlProcessHeap(),
  208. 0,
  209. RtlLengthSid( pSid ) );
  210. if ( pSidCopy )
  211. {
  212. RtlCopyMemory( pSidCopy, pSid, RtlLengthSid( pSid ) );
  213. }
  214. RtlSetGroupSecurityDescriptor( pNewSD, pSidCopy, FALSE );
  215. }
  216. RtlFreeHeap(RtlProcessHeap(), 0, pSD);
  217. *pSDToUse = pNewSD;
  218. return(Status);
  219. }
  220. }
  221. return(STATUS_NO_MEMORY);
  222. }
  223. else
  224. {
  225. GetSDFromKey_BadAcl:
  226. //
  227. // Free the SD that we have allocated
  228. //
  229. if (pSD)
  230. {
  231. RtlFreeHeap(RtlProcessHeap(), 0, pSD);
  232. }
  233. //
  234. // Key exists, but there is no security descriptor, or it is unreadable
  235. // for whatever reason.
  236. //
  237. pSD = RtlAllocateHeap(RtlProcessHeap(), 0,
  238. sizeof(SECURITY_DESCRIPTOR) );
  239. if (pSD)
  240. {
  241. InitializeSecurityDescriptor( pSD,
  242. SECURITY_DESCRIPTOR_REVISION );
  243. Status = RtlAllocateAndInitializeSid(
  244. &NtAuthority,
  245. 2,
  246. SECURITY_BUILTIN_DOMAIN_RID,
  247. DOMAIN_ALIAS_RID_ADMINS,
  248. 0, 0, 0, 0, 0, 0,
  249. &AdminSid );
  250. SizeOfAcl = sizeof( ACL ) + sizeof( ACL ) + sizeof( ACE_HEADER ) +
  251. RtlLengthRequiredSid( 2 );
  252. Acl = RtlAllocateHeap( RtlProcessHeap(), 0, SizeOfAcl );
  253. if ( NT_SUCCESS( Status ) &&
  254. (Acl != NULL ))
  255. {
  256. (VOID) RtlCreateAcl(Acl,
  257. SizeOfAcl,
  258. ACL_REVISION );
  259. Status = RtlAddAccessAllowedAce(
  260. Acl,
  261. ACL_REVISION,
  262. REGSEC_READ | REGSEC_WRITE,
  263. AdminSid );
  264. if ( NT_SUCCESS( Status ) )
  265. {
  266. Status = RtlSetDaclSecurityDescriptor(
  267. pSD,
  268. TRUE,
  269. Acl,
  270. FALSE );
  271. if ( NT_SUCCESS( Status ) )
  272. {
  273. RtlFreeSid( AdminSid );
  274. *pSDToUse = pSD;
  275. return STATUS_SUCCESS ;
  276. }
  277. }
  278. }
  279. if ( AdminSid )
  280. {
  281. RtlFreeSid( AdminSid );
  282. }
  283. if ( NT_SUCCESS( Status ) )
  284. {
  285. Status = STATUS_NO_MEMORY ;
  286. }
  287. }
  288. return(STATUS_NO_MEMORY);
  289. }
  290. return Status ;
  291. }
  292. NTSTATUS
  293. RegSecCheckIfAclValid(
  294. VOID
  295. )
  296. /*++
  297. Routine Description:
  298. Checks if the local copy of the ACL from the registry is still valid (that is,
  299. no one has changed it. If it is gone, the ACL is destroyed.
  300. Arguments:
  301. None.
  302. Returns:
  303. STATUS_SUCCESS if the state of the ACL is valid (whether it is present or not),
  304. other error if an error occurred.
  305. --*/
  306. {
  307. HANDLE hKey;
  308. OBJECT_ATTRIBUTES ObjAttr;
  309. UNICODE_STRING UniString;
  310. PKEY_BASIC_INFORMATION KeyInfo ;
  311. HANDLE Token ;
  312. HANDLE NullHandle ;
  313. UCHAR Buffer[ sizeof( KEY_BASIC_INFORMATION ) +
  314. sizeof( RemoteRegistryKey ) + 16 ];
  315. NTSTATUS Status ;
  316. ULONG BufferSize ;
  317. RtlInitUnicodeString( &UniString, RemoteRegistryKey );
  318. InitializeObjectAttributes( &ObjAttr,
  319. &UniString,
  320. OBJ_CASE_INSENSITIVE,
  321. NULL, NULL);
  322. //
  323. // Open the thread token. If we're in the middle of an RPC call, we won't be
  324. // able to open the key (necessarily). So, revert to local system in order to
  325. // open successfully.
  326. Status = NtOpenThreadToken( NtCurrentThread(),
  327. MAXIMUM_ALLOWED,
  328. TRUE,
  329. &Token );
  330. if ( ( Status == STATUS_NO_IMPERSONATION_TOKEN ) ||
  331. ( Status == STATUS_NO_TOKEN ) )
  332. {
  333. Token = NULL ;
  334. }
  335. else if ( Status == STATUS_SUCCESS )
  336. {
  337. NullHandle = NULL ;
  338. Status = NtSetInformationThread( NtCurrentThread(),
  339. ThreadImpersonationToken,
  340. (PVOID) &NullHandle,
  341. (ULONG) sizeof( NullHandle ) );
  342. }
  343. else
  344. {
  345. return Status ;
  346. }
  347. Status = NtOpenKey( &hKey,
  348. KEY_READ,
  349. &ObjAttr);
  350. if ( Token )
  351. {
  352. NTSTATUS RestoreStatus;
  353. RestoreStatus = NtSetInformationThread( NtCurrentThread(),
  354. ThreadImpersonationToken,
  355. (PVOID) &Token,
  356. sizeof( NullHandle ) );
  357. NtClose( Token );
  358. if ( !NT_SUCCESS( RestoreStatus ) )
  359. {
  360. Status = RestoreStatus ;
  361. }
  362. }
  363. if ( !NT_SUCCESS( Status ) )
  364. {
  365. if ( ( Status == STATUS_OBJECT_PATH_NOT_FOUND ) ||
  366. ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) )
  367. {
  368. //
  369. // The key is not present. Either, the key has never been present,
  370. // in which case we're essentially done, or the key has been deleted.
  371. // If the key is deleted, we need to get rid of the remote acl.
  372. //
  373. if ( WinregChange.QuadPart )
  374. {
  375. //
  376. // Ok, the key has been deleted. Get the exclusive lock and get to work.
  377. //
  378. RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
  379. //
  380. // Make sure no one else got through and deleted it already:
  381. //
  382. if ( WinregChange.QuadPart )
  383. {
  384. RtlFreeHeap( RtlProcessHeap(), 0, RemoteRegistrySD );
  385. RemoteRegistrySD = NULL ;
  386. WinregChange.QuadPart = 0 ;
  387. }
  388. RtlReleaseResource( &RegSecReloadLock );
  389. }
  390. Status = STATUS_SUCCESS ;
  391. }
  392. return Status ;
  393. }
  394. Status = NtQueryKey( hKey,
  395. KeyBasicInformation,
  396. Buffer,
  397. sizeof( Buffer ),
  398. & BufferSize );
  399. if ( !NT_SUCCESS( Status ) )
  400. {
  401. NtClose( hKey );
  402. return Status ;
  403. }
  404. KeyInfo = (PKEY_BASIC_INFORMATION) Buffer ;
  405. //
  406. // See if it has changed
  407. //
  408. if ( KeyInfo->LastWriteTime.QuadPart > WinregChange.QuadPart )
  409. {
  410. RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
  411. //
  412. // Since the last check was not safe, try again. Another thread
  413. // may have updated things already.
  414. //
  415. if ( KeyInfo->LastWriteTime.QuadPart > WinregChange.QuadPart )
  416. {
  417. //
  418. // Ok, this one is out of date. If there is already an SD
  419. // allocated, free it. We can do that, since every other thread
  420. // either is waiting for a shared access, or has also noticed that
  421. // it is out of date, and waiting for exclusive access.
  422. //
  423. if ( RemoteRegistrySD )
  424. {
  425. RtlFreeHeap( RtlProcessHeap(), 0, RemoteRegistrySD );
  426. RemoteRegistrySD = NULL ;
  427. }
  428. Status = RegSecReadSDFromRegistry( hKey, &RemoteRegistrySD );
  429. if ( NT_SUCCESS( Status ) )
  430. {
  431. WinregChange.QuadPart = KeyInfo->LastWriteTime.QuadPart ;
  432. }
  433. }
  434. RtlReleaseResource( &RegSecReloadLock );
  435. }
  436. NtClose( hKey );
  437. return Status ;
  438. }
  439. //+---------------------------------------------------------------------------
  440. //
  441. // Function: RegSecReadAllowedPath
  442. //
  443. // Synopsis: Pull the Allowed paths out of the registry, and set up a
  444. // table for searching later. This is a flat list, since the
  445. // number of elements by default is 2, and shouldn't grow much
  446. // bigger.
  447. //
  448. // Arguments: [hKey] --
  449. // [Value] --
  450. // [List] --
  451. // [ListBase] --
  452. // [ListCount] --
  453. //
  454. // History: 5-17-96 RichardW Created
  455. //
  456. // Notes:
  457. //
  458. //----------------------------------------------------------------------------
  459. BOOL
  460. RegSecReadAllowedPath(
  461. HANDLE hKey,
  462. PWSTR Value,
  463. PUNICODE_STRING * List,
  464. PUCHAR * ListBase,
  465. PULONG ListCount)
  466. {
  467. NTSTATUS Status;
  468. UNICODE_STRING UniString;
  469. PKEY_VALUE_PARTIAL_INFORMATION pValue;
  470. ULONG Size;
  471. PWSTR Scan;
  472. ULONG StringCount;
  473. PUNICODE_STRING Paths;
  474. //
  475. // Read the value size:
  476. //
  477. RtlInitUnicodeString( &UniString, Value );
  478. Status = NtQueryValueKey( hKey,
  479. &UniString,
  480. KeyValuePartialInformation,
  481. NULL,
  482. 0,
  483. &Size );
  484. if ( !NT_SUCCESS( Status ) && (Status != STATUS_BUFFER_TOO_SMALL))
  485. {
  486. if ( (Status == STATUS_OBJECT_PATH_NOT_FOUND) ||
  487. (Status == STATUS_OBJECT_NAME_NOT_FOUND) )
  488. {
  489. return( TRUE );
  490. }
  491. return FALSE ;
  492. }
  493. //
  494. // Allocate enough:
  495. //
  496. pValue = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
  497. if ( pValue )
  498. {
  499. Status = NtQueryValueKey( hKey,
  500. &UniString,
  501. KeyValuePartialInformation,
  502. pValue,
  503. Size,
  504. &Size );
  505. }
  506. if ( !pValue )
  507. {
  508. return( FALSE );
  509. }
  510. //
  511. // Okay, we should have a multi-valued set of paths that we can
  512. // allow access to despite the access control.
  513. //
  514. if ( pValue->Type != REG_MULTI_SZ )
  515. {
  516. RtlFreeHeap( RtlProcessHeap(), 0, pValue );
  517. return( FALSE );
  518. }
  519. //
  520. // Scan list, determine how many strings:
  521. //
  522. Scan = (PWSTR) pValue->Data;
  523. StringCount = 0;
  524. while ( *Scan )
  525. {
  526. while ( *Scan )
  527. {
  528. Scan ++;
  529. }
  530. StringCount ++;
  531. Scan ++;
  532. }
  533. //
  534. // Allocate enough UNICODE_STRING structs to point to each string
  535. //
  536. Paths = RtlAllocateHeap( RtlProcessHeap(), 0,
  537. StringCount * sizeof(UNICODE_STRING) );
  538. if ( !Paths )
  539. {
  540. RtlFreeHeap( RtlProcessHeap(), 0, pValue );
  541. return( FALSE );
  542. }
  543. Scan = ( PWSTR ) pValue->Data;
  544. *ListCount = StringCount;
  545. StringCount = 0;
  546. //
  547. // Set up one UNICODE_STRING per string in the multi_sz,
  548. //
  549. while ( *Scan )
  550. {
  551. RtlInitUnicodeString( &Paths[ StringCount ],
  552. Scan );
  553. while ( *Scan)
  554. {
  555. Scan ++;
  556. }
  557. StringCount ++;
  558. Scan ++;
  559. }
  560. //
  561. // And pass the list back.
  562. //
  563. *ListBase = (PUCHAR) pValue;
  564. *List = Paths;
  565. return( TRUE );
  566. }
  567. //+---------------------------------------------------------------------------
  568. //
  569. // Function: RegSecReadAllowedPaths
  570. //
  571. // Synopsis: Reads the allowed paths out of the registry
  572. //
  573. // Arguments: (none)
  574. //
  575. // History: 5-17-96 RichardW Created
  576. //
  577. // Notes:
  578. //
  579. //----------------------------------------------------------------------------
  580. NTSTATUS
  581. RegSecCheckAllowedPaths(
  582. VOID
  583. )
  584. {
  585. HANDLE hKey;
  586. OBJECT_ATTRIBUTES ObjAttr;
  587. UNICODE_STRING UniString;
  588. NTSTATUS Status;
  589. HANDLE Token ;
  590. HANDLE NullHandle ;
  591. PKEY_BASIC_INFORMATION KeyInfo ;
  592. UCHAR Buffer[ sizeof( KEY_BASIC_INFORMATION ) +
  593. sizeof( AllowedPathsKey ) + 16 ];
  594. ULONG BufferSize ;
  595. RtlInitUnicodeString(&UniString, AllowedPathsKey);
  596. InitializeObjectAttributes( &ObjAttr,
  597. &UniString,
  598. OBJ_CASE_INSENSITIVE,
  599. NULL, NULL);
  600. //
  601. // Open the thread token. If we're in the middle of an RPC call, we won't be
  602. // able to open the key (necessarily). So, revert to local system in order to
  603. // open successfully.
  604. Status = NtOpenThreadToken( NtCurrentThread(),
  605. MAXIMUM_ALLOWED,
  606. TRUE,
  607. &Token );
  608. if ( ( Status == STATUS_NO_IMPERSONATION_TOKEN ) ||
  609. ( Status == STATUS_NO_TOKEN ) )
  610. {
  611. Token = NULL ;
  612. }
  613. else if ( Status == STATUS_SUCCESS )
  614. {
  615. NullHandle = NULL ;
  616. Status = NtSetInformationThread( NtCurrentThread(),
  617. ThreadImpersonationToken,
  618. (PVOID) &NullHandle,
  619. (ULONG) sizeof( NullHandle ) );
  620. }
  621. else
  622. {
  623. return Status ;
  624. }
  625. //
  626. // Open the key in local system context
  627. //
  628. Status = NtOpenKey( &hKey,
  629. KEY_READ,
  630. &ObjAttr);
  631. //
  632. // Immediately restore back to the client token.
  633. //
  634. if ( Token )
  635. {
  636. NTSTATUS RestoreStatus;
  637. RestoreStatus = NtSetInformationThread( NtCurrentThread(),
  638. ThreadImpersonationToken,
  639. (PVOID) &Token,
  640. sizeof( NullHandle ) );
  641. NtClose( Token );
  642. if ( !NT_SUCCESS( RestoreStatus ) )
  643. {
  644. Status = RestoreStatus ;
  645. }
  646. }
  647. if ( !NT_SUCCESS( Status ) )
  648. {
  649. if ( ( Status == STATUS_OBJECT_PATH_NOT_FOUND ) ||
  650. ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) )
  651. {
  652. //
  653. // The key is not present. Either, the key has never been present,
  654. // in which case we're essentially done, or the key has been deleted.
  655. // If the key is deleted, we need to get rid of the remote acl.
  656. //
  657. if ( AllowedPathsChange.QuadPart )
  658. {
  659. //
  660. // Ok, the key has been deleted. Get the exclusive lock and get to work.
  661. //
  662. RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
  663. //
  664. // Make sure no one else has freed it already:
  665. //
  666. if ( AllowedPathsChange.QuadPart )
  667. {
  668. if ( MachineAllowedPaths )
  669. {
  670. RtlFreeHeap( RtlProcessHeap(), 0, MachineAllowedPaths );
  671. RtlFreeHeap( RtlProcessHeap(), 0, MachineAllowedPathsBase );
  672. MachineAllowedPaths = NULL ;
  673. MachineAllowedPathsBase = NULL ;
  674. }
  675. if ( UsersAllowedPaths )
  676. {
  677. RtlFreeHeap( RtlProcessHeap(), 0, UsersAllowedPaths );
  678. RtlFreeHeap( RtlProcessHeap(), 0, UsersAllowedPathsBase );
  679. UsersAllowedPaths = NULL ;
  680. UsersAllowedPathsBase = NULL ;
  681. }
  682. AllowedPathsChange.QuadPart = 0;
  683. }
  684. RtlReleaseResource( &RegSecReloadLock );
  685. }
  686. Status = STATUS_SUCCESS ;
  687. }
  688. return Status ;
  689. }
  690. Status = NtQueryKey( hKey,
  691. KeyBasicInformation,
  692. Buffer,
  693. sizeof( Buffer ),
  694. & BufferSize );
  695. if ( !NT_SUCCESS( Status ) )
  696. {
  697. NtClose( hKey );
  698. return Status ;
  699. }
  700. KeyInfo = (PKEY_BASIC_INFORMATION) Buffer ;
  701. //
  702. // See if it has changed
  703. //
  704. if ( KeyInfo->LastWriteTime.QuadPart > AllowedPathsChange.QuadPart )
  705. {
  706. //
  707. // Well, it changed. So, we need to flush out the old (familiar?) stuff,
  708. // and reload with the new stuff. So, back to the synchronization games.
  709. //
  710. RtlAcquireResourceExclusive( &RegSecReloadLock, TRUE );
  711. //
  712. // Make sure no one else beat us to it
  713. //
  714. if ( KeyInfo->LastWriteTime.QuadPart > AllowedPathsChange.QuadPart )
  715. {
  716. if ( MachineAllowedPaths )
  717. {
  718. RtlFreeHeap( RtlProcessHeap(), 0, MachineAllowedPaths );
  719. RtlFreeHeap( RtlProcessHeap(), 0, MachineAllowedPathsBase );
  720. MachineAllowedPaths = NULL ;
  721. MachineAllowedPathsBase = NULL ;
  722. }
  723. if ( UsersAllowedPaths )
  724. {
  725. RtlFreeHeap( RtlProcessHeap(), 0, UsersAllowedPaths );
  726. RtlFreeHeap( RtlProcessHeap(), 0, UsersAllowedPathsBase );
  727. UsersAllowedPaths = NULL ;
  728. UsersAllowedPathsBase = NULL ;
  729. }
  730. //
  731. // Read in the paths allowed:
  732. //
  733. RegSecReadAllowedPath( hKey,
  734. MachineValue,
  735. &MachineAllowedPaths,
  736. &MachineAllowedPathsBase,
  737. &MachineAllowedPathsCount
  738. );
  739. RegSecReadAllowedPath( hKey,
  740. UsersValue,
  741. &UsersAllowedPaths,
  742. &UsersAllowedPathsBase,
  743. &UsersAllowedPathsCount
  744. );
  745. }
  746. RtlReleaseResource( &RegSecReloadLock );
  747. }
  748. NtClose( hKey );
  749. return STATUS_SUCCESS ;
  750. }
  751. //+---------------------------------------------------------------------------
  752. //
  753. // Function: InitializeRemoteSecurity
  754. //
  755. // Synopsis: Hook to initialize our look-aside stuff
  756. //
  757. // Arguments: (none)
  758. //
  759. // History: 5-17-96 RichardW Created
  760. //
  761. // Notes:
  762. //
  763. //----------------------------------------------------------------------------
  764. BOOL
  765. InitializeRemoteSecurity(
  766. VOID
  767. )
  768. {
  769. NTSTATUS Status ;
  770. try
  771. {
  772. RtlInitializeResource( &RegSecReloadLock );
  773. Status = STATUS_SUCCESS ;
  774. }
  775. except (EXCEPTION_EXECUTE_HANDLER)
  776. {
  777. Status = GetExceptionCode();
  778. }
  779. if ( !NT_SUCCESS( Status ) )
  780. {
  781. return Status ;
  782. }
  783. RemoteRegistryMappings.GenericRead = REGSEC_READ;
  784. RemoteRegistryMappings.GenericWrite = REGSEC_WRITE;
  785. RemoteRegistryMappings.GenericExecute = REGSEC_READ;
  786. RemoteRegistryMappings.GenericAll = REGSEC_READ | REGSEC_WRITE;
  787. WinregChange.QuadPart = 0 ;
  788. AllowedPathsChange.QuadPart = 0 ;
  789. return( TRUE );
  790. }
  791. //+---------------------------------------------------------------------------
  792. //
  793. // Function: RegSecCheckRemoteAccess
  794. //
  795. // Synopsis: Check remote access against the security descriptor we built
  796. // on the side.
  797. //
  798. // Arguments: [phKey] --
  799. //
  800. // History: 5-17-96 RichardW Created
  801. //
  802. // Notes:
  803. //
  804. //----------------------------------------------------------------------------
  805. BOOL
  806. RegSecCheckRemoteAccess(
  807. PRPC_HKEY phKey)
  808. {
  809. NTSTATUS Status;
  810. ACCESS_MASK Mask;
  811. NTSTATUS AccessStatus;
  812. HANDLE Token;
  813. ULONG Size;
  814. UCHAR QuickBuffer[sizeof(PRIVILEGE_SET) + 4 * sizeof(LUID_AND_ATTRIBUTES)];
  815. PPRIVILEGE_SET PrivSet;
  816. ULONG PrivilegeSetLen;
  817. Status = RegSecCheckIfAclValid();
  818. if ( !NT_SUCCESS( Status ) )
  819. {
  820. return FALSE ;
  821. }
  822. RtlAcquireResourceShared( &RegSecReloadLock, TRUE );
  823. if ( RemoteRegistrySD )
  824. {
  825. //
  826. // Capture the thread's token
  827. //
  828. Status = NtOpenThreadToken(
  829. NtCurrentThread(),
  830. MAXIMUM_ALLOWED,
  831. TRUE,
  832. &Token );
  833. if ( !NT_SUCCESS(Status) )
  834. {
  835. RtlReleaseResource( &RegSecReloadLock );
  836. return( FALSE );
  837. }
  838. PrivSet = (PPRIVILEGE_SET) QuickBuffer;
  839. PrivilegeSetLen = sizeof( QuickBuffer );
  840. //
  841. // Do the access check.
  842. //
  843. Status = NtAccessCheck( RemoteRegistrySD,
  844. Token,
  845. MAXIMUM_ALLOWED,
  846. &RemoteRegistryMappings,
  847. PrivSet,
  848. &PrivilegeSetLen,
  849. &Mask,
  850. &AccessStatus );
  851. RtlReleaseResource( &RegSecReloadLock );
  852. (void) NtClose( Token );
  853. if ( NT_SUCCESS( Status ) )
  854. {
  855. if ( NT_SUCCESS( AccessStatus ) &&
  856. (Mask & (REGSEC_READ | REGSEC_WRITE)) )
  857. {
  858. return( TRUE );
  859. }
  860. return( FALSE );
  861. }
  862. else
  863. {
  864. return FALSE ;
  865. }
  866. }
  867. RtlReleaseResource( &RegSecReloadLock );
  868. return( TRUE );
  869. }
  870. //+---------------------------------------------------------------------------
  871. //
  872. // Function: RegSecCheckPath
  873. //
  874. // Synopsis: Check a specific key path if we should ignore the current
  875. // ACL.
  876. //
  877. // Arguments: [hKey] --
  878. // [pSubKey] --
  879. //
  880. // History: 5-17-96 RichardW Created
  881. //
  882. // Notes:
  883. //
  884. //----------------------------------------------------------------------------
  885. BOOL
  886. RegSecCheckPath(
  887. HKEY hKey,
  888. PUNICODE_STRING pSubKey)
  889. {
  890. UNICODE_STRING Comparator;
  891. UNICODE_STRING String;
  892. ULONG i;
  893. ULONG Count = 0;
  894. PUNICODE_STRING List;
  895. BOOL Success ;
  896. NTSTATUS Status ;
  897. Status = RegSecCheckAllowedPaths();
  898. if ( !NT_SUCCESS( Status ) )
  899. {
  900. return FALSE ;
  901. }
  902. if ( (pSubKey->Buffer == NULL) ||
  903. (pSubKey->Length == 0 ) ||
  904. (pSubKey->MaximumLength == 0 ) )
  905. {
  906. return FALSE ;
  907. }
  908. RtlAcquireResourceShared( &RegSecReloadLock, TRUE );
  909. if ( REGSEC_TEST_HANDLE( hKey, CHECK_USER_PATHS ) )
  910. {
  911. Count = UsersAllowedPathsCount;
  912. List = UsersAllowedPaths;
  913. }
  914. if ( REGSEC_TEST_HANDLE( hKey, CHECK_MACHINE_PATHS ) )
  915. {
  916. Count = MachineAllowedPathsCount;
  917. List = MachineAllowedPaths;
  918. }
  919. Success = FALSE ;
  920. for ( i = 0 ; i < Count ; i++ )
  921. {
  922. String = *pSubKey;
  923. //
  924. // Ah ha, RPC strings often have the NULL included in the length.
  925. // touch that up.
  926. //
  927. while ( (String.Length != 0) && (String.Buffer[ (String.Length / sizeof(WCHAR)) - 1] == L'\0') )
  928. {
  929. String.Length -= sizeof(WCHAR) ;
  930. }
  931. Comparator = List[ i ];
  932. //
  933. // If the Comparator is a prefix of the sub key, allow it (for spooler)
  934. //
  935. if ( String.Length > Comparator.Length )
  936. {
  937. if ( String.Buffer[ Comparator.Length / sizeof(WCHAR) ] == L'\\' )
  938. {
  939. //
  940. // Length-wise, it could be an ancestor
  941. //
  942. String.Length = Comparator.Length;
  943. }
  944. }
  945. //
  946. // If it matches, let it go...
  947. //
  948. if ( RtlCompareUnicodeString( &String, &Comparator, TRUE ) == 0 )
  949. {
  950. Success = TRUE ;
  951. break;
  952. }
  953. }
  954. RtlReleaseResource( &RegSecReloadLock ) ;
  955. return( Success );
  956. }