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.

981 lines
23 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. lsaprtl.c
  5. Abstract:
  6. Local Security Authority - Temporary Rtl Routine Definitions.
  7. This file contains routines used in the LSA that could be made into Rtl
  8. routines. They have been written in general purpose form with this in
  9. mind - the only exception to thisa is that their names have Lsap prefixes
  10. to indicate that they are currently used only by the LSA.
  11. Author:
  12. Scott Birrell (ScottBi) April 8, 1992
  13. Environment:
  14. Revision History:
  15. --*/
  16. #include <lsacomp.h>
  17. #include <align.h>
  18. BOOLEAN
  19. LsapRtlPrefixSid(
  20. IN PSID PrefixSid,
  21. IN PSID Sid
  22. )
  23. /*++
  24. Routine Description:
  25. This function checks if one Sid is the Prefix Sid of another.
  26. Arguments:
  27. PrefixSid - Pointer to Prefix Sid.
  28. Sid - Pointer to Sid to be checked.
  29. Return Values:
  30. BOOLEAN - TRUE if PrefixSid is the Prefix Sid of Sid, else FALSE.
  31. --*/
  32. {
  33. BOOLEAN BooleanStatus = FALSE;
  34. if ((*RtlSubAuthorityCountSid(Sid)) > 0) {
  35. //
  36. // Decrement the SubAuthorityCount of Sid temporarily.
  37. //
  38. (*RtlSubAuthorityCountSid(Sid))--;
  39. //
  40. // Compare the Prefix Sid with the modified Sid.
  41. //
  42. BooleanStatus = RtlEqualSid( PrefixSid, Sid);
  43. //
  44. // Restore the original SubAuthorityCount.
  45. //
  46. (*RtlSubAuthorityCountSid(Sid))++;
  47. }
  48. return(BooleanStatus);
  49. }
  50. BOOLEAN
  51. LsapRtlPrefixName(
  52. IN PUNICODE_STRING PrefixName,
  53. IN PUNICODE_STRING Name
  54. )
  55. /*++
  56. Routine Description:
  57. This function checks if a Name has the given name as a Prefix
  58. Arguments:
  59. PrefixName - Pointer to Prefix Name.
  60. Name - Pointer to Name to be checked.
  61. Return Values:
  62. BOOLEAN - TRUE if the Name is composite (i.e. contains a "\") and
  63. PrefixName is the Prefix part of Name, else FALSE.
  64. --*/
  65. {
  66. UNICODE_STRING TruncatedName = *Name;
  67. if ((PrefixName->Length < Name->Length) &&
  68. Name->Buffer[PrefixName->Length / 2] == L'\\') {
  69. TruncatedName.Length = PrefixName->Length;
  70. if (RtlEqualUnicodeString(PrefixName, &TruncatedName, FALSE)) {
  71. return(TRUE);
  72. }
  73. }
  74. return(FALSE);
  75. }
  76. VOID
  77. LsapRtlSplitNames(
  78. IN PUNICODE_STRING Names,
  79. IN ULONG Count,
  80. IN PUNICODE_STRING Separator,
  81. OUT PUNICODE_STRING PrefixNames,
  82. OUT PUNICODE_STRING SuffixNames
  83. )
  84. /*++
  85. Routine Description:
  86. This function splits an array of Names into Prefix and Suffix parts
  87. separated by the given separator. The input array may contain names of
  88. the following form:
  89. <SuffixName>
  90. <PrefixName> "\" <SuffixName>
  91. The NULL string
  92. Note that the output arrays will reference the original name strings.
  93. No copying is done.
  94. Arguments:
  95. Names - Pointer to array of Unicode Names.
  96. Count - Count of Names in Names.
  97. PrefixNames - Pointer to an array of Count Unicode String structures
  98. that will be initialized to point to the Prefix portions of the
  99. Names.
  100. SuffixNames - Pointer to an array of Count Unicode String structures
  101. that will be initialized to point to the Suffix portions of the
  102. Names.
  103. Return Values:
  104. None.
  105. --*/
  106. {
  107. ULONG Index;
  108. LONG SeparatorOffset;
  109. LONG WideSeparatorOffset;
  110. //
  111. // Scan each name, initializing the output Unicode structures.
  112. //
  113. for (Index = 0; Index < Count; Index++) {
  114. PrefixNames[Index] = Names[Index];
  115. SuffixNames[Index] = Names[Index];
  116. //
  117. // Locate the separator "\" if any.
  118. //
  119. SeparatorOffset = LsapRtlFindCharacterInUnicodeString(
  120. &Names[Index],
  121. Separator,
  122. FALSE
  123. );
  124. //
  125. // If there is a separator, make the Prefix Name point to the
  126. // part of the name before the separator and make the Suffix Name
  127. // point to the part of the name after the separator. If there
  128. // is no separator, set the Prefix Name part to Null. Rememeber
  129. // that the Length fields are byte counts, not Wide Character
  130. // counts.
  131. //
  132. if (SeparatorOffset >= 0) {
  133. WideSeparatorOffset = (SeparatorOffset / sizeof(WCHAR));
  134. PrefixNames[Index].Length = (USHORT) SeparatorOffset;
  135. SuffixNames[Index].Buffer += (WideSeparatorOffset + 1);
  136. SuffixNames[Index].Length -= (USHORT)(SeparatorOffset + sizeof(WCHAR));
  137. } else {
  138. WideSeparatorOffset = SeparatorOffset;
  139. PrefixNames[Index].Length = 0;
  140. }
  141. //
  142. // Set MaximumLengths equal to Lengths and, for safety, clear buffer
  143. // pointers(s) to NULL in output strings if Length(s) are 0.
  144. //
  145. PrefixNames[Index].MaximumLength = PrefixNames[Index].Length;
  146. SuffixNames[Index].MaximumLength = SuffixNames[Index].Length;
  147. if (PrefixNames[Index].Length == 0) {
  148. PrefixNames[Index].Buffer = NULL;
  149. }
  150. if (SuffixNames[Index].Length == 0) {
  151. SuffixNames[Index].Buffer = NULL;
  152. }
  153. }
  154. }
  155. LONG
  156. LsapRtlFindCharacterInUnicodeString(
  157. IN PUNICODE_STRING InputString,
  158. IN PUNICODE_STRING Character,
  159. IN BOOLEAN CaseInsensitive
  160. )
  161. /*++
  162. Routine Description:
  163. This function returns the byte offset of the first occurrence (if any) of
  164. a Unicode Character within a Unicode String.
  165. Arguments
  166. InputString - Pointer to Unicode String to be searched.
  167. Character - Pointer to Unicode String initialized to character
  168. to be searched for.
  169. CaseInsensitive - TRUE if case is to be ignored, else FALSE.
  170. NOTE - Only FALSE is supported just now.
  171. Return Value:
  172. LONG - If the character is present within the string, its non-negative
  173. byte offset is returned. If the character is not present within
  174. the string, a negative value is returned.
  175. --*/
  176. {
  177. BOOLEAN CharacterFound = FALSE;
  178. ULONG Offset = 0;
  179. if (!CaseInsensitive) {
  180. Offset = 0;
  181. while (Offset < InputString->Length) {
  182. if (*(Character->Buffer) ==
  183. InputString->Buffer[Offset / sizeof (WCHAR)]) {
  184. CharacterFound = TRUE;
  185. break;
  186. }
  187. Offset += 2;
  188. }
  189. } else {
  190. //
  191. // Case Insensitive is not supported
  192. //
  193. CharacterFound = FALSE;
  194. }
  195. if (!CharacterFound) {
  196. Offset = LSA_UNKNOWN_ID;
  197. }
  198. return(Offset);
  199. }
  200. VOID
  201. LsapRtlSetSecurityAccessMask(
  202. IN SECURITY_INFORMATION SecurityInformation,
  203. OUT PACCESS_MASK DesiredAccess
  204. )
  205. /*++
  206. Routine Description:
  207. NOTE! THIS ROUTINE IS IDENTICAL WITH SeSetSecurityAccessMask()
  208. IN \nt\private\ntos\se\semethod.c
  209. This routine builds an access mask representing the accesses necessary
  210. to set the object security information specified in the SecurityInformation
  211. parameter. While it is not difficult to determine this information,
  212. the use of a single routine to generate it will ensure minimal impact
  213. when the security information associated with an object is extended in
  214. the future (to include mandatory access control information).
  215. Arguments:
  216. SecurityInformation - Identifies the object's security information to be
  217. modified.
  218. DesiredAccess - Points to an access mask to be set to represent the
  219. accesses necessary to modify the information specified in the
  220. SecurityInformation parameter.
  221. Return Value:
  222. None.
  223. --*/
  224. {
  225. //
  226. // Figure out accesses needed to perform the indicated operation(s).
  227. //
  228. (*DesiredAccess) = 0;
  229. if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
  230. (SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
  231. (*DesiredAccess) |= WRITE_OWNER;
  232. }
  233. if (SecurityInformation & DACL_SECURITY_INFORMATION) {
  234. (*DesiredAccess) |= WRITE_DAC;
  235. }
  236. if (SecurityInformation & SACL_SECURITY_INFORMATION) {
  237. (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
  238. }
  239. return;
  240. }
  241. VOID
  242. LsapRtlQuerySecurityAccessMask(
  243. IN SECURITY_INFORMATION SecurityInformation,
  244. OUT PACCESS_MASK DesiredAccess
  245. )
  246. /*++
  247. Routine Description:
  248. NOTE! THIS ROUTINE IS IDENTICAL WITH SeQuerySecurityAccessMask()
  249. IN \nt\private\ntos\se\semethod.c.
  250. This routine builds an access mask representing the accesses necessary
  251. to query the object security information specified in the
  252. SecurityInformation parameter. While it is not difficult to determine
  253. this information, the use of a single routine to generate it will ensure
  254. minimal impact when the security information associated with an object is
  255. extended in the future (to include mandatory access control information).
  256. Arguments:
  257. SecurityInformation - Identifies the object's security information to be
  258. queried.
  259. DesiredAccess - Points to an access mask to be set to represent the
  260. accesses necessary to query the information specified in the
  261. SecurityInformation parameter.
  262. Return Value:
  263. None.
  264. --*/
  265. {
  266. //
  267. // Figure out accesses needed to perform the indicated operation(s).
  268. //
  269. (*DesiredAccess) = 0;
  270. if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
  271. (SecurityInformation & GROUP_SECURITY_INFORMATION) ||
  272. (SecurityInformation & DACL_SECURITY_INFORMATION)) {
  273. (*DesiredAccess) |= READ_CONTROL;
  274. }
  275. if ((SecurityInformation & SACL_SECURITY_INFORMATION)) {
  276. (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
  277. }
  278. return;
  279. }
  280. NTSTATUS
  281. LsapRtlSidToUnicodeRid(
  282. IN PSID Sid,
  283. OUT PUNICODE_STRING UnicodeRid
  284. )
  285. /*++
  286. Routine Description:
  287. This function extracts the Relative Id (Rid) from a Sid and
  288. converts it to a Unicode String. The Rid is extracted and converted
  289. to an 8-digit Unicode Integer.
  290. Arguments:
  291. Sid - Pointer to the Sid to be converted. It is the caller's
  292. responsibility to ensure that the Sid has valid syntax.
  293. UnicodeRid - Pointer to a Unicode String structure that will receive
  294. the Rid in Unicode form. Note that memory for the string buffer
  295. in this Unicode String will be allocated by this routine if
  296. successful. The caller must free this memory after use by calling
  297. RtlFreeUnicodeString.
  298. Return Value:
  299. NTSTATUS - Standard Nt Status code
  300. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources
  301. to allocate buffer for Unicode String name.
  302. --*/
  303. {
  304. NTSTATUS Status;
  305. ULONG Rid;
  306. UCHAR SubAuthorityCount;
  307. UCHAR RidNameBufferAnsi[9];
  308. ANSI_STRING CharacterSidAnsi;
  309. //
  310. // First, verify that the given Sid is valid
  311. //
  312. if (!RtlValidSid( Sid )) {
  313. return STATUS_INVALID_PARAMETER;
  314. }
  315. //
  316. // Sid is valid. If however, the SubAuthorityCount is zero,
  317. // we cannot have a Rid so return error.
  318. //
  319. SubAuthorityCount = ((PISID) Sid)->SubAuthorityCount;
  320. if (SubAuthorityCount == 0) {
  321. return STATUS_INVALID_PARAMETER;
  322. }
  323. //
  324. // Sid has at least one subauthority. Get the lowest subauthority
  325. // (i.e. the Rid).
  326. //
  327. Rid = ((PISID) Sid)->SubAuthority[SubAuthorityCount - 1];
  328. //
  329. // Now convert the Rid to an 8-digit numeric character string
  330. //
  331. Status = RtlIntegerToChar( Rid, 16, -8, RidNameBufferAnsi );
  332. //
  333. // Need to add null terminator to string
  334. //
  335. RidNameBufferAnsi[8] = 0;
  336. //
  337. // Initialize an ANSI string structure with the converted name.
  338. //
  339. RtlInitString( &CharacterSidAnsi, RidNameBufferAnsi );
  340. //
  341. // Convert the ANSI string structure to Unicode form
  342. //
  343. Status = RtlAnsiStringToUnicodeString(
  344. UnicodeRid,
  345. &CharacterSidAnsi,
  346. TRUE
  347. );
  348. if (!NT_SUCCESS(Status)) {
  349. Status = STATUS_INSUFFICIENT_RESOURCES;
  350. }
  351. return Status;
  352. }
  353. NTSTATUS
  354. LsapRtlWellKnownPrivilegeCheck(
  355. IN PVOID ObjectHandle,
  356. IN ULONG PrivilegeId
  357. )
  358. /*++
  359. Routine Description:
  360. This function checks if the given well known privilege is enabled for an
  361. impersonated client.
  362. Arguments:
  363. ObjectHandle - used for auditing
  364. PrivilegeId - Specifies the well known Privilege Id
  365. Return Value:
  366. NTSTATUS - Standard Nt Result Code
  367. STATUS_SUCCESS - The call completed successfully and the client
  368. is either trusted or has the necessary privilege enabled.
  369. STATUS_PRIVILEGE_NOT_HELD - necessary privilege is not enabled
  370. --*/
  371. {
  372. NTSTATUS Status, SecondaryStatus;
  373. BOOLEAN PrivilegeHeld = FALSE;
  374. HANDLE ClientToken = NULL;
  375. PRIVILEGE_SET Privilege;
  376. BOOLEAN ClientImpersonatedHere = FALSE;
  377. UNICODE_STRING SubsystemName;
  378. //
  379. // Impersonate the client.
  380. //
  381. Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
  382. if ( !NT_SUCCESS(Status) ) {
  383. goto WellKnownPrivilegeCheckError;
  384. }
  385. ClientImpersonatedHere = TRUE;
  386. //
  387. // Open the current thread's impersonation token.
  388. //
  389. Status = NtOpenThreadToken(
  390. NtCurrentThread(),
  391. TOKEN_QUERY,
  392. TRUE,
  393. &ClientToken
  394. );
  395. if ( !NT_SUCCESS(Status) ) {
  396. goto WellKnownPrivilegeCheckError;
  397. }
  398. //
  399. // OK, we have a token open. Now check for the privilege to execute this
  400. // service.
  401. //
  402. Privilege.PrivilegeCount = 1;
  403. Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
  404. Privilege.Privilege[0].Luid = RtlConvertLongToLuid(PrivilegeId);
  405. Privilege.Privilege[0].Attributes = 0;
  406. Status = NtPrivilegeCheck(
  407. ClientToken,
  408. &Privilege,
  409. &PrivilegeHeld
  410. );
  411. if (!NT_SUCCESS(Status)) {
  412. goto WellKnownPrivilegeCheckError;
  413. }
  414. RtlInitUnicodeString( &SubsystemName, L"LSA" );
  415. (VOID) NtPrivilegeObjectAuditAlarm ( &SubsystemName,
  416. ObjectHandle,
  417. ClientToken,
  418. ACCESS_SYSTEM_SECURITY,
  419. &Privilege,
  420. PrivilegeHeld
  421. );
  422. if ( !PrivilegeHeld ) {
  423. Status = STATUS_PRIVILEGE_NOT_HELD;
  424. goto WellKnownPrivilegeCheckError;
  425. }
  426. WellKnownPrivilegeCheckFinish:
  427. //
  428. // If we impersonated the client, revert to ourself.
  429. //
  430. if (ClientImpersonatedHere) {
  431. SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf());
  432. }
  433. //
  434. // If necessary, close the client token.
  435. //
  436. if (ClientToken != NULL) {
  437. SecondaryStatus = NtClose( ClientToken );
  438. ASSERT(NT_SUCCESS(SecondaryStatus));
  439. ClientToken = NULL;
  440. }
  441. return(Status);
  442. WellKnownPrivilegeCheckError:
  443. goto WellKnownPrivilegeCheckFinish;
  444. }
  445. NTSTATUS
  446. LsapSplitSid(
  447. IN PSID AccountSid,
  448. IN OUT PSID *DomainSid,
  449. OUT ULONG *Rid
  450. )
  451. /*++
  452. Routine Description:
  453. This function splits a sid into its domain sid and rid. The caller
  454. can either provide a memory buffer for the returned DomainSid, or
  455. request that one be allocated. If the caller provides a buffer, the buffer
  456. is assumed to be of sufficient size. If allocated on the caller's behalf,
  457. the buffer must be freed when no longer required via MIDL_user_free.
  458. Arguments:
  459. AccountSid - Specifies the Sid to be split. The Sid is assumed to be
  460. syntactically valid. Sids with zero subauthorities cannot be split.
  461. DomainSid - Pointer to location containing either NULL or a pointer to
  462. a buffer in which the Domain Sid will be returned. If NULL is
  463. specified, memory will be allocated on behalf of the caller.
  464. Return Value:
  465. NTSTATUS - Standard Nt Result Code
  466. STATUS_SUCCESS - The call completed successfully.
  467. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
  468. such as memory, to complete the call successfully.
  469. STATUS_INVALID_SID - The Sid is has a subauthority count of 0.
  470. --*/
  471. {
  472. NTSTATUS NtStatus;
  473. UCHAR AccountSubAuthorityCount;
  474. ULONG AccountSidLength;
  475. //
  476. // Calculate the size of the domain sid
  477. //
  478. AccountSubAuthorityCount = *RtlSubAuthorityCountSid(AccountSid);
  479. if (AccountSubAuthorityCount < 1) {
  480. NtStatus = STATUS_INVALID_SID;
  481. goto SplitSidError;
  482. }
  483. AccountSidLength = RtlLengthSid(AccountSid);
  484. //
  485. // If no buffer is required for the Domain Sid, we have to allocate one.
  486. //
  487. if (*DomainSid == NULL) {
  488. //
  489. // Allocate space for the domain sid (allocate the same size as the
  490. // account sid so we can use RtlCopySid)
  491. //
  492. *DomainSid = MIDL_user_allocate(AccountSidLength);
  493. if (*DomainSid == NULL) {
  494. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  495. goto SplitSidError;
  496. }
  497. }
  498. //
  499. // Copy the Account sid into the Domain sid
  500. //
  501. RtlMoveMemory(*DomainSid, AccountSid, AccountSidLength);
  502. //
  503. // Decrement the domain sid sub-authority count
  504. //
  505. (*RtlSubAuthorityCountSid(*DomainSid))--;
  506. //
  507. // Copy the rid out of the account sid
  508. //
  509. *Rid = *RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount-1);
  510. NtStatus = STATUS_SUCCESS;
  511. SplitSidFinish:
  512. return(NtStatus);
  513. SplitSidError:
  514. goto SplitSidFinish;
  515. }
  516. ULONG
  517. LsapDsSizeAuthInfo(
  518. IN PLSAPR_AUTH_INFORMATION AuthInfo,
  519. IN ULONG Infos
  520. )
  521. /*++
  522. Routine Description:
  523. This function returns the size, in bytes, of an authentication information structure
  524. Arguments:
  525. AuthInfo - AuthenticationInformation to size
  526. Infos - Number of items in the list
  527. Returns:
  528. Size, in bytes, of the AuthInfos
  529. --*/
  530. {
  531. ULONG Len = 0, i;
  532. if ( AuthInfo == NULL ) {
  533. return( 0 );
  534. }
  535. for ( i = 0 ; i < Infos; i++ ) {
  536. //
  537. // This calculation must match LsapDsMarshalAuthInfo
  538. //
  539. Len += sizeof(LARGE_INTEGER) +
  540. sizeof(ULONG) +
  541. sizeof(ULONG) +
  542. ROUND_UP_COUNT(AuthInfo[ i ].AuthInfoLength, ALIGN_DWORD);
  543. }
  544. return( Len );
  545. }
  546. VOID
  547. LsapDsMarshalAuthInfo(
  548. IN PBYTE Buffer,
  549. IN PLSAPR_AUTH_INFORMATION AuthInfo,
  550. IN ULONG Infos
  551. )
  552. /*++
  553. Routine Description:
  554. This function will marshal an authinfo list into an already allocated buffer
  555. Arguments:
  556. Buffer - Buffer to marshal into
  557. AuthInfo - AuthenticationInformation to marshal
  558. Infos - Number of items in the list
  559. Returns:
  560. VOID
  561. --*/
  562. {
  563. ULONG i;
  564. if ( AuthInfo != NULL ) {
  565. for (i = 0; i < Infos ; i++ ) {
  566. ULONG AlignmentBytes;
  567. RtlCopyMemory( Buffer, &AuthInfo[i].LastUpdateTime, sizeof( LARGE_INTEGER ) );
  568. Buffer += sizeof( LARGE_INTEGER );
  569. *(PULONG)Buffer = AuthInfo[i].AuthType;
  570. Buffer += sizeof ( ULONG );
  571. *(PULONG)Buffer = AuthInfo[i].AuthInfoLength;
  572. Buffer += sizeof ( ULONG );
  573. RtlCopyMemory( Buffer, AuthInfo[i].AuthInfo, AuthInfo[i].AuthInfoLength );
  574. Buffer += AuthInfo[i].AuthInfoLength;
  575. // Zero out the next couple of bytes in the DWORD.
  576. AlignmentBytes = ROUND_UP_COUNT(AuthInfo[ i ].AuthInfoLength, ALIGN_DWORD) -
  577. AuthInfo[ i ].AuthInfoLength;
  578. RtlZeroMemory( Buffer, AlignmentBytes );
  579. Buffer += AlignmentBytes;
  580. }
  581. }
  582. }
  583. NTSTATUS
  584. LsapDsMarshalAuthInfoHalf(
  585. IN PLSAPR_TRUST_DOMAIN_AUTH_INFO_HALF AuthInfo,
  586. OUT PULONG Length,
  587. OUT PBYTE *Buffer
  588. )
  589. /*++
  590. Routine Description:
  591. This function will take an AuthInfo half and marshal it into a single self
  592. relative buffer.
  593. Arguments:
  594. AuthInfo - AuthenticationInformation to marshal
  595. Length - Returns the length of the allocated buffer.
  596. Buffer - Returns an allocated buffer containing the marshalled auth info
  597. The buffer should be freed using MIDL_user_free.
  598. Returns:
  599. STATUS_SUCCESS - Success
  600. STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed.
  601. --*/
  602. {
  603. NTSTATUS Status = STATUS_SUCCESS;
  604. PBYTE LocalBuffer, Current;
  605. ULONG Len, PrevLen;
  606. if ( AuthInfo == NULL ) {
  607. *Length = 0;
  608. *Buffer = NULL;
  609. return STATUS_SUCCESS;
  610. }
  611. try {
  612. //
  613. // First, size the entire auth info buffer...
  614. //
  615. Len = LsapDsSizeAuthInfo( AuthInfo->AuthenticationInformation, AuthInfo->AuthInfos );
  616. PrevLen = LsapDsSizeAuthInfo( AuthInfo->PreviousAuthenticationInformation,
  617. AuthInfo->AuthInfos );
  618. //
  619. // The format of the buffer we will create is:
  620. //
  621. LocalBuffer = MIDL_user_allocate( Len + PrevLen + ( 3 * sizeof( ULONG ) ) );
  622. if ( LocalBuffer == NULL ) {
  623. Status = STATUS_INSUFFICIENT_RESOURCES;
  624. } else {
  625. //
  626. // The format of the buffer is:
  627. //
  628. // [Info count][OffsetCurrent][OffsetPrevious] and then some number of the
  629. // following:
  630. // [UpdateTime(LargeInteger)][AuthType][AuthInfoLen][data (sizeis(AuthInfoLen) ]
  631. //
  632. //
  633. // Number of items...
  634. //
  635. *(PULONG)LocalBuffer = AuthInfo->AuthInfos;
  636. Current = LocalBuffer + sizeof( ULONG );
  637. //
  638. //
  639. *(PULONG)(Current) = 3 * sizeof(ULONG);
  640. *(PULONG)(Current + sizeof(ULONG)) = *(PULONG)Current + Len;
  641. Current += 2 * sizeof(ULONG);
  642. LsapDsMarshalAuthInfo( Current,
  643. AuthInfo->AuthenticationInformation,
  644. AuthInfo->AuthInfos );
  645. Current += Len;
  646. LsapDsMarshalAuthInfo( Current,
  647. AuthInfo->PreviousAuthenticationInformation,
  648. AuthInfo->AuthInfos );
  649. Status = STATUS_SUCCESS;
  650. }
  651. *Length = Len + PrevLen + ( 3 * sizeof( ULONG ) );
  652. *Buffer = LocalBuffer;
  653. } except( EXCEPTION_EXECUTE_HANDLER ) {
  654. Status = GetExceptionCode();
  655. }
  656. return( Status );
  657. }