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.

913 lines
24 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. aucred.c
  5. Abstract:
  6. This module provides credential management services within the
  7. LSA subsystem. Some of these services are indirectly available for use
  8. by authentication packages.
  9. Author:
  10. Jim Kelly (JimK) 27-February-1991
  11. Revision History:
  12. --*/
  13. #include <lsapch2.h>
  14. RTL_RESOURCE AuCredLock ;
  15. #define AuReadLockCreds() RtlAcquireResourceShared( &AuCredLock, TRUE );
  16. #define AuWriteLockCreds() RtlAcquireResourceExclusive( &AuCredLock, TRUE );
  17. #define AuUnlockCreds() RtlReleaseResource( &AuCredLock );
  18. NTSTATUS
  19. LsapInitializeCredentials(
  20. VOID
  21. )
  22. {
  23. __try {
  24. RtlInitializeResource( &AuCredLock );
  25. return STATUS_SUCCESS;
  26. } __except(EXCEPTION_EXECUTE_HANDLER)
  27. {
  28. return STATUS_INSUFFICIENT_RESOURCES;
  29. }
  30. }
  31. NTSTATUS
  32. LsapAddCredential(
  33. IN PLUID LogonId,
  34. IN ULONG AuthenticationPackage,
  35. IN PSTRING PrimaryKeyValue,
  36. IN PSTRING Credentials
  37. )
  38. /*++
  39. Routine Description:
  40. This service is used by authentication packages to add credentials to a
  41. logon session. These credentials may later be referenced using
  42. GetCredentials().
  43. This service acquires the AuLock.
  44. Arguments:
  45. LogonId - The session ID of logon session to add credentials to.
  46. AuthenticationPackage - The authentication package ID of the
  47. calling authentication package. This was received in the
  48. InitializePackage() call during DLL initialization.
  49. PrimaryKeyValue - Points to a string containing a value that the
  50. authentication package will later want to reference as a
  51. primary key of the credential data. This may be used, for
  52. example, to keep the name of the domain or server the
  53. credentials are related to. The format and meaning of this
  54. string are authentication package-specific. Note that the
  55. string value does not have to be unique, even for the
  56. specified logon session. For example, there could be two
  57. passwords for the same domain, each with the passwords stored
  58. as credentials and the domain name stored as the primary key.
  59. Credentials - Points to a string containing data representing
  60. user credentials. The format and meaning of this string are
  61. authentication package-specific.
  62. Return Status:
  63. STATUS_SUCCESS - The credentials were successfully added.
  64. STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
  65. not be found.
  66. --*/
  67. {
  68. PLSAP_LOGON_SESSION LogonSession;
  69. PLSAP_PACKAGE_CREDENTIALS Package;
  70. PLSAP_CREDENTIALS NewCredentials = NULL;
  71. USHORT MaxPrimary;
  72. USHORT MaxCredentials;
  73. NTSTATUS Status = STATUS_SUCCESS;
  74. //
  75. // Get a pointer to the logon session
  76. //
  77. LogonSession = LsapLocateLogonSession( LogonId );
  78. if ( LogonSession == NULL ) {
  79. return STATUS_NO_SUCH_LOGON_SESSION;
  80. }
  81. //
  82. // Allocate blocks needed to represent this credential
  83. // and copy the primary key and credential strings.
  84. //
  85. MaxPrimary = ROUND_UP_COUNT((PrimaryKeyValue->Length+sizeof(CHAR)), ALIGN_WORST);
  86. MaxCredentials = ROUND_UP_COUNT((Credentials->Length+sizeof(CHAR)), ALIGN_WORST);
  87. NewCredentials = LsapAllocatePrivateHeap( (ULONG)sizeof(LSAP_CREDENTIALS) +
  88. MaxPrimary +
  89. MaxCredentials
  90. );
  91. if ( NewCredentials == NULL )
  92. {
  93. Status = STATUS_NO_MEMORY;
  94. goto Cleanup;
  95. }
  96. NewCredentials->PrimaryKey.MaximumLength = MaxPrimary;
  97. NewCredentials->PrimaryKey.Buffer = (PSTR)(NewCredentials+1);
  98. NewCredentials->Credentials.MaximumLength = MaxCredentials;
  99. NewCredentials->Credentials.Buffer = NewCredentials->PrimaryKey.Buffer +
  100. NewCredentials->PrimaryKey.MaximumLength;
  101. RtlCopyString( &NewCredentials->PrimaryKey, PrimaryKeyValue );
  102. RtlCopyString( &NewCredentials->Credentials, Credentials );
  103. //
  104. // Now get a pointer to the Package's credentials
  105. // (create one if necessary)
  106. //
  107. AuWriteLockCreds();
  108. Package = LsapGetPackageCredentials(
  109. LogonSession,
  110. AuthenticationPackage,
  111. TRUE
  112. );
  113. if ( !Package )
  114. {
  115. AuUnlockCreds();
  116. Status = STATUS_NO_MEMORY;
  117. goto Cleanup;
  118. }
  119. //
  120. // insert new credentials in list.
  121. //
  122. NewCredentials->NextCredentials = Package->Credentials;
  123. Package->Credentials = NewCredentials;
  124. AuUnlockCreds();
  125. LsapReleaseLogonSession( LogonSession );
  126. return STATUS_SUCCESS;
  127. Cleanup:
  128. LsapReleaseLogonSession( LogonSession );
  129. if ( NewCredentials )
  130. {
  131. if ( NewCredentials->PrimaryKey.Buffer )
  132. {
  133. ZeroMemory( NewCredentials->PrimaryKey.Buffer,
  134. NewCredentials->PrimaryKey.Length );
  135. }
  136. if ( NewCredentials->Credentials.Buffer )
  137. {
  138. ZeroMemory( NewCredentials->Credentials.Buffer,
  139. NewCredentials->Credentials.Length );
  140. }
  141. LsapFreePrivateHeap( NewCredentials );
  142. }
  143. return Status;
  144. }
  145. NTSTATUS
  146. LsapGetCredentials(
  147. IN PLUID LogonId,
  148. IN ULONG AuthenticationPackage,
  149. IN OUT PULONG QueryContext,
  150. IN BOOLEAN RetrieveAllCredentials,
  151. IN PSTRING PrimaryKeyValue,
  152. OUT PULONG PrimaryKeyLength,
  153. IN PSTRING Credentials
  154. )
  155. /*++
  156. Routine Description:
  157. This service is used by authentication packages to retrieve credentials
  158. associated with a logon session. It is expected that each authentication
  159. package will provide its own version of this service to its "clients".
  160. For example, the MSV1_0 authentication package will provide services for
  161. the LM Redirector to retrieve credentials (and probably establish them)
  162. for remote accesses. These authentication package level services may be
  163. implemented using the LsaCallAuthenticationPackage() API.
  164. This service acquires the AuLock.
  165. Arguments:
  166. LogonId - The session ID of logon session from which credentials
  167. are to be retrieved.
  168. AuthenticationPackage - The authentication package ID of the
  169. calling authentication package. Authentication packages
  170. should only retrieve their own credentials.
  171. QueryContext - A context value used across successive calls to
  172. retrieve multiple credentials. The first time this service
  173. is used, the value pointed to by this argument should be
  174. zero. Thereafter, this value will be updated to allow
  175. retrieval to continue where it left off. This value should,
  176. therefore, not be changed until all credentials of a given
  177. query operation have been retrieved.
  178. RetrieveAllCredentials - A boolean value indicating whether all
  179. credentials for the specified logon session should be
  180. retrieved (TRUE), or only those matching the specified
  181. PrimaryKeyValue (FALSE).
  182. PrimaryKeyValue - This parameter serves two purposes. If the
  183. RetrieveAllCredentials argument is FALSE, then this string
  184. contains the value to use as a primary key lookup value. In
  185. this case, only credentials whose primary key matches this
  186. one (and belonging to the correct logon session) will be
  187. retrieved. If, however, the RetrieveAllCredentials argument
  188. is FALSE, then the value of this string are ignored. In this
  189. case, the primary key value of each retrieved credential will
  190. be returned in this string.
  191. PrimaryKeyLength - If the RetrieveAllCredentials argument value
  192. is FALSE, then this argument receives the length needed to
  193. store the PrimaryKeyValue. If this value is larger than the
  194. length of the PrimaryKeyValue string, then
  195. STATUS_BUFFER_OVERFLOW is returned and no data is retrieved.
  196. Credentials - Points to a string whose buffer is to be set to
  197. contain the retrieved credential.
  198. Return Status:
  199. STATUS_MORE_ENTRIES - Credentials were successfully retrieved,
  200. and there are more available.
  201. STATUS_SUCCESS - Credentials were successfully retrieved and
  202. there are no more available.
  203. STATUS_UNSUCCESSFUL - No more credentials are available. If
  204. returned on the first call, then there are no credentials
  205. matching the selection criteria.
  206. STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
  207. not be found.
  208. STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
  209. the PrimaryKeyValue was not large enough to hold the data.
  210. In this case, no data was retrieved. However, the length value
  211. is returned so that appropriately sized buffer can be passed in
  212. a successive call.
  213. --*/
  214. {
  215. //
  216. // NOTE: The QueryContext value is an index of the last retrieved
  217. // credential matching the selection criteria. To continue
  218. // a search for successive credentials, skip QueryContext
  219. // number of entries first.
  220. //
  221. // This has the problem of changes between calls screwing
  222. // up the result of successive calls. That's tough.
  223. //
  224. NTSTATUS Status = STATUS_SUCCESS;
  225. PLSAP_LOGON_SESSION LogonSession;
  226. PLSAP_PACKAGE_CREDENTIALS Package;
  227. PLSAP_CREDENTIALS NextCredentials;
  228. ULONG i;
  229. BOOLEAN SelectionMatch;
  230. //
  231. // Get a pointer to the logon session
  232. //
  233. LogonSession = LsapLocateLogonSession( LogonId );
  234. if ( LogonSession == NULL ) {
  235. return STATUS_NO_SUCH_LOGON_SESSION;
  236. }
  237. AuReadLockCreds();
  238. //
  239. // Now get a pointer to the Package's credentials
  240. //
  241. Package = LsapGetPackageCredentials(
  242. LogonSession,
  243. AuthenticationPackage,
  244. FALSE
  245. );
  246. if ( Package == NULL ) {
  247. AuUnlockCreds();
  248. LsapReleaseLogonSession( LogonSession );
  249. return STATUS_UNSUCCESSFUL;
  250. }
  251. //
  252. // skip the credentials already evaluated in previous calls...
  253. //
  254. i = (*QueryContext);
  255. NextCredentials = Package->Credentials;
  256. while ( i > 0 ) {
  257. //
  258. // See if we have reached the end of the list
  259. //
  260. if (NextCredentials == NULL) {
  261. AuUnlockCreds();
  262. LsapReleaseLogonSession( LogonSession );
  263. return STATUS_UNSUCCESSFUL;
  264. }
  265. //
  266. // Nope, skip the next one...
  267. //
  268. NextCredentials = NextCredentials->NextCredentials;
  269. i -= 1;
  270. }
  271. //
  272. // Start evaluating each credential for a criteria match.
  273. //
  274. SelectionMatch = FALSE;
  275. while ( NextCredentials != NULL && !SelectionMatch ) {
  276. (*QueryContext) += 1;
  277. if (RetrieveAllCredentials) {
  278. SelectionMatch = TRUE;
  279. Status = LsapReturnCredential(
  280. NextCredentials,
  281. Credentials,
  282. TRUE,
  283. PrimaryKeyValue,
  284. PrimaryKeyLength
  285. );
  286. }
  287. //
  288. // Only retrieving credentials that match the specified primary
  289. // key.
  290. //
  291. if ( RtlEqualString( &NextCredentials->PrimaryKey, PrimaryKeyValue, FALSE) ) {
  292. SelectionMatch = TRUE;
  293. Status = LsapReturnCredential(
  294. NextCredentials,
  295. Credentials,
  296. FALSE,
  297. NULL,
  298. NULL
  299. );
  300. }
  301. NextCredentials = NextCredentials->NextCredentials;
  302. }
  303. AuUnlockCreds();
  304. LsapReleaseLogonSession( LogonSession );
  305. //
  306. // Figure out what return value to send.
  307. //
  308. if (SelectionMatch) {
  309. if ( Status == STATUS_BUFFER_OVERFLOW ) {
  310. (*QueryContext) -= 1;
  311. return STATUS_BUFFER_OVERFLOW;
  312. }
  313. if ( Status == STATUS_SUCCESS) {
  314. if ( NextCredentials == NULL ) {
  315. return STATUS_SUCCESS;
  316. } else {
  317. return STATUS_MORE_ENTRIES;
  318. }
  319. }
  320. } else {
  321. //
  322. // didn't find a credential matching the selection criteria.
  323. //
  324. return STATUS_UNSUCCESSFUL;
  325. }
  326. return STATUS_UNSUCCESSFUL ;
  327. }
  328. NTSTATUS
  329. LsapReturnCredential(
  330. IN PLSAP_CREDENTIALS SourceCredentials,
  331. IN PSTRING TargetCredentials,
  332. IN BOOLEAN ReturnPrimaryKey,
  333. IN PSTRING PrimaryKeyValue OPTIONAL,
  334. OUT PULONG PrimaryKeyLength OPTIONAL
  335. )
  336. /*++
  337. Routine Description:
  338. This routine returns a copy of the credentials in the specified
  339. credential record. It also, optionally, returns a copy of the
  340. primary key value.
  341. Arguments:
  342. SourceCredentials - Points to a credential record whose credential
  343. string and, optionally, primary key are to be copied.
  344. TargetCredentials - Points to a string whose buffer is to be set to
  345. contain a copy of the credential. This copy will be allocated
  346. using LsapAllocateLsaHeap().
  347. ReturnPrimaryKey - A boolean indicating whether or not to return
  348. a copy of the primary key. TRUE indicates a copy should be
  349. returned. FALSE indicates a copy should not be returned.
  350. PrimaryKeyValue - Points to a string whose buffer is to be set to
  351. contain a copy of the primary key. This copy will be allocated
  352. using LsapAllocateLsaHeap(). This parameter is ignored if the
  353. ReturnPrimaryKey argument value is FALSE.
  354. PrimaryKeyLength - Points to a value which will receive the
  355. length of the primary key value. If this value is larger than the
  356. length of the PrimaryKeyValue string, then STATUS_BUFFER_OVERFLOW
  357. is returned and no data is retrieved.
  358. Return Status:
  359. STATUS_SUCCESS - Credentials were successfully returned.
  360. STATUS_BUFFER_OVERFLOW - Indicates the string provided to receive
  361. the PrimaryKeyValue was not large enough to hold the data.
  362. In this case, no data was retrieved. However, the length value
  363. is returned so that appropriately sized buffer can be passed in
  364. a successive call.
  365. --*/
  366. {
  367. ULONG Length;
  368. //
  369. // First try to return the primary key value, since we can encounter
  370. // a buffer overflow situation in doing so that would prevent us from
  371. // returning a copy of the credential string.
  372. //
  373. if (ReturnPrimaryKey) {
  374. (*PrimaryKeyLength) = SourceCredentials->PrimaryKey.Length + 1;
  375. if ( (*PrimaryKeyLength) > PrimaryKeyValue->MaximumLength ) {
  376. return STATUS_BUFFER_OVERFLOW;
  377. }
  378. //
  379. // It fits
  380. //
  381. RtlCopyString( PrimaryKeyValue, &SourceCredentials->PrimaryKey );
  382. }
  383. //
  384. // Now allocate and copy the credential string copy.
  385. //
  386. TargetCredentials->MaximumLength = SourceCredentials->Credentials.Length
  387. + (USHORT)1;
  388. Length = (ULONG)TargetCredentials->MaximumLength;
  389. TargetCredentials->Buffer = (PCHAR)LsapAllocateLsaHeap( Length );
  390. if ( TargetCredentials->Buffer )
  391. {
  392. RtlCopyString( TargetCredentials, &SourceCredentials->Credentials );
  393. return STATUS_SUCCESS ;
  394. }
  395. else
  396. {
  397. return STATUS_NO_MEMORY ;
  398. }
  399. }
  400. NTSTATUS
  401. LsapDeleteCredential(
  402. IN PLUID LogonId,
  403. IN ULONG AuthenticationPackage,
  404. IN PSTRING PrimaryKeyValue
  405. )
  406. /*++
  407. Routine Description:
  408. This service is used to delete an existing credential. This service
  409. deletes the first credential it finds with a matching logon session,
  410. authentication package ID, and primary key value. If thee are
  411. multiple credentials that match this criteria, only one of them is
  412. deleted.
  413. This status acquires the AuLock.
  414. Arguments:
  415. LogonId - The session ID of logon session whose credentials are to be
  416. deleted.
  417. AuthenticationPackage - The authentication package ID of the
  418. calling authentication package. This was received in the
  419. InitializePackage() call during DLL initialization.
  420. PrimaryKeyValue - Points to string containing the primary key value
  421. of the credential to be deleted.
  422. Return Status:
  423. STATUS_SUCCESS - The credentials were successfully deleted.
  424. STATUS_NO_SUCH_LOGON_SESSION - The specified logon session could
  425. not be found.
  426. STATUS_UNSUCCESSFUL - No such credential could be found.
  427. --*/
  428. {
  429. PLSAP_LOGON_SESSION LogonSession;
  430. PLSAP_PACKAGE_CREDENTIALS Package;
  431. PLSAP_CREDENTIALS *NextCredentials, GoodByeCredentials;
  432. //
  433. // Get a pointer to the logon session
  434. //
  435. LogonSession = LsapLocateLogonSession( LogonId );
  436. if ( LogonSession == NULL ) {
  437. return STATUS_NO_SUCH_LOGON_SESSION;
  438. }
  439. AuWriteLockCreds();
  440. //
  441. // Now get a pointer to the Package's credentials
  442. //
  443. Package = LsapGetPackageCredentials(
  444. LogonSession,
  445. AuthenticationPackage,
  446. FALSE
  447. );
  448. if ( Package == NULL ) {
  449. AuUnlockCreds();
  450. LsapReleaseLogonSession( LogonSession );
  451. return STATUS_UNSUCCESSFUL;
  452. }
  453. //
  454. // Start evaluating each credential for a primary key value match.
  455. //
  456. NextCredentials = &Package->Credentials;
  457. while ( (*NextCredentials) != NULL ) {
  458. if ( RtlEqualString(
  459. &(*NextCredentials)->PrimaryKey,
  460. PrimaryKeyValue,
  461. FALSE)
  462. ) {
  463. //
  464. // remove it from the list
  465. //
  466. GoodByeCredentials = (*NextCredentials);
  467. (*NextCredentials) = GoodByeCredentials->NextCredentials;
  468. AuUnlockCreds();
  469. LsapReleaseLogonSession( LogonSession );
  470. //
  471. // Zero the contents of the credential record.
  472. //
  473. if( GoodByeCredentials->PrimaryKey.Buffer != NULL )
  474. {
  475. SecureZeroMemory( GoodByeCredentials->PrimaryKey.Buffer,
  476. GoodByeCredentials->PrimaryKey.Length );
  477. }
  478. if( GoodByeCredentials->Credentials.Buffer != NULL )
  479. {
  480. SecureZeroMemory( GoodByeCredentials->Credentials.Buffer,
  481. GoodByeCredentials->Credentials.Length );
  482. }
  483. //
  484. // Free the credential record itself.
  485. //
  486. LsapFreePrivateHeap( GoodByeCredentials );
  487. return STATUS_SUCCESS;
  488. }
  489. NextCredentials = &(*NextCredentials)->NextCredentials;
  490. }
  491. AuUnlockCreds();
  492. LsapReleaseLogonSession( LogonSession );
  493. //
  494. // Nothing matched
  495. //
  496. return STATUS_UNSUCCESSFUL;
  497. }
  498. PLSAP_PACKAGE_CREDENTIALS
  499. LsapGetPackageCredentials(
  500. IN PLSAP_LOGON_SESSION LogonSession,
  501. IN ULONG PackageId,
  502. IN BOOLEAN CreateIfNecessary
  503. )
  504. /*++
  505. Routine Description:
  506. This service returns a pointer to a specified package's credential
  507. record. If no such record exists, one will optionally be created.
  508. It is assumed that either the LogonSession record is not currently
  509. in the logon session record list, or, if it is, that the AuLock
  510. is currently held.
  511. Arguments:
  512. LogonSession - Pointer to a logon session record within which to
  513. work.
  514. PackageId - The authentication package ID to look for.
  515. CreateIfNecessary - A boolean indicating whether or not the package
  516. record is to be created if one does not already exist. TRUE
  517. indicates the package is to be created if necessary, FALSE indicates
  518. the record should not be created.
  519. Return Status:
  520. non-NULL - A pointer to the specified package record.
  521. NULL - The specified package record does not exist (and one was not
  522. created automatically).
  523. --*/
  524. {
  525. PLSAP_PACKAGE_CREDENTIALS *NextPackage, TargetPackage;
  526. //
  527. // See if the session exists
  528. //
  529. NextPackage = &LogonSession->Packages;
  530. while ( (*NextPackage) != NULL) {
  531. if ( (*NextPackage)->PackageId == PackageId ) {
  532. //
  533. // Found it
  534. //
  535. TargetPackage = (*NextPackage);
  536. return TargetPackage;
  537. }
  538. //
  539. // Move on to next package.
  540. //
  541. NextPackage = &(*NextPackage)->NextPackage;
  542. }
  543. //
  544. // No such package exists yet.
  545. // Create one if necessary.
  546. if ( !CreateIfNecessary ) {
  547. return NULL;
  548. }
  549. TargetPackage = LsapAllocateLsaHeap( (ULONG)sizeof(LSAP_PACKAGE_CREDENTIALS) );
  550. if ( TargetPackage )
  551. {
  552. TargetPackage->PackageId = PackageId;
  553. TargetPackage->Credentials = NULL;
  554. TargetPackage->NextPackage = LogonSession->Packages;
  555. LogonSession->Packages = TargetPackage;
  556. }
  557. return TargetPackage;
  558. }
  559. VOID
  560. LsapFreePackageCredentialList(
  561. IN PLSAP_PACKAGE_CREDENTIALS PackageCredentialList
  562. )
  563. /*++
  564. Routine Description:
  565. This service frees a list of packge credential records. This service
  566. is not expected to be exposed to authentication packages.
  567. This service expects not to have to acquire the AuLock. This may be
  568. because it is already held, or because the credentials being freed
  569. are no longer accessible via the global variables.
  570. Arguments:
  571. PackageCredentialList - Is a pointer to a list of LSA_PACKAGE_CREDENTIALS
  572. data structures.
  573. Return Status:
  574. None.
  575. --*/
  576. {
  577. PLSAP_PACKAGE_CREDENTIALS NextPackage, GoodByePackage;
  578. //
  579. // Get rid of each PACKAGE_CREDENTIAL record.
  580. //
  581. NextPackage = PackageCredentialList;
  582. while ( NextPackage != NULL ) {
  583. //
  584. // Save a pointer to the next package
  585. //
  586. GoodByePackage = NextPackage;
  587. NextPackage = GoodByePackage->NextPackage;
  588. LsapFreeCredentialList( GoodByePackage->Credentials );
  589. //
  590. // Free the package record itself.
  591. //
  592. LsapFreeLsaHeap( GoodByePackage );
  593. }
  594. return;
  595. }
  596. VOID
  597. LsapFreeCredentialList(
  598. IN PLSAP_CREDENTIALS CredentialList
  599. )
  600. /*++
  601. Routine Description:
  602. This service frees a list of credential records. This service is not
  603. expected to be exposed to authentication packages.
  604. This service expects not to have to acquire the AuLock. This may be
  605. because it is already held, or because the credentials being freed
  606. are no longer accessible via the global variables.
  607. Arguments:
  608. CredentialList - Is a pointer to a list of LSA_CREDENTIALS data
  609. structures.
  610. Return Status:
  611. --*/
  612. {
  613. PLSAP_CREDENTIALS NextCredentials, GoodByeCredentials;
  614. //
  615. // Get rid of each PACKAGE_CREDENTIAL record.
  616. //
  617. NextCredentials = CredentialList;
  618. while ( NextCredentials != NULL ) {
  619. //
  620. // Save a pointer to the next credential
  621. //
  622. GoodByeCredentials = NextCredentials;
  623. NextCredentials = GoodByeCredentials->NextCredentials;
  624. //
  625. // Zero the contents of this credential record.
  626. //
  627. if( GoodByeCredentials->PrimaryKey.Buffer != NULL )
  628. {
  629. SecureZeroMemory( GoodByeCredentials->PrimaryKey.Buffer,
  630. GoodByeCredentials->PrimaryKey.Length );
  631. }
  632. if( GoodByeCredentials->Credentials.Buffer != NULL )
  633. {
  634. SecureZeroMemory( GoodByeCredentials->Credentials.Buffer,
  635. GoodByeCredentials->Credentials.Length );
  636. }
  637. //
  638. // Free the credential record itself.
  639. //
  640. LsapFreePrivateHeap( GoodByeCredentials );
  641. }
  642. return;
  643. }