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

1821 lines
48 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. dbpriv.c
  5. Abstract:
  6. LSA - Database - Privilege Object Private API Workers
  7. NOTE: This module should remain as portable code that is independent
  8. of the implementation of the LSA Database. As such, it is
  9. permitted to use only the exported LSA Database interfaces
  10. contained in db.h and NOT the private implementation
  11. dependent functions in dbp.h.
  12. Author:
  13. Jim Kelly (JimK) March 24, 1992
  14. Environment:
  15. Revision History:
  16. 06-April-1999 kumarp
  17. Since NT5 does not support extensible privilege dlls, code that
  18. supports this has been removed. This code is available in
  19. dbpriv.c@v10. The code should be re-added to this file post NT5.
  20. --*/
  21. #include <lsapch2.h>
  22. #include "dbp.h"
  23. #include "adtp.h"
  24. ///////////////////////////////////////////////////////////////////////////
  25. // //
  26. // //
  27. // Module-wide data types //
  28. // //
  29. // //
  30. ///////////////////////////////////////////////////////////////////////////
  31. typedef struct _LSAP_DLL_DESCRIPTOR {
  32. WORD Count;
  33. WORD Language;
  34. PVOID DllHandle;
  35. } LSAP_DLL_DESCRIPTOR, *PLSAP_DLL_DESCRIPTOR;
  36. ///////////////////////////////////////////////////////////////////////////
  37. // //
  38. // //
  39. // Module-wide variables //
  40. // //
  41. // //
  42. ///////////////////////////////////////////////////////////////////////////
  43. #define MAX_PRIVILEGE_DISPLAY_NAME_CHARS 256
  44. //
  45. // Until we actually have a privilege object, keep well known privilege
  46. // information as global data. The information for each privilege is
  47. // kept in a an array POLICY_PRIVILEGE_DEFINITION structures.
  48. //
  49. ULONG LsapWellKnownPrivilegeCount;
  50. USHORT LsapWellKnownPrivilegeMaxLen;
  51. POLICY_PRIVILEGE_DEFINITION LsapKnownPrivilege[SE_MAX_WELL_KNOWN_PRIVILEGE];
  52. //
  53. // we store the known privilege names in this array so that we do not have to
  54. // load them from msprivs.dll every time. this will change when we
  55. // support vendor supplied priv. dlls.
  56. //
  57. static LPCWSTR LsapKnownPrivilageNames[] =
  58. {
  59. SE_CREATE_TOKEN_NAME,
  60. SE_ASSIGNPRIMARYTOKEN_NAME,
  61. SE_LOCK_MEMORY_NAME,
  62. SE_INCREASE_QUOTA_NAME,
  63. SE_MACHINE_ACCOUNT_NAME,
  64. SE_TCB_NAME,
  65. SE_SECURITY_NAME,
  66. SE_TAKE_OWNERSHIP_NAME,
  67. SE_LOAD_DRIVER_NAME,
  68. SE_SYSTEM_PROFILE_NAME,
  69. SE_SYSTEMTIME_NAME,
  70. SE_PROF_SINGLE_PROCESS_NAME,
  71. SE_INC_BASE_PRIORITY_NAME,
  72. SE_CREATE_PAGEFILE_NAME,
  73. SE_CREATE_PERMANENT_NAME,
  74. SE_BACKUP_NAME,
  75. SE_RESTORE_NAME,
  76. SE_SHUTDOWN_NAME,
  77. SE_DEBUG_NAME,
  78. SE_AUDIT_NAME,
  79. SE_SYSTEM_ENVIRONMENT_NAME,
  80. SE_CHANGE_NOTIFY_NAME,
  81. SE_REMOTE_SHUTDOWN_NAME,
  82. SE_UNDOCK_NAME,
  83. SE_SYNC_AGENT_NAME,
  84. SE_ENABLE_DELEGATION_NAME,
  85. SE_MANAGE_VOLUME_NAME,
  86. SE_IMPERSONATE_NAME,
  87. SE_CREATE_GLOBAL_NAME
  88. };
  89. static UINT LsapNumKnownPrivileges = sizeof(LsapKnownPrivilageNames) /
  90. sizeof(LsapKnownPrivilageNames[0]);
  91. //
  92. // Array of handles to DLLs containing privilege definitions
  93. //
  94. ULONG LsapPrivilegeDllCount;
  95. PLSAP_DLL_DESCRIPTOR LsapPrivilegeDlls; //Array
  96. //
  97. // TEMPORARY: Name of Microsoft's standard privilege names DLL
  98. //
  99. WCHAR MsDllNameString[] = L"msprivs";
  100. UNICODE_STRING MsDllName;
  101. ///////////////////////////////////////////////////////////////////////////
  102. // //
  103. // //
  104. // Internal routine templates //
  105. // //
  106. // //
  107. ///////////////////////////////////////////////////////////////////////////
  108. NTSTATUS
  109. LsapLookupKnownPrivilegeName(
  110. PLUID Value,
  111. PUNICODE_STRING *Name
  112. );
  113. NTSTATUS
  114. LsapLookupKnownPrivilegeValue(
  115. PUNICODE_STRING Name,
  116. PLUID Value
  117. );
  118. NTSTATUS
  119. LsapLookupPrivilegeDisplayName(
  120. IN PUNICODE_STRING ProgrammaticName,
  121. IN WORD ClientLanguage,
  122. IN WORD ClientSystemDefaultLanguage,
  123. OUT PUNICODE_STRING *DisplayName,
  124. OUT PWORD LanguageReturned
  125. );
  126. NTSTATUS
  127. LsapGetPrivilegeDisplayName(
  128. IN ULONG DllIndex,
  129. IN ULONG PrivilegeIndex,
  130. IN WORD ClientLanguage,
  131. IN WORD ClientSystemDefaultLanguage,
  132. OUT PUNICODE_STRING *DisplayName,
  133. OUT PWORD LanguageReturned
  134. );
  135. NTSTATUS
  136. LsapGetPrivilegeDisplayNameResourceId(
  137. IN PUNICODE_STRING Name,
  138. IN ULONG DllIndex,
  139. OUT PULONG PrivilegeIndex
  140. );
  141. NTSTATUS
  142. LsapOpenPrivilegeDlls( VOID );
  143. NTSTATUS
  144. LsapGetPrivilegeDllNames(
  145. OUT PUNICODE_STRING *DllNames,
  146. OUT PULONG DllCount
  147. );
  148. ///////////////////////////////////////////////////////////////////////////
  149. // //
  150. // //
  151. // RPC stub-called routines //
  152. // //
  153. // //
  154. ///////////////////////////////////////////////////////////////////////////
  155. NTSTATUS
  156. LsarEnumeratePrivileges(
  157. IN LSAPR_HANDLE PolicyHandle,
  158. IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
  159. OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
  160. IN ULONG PreferedMaximumLength
  161. )
  162. /*++
  163. Routine Description:
  164. This function returnes information about privileges known on this
  165. system. This call requires POLICY_VIEW_LOCAL_INFORMATION access
  166. to the Policy Object. Since there may be more information than
  167. can be returned in a single call of the routine, multiple calls
  168. can be made to get all of the information. To support this feature,
  169. the caller is provided with a handle that can be used across calls to
  170. the API. On the initial call, EnumerationContext should point to a
  171. variable that has been initialized to 0.
  172. WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
  173. WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
  174. ABOUT LOADED PRIVILEGES.
  175. Arguments:
  176. PolicyHandle - Handle from an LsarOpenPolicy() call.
  177. EnumerationContext - API specific handle to allow multiple calls
  178. (see Routine Description).
  179. EnumerationBuffer - Pointer to structure that will be initialized to
  180. contain a count of the privileges returned and a pointer to an
  181. array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
  182. the privileges.
  183. PreferedMaximumLength - Prefered maximim length of returned data
  184. (in 8-bit bytes). This is not a hard upper limit, but serves as
  185. a guide. Due to data conversion between systems with different
  186. natural data sizes, the actual amount of data returned may be
  187. greater than this value.
  188. CountReturned - Number of entries returned.
  189. Return Values:
  190. NTSTATUS - Standard Nt Result Code.
  191. STATUS_SUCCESS - The call completed successfully.
  192. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
  193. such as memory, to complete the call.
  194. STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
  195. a Policy object.
  196. STATUS_ACCESS_DENIED - The caller does not have the necessary
  197. access to perform the operation.
  198. STATUS_MORE_ENTRIES - There are more entries, so call again. This
  199. is an informational status only.
  200. STATUS_NO_MORE_ENTRIES - No entries were returned because there
  201. are no more.
  202. --*/
  203. {
  204. NTSTATUS Status, PreliminaryStatus;
  205. BOOLEAN ObjectReferenced = FALSE;
  206. LsarpReturnCheckSetup();
  207. //
  208. // Acquire the Lsa Database lock. Verify that PolicyHandle is a valid
  209. // hadnle to a Policy Object and is trusted or has the necessary accesses.
  210. // Reference the handle.
  211. //
  212. Status = LsapDbReferenceObject(
  213. PolicyHandle,
  214. POLICY_VIEW_LOCAL_INFORMATION,
  215. PolicyObject,
  216. NullObject,
  217. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
  218. );
  219. if (!NT_SUCCESS(Status)) {
  220. goto EnumeratePrivilegesError;
  221. }
  222. ObjectReferenced = TRUE;
  223. //
  224. // Call Privilege Enumeration Routine.
  225. //
  226. Status = LsapDbEnumeratePrivileges(
  227. EnumerationContext,
  228. EnumerationBuffer,
  229. PreferedMaximumLength
  230. );
  231. if (!NT_SUCCESS(Status)) {
  232. goto EnumeratePrivilegesError;
  233. }
  234. EnumeratePrivilegesFinish:
  235. //
  236. // If necessary, dereference the Policy Object, release the LSA Database
  237. // lock and return. Preserve current Status where appropriate.
  238. //
  239. if (ObjectReferenced) {
  240. PreliminaryStatus = Status;
  241. Status = LsapDbDereferenceObject(
  242. &PolicyHandle,
  243. PolicyObject,
  244. NullObject,
  245. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
  246. (SECURITY_DB_DELTA_TYPE) 0,
  247. PreliminaryStatus
  248. );
  249. ObjectReferenced = FALSE;
  250. if ((!NT_SUCCESS(Status)) && NT_SUCCESS(PreliminaryStatus)) {
  251. goto EnumeratePrivilegesError;
  252. }
  253. }
  254. LsarpReturnPrologue();
  255. return(Status);
  256. EnumeratePrivilegesError:
  257. goto EnumeratePrivilegesFinish;
  258. }
  259. NTSTATUS
  260. LsapDbEnumeratePrivileges(
  261. IN OUT PLSA_ENUMERATION_HANDLE EnumerationContext,
  262. OUT PLSAPR_PRIVILEGE_ENUM_BUFFER EnumerationBuffer,
  263. IN ULONG PreferedMaximumLength
  264. )
  265. /*++
  266. Routine Description:
  267. This function returnes information about the Privileges that exist
  268. in the system.access to the Policy Object. Since there
  269. may be more information than can be returned in a single call of the
  270. routine, multiple calls can be made to get all of the information.
  271. To support this feature, the caller is provided with a handle that can
  272. be used across calls to the API. On the initial call, EnumerationContext
  273. should point to a variable that has been initialized to 0.
  274. WARNING! CURRENTLY, THIS FUNCTION ONLY RETURNS INFORMATION ABOUT
  275. WELL-KNOWN PRIVILEGES. LATER, IT WILL RETURN INFORMATION
  276. ABOUT LOADED PRIVILEGES.
  277. Arguments:
  278. EnumerationContext - API specific handle to allow multiple calls
  279. (see Routine Description).
  280. EnumerationBuffer - Pointer to structure that will be initialized to
  281. contain a count of the privileges returned and a pointer to an
  282. array of structures of type LSAPR_POLICY_PRIVILEGE_DEF describing
  283. the privileges.
  284. PreferedMaximumLength - Prefered maximim length of returned data
  285. (in 8-bit bytes). This is not a hard upper limit, but serves as
  286. a guide. Due to data conversion between systems with different
  287. natural data sizes, the actual amount of data returned may be
  288. greater than this value.
  289. CountReturned - Number of entries returned.
  290. Return Values:
  291. NTSTATUS - Standard Nt Result Code.
  292. STATUS_SUCCESS - The call completed successfully.
  293. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
  294. such as memory, to complete the call.
  295. STATUS_INVALID_HANDLE - PolicyHandle is not a valid handle to
  296. a Policy object.
  297. STATUS_ACCESS_DENIED - The caller does not have the necessary
  298. access to perform the operation.
  299. STATUS_MORE_ENTRIES - There are more entries, so call again. This
  300. is an informational status only.
  301. STATUS_NO_MORE_ENTRIES - No entries were returned because there
  302. are no more.
  303. --*/
  304. {
  305. NTSTATUS Status = STATUS_NO_MORE_ENTRIES;
  306. ULONG WellKnownPrivilegeCount = (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE + 1);
  307. ULONG Index;
  308. //
  309. // If the Enumeration Context Value given exceeds the total count of
  310. // Privileges, return an error.
  311. //
  312. if (*EnumerationContext >= WellKnownPrivilegeCount) {
  313. Status = STATUS_NO_MORE_ENTRIES;
  314. goto EnumeratePrivilegesError;
  315. }
  316. //
  317. // Since there are only a small number of privileges, we will
  318. // return all of the information in one go, so allocate memory
  319. // for output array of Privilege Definition structures.
  320. //
  321. EnumerationBuffer->Entries = WellKnownPrivilegeCount;
  322. EnumerationBuffer->Privileges =
  323. MIDL_user_allocate(
  324. WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
  325. );
  326. if (EnumerationBuffer->Privileges == NULL) {
  327. Status = STATUS_INSUFFICIENT_RESOURCES;
  328. goto EnumeratePrivilegesError;
  329. }
  330. RtlZeroMemory(
  331. EnumerationBuffer->Privileges,
  332. WellKnownPrivilegeCount * sizeof (POLICY_PRIVILEGE_DEFINITION)
  333. );
  334. //
  335. // Now lookup each of the Well Known Privileges.
  336. //
  337. for( Index = *EnumerationContext;
  338. Index < WellKnownPrivilegeCount;
  339. Index++) {
  340. EnumerationBuffer->Privileges[ Index ].LocalValue
  341. = LsapKnownPrivilege[ Index ].LocalValue;
  342. Status = LsapRpcCopyUnicodeString(
  343. NULL,
  344. (PUNICODE_STRING) &EnumerationBuffer->Privileges[ Index].Name,
  345. &LsapKnownPrivilege[ Index ].Name
  346. );
  347. if (!NT_SUCCESS(Status)) {
  348. break;
  349. }
  350. }
  351. if (!NT_SUCCESS(Status)) {
  352. goto EnumeratePrivilegesError;
  353. }
  354. *EnumerationContext = Index;
  355. EnumeratePrivilegesFinish:
  356. return(Status);
  357. EnumeratePrivilegesError:
  358. //
  359. // If necessary, free any memory buffers allocated for Well Known Privilege
  360. // Programmatic Names.
  361. //
  362. if (EnumerationBuffer->Privileges != NULL) {
  363. for( Index = 0;
  364. Index < WellKnownPrivilegeCount;
  365. Index++) {
  366. if ( EnumerationBuffer->Privileges[ Index].Name.Buffer != NULL) {
  367. MIDL_user_free( EnumerationBuffer->Privileges[ Index ].Name.Buffer );
  368. }
  369. }
  370. MIDL_user_free( EnumerationBuffer->Privileges );
  371. EnumerationBuffer->Privileges = NULL;
  372. }
  373. EnumerationBuffer->Entries = 0;
  374. *EnumerationContext = 0;
  375. goto EnumeratePrivilegesFinish;
  376. UNREFERENCED_PARAMETER( PreferedMaximumLength );
  377. }
  378. NTSTATUS
  379. LsarLookupPrivilegeValue(
  380. IN LSAPR_HANDLE PolicyHandle,
  381. IN PLSAPR_UNICODE_STRING Name,
  382. OUT PLUID Value
  383. )
  384. /*++
  385. Routine Description:
  386. This function is the LSA server RPC worker routine for the
  387. LsaLookupPrivilegeValue() API.
  388. Arguments:
  389. PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
  390. must be open for POLICY_LOOKUP_NAMES access.
  391. Name - Is the privilege's programmatic name.
  392. Value - Receives the locally unique ID the privilege is known by on the
  393. target machine.
  394. Return Value:
  395. NTSTATUS - The privilege was found and returned.
  396. STATUS_ACCESS_DENIED - Caller does not have the appropriate access
  397. to complete the operation.
  398. STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
  399. found.
  400. --*/
  401. {
  402. NTSTATUS Status;
  403. LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
  404. LsarpReturnCheckSetup();
  405. //
  406. // Validate the input buffer
  407. //
  408. if ( !LsapValidateLsaUnicodeString( Name ) ) {
  409. return( STATUS_INVALID_PARAMETER );
  410. }
  411. //
  412. // Make sure we know what RPC is doing to/for us
  413. //
  414. ASSERT( Name != NULL );
  415. //
  416. // make sure the caller has the appropriate access
  417. //
  418. Status = LsapDbReferenceObject(
  419. PolicyHandle,
  420. POLICY_LOOKUP_NAMES,
  421. PolicyObject,
  422. NullObject,
  423. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
  424. );
  425. if (!NT_SUCCESS(Status)) {
  426. return(Status);
  427. }
  428. //
  429. // No need to hold onto the Policy object after this..
  430. // We just needed it for access validation purposes.
  431. //
  432. Status = LsapDbDereferenceObject(
  433. &PolicyHandle,
  434. PolicyObject,
  435. NullObject,
  436. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
  437. (SECURITY_DB_DELTA_TYPE) 0,
  438. Status
  439. );
  440. if (NT_SUCCESS(Status)) {
  441. if (Name->Buffer == 0 || Name->Length == 0) {
  442. return(STATUS_NO_SUCH_PRIVILEGE);
  443. }
  444. Status = LsapLookupKnownPrivilegeValue( (PUNICODE_STRING) Name, Value );
  445. }
  446. LsarpReturnPrologue();
  447. return(Status);
  448. }
  449. NTSTATUS
  450. LsarLookupPrivilegeName(
  451. IN LSAPR_HANDLE PolicyHandle,
  452. IN PLUID Value,
  453. OUT PLSAPR_UNICODE_STRING *Name
  454. )
  455. /*++
  456. Routine Description:
  457. This function is the LSA server RPC worker routine for the
  458. LsaLookupPrivilegeName() API.
  459. Arguments:
  460. PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
  461. must be open for POLICY_LOOKUP_NAMES access.
  462. Value - is the locally unique ID the privilege is known by on the
  463. target machine.
  464. Name - Receives the privilege's programmatic name.
  465. Return Value:
  466. NTSTATUS - Standard Nt Result Code
  467. STATUS_SUCCESS - The privilege was found and returned.
  468. STATUS_ACCESS_DENIED - Caller does not have the appropriate access
  469. to complete the operation.
  470. STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
  471. found.
  472. --*/
  473. {
  474. NTSTATUS Status;
  475. LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
  476. LsarpReturnCheckSetup();
  477. //
  478. // make sure we know what RPC is doing to/for us
  479. //
  480. ASSERT( Name != NULL );
  481. ASSERT( (*Name) == NULL );
  482. //
  483. // make sure the caller has the appropriate access
  484. //
  485. Status = LsapDbReferenceObject(
  486. PolicyHandle,
  487. POLICY_LOOKUP_NAMES,
  488. PolicyObject,
  489. NullObject,
  490. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
  491. );
  492. if (!NT_SUCCESS(Status)) {
  493. return(Status);
  494. }
  495. //
  496. // No need to hold onto the Policy object after this..
  497. // We just needed it for access validation purposes.
  498. //
  499. Status = LsapDbDereferenceObject(
  500. &PolicyHandle,
  501. PolicyObject,
  502. NullObject,
  503. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
  504. (SECURITY_DB_DELTA_TYPE) 0,
  505. Status
  506. );
  507. if (NT_SUCCESS(Status)) {
  508. Status = LsapLookupKnownPrivilegeName( Value,(PUNICODE_STRING *) Name );
  509. }
  510. LsarpReturnPrologue();
  511. return(Status);
  512. }
  513. NTSTATUS
  514. LsarLookupPrivilegeDisplayName(
  515. IN LSAPR_HANDLE PolicyHandle,
  516. IN PLSAPR_UNICODE_STRING Name,
  517. IN SHORT ClientLanguage,
  518. IN SHORT ClientSystemDefaultLanguage,
  519. OUT PLSAPR_UNICODE_STRING *DisplayName,
  520. OUT PWORD LanguageReturned
  521. )
  522. /*++
  523. Routine Description:
  524. This function is the LSA server RPC worker routine for the
  525. LsaLookupPrivilegeDisplayName() API.
  526. Arguments:
  527. PolicyHandle - Handle from an LsaOpenPolicy() call. This handle
  528. must be open for POLICY_LOOKUP_NAMES access.
  529. Name - The programmatic privilege name to look up.
  530. ClientLanguage - The prefered language to be returned.
  531. ClientSystemDefaultLanguage - The alternate prefered language
  532. to be returned.
  533. DisplayName - Receives the privilege's displayable name.
  534. LanguageReturned - The language actually returned.
  535. Return Value:
  536. NTSTATUS - The privilege text was found and returned.
  537. STATUS_ACCESS_DENIED - Caller does not have the appropriate access
  538. to complete the operation.
  539. STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
  540. found.
  541. --*/
  542. {
  543. NTSTATUS Status;
  544. LSAP_DB_HANDLE Handle = (LSAP_DB_HANDLE) PolicyHandle;
  545. LsarpReturnCheckSetup();
  546. //
  547. // make sure we know what RPC is doing to/for us
  548. //
  549. ASSERT( DisplayName != NULL );
  550. ASSERT( (*DisplayName) == NULL );
  551. ASSERT( LanguageReturned != NULL );
  552. //
  553. // Validate the input buffer
  554. //
  555. if ( !LsapValidateLsaUnicodeString( Name ) ) {
  556. return( STATUS_INVALID_PARAMETER );
  557. }
  558. //
  559. // make sure the caller has the appropriate access
  560. //
  561. Status = LsapDbReferenceObject(
  562. PolicyHandle,
  563. POLICY_LOOKUP_NAMES,
  564. PolicyObject,
  565. NullObject,
  566. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
  567. );
  568. if (!NT_SUCCESS(Status)) {
  569. return(Status);
  570. }
  571. //
  572. // No need to hold onto the Policy object after this..
  573. // We just needed it for access validation purposes.
  574. //
  575. Status = LsapDbDereferenceObject(
  576. &PolicyHandle,
  577. PolicyObject,
  578. NullObject,
  579. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
  580. (SECURITY_DB_DELTA_TYPE) 0,
  581. Status
  582. );
  583. if (NT_SUCCESS(Status)) {
  584. if (Name->Buffer == 0 || Name->Length == 0) {
  585. return(STATUS_NO_SUCH_PRIVILEGE);
  586. }
  587. Status = LsapLookupPrivilegeDisplayName(
  588. (PUNICODE_STRING)Name,
  589. (WORD)ClientLanguage,
  590. (WORD)ClientSystemDefaultLanguage,
  591. (PUNICODE_STRING *)DisplayName,
  592. LanguageReturned
  593. );
  594. }
  595. LsarpReturnPrologue();
  596. return(Status);
  597. }
  598. ///////////////////////////////////////////////////////////////////////////
  599. // //
  600. // //
  601. // Internal Routines //
  602. // //
  603. // //
  604. ///////////////////////////////////////////////////////////////////////////
  605. NTSTATUS
  606. LsapLookupKnownPrivilegeNameQuickly(
  607. IN PLUID Value,
  608. OUT UNICODE_STRING *Name
  609. )
  610. /*++
  611. Routine Description:
  612. Lookup a privilege luid to find the corresponding privilege name.
  613. Arguments:
  614. Value - LUID to lookup
  615. Name - pointer to privilege name
  616. Return Value:
  617. NTSTATUS - Standard Nt Result Code
  618. The name returned must NOT be freed or modified by the caller.
  619. Notes:
  620. The 'Quickly' in the name refers to an index based lookup.
  621. This is highly dependent on the privilege values (ntseapi.h)
  622. being consecutive integers. If there is a hole introduced in the
  623. well known privilege values, then this function will need to be fixed.
  624. --*/
  625. {
  626. ULONG LowPart = Value->LowPart;
  627. NTSTATUS Status = STATUS_SUCCESS;
  628. if ((Value->HighPart == 0) &&
  629. (LowPart >= SE_MIN_WELL_KNOWN_PRIVILEGE) &&
  630. (LowPart <= SE_MAX_WELL_KNOWN_PRIVILEGE))
  631. {
  632. *Name = LsapKnownPrivilege[LowPart-SE_MIN_WELL_KNOWN_PRIVILEGE].Name;
  633. }
  634. else
  635. {
  636. Status = STATUS_NO_SUCH_PRIVILEGE;
  637. }
  638. return Status;
  639. }
  640. NTSTATUS
  641. LsapLookupKnownPrivilegeName(
  642. IN PLUID Value,
  643. OUT PUNICODE_STRING *Name
  644. )
  645. /*++
  646. Routine Description:
  647. Look up the specified LUID and return the corresponding
  648. privilege's programmatic name (if found).
  649. FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
  650. THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
  651. SEARCH A LIST OF LOADED PRIVILEGES.
  652. Arguments:
  653. Value - Value to look up.
  654. Name - Receives the corresponding name - allocated with
  655. MIDL_user_allocate() and ready to return via an RPC stub.
  656. Return Value:
  657. STATUS_SUCCESS - Succeeded.
  658. STATUS_NO_MEMORY - Indicates there was not enough heap memory available
  659. to produce the final TokenInformation structure.
  660. STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
  661. found.
  662. --*/
  663. {
  664. ULONG i;
  665. UNICODE_STRING PrivName;
  666. PUNICODE_STRING ReturnName;
  667. NTSTATUS Status=STATUS_SUCCESS;
  668. Status = LsapLookupKnownPrivilegeNameQuickly( Value, &PrivName );
  669. if (Status == STATUS_SUCCESS) {
  670. ReturnName = MIDL_user_allocate( sizeof(UNICODE_STRING) );
  671. if (ReturnName == NULL) {
  672. return(STATUS_NO_MEMORY);
  673. }
  674. *ReturnName = PrivName;
  675. ReturnName->Buffer = MIDL_user_allocate( ReturnName->MaximumLength );
  676. if (ReturnName->Buffer == NULL) {
  677. MIDL_user_free( ReturnName );
  678. return(STATUS_NO_MEMORY);
  679. }
  680. RtlCopyUnicodeString( ReturnName, &PrivName );
  681. (*Name) = ReturnName;
  682. }
  683. return Status;
  684. }
  685. NTSTATUS
  686. LsapLookupKnownPrivilegeValue(
  687. PUNICODE_STRING Name,
  688. PLUID Value
  689. )
  690. /*++
  691. Routine Description:
  692. Look up the specified name and return the corresponding
  693. privilege's locally assigned value (if found).
  694. FOR NOW, WE ONLY SUPPORT WELL-KNOWN MICROSOFT PRIVILEGES.
  695. THESE ARE HARD-CODED HERE. IN THE FUTURE, WE MUST ALSO
  696. SEARCH A LIST OF LOADED PRIVILEGES.
  697. Arguments:
  698. Name - The name to look up.
  699. Value - Receives the corresponding locally assigned value.
  700. Return Value:
  701. STATUS_SUCCESS - Succeeded.
  702. STATUS_NO_SUCH_PRIVILEGE - The specified privilege could not be
  703. found.
  704. --*/
  705. {
  706. ULONG i;
  707. BOOLEAN Found;
  708. for ( i=0; i<LsapWellKnownPrivilegeCount; i++) {
  709. Found = RtlEqualUnicodeString( Name, &LsapKnownPrivilege[i].Name, TRUE );
  710. if (Found) {
  711. (*Value) = LsapKnownPrivilege[i].LocalValue;
  712. return(STATUS_SUCCESS);
  713. }
  714. }
  715. return(STATUS_NO_SUCH_PRIVILEGE);
  716. }
  717. NTSTATUS
  718. LsapGetPrivilegeDisplayNameResourceId(
  719. IN PUNICODE_STRING Name,
  720. IN ULONG DllIndex,
  721. OUT PULONG ResourceId
  722. )
  723. /*++
  724. Routine Description:
  725. This routine maps a privilege programmatic name in a dll to
  726. its display name resource id in the same dll.
  727. Currently since we support only one dll, it simply ignores
  728. DllIndex and uses the internal table to speed up things.
  729. Arguments:
  730. Name - The programmatic name of the privilege to look up.
  731. E.g., "SeTakeOwnershipPrivilege".
  732. DllIndex - The index of the privilege DLL to look in.
  733. PrivilegeIndex - Receives the index of the privilege entry in this
  734. resource file.
  735. Return Value:
  736. STATUS_SUCCESS - The pivilege has been successfully located.
  737. STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located.
  738. --*/
  739. {
  740. UINT i;
  741. for (i=0; i<LsapNumKnownPrivileges; i++)
  742. {
  743. if (CSTR_EQUAL ==
  744. CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  745. SORT_DEFAULT),
  746. NORM_IGNORECASE | SORT_STRINGSORT,
  747. Name->Buffer, Name->Length / sizeof(WCHAR),
  748. LsapKnownPrivilageNames[i], -1))
  749. {
  750. *ResourceId = i;
  751. return STATUS_SUCCESS;
  752. }
  753. }
  754. return STATUS_NO_SUCH_PRIVILEGE;
  755. }
  756. NTSTATUS
  757. LsapLookupPrivilegeDisplayName(
  758. IN PUNICODE_STRING ProgrammaticName,
  759. IN WORD ClientLanguage,
  760. IN WORD ClientSystemDefaultLanguage,
  761. OUT PUNICODE_STRING *DisplayName,
  762. OUT PWORD LanguageReturned
  763. )
  764. /*++
  765. Routine Description:
  766. This routine looks through each of the privilege DLLs for the
  767. specified privilege. If found, its displayable name is returned.
  768. Arguments:
  769. ProgrammaticName - The programmatic name of the privilege to look up.
  770. E.g., "SeTakeOwnershipPrivilege".
  771. ClientLanguage - The prefered language to be returned.
  772. ClientSystemDefaultLanguage - The alternate prefered language
  773. to be returned.
  774. DisplayName - receives a pointer to a buffer containing the displayable
  775. name associated with the privilege.
  776. E.g., "Take ownership of files or other objects".
  777. The UNICODE_STRING and the buffer pointed to by that structure
  778. are individually allocated using MIDL_user_allocate() and must
  779. be freed using MIDL_user_free().
  780. LanguageReturned - The language actually returned.
  781. Return Value:
  782. STATUS_SUCCESS - The name have been successfully retrieved.
  783. STATUS_NO_MEMORY - Not enough heap was available to return the
  784. information.
  785. STATUS_NO_SUCH_PRIVILEGE - The privilege could not be located
  786. in any of the privilege DLLs.
  787. --*/
  788. {
  789. NTSTATUS Status = STATUS_NO_SUCH_PRIVILEGE;
  790. ULONG DllIndex, PrivilegeIndex;
  791. for ( DllIndex=0; DllIndex<LsapPrivilegeDllCount; DllIndex++) {
  792. Status = LsapGetPrivilegeDisplayNameResourceId(
  793. (PUNICODE_STRING)ProgrammaticName,
  794. DllIndex,
  795. &PrivilegeIndex
  796. );
  797. if (NT_SUCCESS(Status)) {
  798. Status = LsapGetPrivilegeDisplayName( DllIndex,
  799. PrivilegeIndex,
  800. ClientLanguage,
  801. ClientSystemDefaultLanguage,
  802. DisplayName,
  803. LanguageReturned
  804. );
  805. return(Status);
  806. }
  807. }
  808. return(Status);
  809. }
  810. NTSTATUS
  811. LsapFindStringResource(
  812. IN HMODULE hModule,
  813. IN WORD wResourceId,
  814. IN WORD wLangId,
  815. OUT LPCWSTR* ppString,
  816. OUT PUINT pcchString
  817. )
  818. /*++
  819. Routine Description:
  820. Locate the specified string resource for the given language
  821. in the specified module.
  822. Arguments:
  823. hModule - handle of module to look into
  824. wResourceId - resource ID
  825. wLangId - language ID
  826. ppString - string returned.
  827. pcchString - number of characters in the string
  828. Return Value:
  829. NTSTATUS - Standard Nt Result Code
  830. Notes:
  831. This code is taken directly from LoadStringOrError in
  832. /nt/windows/Core/ntuser/client/rtlres.c and massaged a bit to make
  833. it look like other LSA code.
  834. --*/
  835. {
  836. NTSTATUS Status = STATUS_SUCCESS;
  837. DWORD dwError = NO_ERROR;
  838. HRSRC hResInfo;
  839. HANDLE hStringSeg;
  840. LPTSTR lpsz;
  841. UINT cch;
  842. //
  843. // each block has 16 strings. find the block that has
  844. // the string we want.
  845. //
  846. WORD wStringBlock = (((USHORT)wResourceId >> 4) + 1);
  847. if (ppString == NULL)
  848. {
  849. Status = STATUS_INVALID_PARAMETER;
  850. goto Cleanup;
  851. }
  852. *ppString = NULL;
  853. cch = 0;
  854. //
  855. // String Tables are broken up into 16 string segments. Find the segment
  856. // containing the string we are interested in.
  857. //
  858. if (hResInfo = FindResourceEx(
  859. hModule,
  860. RT_STRING,
  861. (LPWSTR) wStringBlock,
  862. wLangId))
  863. {
  864. //
  865. // Load that segment.
  866. //
  867. hStringSeg = LoadResource(hModule, hResInfo);
  868. //
  869. // Lock the resource.
  870. //
  871. if (lpsz = (LPTSTR) LockResource(hStringSeg))
  872. {
  873. //
  874. // Move past the other strings in this segment.
  875. // (16 strings in a segment -> & 0x0F)
  876. //
  877. wResourceId &= 0x0F;
  878. while (TRUE)
  879. {
  880. cch = *((WCHAR *)lpsz++); // PASCAL like string count
  881. // first UTCHAR is count if TCHARs
  882. if (wResourceId-- == 0) break;
  883. lpsz += cch; // Step to start of next string
  884. }
  885. *(LPTSTR *)ppString = lpsz;
  886. *pcchString = cch;
  887. UnlockResource(hStringSeg);
  888. }
  889. else
  890. {
  891. goto GetError;
  892. }
  893. }
  894. else
  895. {
  896. goto GetError;
  897. }
  898. Cleanup:
  899. return Status;
  900. GetError:
  901. dwError = GetLastError();
  902. switch (dwError)
  903. {
  904. case ERROR_RESOURCE_LANG_NOT_FOUND:
  905. Status = STATUS_RESOURCE_LANG_NOT_FOUND;
  906. break;
  907. default:
  908. Status = LsapWinerrorToNtStatus( dwError );
  909. break;
  910. }
  911. goto Cleanup;
  912. }
  913. NTSTATUS
  914. LsapGetPrivilegeDisplayName(
  915. IN ULONG DllIndex,
  916. IN ULONG PrivilegeIndex,
  917. IN WORD ClientLanguage,
  918. IN WORD ClientSystemDefaultLanguage,
  919. OUT PUNICODE_STRING *DisplayName,
  920. OUT PWORD LanguageReturned
  921. )
  922. /*++
  923. Routine Description:
  924. This routine returns a copy of the specified privilege's display name.
  925. The copy of the name is returned in two buffers allocated using
  926. MIDL_user_allocate(). This allows the information to be returned
  927. via an RPC service so that RPC generated stubs will correctly free
  928. the buffers.
  929. Every attempt is made to retrieve a language that the client prefers
  930. (first the client, then the client's system). Failing this, this
  931. routine may return another language (such as the server's default
  932. language).
  933. Arguments:
  934. DllIndex - The index of the privilege DLL to use.
  935. PrivilegeIndex - The index of the privilege entry in the DLL whose
  936. display name is to be returned.
  937. ClientLanguage - The language to return if possible.
  938. ClientSystemDefaultLanguage - If ClientLanguage couldn't be found, then
  939. return this language if possible.
  940. DisplayName - receives a pointer to a buffer containing the displayable
  941. name associated with the privilege.
  942. The UNICODE_STRING and the buffer pointed to by that structure
  943. are individually allocated using MIDL_user_allocate() and must
  944. be freed using MIDL_user_free().
  945. LanguageReturned - Receives the language actually retrieved.
  946. If neither ClientLanguage nor ClientSystemDefaultLanguage could be
  947. found, then this value may contain yet another value.
  948. Return Value:
  949. STATUS_SUCCESS - The display name has been successfully returned.
  950. STATUS_NO_MEMORY - Not enough heap was available to return the
  951. information.
  952. --*/
  953. {
  954. NTSTATUS Status=STATUS_NO_SUCH_PRIVILEGE;
  955. WORD Languages[] =
  956. {
  957. ClientLanguage,
  958. MAKELANGID( PRIMARYLANGID(ClientLanguage), SUBLANG_NEUTRAL),
  959. ClientSystemDefaultLanguage,
  960. MAKELANGID( PRIMARYLANGID(ClientSystemDefaultLanguage), SUBLANG_NEUTRAL),
  961. MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )
  962. };
  963. UINT NumLanguages = sizeof(Languages) / sizeof(Languages[0]);
  964. UINT i;
  965. PUNICODE_STRING ReturnedName = NULL;
  966. PCWSTR pszPrivilegeName=NULL;
  967. DWORD dwPrivilegeNameLen=0;
  968. for (i = 0; i < NumLanguages; i++)
  969. {
  970. Status = LsapFindStringResource(
  971. LsapPrivilegeDlls[ DllIndex ].DllHandle,
  972. (WORD) PrivilegeIndex,
  973. Languages[i],
  974. &pszPrivilegeName,
  975. &dwPrivilegeNameLen
  976. );
  977. if ( NT_SUCCESS( Status ))
  978. {
  979. DsysAssertMsg( pszPrivilegeName && dwPrivilegeNameLen,
  980. "LsapGetPrivilegeDisplayName" );
  981. if (ReturnedName = MIDL_user_allocate(sizeof(UNICODE_STRING)))
  982. {
  983. ReturnedName->Length = (USHORT) dwPrivilegeNameLen * sizeof(WCHAR);
  984. ReturnedName->MaximumLength = ReturnedName->Length + sizeof(WCHAR);
  985. if (ReturnedName->Buffer =
  986. MIDL_user_allocate(ReturnedName->MaximumLength))
  987. {
  988. RtlCopyMemory(ReturnedName->Buffer, pszPrivilegeName,
  989. ReturnedName->Length);
  990. ReturnedName->Buffer[dwPrivilegeNameLen] = 0;
  991. *LanguageReturned = Languages[i];
  992. *DisplayName = ReturnedName;
  993. Status = STATUS_SUCCESS;
  994. break;
  995. }
  996. else
  997. {
  998. MIDL_user_free( ReturnedName );
  999. Status = STATUS_NO_MEMORY;
  1000. break;
  1001. }
  1002. }
  1003. else
  1004. {
  1005. Status = STATUS_NO_MEMORY;
  1006. break;
  1007. }
  1008. }
  1009. }
  1010. return(Status);
  1011. }
  1012. NTSTATUS
  1013. LsapDbInitializePrivilegeObject( VOID )
  1014. /*++
  1015. Routine Description:
  1016. This function performs initialization functions related to
  1017. the LSA privilege object.
  1018. This includes:
  1019. Initializing some variables.
  1020. Loading DLLs containing displayable privilege names.
  1021. Arguments:
  1022. None.
  1023. Return Value:
  1024. STATUS_SUCCESS - The names have been successfully retrieved.
  1025. STATUS_NO_MEMORY - Not enough memory was available to initialize.
  1026. --*/
  1027. {
  1028. NTSTATUS
  1029. Status,
  1030. NtStatus;
  1031. ULONG
  1032. i;
  1033. UNICODE_STRING Temp ;
  1034. Status = LsapOpenPrivilegeDlls( );
  1035. if (!NT_SUCCESS(Status)) {
  1036. #if DBG
  1037. DbgPrint("\n");
  1038. DbgPrint(" LSASS: Failed loading privilege display name DLLs.\n");
  1039. DbgPrint(" This is not fatal, but may cause some peculiarities\n");
  1040. DbgPrint(" in User Interfaces that display privileges.\n\n");
  1041. #endif //DBG
  1042. return(Status);
  1043. }
  1044. LsapWellKnownPrivilegeMaxLen = 0;
  1045. //
  1046. // Now set up our internal well-known privilege LUID to programmatic name
  1047. // mapping.
  1048. //
  1049. for (i=0; i<LsapNumKnownPrivileges; i++)
  1050. {
  1051. LsapKnownPrivilege[i].LocalValue =
  1052. RtlConvertLongToLuid(i + SE_MIN_WELL_KNOWN_PRIVILEGE);
  1053. RtlInitUnicodeString(&LsapKnownPrivilege[i].Name,
  1054. LsapKnownPrivilageNames[i]);
  1055. //
  1056. // find the length of the longest well known privilege
  1057. //
  1058. if (LsapWellKnownPrivilegeMaxLen < LsapKnownPrivilege[i].Name.Length)
  1059. {
  1060. LsapWellKnownPrivilegeMaxLen = LsapKnownPrivilege[i].Name.Length;
  1061. }
  1062. }
  1063. LsapWellKnownPrivilegeCount = i;
  1064. ASSERT( i == (SE_MAX_WELL_KNOWN_PRIVILEGE - SE_MIN_WELL_KNOWN_PRIVILEGE +1));
  1065. return(Status);
  1066. }
  1067. NTSTATUS
  1068. LsapOpenPrivilegeDlls( )
  1069. /*++
  1070. Routine Description:
  1071. This function opens all the privilege DLLs that it can.
  1072. Arguments:
  1073. None.
  1074. Return Value:
  1075. STATUS_SUCCESS - The names have been successfully retrieved.
  1076. STATUS_NO_MEMORY - Not enough heap was available to return the
  1077. information.
  1078. --*/
  1079. {
  1080. NTSTATUS Status;
  1081. ULONG PotentialDlls, FoundDlls, i;
  1082. PUNICODE_STRING DllNames;
  1083. //
  1084. // Get the names of the DLLs out of the registry
  1085. //
  1086. Status = LsapGetPrivilegeDllNames( &DllNames, &PotentialDlls );
  1087. if (!NT_SUCCESS(Status)) {
  1088. return(Status);
  1089. }
  1090. //
  1091. // Allocate enough memory to hold handles to all potential DLLs.
  1092. //
  1093. LsapPrivilegeDlls = RtlAllocateHeap(
  1094. RtlProcessHeap(), 0,
  1095. PotentialDlls*sizeof(LSAP_DLL_DESCRIPTOR)
  1096. );
  1097. if (LsapPrivilegeDlls == NULL) {
  1098. return(STATUS_NO_MEMORY);
  1099. }
  1100. FoundDlls = 0;
  1101. for ( i=0; i<PotentialDlls; i++) {
  1102. Status = LdrLoadDll(
  1103. NULL,
  1104. NULL,
  1105. &DllNames[i],
  1106. &LsapPrivilegeDlls[FoundDlls].DllHandle
  1107. );
  1108. if (NT_SUCCESS(Status)) {
  1109. FoundDlls++;
  1110. }
  1111. }
  1112. LsapPrivilegeDllCount = FoundDlls;
  1113. #if DBG
  1114. if (FoundDlls == 0) {
  1115. DbgPrint("\n");
  1116. DbgPrint("LSASS: Zero privilege DLLs loaded. We expected at\n");
  1117. DbgPrint(" least msprivs.dll to be loaded. Privilege\n");
  1118. DbgPrint(" names will not be displayed at UI properly.\n\n");
  1119. }
  1120. #endif //DBG
  1121. //
  1122. //
  1123. // !!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!
  1124. //
  1125. // Before supporting user loadable privilege DLLs, we must add
  1126. // code here to validate the structure of the loaded DLL. This
  1127. // is necessary to prevent an invalid privilege DLL structure
  1128. // from causing us to crash.
  1129. //
  1130. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1131. //
  1132. //
  1133. // This routine validates the structure of each loaded DLL.
  1134. // Any found to be invalid will be logged and discarded.
  1135. //
  1136. //Status = LsapValidatePrivilegeDlls();
  1137. return(Status);
  1138. }
  1139. NTSTATUS
  1140. LsapGetPrivilegeDllNames(
  1141. OUT PUNICODE_STRING *DllNames,
  1142. OUT PULONG DllCount
  1143. )
  1144. /*++
  1145. Routine Description:
  1146. This function obtains the names of DLLs containing privilege
  1147. definitions from the registry.
  1148. Arguments:
  1149. DllNames - Receives a pointer to an array of UNICODE_STRINGs
  1150. This buffer must be freed using LsapFreePrivilegeDllNames.
  1151. DllCount - Receives the number of DLL names returned.
  1152. Return Value:
  1153. STATUS_SUCCESS - The names have been successfully retrieved.
  1154. STATUS_NO_MEMORY - Not enough heap was available to return the
  1155. information.
  1156. --*/
  1157. {
  1158. //
  1159. // For the time being, just hard-code our one, known privilege DLL
  1160. // name as a return value.
  1161. (*DllCount) = 1;
  1162. MsDllName.Length = 14;
  1163. MsDllName.MaximumLength = 14;
  1164. MsDllName.Buffer = &MsDllNameString[0];
  1165. (*DllNames) = &MsDllName;
  1166. return(STATUS_SUCCESS);
  1167. }
  1168. NTSTATUS
  1169. LsapBuildPrivilegeAuditString(
  1170. IN PPRIVILEGE_SET PrivilegeSet,
  1171. OUT PUNICODE_STRING ResultantString,
  1172. OUT PBOOLEAN FreeWhenDone
  1173. )
  1174. /*++
  1175. Routine Description:
  1176. This function builds a unicode string representing the specified
  1177. privileges. The privilege strings returned are program names.
  1178. These are not as human-friendly as the display names, but I don't
  1179. think we stand a chance of actually showing several display names
  1180. in an audit viewer.
  1181. if no privileges are present in the privilege set, then a string
  1182. containing a dash is returned.
  1183. Arguments:
  1184. PrivilegeSet - points to the privilege set to be converted to string
  1185. format.
  1186. ResultantString - Points to the unicode string header. The body of this
  1187. unicode string will be set to point to the resultant output value
  1188. if successful. Otherwise, the Buffer field of this parameter
  1189. will be set to NULL.
  1190. FreeWhenDone - If TRUE, indicates that the body of ResultantString
  1191. must be freed to process heap when no longer needed.
  1192. Return Values:
  1193. STATUS_NO_MEMORY - indicates memory could not be allocated
  1194. for the string body.
  1195. All other Result Codes are generated by called routines.
  1196. --*/
  1197. {
  1198. NTSTATUS Status;
  1199. USHORT LengthNeeded;
  1200. ULONG j;
  1201. ULONG i;
  1202. PLUID Privilege;
  1203. UNICODE_STRING LineFormatting;
  1204. UNICODE_STRING QuestionMark;
  1205. UNICODE_STRING PrivName;
  1206. PWSTR NextName;
  1207. //
  1208. // make sure that the max length has been calculated
  1209. // (SE_INC_BASE_PRIORITY_NAME currently has the longest name
  1210. // therefore make sure that the length is at least that much)
  1211. //
  1212. DsysAssert(LsapWellKnownPrivilegeMaxLen >=
  1213. (sizeof(SE_INC_BASE_PRIORITY_NAME) - sizeof(WCHAR)));
  1214. if (PrivilegeSet->PrivilegeCount == 0) {
  1215. //
  1216. // No privileges. Return a dash
  1217. //
  1218. Status = LsapAdtBuildDashString( ResultantString, FreeWhenDone );
  1219. return(Status);
  1220. }
  1221. if (!IsValidPrivilegeCount(PrivilegeSet->PrivilegeCount)) {
  1222. return STATUS_INVALID_PARAMETER;
  1223. }
  1224. RtlInitUnicodeString( &LineFormatting, L"\r\n\t\t\t");
  1225. RtlInitUnicodeString( &QuestionMark, L"?");
  1226. //
  1227. // for better performance, we calculate the total length required
  1228. // to store privilege names based on the longest privilege,
  1229. // instead of going over the privilege-set twice (once to calcualte
  1230. // length and one more time to actually build the string)
  1231. //
  1232. LengthNeeded = (USHORT) (PrivilegeSet->PrivilegeCount *
  1233. ( LsapWellKnownPrivilegeMaxLen + LineFormatting.Length ));
  1234. //
  1235. // Subtract off the length of the last line-formatting.
  1236. // It isn't needed for the last line.
  1237. // BUT! Add in enough for a null termination.
  1238. //
  1239. LengthNeeded = LengthNeeded - LineFormatting.Length + sizeof( WCHAR );
  1240. //
  1241. // We now have the length we need.
  1242. // Allocate the buffer and go through the list copying names.
  1243. //
  1244. ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, (ULONG)LengthNeeded);
  1245. if (ResultantString->Buffer == NULL) {
  1246. return(STATUS_NO_MEMORY);
  1247. }
  1248. //ResultantString->Length = LengthNeeded - (USHORT)sizeof(UNICODE_NULL);
  1249. ResultantString->MaximumLength = LengthNeeded;
  1250. NextName = ResultantString->Buffer;
  1251. for (j=0; j<PrivilegeSet->PrivilegeCount; j++) {
  1252. Privilege = &(PrivilegeSet->Privilege[j].Luid);
  1253. Status = LsapLookupKnownPrivilegeNameQuickly( Privilege, &PrivName );
  1254. if (Status == STATUS_SUCCESS) {
  1255. //
  1256. // Copy the privilege name if lookup succedded
  1257. //
  1258. RtlCopyMemory( NextName, PrivName.Buffer, PrivName.Length );
  1259. NextName = (PWSTR)((PCHAR)NextName + PrivName.Length);
  1260. } else {
  1261. //
  1262. // else copy a '?'
  1263. //
  1264. RtlCopyMemory( NextName, QuestionMark.Buffer, QuestionMark.Length );
  1265. NextName = (PWSTR)((PCHAR)NextName + QuestionMark.Length);
  1266. }
  1267. //
  1268. // Copy the line formatting string, unless this is the last priv.
  1269. //
  1270. if (j<PrivilegeSet->PrivilegeCount-1) {
  1271. RtlCopyMemory( NextName,
  1272. LineFormatting.Buffer,
  1273. LineFormatting.Length
  1274. );
  1275. NextName = (PWSTR)((PCHAR)NextName + LineFormatting.Length);
  1276. }
  1277. }
  1278. //
  1279. // Add a null to the end
  1280. //
  1281. (*NextName) = (UNICODE_NULL);
  1282. ResultantString->Length = (USHORT) (((PBYTE) NextName) - ((PBYTE) ResultantString->Buffer));
  1283. DsysAssertMsg( ResultantString->Length <= ResultantString->MaximumLength,
  1284. "LsapBuildPrivilegeAuditString" );
  1285. (*FreeWhenDone) = TRUE;
  1286. return(STATUS_SUCCESS);
  1287. }