Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9902 lines
266 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. credmgr.cxx
  5. Abstract:
  6. Credential Manager Interfaces
  7. Author:
  8. Cliff Van Dyke (CliffV)
  9. Environment:
  10. Revision History:
  11. --*/
  12. #include <lsapch.hxx>
  13. extern "C" {
  14. #include <wincrypt.h>
  15. #include <windns.h>
  16. #include <align.h>
  17. #include <rc4.h>
  18. #include <des.h>
  19. #include <modes.h>
  20. #include <cryptdll.h>
  21. #include <names.h>
  22. #include <smbgtpt.h>
  23. #include <shfolder.h>
  24. #include <netlibnt.h>
  25. #include <stddef.h>
  26. }
  27. //
  28. // Include routines common with netapi32.dll
  29. //
  30. #include <credp.h>
  31. #include <..\netclient\credapi.c>
  32. extern "C"
  33. {
  34. BOOLEAN
  35. LsapIsRunningOnPersonal(
  36. VOID
  37. );
  38. }
  39. //
  40. // Local structures
  41. //
  42. //
  43. // Structure describing a canonical credential
  44. //
  45. // This is the structure of a single credential as stored in memory in the LSA process.
  46. //
  47. typedef struct _CANONICAL_CREDENTIAL {
  48. //
  49. // The Credential itself
  50. //
  51. CREDENTIALW Cred;
  52. //
  53. // The size in bytes of the clear text credential blob
  54. //
  55. ULONG ClearCredentialBlobSize;
  56. //
  57. // Link to next entry in the list of credentials in the credential set.
  58. // Access serialized by UserCredentialSets->CritSect
  59. //
  60. LIST_ENTRY Next;
  61. //
  62. // UNICODE_STRING form of Cred.TargetName
  63. //
  64. UNICODE_STRING TargetName;
  65. //
  66. // UNICODE_STRING form of Cred.TargetAlias
  67. //
  68. UNICODE_STRING TargetAlias;
  69. //
  70. // UNICODE_STRING form of Cred.UserName
  71. //
  72. UNICODE_STRING UserName;
  73. //
  74. // Describe the wildcard nature of this credential
  75. //
  76. WILDCARD_TYPE WildcardType;
  77. //
  78. // TargetName with the wildcard characters removed.
  79. //
  80. // The exact value is a function on WildcardType:
  81. // WcDfsShareName: this is the 'server name' portion of the string
  82. // WcServerWildcard: this is the 'server name' portion of the string preceeded by a .
  83. // WcDomainWildcard: this is the 'domain name' portion of the string
  84. // All Others: This is a copy of TargetName
  85. //
  86. // ??? I don't think we use it for WcDfsShare name any more.
  87. UNICODE_STRING NonWildcardedTargetName;
  88. //
  89. // Size (in bytes) of this structure and all of the pointed to strings.
  90. //
  91. ULONG AllocatedSize;
  92. //
  93. // True if credential is to be returned to the caller.
  94. // Access serialized by UserCredentialSets->CritSect
  95. //
  96. BOOLEAN ReturnMe;
  97. } CANONICAL_CREDENTIAL, *PCANONICAL_CREDENTIAL;
  98. //
  99. // Structure describing when a credential should be prompted for.
  100. //
  101. // In general, this structure exists on a per-session basis for each credential that needs prompt
  102. // data to be stored.
  103. // In the future, this structure might contain other per-session/per-credential data.
  104. //
  105. typedef struct _PROMPT_DATA {
  106. //
  107. // Link to next entry in the list of PROMPT_DATA for this session.
  108. // Access serialized by UserCredentialSets->CritSect
  109. //
  110. LIST_ENTRY Next;
  111. //
  112. // Target Name of the credential this prompt data is for.
  113. //
  114. UNICODE_STRING TargetName;
  115. //
  116. // Type of the credential this prompt data is for.
  117. //
  118. DWORD Type;
  119. //
  120. // Persistence of the credential this prompt data is for.
  121. //
  122. DWORD Persist;
  123. //
  124. // Boolean indicating if this credential was been written yet in this session.
  125. //
  126. BOOLEAN Written;
  127. } PROMPT_DATA, *PPROMPT_DATA;
  128. //
  129. // Structure describing a canonical target info
  130. //
  131. typedef struct _CANONICAL_TARGET_INFO {
  132. UNICODE_STRING TargetName;
  133. UNICODE_STRING NetbiosServerName;
  134. UNICODE_STRING DnsServerName;
  135. UNICODE_STRING NetbiosDomainName;
  136. UNICODE_STRING DnsDomainName;
  137. UNICODE_STRING DnsTreeName;
  138. UNICODE_STRING PackageName;
  139. DWORD Flags;
  140. DWORD CredTypeCount;
  141. LPDWORD CredTypes;
  142. //
  143. // Define how a credential matches a target info.
  144. //
  145. // This list is ordered from most specific to least specific
  146. //
  147. #define CRED_DFS_SHARE_NAME 0
  148. #define CRED_DNS_SERVER_NAME 1
  149. #define CRED_NETBIOS_SERVER_NAME 2
  150. #define CRED_TARGET_NAME 3
  151. #define CRED_WILDCARD_SERVER_NAME 4
  152. #define CRED_DNS_DOMAIN_NAME 5
  153. #define CRED_NETBIOS_DOMAIN_NAME 6
  154. #define CRED_UNIVERSAL_SESSION_NAME 7
  155. #define CRED_UNIVERSAL_NAME 8
  156. #define CRED_MAX_ALIASES 9
  157. //
  158. // Link into SessionCredSets->TargetInfoHashTable
  159. //
  160. LIST_ENTRY HashNext;
  161. //
  162. // Link into SessionCredSets->TargetInfoLruList
  163. //
  164. LIST_ENTRY LruNext;
  165. } CANONICAL_TARGET_INFO, *PCANONICAL_TARGET_INFO;
  166. //
  167. // Structure describing an encryptable credential set
  168. //
  169. // This is the structure of a credential set as it appears on disk.
  170. // It appears as a MARSHALED_CREDENTIAL_SET structure followed by a series of
  171. // MARSHALED_CREDENTIAL structures.
  172. //
  173. typedef struct _MARSHALED_CREDENTIAL_SET {
  174. // Version of this structure
  175. ULONG Version;
  176. #define MARSHALED_CREDENTIAL_SET_VERSION 1
  177. // Size in bytes of the entire credential set.
  178. ULONG Size;
  179. } MARSHALED_CREDENTIAL_SET, *PMARSHALED_CREDENTIAL_SET;
  180. typedef struct _MARSHALED_CREDENTIAL {
  181. //
  182. // Size in bytes of the entire credential (including variable length fields)
  183. //
  184. ULONG EntrySize;
  185. //
  186. // Fields from the CREDENTIALW structure
  187. //
  188. DWORD Flags;
  189. DWORD Type;
  190. FILETIME LastWritten;
  191. DWORD CredentialBlobSize;
  192. DWORD Persist;
  193. DWORD AttributeCount;
  194. DWORD Expansion1; // This field is reserved for expansion
  195. DWORD Expansion2; // This field is reserved for expansion
  196. } MARSHALED_CREDENTIAL, *PMARSHALED_CREDENTIAL;
  197. //
  198. // Macro to pick a credential set for a particular peristance.
  199. //
  200. #define CREDENTIAL_FILE_NAME L"Credentials";
  201. #define CRED_PERSIST_MIN CRED_PERSIST_SESSION
  202. #define CRED_PERSIST_MAX CRED_PERSIST_ENTERPRISE
  203. #define PersistToCredentialSet( _CredentialSets, _Persist ) \
  204. (((_Persist) == CRED_PERSIST_SESSION) ? \
  205. (_CredentialSets)->SessionCredSets->SessionCredSet : \
  206. (((_Persist) == CRED_PERSIST_LOCAL_MACHINE) ? \
  207. (_CredentialSets)->UserCredentialSets->LocalMachineCredSet : \
  208. (_CredentialSets)->UserCredentialSets->EnterpriseCredSet ) )
  209. //
  210. // Macro returns TRUE if the CredentialBlob is to be persisted
  211. //
  212. // Don't persist PINS passwords. We entrust the PIN to the CSP.
  213. //
  214. #define PersistCredBlob( _Credential ) \
  215. ( (_Credential)->Type != CRED_TYPE_DOMAIN_CERTIFICATE )
  216. //
  217. // Macro returns TRUE if the credential is to be prompted for
  218. //
  219. // If there is no prompt data,
  220. // we've never prompted for this credential since logon.
  221. // If there is prompt data,
  222. // we can rely on the boolean
  223. //
  224. #define ShouldPromptNow( _PromptData ) \
  225. ((_PromptData) == NULL || !(_PromptData)->Written )
  226. //
  227. // Globals
  228. //
  229. // List of USER_CREDENTIAL_SETS for each logged on user.
  230. RTL_CRITICAL_SECTION CredentialSetListLock;
  231. LIST_ENTRY CredentialSetList;
  232. //
  233. // Configurable values
  234. //
  235. #define CRED_TARGET_INFO_MAX_COUNT 1000;
  236. ULONG CredTargetInfoMaxCount;
  237. ULONG CredDisableDomainCreds;
  238. ULONG CredIsPersonal;
  239. //
  240. // Define the default order for returning credentials from CredReadDomainCredentials
  241. //
  242. ULONG CredTypeDefaultOrder[] =
  243. {
  244. CRED_TYPE_DOMAIN_CERTIFICATE,
  245. CRED_TYPE_DOMAIN_PASSWORD,
  246. CRED_TYPE_DOMAIN_VISIBLE_PASSWORD
  247. };
  248. //
  249. // Memory containing key for LSA protected memory.
  250. //
  251. PVOID CredLockedMemory = NULL;
  252. ULONG CredLockedMemorySize = 0;
  253. //
  254. // DES-X keystate
  255. //
  256. DESXTable *g_pDESXKey = NULL;
  257. unsigned __int64 g_Feedback;
  258. //
  259. // RC4 keystate
  260. //
  261. PBYTE g_pRandomKey = NULL;
  262. ULONG g_cbRandomKey = 0;
  263. //
  264. // Forwards
  265. //
  266. NTSTATUS
  267. CredpWriteCredential(
  268. IN PCREDENTIAL_SETS CredentialSets,
  269. IN OUT PCANONICAL_CREDENTIAL *NewCredential,
  270. IN BOOLEAN FromPersistedFile,
  271. IN BOOLEAN WritePinToCsp,
  272. IN BOOLEAN PromptedFor,
  273. IN DWORD Flags,
  274. OUT PCANONICAL_CREDENTIAL *OldCredential OPTIONAL,
  275. OUT PPROMPT_DATA *OldPromptData OPTIONAL,
  276. OUT PPROMPT_DATA *NewPromptData OPTIONAL
  277. );
  278. extern DWORD
  279. DPAPINotifyPasswordChange(
  280. IN PUNICODE_STRING NetbiosDomainName,
  281. IN PUNICODE_STRING UserName,
  282. IN PUNICODE_STRING OldPassword,
  283. IN PUNICODE_STRING NewPassword
  284. );
  285. VOID
  286. LsaINotifyPasswordChanged(
  287. IN PUNICODE_STRING NetbiosDomainName OPTIONAL,
  288. IN PUNICODE_STRING UserName,
  289. IN PUNICODE_STRING DnsDomainName OPTIONAL,
  290. IN PUNICODE_STRING Upn OPTIONAL,
  291. IN PUNICODE_STRING OldPassword OPTIONAL,
  292. IN PUNICODE_STRING NewPassword,
  293. IN BOOLEAN Impersonating
  294. )
  295. /*++
  296. Routine Description:
  297. This routine is a callback from the MSV authentication package following a password
  298. change. This routine will update any password caches maintained in the LSA.
  299. Arguments:
  300. NetbiosDomainName - Netbios domain name of the user whose password was changed
  301. OPTIONAL if we're caching MIT realm credentials
  302. UserName - User name of the user whose password was changed
  303. DnsDomainName - If known, Dns Domain Name of the user whose password was changed
  304. Upn - If known, the Upn of the user whose password was changed
  305. OldPassword - If known, the previous password for the user.
  306. NewPassword - The new password for the user.
  307. Impersonating - If TRUE, this routine is called while impersonating the user changing
  308. the password. That user isn't necessarily UserName.
  309. Return Values:
  310. None
  311. --*/
  312. {
  313. //
  314. // Notify the credential manager of the change.
  315. //
  316. if ( Impersonating ) {
  317. CredpNotifyPasswordChange( NetbiosDomainName,
  318. UserName,
  319. DnsDomainName,
  320. Upn,
  321. NewPassword );
  322. }
  323. //
  324. // Notify DPAPI of the change.
  325. //
  326. if (ARGUMENT_PRESENT(NetbiosDomainName)) {
  327. DPAPINotifyPasswordChange( NetbiosDomainName,
  328. UserName,
  329. OldPassword,
  330. NewPassword);
  331. }
  332. }
  333. VOID
  334. LsaEncryptMemory(
  335. PBYTE pData,
  336. ULONG cbData,
  337. int Operation
  338. )
  339. /*++
  340. Routine Description:
  341. This routine encrypts the specified buffer in place with a key that exists until
  342. the next reboot.
  343. The purpose of the routine is to protect sensitive data that will be swapped
  344. to the page file.
  345. Arguments:
  346. pData - Pointer to the data to encrypt
  347. cbData - Length (in bytes) of the data to encrypt
  348. Operation - ENCRYPT or DECRYPT
  349. Return Values:
  350. None
  351. --*/
  352. {
  353. if( pData == NULL || cbData == 0 ) {
  354. return;
  355. }
  356. DsysAssert( ((DESX_BLOCKLEN % 8) == 0) );
  357. if( (cbData & (DESX_BLOCKLEN-1)) == 0 )
  358. {
  359. unsigned __int64 feedback;
  360. ULONG BlockCount;
  361. BlockCount = cbData / DESX_BLOCKLEN;
  362. feedback = g_Feedback;
  363. while( BlockCount-- )
  364. {
  365. CBC(
  366. desx, // desx is the cipher routine
  367. DESX_BLOCKLEN,
  368. pData, // result buffer.
  369. pData, // input buffer.
  370. g_pDESXKey,
  371. Operation,
  372. (unsigned char*)&feedback
  373. );
  374. pData += DESX_BLOCKLEN;
  375. }
  376. } else {
  377. RC4_KEYSTRUCT rc4key;
  378. rc4_key( &rc4key, g_cbRandomKey, g_pRandomKey );
  379. rc4( &rc4key, cbData, pData );
  380. RtlZeroMemory( &rc4key, sizeof(rc4key) );
  381. }
  382. return;
  383. }
  384. VOID
  385. LsaProtectMemory(
  386. VOID *pData,
  387. ULONG cbData
  388. )
  389. /*++
  390. Routine Description:
  391. This routine encrypts the specified buffer in place with a key that exists until
  392. the next reboot.
  393. The purpose of the routine is to protect sensitive data that will be swapped
  394. to the page file.
  395. Arguments:
  396. pData - Pointer to the data to encrypt
  397. cbData - Length (in bytes) of the data to encrypt
  398. Return Values:
  399. None
  400. --*/
  401. {
  402. LsaEncryptMemory( (PBYTE)pData, cbData, ENCRYPT );
  403. }
  404. VOID
  405. LsaUnprotectMemory(
  406. VOID *pData,
  407. ULONG cbData
  408. )
  409. /*++
  410. Routine Description:
  411. This routine decrypts the specified buffer in place with a key that exists until
  412. the next reboot.
  413. The purpose of the routine is to un protect sensitive data that was encrypted via
  414. LsaProtectMemory.
  415. Arguments:
  416. pData - Pointer to the data to decrypt
  417. cbData - Length (in bytes) of the data to decrypt
  418. Return Values:
  419. None
  420. --*/
  421. {
  422. LsaEncryptMemory( (PBYTE)pData, cbData, DECRYPT );
  423. }
  424. extern "C"
  425. VOID
  426. LsaCleanupProtectedMemory(
  427. VOID
  428. )
  429. /*++
  430. Routine Description:
  431. This routine cleans up the LsaProtectMemory subsystem
  432. Arguments:
  433. None
  434. Return Values:
  435. None
  436. --*/
  437. {
  438. if( CredLockedMemory ) {
  439. ZeroMemory( CredLockedMemory, CredLockedMemorySize );
  440. VirtualFree( CredLockedMemory, 0, MEM_RELEASE );
  441. CredLockedMemory = NULL;
  442. }
  443. }
  444. extern "C"
  445. NTSTATUS
  446. LsaInitializeProtectedMemory(
  447. VOID
  448. )
  449. /*++
  450. Routine Description:
  451. This routine initializes the LsaProtectMemory subsystem
  452. Arguments:
  453. None
  454. Return Values:
  455. Status of the operation
  456. --*/
  457. {
  458. NTSTATUS Status;
  459. //
  460. // Lock enough memory to contain the maximum size key the algorithm supports.
  461. //
  462. g_cbRandomKey = 256;
  463. CredLockedMemorySize = sizeof(DESXTable) + g_cbRandomKey;
  464. CredLockedMemory = VirtualAlloc(
  465. NULL,
  466. CredLockedMemorySize,
  467. MEM_COMMIT,
  468. PAGE_READWRITE );
  469. if ( CredLockedMemory == NULL ) {
  470. return I_RpcMapWin32Status( GetLastError() );
  471. }
  472. //
  473. // lock memory.
  474. //
  475. if (!VirtualLock( CredLockedMemory, CredLockedMemorySize )) {
  476. Status = I_RpcMapWin32Status( GetLastError() );
  477. goto Cleanup;
  478. }
  479. //
  480. // setup DESX key.
  481. //
  482. g_pDESXKey = (DESXTable*)CredLockedMemory;
  483. g_pRandomKey = (PBYTE)( (PBYTE)g_pDESXKey + sizeof(DESXTable) );
  484. if ( !RtlGenRandom( g_pRandomKey, DESX_KEYSIZE )) {
  485. Status = STATUS_UNSUCCESSFUL;
  486. goto Cleanup;
  487. }
  488. if ( !RtlGenRandom( (PUCHAR)&g_Feedback, sizeof(g_Feedback) )) {
  489. Status = STATUS_UNSUCCESSFUL;
  490. goto Cleanup;
  491. }
  492. desxkey( g_pDESXKey, g_pRandomKey );
  493. //
  494. // generate random key in page locked memory.
  495. //
  496. if ( !RtlGenRandom( g_pRandomKey, g_cbRandomKey )) {
  497. Status = STATUS_UNSUCCESSFUL;
  498. goto Cleanup;
  499. }
  500. Status = STATUS_SUCCESS;
  501. Cleanup:
  502. if(!NT_SUCCESS(Status))
  503. {
  504. LsaCleanupProtectedMemory();
  505. }
  506. return Status;
  507. }
  508. NTSTATUS
  509. CredpInitialize(
  510. VOID
  511. )
  512. /*++
  513. Routine Description:
  514. This routine initializes the credential manager. It is called once during LSA
  515. initialization.
  516. Arguments:
  517. None.
  518. Return Values:
  519. Status of the operation.
  520. --*/
  521. {
  522. NTSTATUS Status;
  523. DWORD WinStatus;
  524. ULONG i;
  525. HKEY LsaKey;
  526. //
  527. // Initialize LSA memory protection subsystem
  528. // (This initialization could have happened earlier, but the cred manager
  529. // is its first client.)
  530. //
  531. Status = LsaInitializeProtectedMemory();
  532. if ( !NT_SUCCESS(Status) ) {
  533. return Status;
  534. }
  535. //
  536. // Initialize credential set globals.
  537. //
  538. Status = RtlInitializeCriticalSection( &CredentialSetListLock );
  539. if ( !NT_SUCCESS(Status) ) {
  540. return Status;
  541. }
  542. InitializeListHead( &CredentialSetList );
  543. //
  544. // Domain creds are always disabled on personal
  545. //
  546. if ( LsapIsRunningOnPersonal() ) {
  547. CredIsPersonal = TRUE;
  548. }
  549. //
  550. // Initialize default values of configurable values.
  551. //
  552. CredTargetInfoMaxCount = CRED_TARGET_INFO_MAX_COUNT;
  553. CredDisableDomainCreds = FALSE;
  554. //
  555. // Grab registry settings.
  556. //
  557. WinStatus = RegOpenKeyExA(
  558. HKEY_LOCAL_MACHINE,
  559. "System\\CurrentControlSet\\Control\\Lsa",
  560. 0,
  561. KEY_READ,
  562. &LsaKey );
  563. if ( WinStatus == NO_ERROR ) {
  564. ULONG Value;
  565. ULONG Type;
  566. ULONG Size = sizeof(DWORD);
  567. //
  568. // Get the cache size
  569. //
  570. WinStatus = RegQueryValueExA(
  571. LsaKey,
  572. "TargetInfoCacheSize",
  573. 0,
  574. &Type,
  575. (PUCHAR) &Value,
  576. &Size );
  577. if ( WinStatus == NO_ERROR ) {
  578. //
  579. // Don't allow ridiculously small values.
  580. //
  581. if ( Value == 0 ) {
  582. Value = 1;
  583. }
  584. CredTargetInfoMaxCount = Value;
  585. }
  586. //
  587. // Get whether domain creds are disabled
  588. //
  589. Size = sizeof(DWORD);
  590. WinStatus = RegQueryValueExA(
  591. LsaKey,
  592. "DisableDomainCreds",
  593. 0,
  594. &Type,
  595. (PUCHAR) &Value,
  596. &Size );
  597. if ( WinStatus == NO_ERROR ) {
  598. CredDisableDomainCreds = Value;
  599. }
  600. RegCloseKey( LsaKey );
  601. }
  602. // ???: Don't check this in
  603. // SPMInfoLevel |= DEB_TRACE_CRED;
  604. return STATUS_SUCCESS;
  605. }
  606. DWORD
  607. CredpHashIndexTargetInfo(
  608. IN LPWSTR TargetName
  609. )
  610. /*++
  611. Routine Description:
  612. This routine computes the hash index for a particular server.
  613. Arguments:
  614. TargetName - Name of the server to compute the index for
  615. Return Values:
  616. Hash index
  617. --*/
  618. {
  619. NTSTATUS Status;
  620. OEM_STRING UpcasedString;
  621. UNICODE_STRING NetbiosServerNameString;
  622. CHAR StringBuffer[CNLEN+1];
  623. ULONG i;
  624. DWORD Value = 0;
  625. WCHAR NetbiosServerNameBuffer[CNLEN+1];
  626. DWORD Size;
  627. //
  628. // Convert the name to a netbios name.
  629. // (It might already be one.)
  630. //
  631. // We want to make sure the queried name falls into the same hash bucket.
  632. // So, no matter what form the input name is in, make the hash real generic.
  633. //
  634. Size = CNLEN+1;
  635. if ( DnsHostnameToComputerNameW( TargetName,
  636. NetbiosServerNameBuffer,
  637. &Size ) ) {
  638. TargetName = NetbiosServerNameBuffer;
  639. }
  640. //
  641. // Convert the server name to a canonical form
  642. //
  643. RtlInitUnicodeString( &NetbiosServerNameString, TargetName );
  644. UpcasedString.Buffer = StringBuffer;
  645. UpcasedString.MaximumLength = sizeof(StringBuffer);
  646. Status = RtlUpcaseUnicodeStringToOemString(
  647. &UpcasedString,
  648. &NetbiosServerNameString,
  649. FALSE );
  650. if ( !NT_SUCCESS(Status) ) {
  651. return 0;
  652. }
  653. for ( i=0; i<UpcasedString.Length; i++ ) {
  654. Value += UpcasedString.Buffer[i];
  655. }
  656. return (Value & (CRED_TARGET_INFO_HASH_TABLE_SIZE-1));
  657. }
  658. BOOLEAN
  659. CredpCacheTargetInfo(
  660. IN PCREDENTIAL_SETS CredentialSets,
  661. IN PCANONICAL_TARGET_INFO TargetInfo
  662. )
  663. /*++
  664. Routine Description:
  665. This routine inserts the specified TargetInfo into the target info cache.
  666. On entry, UserCredentialSets->CritSect must be locked.
  667. Arguments:
  668. CredentialSets - A pointer to the referenced credential sets.
  669. TargetInfo - Specifies the target info to insert.
  670. If this routine returns TRUE, the caller may no longer reference or free
  671. the passed in TargetInfo.
  672. Return Values:
  673. TRUE: TargetInfo was inserted.
  674. FALSE: TargetInfo was not inserted.
  675. --*/
  676. {
  677. DWORD Index;
  678. PLIST_ENTRY ListEntry;
  679. PCANONICAL_TARGET_INFO ExistingTargetInfo;
  680. //
  681. // Require a target name name
  682. //
  683. if ( TargetInfo->TargetName.Length == 0 ) {
  684. return FALSE;
  685. }
  686. //
  687. // Compute the hash index
  688. //
  689. Index = CredpHashIndexTargetInfo( TargetInfo->TargetName.Buffer );
  690. //
  691. // Loop through the existing entries deleting any conflicting entry
  692. //
  693. for ( ListEntry = CredentialSets->SessionCredSets->TargetInfoHashTable[Index].Flink ;
  694. ListEntry != &CredentialSets->SessionCredSets->TargetInfoHashTable[Index];
  695. ListEntry = ListEntry->Flink) {
  696. ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, HashNext );
  697. //
  698. // If the target names don't match,
  699. // neither do the entries.
  700. //
  701. if ( !RtlEqualUnicodeString( &TargetInfo->TargetName,
  702. &ExistingTargetInfo->TargetName,
  703. TRUE ) ) {
  704. continue;
  705. }
  706. //
  707. // Remove the existing entry
  708. //
  709. RemoveEntryList( &ExistingTargetInfo->HashNext );
  710. RemoveEntryList( &ExistingTargetInfo->LruNext );
  711. LsapFreeLsaHeap( ExistingTargetInfo );
  712. CredentialSets->SessionCredSets->TargetInfoCount --;
  713. break;
  714. }
  715. //
  716. // Link the new entry into the list
  717. //
  718. InsertHeadList( &CredentialSets->SessionCredSets->TargetInfoHashTable[Index], &TargetInfo->HashNext );
  719. InsertHeadList( &CredentialSets->SessionCredSets->TargetInfoLruList, &TargetInfo->LruNext );
  720. CredentialSets->SessionCredSets->TargetInfoCount ++;
  721. //
  722. // If we now have too many cache entries,
  723. // ditch the oldest.
  724. //
  725. while ( CredentialSets->SessionCredSets->TargetInfoCount > CredTargetInfoMaxCount ) {
  726. ListEntry = RemoveTailList( &CredentialSets->SessionCredSets->TargetInfoLruList );
  727. ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, LruNext );
  728. RemoveEntryList( &ExistingTargetInfo->HashNext );
  729. LsapFreeLsaHeap( ExistingTargetInfo );
  730. CredentialSets->SessionCredSets->TargetInfoCount --;
  731. }
  732. return TRUE;
  733. }
  734. PCANONICAL_TARGET_INFO
  735. CredpFindTargetInfo(
  736. IN PCREDENTIAL_SETS CredentialSets,
  737. IN LPWSTR TargetName
  738. )
  739. /*++
  740. Routine Description:
  741. This routine finds a cached TargetInfo for the named server.
  742. On entry, UserCredentialSets->CritSect must be locked.
  743. Arguments:
  744. CredentialSets - A pointer to the referenced credential sets.
  745. TargetName - Specifies the server name to find the TargetInfo for
  746. Return Values:
  747. Returns the requested TargetInfo.
  748. The returned structure can only be referenced while UserCredentialSets->CritSect remains locked.
  749. NULL - No such target info exists
  750. --*/
  751. {
  752. DWORD Index;
  753. PLIST_ENTRY ListEntry;
  754. PCANONICAL_TARGET_INFO ExistingTargetInfo = NULL;
  755. UNICODE_STRING TargetNameString;
  756. //
  757. // Compute the hash index
  758. //
  759. RtlInitUnicodeString( &TargetNameString, TargetName );
  760. Index = CredpHashIndexTargetInfo( TargetName );
  761. //
  762. // Loop through the entries finding this one.
  763. //
  764. for ( ListEntry = CredentialSets->SessionCredSets->TargetInfoHashTable[Index].Flink ;
  765. ListEntry != &CredentialSets->SessionCredSets->TargetInfoHashTable[Index];
  766. ListEntry = ListEntry->Flink) {
  767. ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, HashNext );
  768. if ( RtlEqualUnicodeString( &TargetNameString,
  769. &ExistingTargetInfo->TargetName,
  770. TRUE ) ) {
  771. break;
  772. }
  773. ExistingTargetInfo = NULL;
  774. }
  775. return ExistingTargetInfo;
  776. }
  777. PCREDENTIAL_SET
  778. CredpAllocateCredSet(
  779. VOID
  780. )
  781. /*++
  782. Routine Description:
  783. Allocates and initializes a CREDENTIAL set
  784. Arguments:
  785. None
  786. Return Values:
  787. Returns the allocated credential set.
  788. The caller must dereference this credential set using CredpDereferenceCredSet.
  789. Return NULL if the memory cannot be allocated.
  790. --*/
  791. {
  792. PCREDENTIAL_SET TempCredentialSet;
  793. //
  794. // Allocate the credential set.
  795. //
  796. TempCredentialSet = (PCREDENTIAL_SET) LsapAllocateLsaHeap( sizeof(CREDENTIAL_SET) );
  797. if ( TempCredentialSet == NULL ) {
  798. return NULL;
  799. }
  800. //
  801. // Fill it in
  802. //
  803. TempCredentialSet->ReferenceCount = 1;
  804. InitializeListHead( &TempCredentialSet->Credentials );
  805. TempCredentialSet->Dirty = FALSE;
  806. TempCredentialSet->BeingWritten = FALSE;
  807. TempCredentialSet->WriteCount = 0;
  808. return TempCredentialSet;
  809. }
  810. NTSTATUS
  811. CredpCreateCredSets(
  812. IN PSID UserSid,
  813. IN PUNICODE_STRING NetbiosDomainName,
  814. OUT PCREDENTIAL_SETS CredentialSets
  815. )
  816. /*++
  817. Routine Description:
  818. Create the credential sets for a user with the specified SID.
  819. If a credential set already exists for the specified user, the existing cred set is used.
  820. Arguments:
  821. UserSid - User sid of the user to create a credential set for.
  822. NetbiosDomainName - Specifies the netbios domain name of the user to create the
  823. credential set for.
  824. CredentialSets - Returns a pointer the various credential sets.
  825. The caller must dereference this credential set using CredpDereferenceCredSets.
  826. Return Values:
  827. The following status codes may be returned:
  828. --*/
  829. {
  830. NTSTATUS Status;
  831. PLIST_ENTRY ListEntry;
  832. ULONG i;
  833. WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  834. DWORD ComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
  835. UNICODE_STRING ComputerNameString;
  836. //
  837. // Initialization
  838. //
  839. RtlEnterCriticalSection( &CredentialSetListLock );
  840. RtlZeroMemory( CredentialSets, sizeof(*CredentialSets) );
  841. //
  842. // Allocate a session specific credential structure
  843. //
  844. CredentialSets->SessionCredSets = (PSESSION_CREDENTIAL_SETS) LsapAllocateLsaHeap( sizeof(SESSION_CREDENTIAL_SETS) );
  845. if ( CredentialSets->SessionCredSets == NULL ) {
  846. Status = STATUS_NO_MEMORY;
  847. goto Cleanup;
  848. }
  849. //
  850. // Initialize it
  851. //
  852. CredentialSets->SessionCredSets->ReferenceCount = 1;
  853. for ( i=0; i<CRED_TARGET_INFO_HASH_TABLE_SIZE; i++ ) {
  854. InitializeListHead( &CredentialSets->SessionCredSets->TargetInfoHashTable[i] );
  855. }
  856. CredentialSets->SessionCredSets->TargetInfoCount = 0;
  857. InitializeListHead( &CredentialSets->SessionCredSets->TargetInfoLruList );
  858. InitializeListHead( &CredentialSets->SessionCredSets->PromptData );
  859. CredentialSets->SessionCredSets->ProfileLoaded = FALSE;
  860. //
  861. // Allocate a session credential set
  862. //
  863. CredentialSets->SessionCredSets->SessionCredSet = CredpAllocateCredSet();
  864. if ( CredentialSets->SessionCredSets->SessionCredSet == NULL ) {
  865. Status = STATUS_NO_MEMORY;
  866. goto Cleanup;
  867. }
  868. //
  869. // Loop through the list of loaded user credential sets trying to find this one
  870. //
  871. for ( ListEntry = CredentialSetList.Flink ;
  872. ListEntry != &CredentialSetList;
  873. ListEntry = ListEntry->Flink) {
  874. CredentialSets->UserCredentialSets = CONTAINING_RECORD( ListEntry, USER_CREDENTIAL_SETS, Next );
  875. if ( RtlEqualSid( UserSid,
  876. CredentialSets->UserCredentialSets->UserSid ) ) {
  877. CredentialSets->UserCredentialSets->ReferenceCount ++;
  878. break;
  879. }
  880. CredentialSets->UserCredentialSets = NULL;
  881. }
  882. //
  883. // If we didn't find one,
  884. // allocate a new one.
  885. //
  886. if ( CredentialSets->UserCredentialSets == NULL ) {
  887. ULONG UserSidSize;
  888. LPBYTE Where;
  889. PUSER_CREDENTIAL_SETS TempUserCredentialSets;
  890. //
  891. // Allocate a user credential set
  892. //
  893. UserSidSize = RtlLengthSid( UserSid );
  894. TempUserCredentialSets = (PUSER_CREDENTIAL_SETS) LsapAllocateLsaHeap(
  895. sizeof(USER_CREDENTIAL_SETS) + UserSidSize );
  896. if ( TempUserCredentialSets == NULL ) {
  897. Status = STATUS_NO_MEMORY;
  898. goto Cleanup;
  899. }
  900. //
  901. // Initialize constant fields
  902. //
  903. TempUserCredentialSets->ReferenceCount = 1;
  904. InitializeListHead( &TempUserCredentialSets->Next );
  905. TempUserCredentialSets->EnterpriseCredSet = NULL;
  906. TempUserCredentialSets->LocalMachineCredSet = NULL;
  907. Where = (LPBYTE)(TempUserCredentialSets+1);
  908. TempUserCredentialSets->UserSid = Where;
  909. RtlCopyMemory( Where, UserSid, UserSidSize );
  910. //
  911. // Initialize the crit sect
  912. //
  913. Status = RtlInitializeCriticalSection( &TempUserCredentialSets->CritSect );
  914. if ( !NT_SUCCESS(Status) ) {
  915. LsapFreeLsaHeap( TempUserCredentialSets );
  916. goto Cleanup;
  917. }
  918. //
  919. // We've initialized to the point that common cleanup can work.
  920. //
  921. CredentialSets->UserCredentialSets = TempUserCredentialSets;
  922. //
  923. // Allocate the credential sets that hang off this structure
  924. //
  925. TempUserCredentialSets->EnterpriseCredSet = CredpAllocateCredSet();
  926. if ( TempUserCredentialSets->EnterpriseCredSet == NULL ) {
  927. Status = STATUS_NO_MEMORY;
  928. goto Cleanup;
  929. }
  930. TempUserCredentialSets->LocalMachineCredSet = CredpAllocateCredSet();
  931. if ( TempUserCredentialSets->LocalMachineCredSet == NULL ) {
  932. Status = STATUS_NO_MEMORY;
  933. goto Cleanup;
  934. }
  935. //
  936. // Insert the credential set into the global list.
  937. //
  938. // Being in the global list doesn't constitute a reference.
  939. //
  940. InsertHeadList( &CredentialSetList, &TempUserCredentialSets->Next );
  941. }
  942. //
  943. // Determine if the user is logging onto a local account
  944. //
  945. if ( !GetComputerName( ComputerName, &ComputerNameSize ) ) {
  946. Status = STATUS_NO_MEMORY;
  947. goto Cleanup;
  948. }
  949. RtlInitUnicodeString( &ComputerNameString, ComputerName );
  950. if ( RtlEqualUnicodeString( NetbiosDomainName,
  951. &ComputerNameString,
  952. TRUE ) ) {
  953. CredentialSets->Flags |= CREDSETS_FLAGS_LOCAL_ACCOUNT;
  954. }
  955. Status = STATUS_SUCCESS;
  956. Cleanup:
  957. if ( !NT_SUCCESS(Status) ) {
  958. CredpDereferenceCredSets( CredentialSets );
  959. }
  960. RtlLeaveCriticalSection( &CredentialSetListLock );
  961. return Status;
  962. }
  963. NTSTATUS
  964. CredpReferenceCredSets(
  965. IN PLUID LogonId,
  966. OUT PCREDENTIAL_SETS CredentialSets
  967. )
  968. /*++
  969. Routine Description:
  970. This routine references a credential sets for the specified LogonId.
  971. Arguments:
  972. LogonId - LogonId of the session to associate the credential with.
  973. CredentialSet - Returns a pointer to the referenced credential set.
  974. The caller must dereference this credential set using CredpDereferenceCredSets.
  975. Return Values:
  976. The following status codes may be returned:
  977. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  978. there is no credential set associated with this logon session.
  979. Network logon sessions do not have an associated credential set.
  980. --*/
  981. {
  982. NTSTATUS Status;
  983. PLSAP_LOGON_SESSION LogonSession = NULL;
  984. //
  985. // Get the credential set from the logon session.
  986. //
  987. LogonSession = LsapLocateLogonSession( LogonId );
  988. if ( LogonSession == NULL ) {
  989. Status = STATUS_NO_SUCH_LOGON_SESSION;
  990. goto Cleanup;
  991. }
  992. if ( LogonSession->CredentialSets.UserCredentialSets == NULL ) {
  993. Status = STATUS_NO_SUCH_LOGON_SESSION;
  994. goto Cleanup;
  995. }
  996. //
  997. // Grab a copy of the cred set pointers and reference them
  998. //
  999. RtlEnterCriticalSection( &CredentialSetListLock );
  1000. *CredentialSets = LogonSession->CredentialSets;
  1001. CredentialSets->UserCredentialSets->ReferenceCount ++;
  1002. CredentialSets->SessionCredSets->ReferenceCount ++;
  1003. RtlLeaveCriticalSection( &CredentialSetListLock );
  1004. Status = STATUS_SUCCESS;
  1005. Cleanup:
  1006. if ( LogonSession != NULL ) {
  1007. LsapReleaseLogonSession( LogonSession );
  1008. }
  1009. return Status;
  1010. }
  1011. VOID
  1012. CredpDereferenceCredSet(
  1013. IN PCREDENTIAL_SET CredentialSet
  1014. )
  1015. /*++
  1016. Routine Description:
  1017. This routine dereferences a credential set.
  1018. Arguments:
  1019. CredentialSet - A pointer to the referenced credential set.
  1020. Return Values:
  1021. None
  1022. --*/
  1023. {
  1024. //
  1025. // Dereference the credential set.
  1026. //
  1027. RtlEnterCriticalSection( &CredentialSetListLock );
  1028. CredentialSet->ReferenceCount --;
  1029. if ( CredentialSet->ReferenceCount == 0 ) {
  1030. //
  1031. // Uninitialize it
  1032. //
  1033. while ( !IsListEmpty( &CredentialSet->Credentials) ) {
  1034. PLIST_ENTRY ListEntry;
  1035. PCANONICAL_CREDENTIAL TempCredential;
  1036. ListEntry = RemoveHeadList( &CredentialSet->Credentials );
  1037. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  1038. LsapFreeLsaHeap( TempCredential );
  1039. }
  1040. //
  1041. // Free the entry.
  1042. //
  1043. LsapFreeLsaHeap( CredentialSet );
  1044. }
  1045. RtlLeaveCriticalSection( &CredentialSetListLock );
  1046. return;
  1047. }
  1048. VOID
  1049. CredpDereferenceUserCredSets(
  1050. IN PUSER_CREDENTIAL_SETS UserCredentialSets
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. This routine dereferences a user credential set.
  1055. Arguments:
  1056. UserCredentialSets - A pointer to the referenced user credential set.
  1057. Return Values:
  1058. None
  1059. --*/
  1060. {
  1061. //
  1062. // Dereference the credential set.
  1063. //
  1064. RtlEnterCriticalSection( &CredentialSetListLock );
  1065. UserCredentialSets->ReferenceCount --;
  1066. if ( UserCredentialSets->ReferenceCount == 0 ) {
  1067. //
  1068. // Remove the entry from the global list.
  1069. //
  1070. RemoveEntryList( &UserCredentialSets->Next );
  1071. //
  1072. // Uninitialize it
  1073. //
  1074. if ( UserCredentialSets->EnterpriseCredSet != NULL ) {
  1075. CredpDereferenceCredSet( UserCredentialSets->EnterpriseCredSet );
  1076. }
  1077. if ( UserCredentialSets->LocalMachineCredSet != NULL ) {
  1078. CredpDereferenceCredSet( UserCredentialSets->LocalMachineCredSet );
  1079. }
  1080. (VOID) RtlDeleteCriticalSection( &UserCredentialSets->CritSect );
  1081. //
  1082. // Free the entry.
  1083. //
  1084. LsapFreeLsaHeap( UserCredentialSets );
  1085. }
  1086. RtlLeaveCriticalSection( &CredentialSetListLock );
  1087. return;
  1088. }
  1089. VOID
  1090. CredpDereferenceSessionCredSets(
  1091. IN PSESSION_CREDENTIAL_SETS SessionCredentialSets
  1092. )
  1093. /*++
  1094. Routine Description:
  1095. This routine dereferences a session credential set.
  1096. Arguments:
  1097. SessionCredentialSets - A pointer to the referenced session credential set.
  1098. Return Values:
  1099. None
  1100. --*/
  1101. {
  1102. PLIST_ENTRY ListEntry;
  1103. //
  1104. // Dereference the credential set.
  1105. //
  1106. RtlEnterCriticalSection( &CredentialSetListLock );
  1107. SessionCredentialSets->ReferenceCount --;
  1108. if ( SessionCredentialSets->ReferenceCount == 0 ) {
  1109. //
  1110. // Uninitialize it
  1111. //
  1112. if ( SessionCredentialSets->SessionCredSet != NULL ) {
  1113. CredpDereferenceCredSet( SessionCredentialSets->SessionCredSet );
  1114. }
  1115. //
  1116. // Free Target Info Cache
  1117. //
  1118. while ( SessionCredentialSets->TargetInfoCount > 0 ) {
  1119. PCANONICAL_TARGET_INFO ExistingTargetInfo;
  1120. ListEntry = RemoveTailList( &SessionCredentialSets->TargetInfoLruList );
  1121. ExistingTargetInfo = CONTAINING_RECORD( ListEntry, CANONICAL_TARGET_INFO, LruNext );
  1122. RemoveEntryList( &ExistingTargetInfo->HashNext );
  1123. LsapFreeLsaHeap( ExistingTargetInfo );
  1124. SessionCredentialSets->TargetInfoCount --;
  1125. }
  1126. //
  1127. // Free the prompt data
  1128. //
  1129. while ( !IsListEmpty( &SessionCredentialSets->PromptData ) ) {
  1130. PPROMPT_DATA PromptData;
  1131. ListEntry = RemoveHeadList( &SessionCredentialSets->PromptData );
  1132. PromptData = CONTAINING_RECORD( ListEntry, PROMPT_DATA, Next );
  1133. LsapFreeLsaHeap( PromptData );
  1134. }
  1135. //
  1136. // Free the entry.
  1137. //
  1138. LsapFreeLsaHeap( SessionCredentialSets );
  1139. }
  1140. RtlLeaveCriticalSection( &CredentialSetListLock );
  1141. return;
  1142. }
  1143. VOID
  1144. CredpDereferenceCredSets(
  1145. IN PCREDENTIAL_SETS CredentialSets
  1146. )
  1147. /*++
  1148. Routine Description:
  1149. This routine dereferences a set of credential sets.
  1150. Arguments:
  1151. CredentialSets - A pointer to the referenced credential sets.
  1152. Return Values:
  1153. None
  1154. --*/
  1155. {
  1156. //
  1157. // Dereference the User wide credential sets
  1158. //
  1159. if ( CredentialSets->UserCredentialSets != NULL ) {
  1160. CredpDereferenceUserCredSets( CredentialSets->UserCredentialSets );
  1161. CredentialSets->UserCredentialSets = NULL;
  1162. }
  1163. //
  1164. // Dereference the session specific credential sets
  1165. //
  1166. if ( CredentialSets->SessionCredSets != NULL ) {
  1167. CredpDereferenceSessionCredSets( CredentialSets->SessionCredSets );
  1168. CredentialSets->SessionCredSets = NULL;
  1169. }
  1170. }
  1171. BOOLEAN
  1172. CredpValidateBuffer(
  1173. IN LPBYTE Buffer OPTIONAL,
  1174. IN ULONG BufferSize,
  1175. IN ULONG MaximumSize,
  1176. IN BOOLEAN NullOk,
  1177. IN ULONG Alignment
  1178. )
  1179. /*++
  1180. Routine Description:
  1181. This routine validates a passed in Buffer
  1182. Arguments:
  1183. Buffer - Buffer to validate
  1184. BufferSize - Size of the buffer in bytes
  1185. MaximumSize - Maximum size of the buffer (in bytes).
  1186. NullOk - if TRUE, a NULL Buffer is OK.
  1187. Alignment - Specifies the alignment requirements of the buffer size.
  1188. Return Values:
  1189. TRUE - Buffer is valid.
  1190. FALSE - Buffer is not valid.
  1191. --*/
  1192. {
  1193. if ( Buffer == NULL ) {
  1194. if ( BufferSize != 0 ) {
  1195. return FALSE;
  1196. }
  1197. if ( !NullOk ) {
  1198. return FALSE;
  1199. }
  1200. return TRUE;
  1201. }
  1202. if ( BufferSize == 0 ) {
  1203. return FALSE;
  1204. }
  1205. if ( BufferSize > MaximumSize ) {
  1206. return FALSE;
  1207. }
  1208. if ( BufferSize != ROUND_UP_COUNT(BufferSize, Alignment) ) {
  1209. return FALSE;
  1210. }
  1211. return TRUE;
  1212. }
  1213. BOOLEAN
  1214. CredpCompareCredToTargetInfo(
  1215. IN PCANONICAL_TARGET_INFO TargetInfo,
  1216. IN PCANONICAL_CREDENTIAL Credential,
  1217. OUT PULONG AliasIndex
  1218. )
  1219. /*++
  1220. Routine Description:
  1221. This routine determines if the specified credential matches the
  1222. specified target info.
  1223. Arguments:
  1224. TargetInfo - A description of the various aliases for a target.
  1225. Credential - Pointer to the credential to match.
  1226. AliasIndex - On success, this parameter returns an index
  1227. indicating which target alias matched the credential.
  1228. Return Values:
  1229. TRUE if the credential matches the target info.
  1230. --*/
  1231. {
  1232. ULONG Index;
  1233. BOOLEAN DnsCompareFailed = FALSE;
  1234. //
  1235. // If the credential isn't a domain credential,
  1236. // it doesn't match;
  1237. //
  1238. if ( !CredpIsDomainCredential(Credential->Cred.Type) ) {
  1239. return FALSE;
  1240. }
  1241. //
  1242. // Make sure that UserNameTarget credentials only match
  1243. // UserNameTarget requests (and vice versa)
  1244. //
  1245. if ( Credential->WildcardType == WcUserName ) {
  1246. if ( (TargetInfo->Flags & CRED_TI_USERNAME_TARGET) == 0 ) {
  1247. return FALSE;
  1248. }
  1249. } else {
  1250. if ( (TargetInfo->Flags & CRED_TI_USERNAME_TARGET) != 0 ) {
  1251. return FALSE;
  1252. }
  1253. }
  1254. //
  1255. // If the target info specifies a list of valid cred types,
  1256. // only return a credential that matches the list.
  1257. //
  1258. if ( TargetInfo->CredTypeCount ) {
  1259. ULONG i;
  1260. for ( i=0; i<TargetInfo->CredTypeCount; i++ ) {
  1261. if ( TargetInfo->CredTypes[i] == Credential->Cred.Type ) {
  1262. break;
  1263. }
  1264. }
  1265. //
  1266. // If the cred doesn't match any of the valid cred types,
  1267. // ignore it.
  1268. //
  1269. if ( i == TargetInfo->CredTypeCount ) {
  1270. return FALSE;
  1271. }
  1272. }
  1273. //
  1274. // Handle credentials that specify DFS share names
  1275. //
  1276. switch (Credential->WildcardType ) {
  1277. case WcDfsShareName:
  1278. //
  1279. // Determine if the target info is for the named dfs share
  1280. //
  1281. if ( TargetInfo->TargetName.Length != 0 &&
  1282. RtlEqualUnicodeString( &TargetInfo->TargetName,
  1283. &Credential->TargetName,
  1284. TRUE ) ) {
  1285. *AliasIndex = CRED_DFS_SHARE_NAME;
  1286. return TRUE;
  1287. }
  1288. break;
  1289. //
  1290. // Handle credentials that are for a specific server
  1291. //
  1292. case WcServerName:
  1293. //
  1294. // Compare the DNS server name
  1295. //
  1296. if ( TargetInfo->DnsServerName.Length != 0 ) {
  1297. //
  1298. // Do an exact comparison.
  1299. //
  1300. // ??? NonWildcardedTargetName should probably be TargetName
  1301. if ( RtlEqualUnicodeString( &TargetInfo->DnsServerName,
  1302. &Credential->NonWildcardedTargetName,
  1303. TRUE ) ) {
  1304. *AliasIndex = CRED_DNS_SERVER_NAME;
  1305. return TRUE;
  1306. }
  1307. DnsCompareFailed = TRUE;
  1308. //
  1309. // If a netbios alias is specified,
  1310. // and we don't know the format of the target info name,
  1311. // compare it.
  1312. //
  1313. if ( Credential->TargetAlias.Length != 0 &&
  1314. (TargetInfo->Flags & CRED_TI_SERVER_FORMAT_UNKNOWN) != 0 ) {
  1315. if ( RtlEqualUnicodeString( &TargetInfo->DnsServerName,
  1316. &Credential->TargetAlias,
  1317. TRUE ) ) {
  1318. *AliasIndex = CRED_NETBIOS_SERVER_NAME;
  1319. return TRUE;
  1320. }
  1321. }
  1322. }
  1323. //
  1324. // Compare the netbios server name
  1325. //
  1326. if ( TargetInfo->NetbiosServerName.Length != 0 ) {
  1327. //
  1328. // If no alias is specified,
  1329. // the TargetName might be the netbios name.
  1330. //
  1331. if ( Credential->TargetAlias.Length == 0 ) {
  1332. // ??? NonWildcardedTargetName should probably be TargetName
  1333. if ( RtlEqualUnicodeString( &TargetInfo->NetbiosServerName,
  1334. &Credential->NonWildcardedTargetName,
  1335. TRUE ) ) {
  1336. *AliasIndex = CRED_NETBIOS_SERVER_NAME;
  1337. return TRUE;
  1338. }
  1339. //
  1340. // If an alias is specified,
  1341. // it is always the netbios name.
  1342. //
  1343. // (Don't compare the alias if the more specific comparision failed.)
  1344. //
  1345. } else if ( !DnsCompareFailed ) {
  1346. if ( RtlEqualUnicodeString( &TargetInfo->NetbiosServerName,
  1347. &Credential->TargetAlias,
  1348. TRUE ) ) {
  1349. *AliasIndex = CRED_NETBIOS_SERVER_NAME;
  1350. return TRUE;
  1351. }
  1352. }
  1353. }
  1354. //
  1355. // Compare the target name if it is different than the Netbios or DNS name already compared
  1356. //
  1357. if ( Credential->WildcardType == WcServerName ) {
  1358. if ( TargetInfo->TargetName.Length != 0 &&
  1359. !RtlEqualUnicodeString( &TargetInfo->TargetName,
  1360. &TargetInfo->DnsServerName,
  1361. TRUE ) &&
  1362. !RtlEqualUnicodeString( &TargetInfo->TargetName,
  1363. &TargetInfo->NetbiosServerName,
  1364. TRUE ) ) {
  1365. //
  1366. // The TargetName might be the netbios name or DNS name
  1367. //
  1368. if ( RtlEqualUnicodeString( &TargetInfo->TargetName,
  1369. &Credential->TargetName,
  1370. TRUE ) ) {
  1371. *AliasIndex = CRED_TARGET_NAME;
  1372. return TRUE;
  1373. }
  1374. if ( RtlEqualUnicodeString( &TargetInfo->TargetName,
  1375. &Credential->TargetAlias,
  1376. TRUE ) ) {
  1377. *AliasIndex = CRED_TARGET_NAME;
  1378. return TRUE;
  1379. }
  1380. }
  1381. }
  1382. break;
  1383. //
  1384. // Handle the server wildcard case
  1385. //
  1386. // If the TargetName is of the form *.xxx.yyy,
  1387. // compare equal if the DnsServerName ends in .xxx.yyy.
  1388. //
  1389. // The comparision includes the . to ensure *.xxx.yyy doesn't match
  1390. // fredxxx.yyy
  1391. //
  1392. case WcServerWildcard:
  1393. //
  1394. // Compare the DNS server name
  1395. //
  1396. if ( TargetInfo->DnsServerName.Length != 0 ) {
  1397. UNICODE_STRING LocalTargetName = Credential->NonWildcardedTargetName;
  1398. UNICODE_STRING LocalServerName = TargetInfo->DnsServerName;
  1399. //
  1400. // Build a server name without the leading characters
  1401. //
  1402. if ( LocalTargetName.Length < LocalServerName.Length ) {
  1403. DWORD TrimAmount = LocalServerName.Length - LocalTargetName.Length;
  1404. LocalServerName.Length = LocalTargetName.Length;
  1405. LocalServerName.MaximumLength = LocalTargetName.Length;
  1406. LocalServerName.Buffer += TrimAmount/sizeof(WCHAR);
  1407. //
  1408. // Compare the names
  1409. //
  1410. if ( RtlEqualUnicodeString( &LocalServerName,
  1411. &LocalTargetName,
  1412. TRUE ) ) {
  1413. *AliasIndex = CRED_WILDCARD_SERVER_NAME;
  1414. return TRUE;
  1415. }
  1416. }
  1417. }
  1418. break;
  1419. //
  1420. // Handle the domain wildcard case.
  1421. //
  1422. // If the target names is of the form <Domain>\*,
  1423. // compare equal if Netbios or Dns domain name is <Domain>
  1424. //
  1425. case WcDomainWildcard: {
  1426. UNICODE_STRING LocalTargetName;
  1427. UNICODE_STRING LocalTargetAlias;
  1428. //
  1429. // Build a target name without the \*
  1430. //
  1431. LocalTargetName = Credential->NonWildcardedTargetName;
  1432. LocalTargetAlias = Credential->TargetAlias;
  1433. if ( LocalTargetAlias.Length > 2 * sizeof(WCHAR) &&
  1434. LocalTargetAlias.Buffer[(LocalTargetAlias.Length/sizeof(WCHAR))-1] == L'*' &&
  1435. LocalTargetAlias.Buffer[(LocalTargetAlias.Length/sizeof(WCHAR))-2] == L'\\' ) {
  1436. LocalTargetAlias.Length -= 2 * sizeof(WCHAR);
  1437. }
  1438. //
  1439. // Compare the DNS domain name
  1440. //
  1441. if ( TargetInfo->DnsDomainName.Length != 0 ) {
  1442. if ( RtlEqualUnicodeString( &TargetInfo->DnsDomainName,
  1443. &LocalTargetName,
  1444. TRUE ) ) {
  1445. *AliasIndex = CRED_DNS_DOMAIN_NAME;
  1446. return TRUE;
  1447. }
  1448. DnsCompareFailed = TRUE;
  1449. //
  1450. // If a netbios alias is specified,
  1451. // and we don't know the format of the target info name,
  1452. // compare it.
  1453. //
  1454. if ( LocalTargetAlias.Length != 0 &&
  1455. (TargetInfo->Flags & CRED_TI_DOMAIN_FORMAT_UNKNOWN) != 0 ) {
  1456. if ( RtlEqualUnicodeString( &TargetInfo->DnsDomainName,
  1457. &LocalTargetAlias,
  1458. TRUE ) ) {
  1459. *AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
  1460. return TRUE;
  1461. }
  1462. }
  1463. }
  1464. //
  1465. // Compare the netbios domain name
  1466. //
  1467. if ( TargetInfo->NetbiosDomainName.Length != 0 ) {
  1468. //
  1469. // If no alias is specified,
  1470. // the TargetName might be the netbios name.
  1471. //
  1472. if ( LocalTargetAlias.Length == 0 ) {
  1473. if ( RtlEqualUnicodeString( &TargetInfo->NetbiosDomainName,
  1474. &LocalTargetName,
  1475. TRUE ) ) {
  1476. *AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
  1477. return TRUE;
  1478. }
  1479. //
  1480. // If an alias is specified,
  1481. // it is always the netbios name.
  1482. //
  1483. // (Don't compare the alias if the more specific comparision failed.)
  1484. //
  1485. } else if ( !DnsCompareFailed ) {
  1486. if ( RtlEqualUnicodeString( &TargetInfo->NetbiosDomainName,
  1487. &LocalTargetAlias,
  1488. TRUE ) ) {
  1489. *AliasIndex = CRED_NETBIOS_DOMAIN_NAME;
  1490. return TRUE;
  1491. }
  1492. }
  1493. }
  1494. break;
  1495. }
  1496. //
  1497. // Handle the * wildcard case.
  1498. //
  1499. case WcUniversalWildcard:
  1500. *AliasIndex = CRED_UNIVERSAL_NAME;
  1501. return TRUE;
  1502. //
  1503. // Handle the * wildcard case.
  1504. //
  1505. case WcUniversalSessionWildcard:
  1506. *AliasIndex = CRED_UNIVERSAL_SESSION_NAME;
  1507. return TRUE;
  1508. //
  1509. // Handles creds that have a user name as the target name
  1510. //
  1511. case WcUserName:
  1512. //
  1513. // Determine if the target info is for this username
  1514. //
  1515. if ( TargetInfo->TargetName.Length != 0 &&
  1516. RtlEqualUnicodeString( &TargetInfo->TargetName,
  1517. &Credential->TargetName,
  1518. TRUE ) ) {
  1519. *AliasIndex = CRED_TARGET_NAME;
  1520. return TRUE;
  1521. }
  1522. break;
  1523. //
  1524. // Catch coding errors
  1525. //
  1526. default:
  1527. ASSERT( FALSE );
  1528. break;
  1529. }
  1530. return FALSE;
  1531. }
  1532. NTSTATUS
  1533. CredpLogonCredsMatchTargetInfo(
  1534. IN PLUID LogonId,
  1535. IN PCANONICAL_TARGET_INFO TargetInfo
  1536. )
  1537. /*++
  1538. Routine Description:
  1539. This routine determines if the specified credential matches the
  1540. specified target info.
  1541. Arguments:
  1542. LogonId - LogonId of the session to check
  1543. TargetInfo - A description of the various aliases for a target.
  1544. Return Values:
  1545. STATUS_SUCCESS - Logon creds match target info
  1546. STATUS_NO_MATCH - Logon creds don't match target info
  1547. Otherwise, fatal error
  1548. --*/
  1549. {
  1550. NTSTATUS Status;
  1551. PLSAP_LOGON_SESSION LogonSession = NULL;
  1552. ULONG AliasIndex;
  1553. CANONICAL_CREDENTIAL CanonicalCredential;
  1554. LPWSTR DottedDnsDomainName = NULL;
  1555. PLSAP_DS_NAME_MAP DnsDomainMap = NULL;
  1556. //
  1557. // If the target machine is a member of a workgroup,
  1558. // never use the *Session cred.
  1559. if ( TargetInfo->Flags & CRED_TI_WORKGROUP_MEMBER ) {
  1560. DebugLog((DEB_TRACE_CRED,
  1561. "*Session: %wZ: not used on workgroup member.\n",
  1562. &TargetInfo->TargetName ));
  1563. Status = STATUS_SUCCESS;
  1564. goto Cleanup;
  1565. }
  1566. //
  1567. // Get the credential set from the logon session.
  1568. //
  1569. LogonSession = LsapLocateLogonSession( LogonId );
  1570. if ( LogonSession == NULL ) {
  1571. Status = STATUS_NO_SUCH_LOGON_SESSION;
  1572. goto Cleanup;
  1573. }
  1574. //
  1575. // Get the DnsDomainName for the logon session. Do not go
  1576. // off-machine if it's not in the cache.
  1577. //
  1578. Status = LsapGetNameForLogonSession(
  1579. LogonSession,
  1580. NameDnsDomain,
  1581. &DnsDomainMap,
  1582. TRUE );
  1583. if ( !NT_SUCCESS(Status) ) {
  1584. DnsDomainMap = NULL;
  1585. } else {
  1586. if ( DnsDomainMap->Name.Length == 0 ) {
  1587. LsapDerefDsNameMap( DnsDomainMap );
  1588. DnsDomainMap = NULL;
  1589. }
  1590. }
  1591. //
  1592. // Check if a *.<DnsDomainName> credential matches the target info
  1593. //
  1594. if ( DnsDomainMap != NULL ) {
  1595. //
  1596. // Clear the cred
  1597. //
  1598. RtlZeroMemory( &CanonicalCredential, sizeof(CanonicalCredential) );
  1599. CanonicalCredential.Cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
  1600. CanonicalCredential.WildcardType = WcServerWildcard;
  1601. //
  1602. // Allocate space on the stack
  1603. //
  1604. SafeAllocaAllocate( DottedDnsDomainName,
  1605. DnsDomainMap->Name.Length + (3*sizeof(WCHAR)) );
  1606. if ( DottedDnsDomainName == NULL ) {
  1607. Status = STATUS_NO_MEMORY;
  1608. goto Cleanup;
  1609. }
  1610. //
  1611. // Build *.<DnsDomainName>
  1612. //
  1613. RtlCopyMemory( DottedDnsDomainName,
  1614. L"*.",
  1615. 2*sizeof(WCHAR) );
  1616. RtlCopyMemory( DottedDnsDomainName+2,
  1617. DnsDomainMap->Name.Buffer,
  1618. DnsDomainMap->Name.Length );
  1619. DottedDnsDomainName[(DnsDomainMap->Name.Length/sizeof(WCHAR))+2] = L'\0';
  1620. //
  1621. // Fill in the Canonical cred
  1622. //
  1623. RtlInitUnicodeString( &CanonicalCredential.TargetName, DottedDnsDomainName );
  1624. CanonicalCredential.NonWildcardedTargetName.Buffer =
  1625. CanonicalCredential.TargetName.Buffer + 1;
  1626. CanonicalCredential.NonWildcardedTargetName.Length =
  1627. CanonicalCredential.TargetName.Length - sizeof(WCHAR);
  1628. CanonicalCredential.NonWildcardedTargetName.MaximumLength =
  1629. CanonicalCredential.TargetName.MaximumLength - sizeof(WCHAR);
  1630. //
  1631. // See if the cred matches the target info
  1632. //
  1633. if ( CredpCompareCredToTargetInfo(
  1634. TargetInfo,
  1635. &CanonicalCredential,
  1636. &AliasIndex ) ) {
  1637. DebugLog((DEB_TRACE_CRED,
  1638. "*Session: %wZ: %ws: not used on wildcard server match.\n",
  1639. &TargetInfo->TargetName,
  1640. DottedDnsDomainName ));
  1641. Status = STATUS_SUCCESS;
  1642. goto Cleanup;
  1643. }
  1644. }
  1645. //
  1646. // Check if a <DomainName>\* credential matches the target info
  1647. //
  1648. if ( LogonSession->AuthorityName.Length != 0 ) {
  1649. //
  1650. // Clear the cred
  1651. //
  1652. RtlZeroMemory( &CanonicalCredential, sizeof(CanonicalCredential) );
  1653. CanonicalCredential.Cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
  1654. CanonicalCredential.WildcardType = WcDomainWildcard;
  1655. //
  1656. // If there is a DNS domain name too,
  1657. // build a cred with an alias
  1658. //
  1659. if ( DnsDomainMap != NULL ) {
  1660. //
  1661. // The primary target name is the DNS domain name
  1662. //
  1663. CanonicalCredential.NonWildcardedTargetName = DnsDomainMap->Name;
  1664. //
  1665. // The alias is the netbios domain name
  1666. //
  1667. CanonicalCredential.TargetAlias = LogonSession->AuthorityName;
  1668. //
  1669. // If there is no DNS domain name,
  1670. // just use the netbios domain name.
  1671. //
  1672. } else {
  1673. CanonicalCredential.NonWildcardedTargetName = LogonSession->AuthorityName;
  1674. }
  1675. //
  1676. // See if the cred matches the target info
  1677. //
  1678. if ( CredpCompareCredToTargetInfo(
  1679. TargetInfo,
  1680. &CanonicalCredential,
  1681. &AliasIndex ) ) {
  1682. DebugLog((DEB_TRACE_CRED,
  1683. "*Session: %wZ: %wZ: not used on wildcard domain match.\n",
  1684. &TargetInfo->TargetName,
  1685. &CanonicalCredential.NonWildcardedTargetName ));
  1686. Status = STATUS_SUCCESS;
  1687. goto Cleanup;
  1688. }
  1689. }
  1690. Status = STATUS_NO_MATCH;
  1691. Cleanup:
  1692. if ( DnsDomainMap != NULL ) {
  1693. LsapDerefDsNameMap( DnsDomainMap );
  1694. }
  1695. if ( LogonSession != NULL ) {
  1696. LsapReleaseLogonSession( LogonSession );
  1697. }
  1698. if ( DottedDnsDomainName != NULL ) {
  1699. SafeAllocaFree( DottedDnsDomainName );
  1700. }
  1701. return Status;
  1702. }
  1703. NTSTATUS
  1704. CredpValidateCredential(
  1705. IN ULONG CredFlags,
  1706. IN PCANONICAL_TARGET_INFO TargetInfo OPTIONAL,
  1707. IN PENCRYPTED_CREDENTIALW EncryptedInputCredential,
  1708. OUT PCANONICAL_CREDENTIAL *ValidatedCredential
  1709. )
  1710. /*++
  1711. Routine Description:
  1712. This routine validates a passed in credential structure. It returns
  1713. a canonicalized version of the credential.
  1714. Arguments:
  1715. CredFlags - Flags changing the behavior of the routine:
  1716. CREDP_FLAGS_IN_PROCESS - Caller is in-process.
  1717. If set, CredentialBlob data is passed in protected via LsapProtectMemory.
  1718. CREDP_FLAGS_CLEAR_PASSWORD - CredentialBlob data is passed in in the clear.
  1719. If neither set, CredentialBlob data is passed in protected via CredpEncodeCredential.
  1720. TargetInfo - Validated Target information that further describes the
  1721. target of the credential.
  1722. EncryptedInputCredential - Specifies the credential to validate.
  1723. ValidatedCredential - Returns a pointer to the canonicalized credential.
  1724. The caller should free this structure by calling LsapFreeLsaHeap.
  1725. Return Values:
  1726. The following status codes may be returned:
  1727. STATUS_INVALID_PARMETER - The input credential is not valid.
  1728. --*/
  1729. {
  1730. NTSTATUS Status;
  1731. DWORD WinStatus;
  1732. PCREDENTIALW InputCredential;
  1733. PCANONICAL_CREDENTIAL ReturnCredential = NULL;
  1734. ULONG TempSize;
  1735. ULONG TargetNameSize;
  1736. ULONG TempTargetNameSize;
  1737. UNICODE_STRING NonWildcardedTargetName;
  1738. ULONG CommentSize;
  1739. ULONG TargetAliasSize;
  1740. LPWSTR TargetAlias;
  1741. LPWSTR AllocatedTargetAlias = NULL;
  1742. ULONG UserNameSize;
  1743. LPWSTR UserName;
  1744. LPWSTR AllocatedUserName = NULL;
  1745. ULONG CredBlobSizeToAlloc;
  1746. ULONG VariableAttributeSize;
  1747. ULONG AllocatedSize;
  1748. WILDCARD_TYPE WildcardType;
  1749. TARGET_NAME_TYPE TargetNameType;
  1750. LPBYTE Where;
  1751. LPBYTE OldWhere;
  1752. ULONG i;
  1753. //
  1754. // Initialization
  1755. //
  1756. RtlInitUnicodeString( &NonWildcardedTargetName, NULL );
  1757. //
  1758. // Validate the pointer itself.
  1759. //
  1760. if ( EncryptedInputCredential == NULL ) {
  1761. Status = STATUS_INVALID_PARAMETER;
  1762. DebugLog((DEB_TRACE_CRED, "ValidateCredential: credential NULL\n" ));
  1763. goto Cleanup;
  1764. }
  1765. InputCredential = &EncryptedInputCredential->Cred;
  1766. //
  1767. // Validate flags
  1768. // (Flags may indicate presence of other fields.)
  1769. //
  1770. if ( (InputCredential->Flags & ~CRED_FLAGS_VALID_FLAGS) != 0 ) {
  1771. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid flags: 0x%lx\n", InputCredential->TargetName, InputCredential->Flags ));
  1772. Status = STATUS_INVALID_PARAMETER;
  1773. goto Cleanup;
  1774. }
  1775. InputCredential->Flags &= ~CRED_FLAGS_PROMPT_NOW; // Ignore "prompt now" bit on input
  1776. //
  1777. // Ensure flags are consistent with type.
  1778. //
  1779. if ( InputCredential->Flags & CRED_FLAGS_USERNAME_TARGET ) {
  1780. if ( !CredpIsDomainCredential(InputCredential->Type ) ) {
  1781. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: UsernameTarget flag for non domain credentrial: 0x%ld\n", InputCredential->TargetName, InputCredential->Type ));
  1782. Status = STATUS_INVALID_PARAMETER;
  1783. goto Cleanup;
  1784. }
  1785. TargetNameType = IsUsernameTarget;
  1786. } else {
  1787. TargetNameType = IsNotUsernameTarget;
  1788. }
  1789. //
  1790. // Validate the Target Name
  1791. //
  1792. Status = CredpValidateTargetName( InputCredential->TargetName,
  1793. InputCredential->Type,
  1794. TargetNameType,
  1795. &InputCredential->UserName,
  1796. &InputCredential->Persist,
  1797. &TargetNameSize,
  1798. &WildcardType,
  1799. &NonWildcardedTargetName );
  1800. if ( !NT_SUCCESS(Status ) ) {
  1801. goto Cleanup;
  1802. }
  1803. //
  1804. // Validate the contained strings.
  1805. //
  1806. if ( !CredpValidateString( InputCredential->Comment, CRED_MAX_STRING_LENGTH, TRUE, &CommentSize ) ||
  1807. !CredpValidateString( InputCredential->TargetAlias, CRED_MAX_STRING_LENGTH, TRUE, &TargetAliasSize ) ||
  1808. !CredpValidateString( InputCredential->UserName, CRED_MAX_USERNAME_LENGTH, TRUE, &UserNameSize ) ) {
  1809. DebugLog(( DEB_TRACE_CRED,
  1810. "ValidateCredential: %ws: Invalid Comment or TargetAlias or UserName\n",
  1811. InputCredential->TargetName ));
  1812. Status = STATUS_INVALID_PARAMETER;
  1813. goto Cleanup;
  1814. }
  1815. TargetAlias = InputCredential->TargetAlias;
  1816. UserName = InputCredential->UserName;
  1817. //
  1818. // Validate the credential blob.
  1819. // The passed in blob size must be the clear text size or the encrypted text size.
  1820. //
  1821. CredBlobSizeToAlloc = AllocatedCredBlobSize( EncryptedInputCredential->ClearCredentialBlobSize );
  1822. if ( CredFlags & CREDP_FLAGS_CLEAR_PASSWORD ) {
  1823. if ( InputCredential->CredentialBlobSize != EncryptedInputCredential->ClearCredentialBlobSize ) {
  1824. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Bad clear cred blob size %ld %ld\n",
  1825. InputCredential->TargetName,
  1826. InputCredential->CredentialBlobSize,
  1827. EncryptedInputCredential->ClearCredentialBlobSize ));
  1828. Status = STATUS_INVALID_PARAMETER;
  1829. goto Cleanup;
  1830. }
  1831. } else {
  1832. if ( InputCredential->CredentialBlobSize != CredBlobSizeToAlloc ) {
  1833. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Bad encrypted cred blob size %ld %ld\n",
  1834. InputCredential->TargetName,
  1835. InputCredential->CredentialBlobSize,
  1836. CredBlobSizeToAlloc ));
  1837. Status = STATUS_INVALID_PARAMETER;
  1838. goto Cleanup;
  1839. }
  1840. }
  1841. if ( !CredpValidateBuffer( InputCredential->CredentialBlob,
  1842. EncryptedInputCredential->ClearCredentialBlobSize,
  1843. CRED_MAX_CREDENTIAL_BLOB_SIZE,
  1844. TRUE,
  1845. InputCredential->Type == CRED_TYPE_GENERIC ?
  1846. ALIGN_BYTE :
  1847. ALIGN_WCHAR ) ) {
  1848. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid credential blob\n", InputCredential->TargetName ));
  1849. Status = STATUS_INVALID_PARAMETER;
  1850. goto Cleanup;
  1851. }
  1852. //
  1853. // Validate the attribute list.
  1854. // Ensure RPC passed us sane information.
  1855. //
  1856. if ( InputCredential->AttributeCount != 0 &&
  1857. InputCredential->Attributes == NULL ) {
  1858. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid attribute buffer\n", InputCredential->TargetName ));
  1859. Status = STATUS_INVALID_PARAMETER;
  1860. goto Cleanup;
  1861. }
  1862. //
  1863. // Ensure there aren't too many attributes
  1864. //
  1865. if ( InputCredential->AttributeCount > CRED_MAX_ATTRIBUTES ) {
  1866. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Too many attributes %ld\n", InputCredential->TargetName, InputCredential->AttributeCount ));
  1867. Status = STATUS_INVALID_PARAMETER;
  1868. goto Cleanup;
  1869. }
  1870. //
  1871. // Validate each attribute.
  1872. //
  1873. VariableAttributeSize = 0;
  1874. for ( i=0; i<InputCredential->AttributeCount; i++ ) {
  1875. if ( !CredpValidateString( InputCredential->Attributes[i].Keyword,
  1876. CRED_MAX_STRING_LENGTH,
  1877. FALSE,
  1878. &TempSize ) ) {
  1879. DebugLog((DEB_TRACE_CRED, "ValidateCredential: %ws: Invalid attribute keyword buffer: %ld\n", InputCredential->TargetName, i ));
  1880. Status = STATUS_INVALID_PARAMETER;
  1881. goto Cleanup;
  1882. }
  1883. VariableAttributeSize += TempSize;
  1884. if ( !CredpValidateBuffer( InputCredential->Attributes[i].Value,
  1885. InputCredential->Attributes[i].ValueSize,
  1886. CRED_MAX_VALUE_SIZE,
  1887. TRUE,
  1888. ALIGN_BYTE ) ) {
  1889. DebugLog(( DEB_TRACE_CRED,
  1890. "ValidateCredential: %ws: %ws: Invalid attribute value buffer: %ld\n",
  1891. InputCredential->TargetName,
  1892. InputCredential->Attributes[i].Keyword,
  1893. i ));
  1894. Status = STATUS_INVALID_PARAMETER;
  1895. goto Cleanup;
  1896. }
  1897. VariableAttributeSize += InputCredential->Attributes[i].ValueSize;
  1898. }
  1899. //
  1900. // Validate the credential type
  1901. //
  1902. // Handle generic credentials
  1903. //
  1904. if ( InputCredential->Type == CRED_TYPE_GENERIC ) {
  1905. /* Drop through */
  1906. //
  1907. // Handle domain credentials
  1908. //
  1909. } else if ( CredpIsDomainCredential(InputCredential->Type) ) {
  1910. LPWSTR RealTargetAlias; // TargetAlias sans wildcard chars
  1911. ULONG RealTargetAliasLength;
  1912. //
  1913. //
  1914. // Domain credentials have a specific Username format
  1915. //
  1916. Status = CredpValidateUserName( UserName, InputCredential->Type, &AllocatedUserName );
  1917. if ( !NT_SUCCESS(Status) ) {
  1918. goto Cleanup;
  1919. }
  1920. // Recompute since CredpValidateUserName canonicalized the name
  1921. UserName = AllocatedUserName;
  1922. UserNameSize = (wcslen( AllocatedUserName ) + 1) * sizeof(WCHAR);
  1923. //
  1924. // Handle where the target alias is specified,
  1925. //
  1926. RealTargetAlias = TargetAlias;
  1927. RealTargetAliasLength = (TargetAliasSize-sizeof(WCHAR))/sizeof(WCHAR);
  1928. if ( TargetAlias != NULL ) {
  1929. //
  1930. // Process alias as a function of Wildcard Type
  1931. //
  1932. switch ( WildcardType ) {
  1933. case WcServerName:
  1934. /* Nothing to do here */
  1935. break;
  1936. case WcDfsShareName:
  1937. case WcServerWildcard:
  1938. case WcUniversalWildcard:
  1939. case WcUniversalSessionWildcard:
  1940. case WcUserName:
  1941. //
  1942. // Server credentials of the form *.xxx.yyy aren't allowed with TargetAliases
  1943. // Otherwise a wildcarded DNS name would have a non-wildcarded netbios name.
  1944. Status = STATUS_INVALID_PARAMETER;
  1945. DebugLog(( DEB_TRACE_CRED,
  1946. "ValidateCredential: %ws: TargetAlias not allowed for server wildcard credential.\n",
  1947. InputCredential->TargetName ));
  1948. goto Cleanup;
  1949. case WcDomainWildcard:
  1950. //
  1951. // If the TargetName is a domain wildcard, so must the TargetAlias
  1952. //
  1953. if ( RealTargetAliasLength > 2 &&
  1954. RealTargetAlias[RealTargetAliasLength-1] == L'*' &&
  1955. RealTargetAlias[RealTargetAliasLength-2] == L'\\' ) {
  1956. //
  1957. // Allocate a buffer for the target alias so we don't have to modify the
  1958. // callers buffer.
  1959. //
  1960. SafeAllocaAllocate( AllocatedTargetAlias, TargetAliasSize );
  1961. if ( AllocatedTargetAlias == NULL ) {
  1962. Status = STATUS_NO_MEMORY;
  1963. goto Cleanup;
  1964. }
  1965. RtlCopyMemory( AllocatedTargetAlias, RealTargetAlias, TargetAliasSize );
  1966. RealTargetAlias = AllocatedTargetAlias;
  1967. RealTargetAliasLength -= 2;
  1968. RealTargetAlias[RealTargetAliasLength] = '\0';
  1969. } else {
  1970. Status = STATUS_INVALID_PARAMETER;
  1971. DebugLog(( DEB_TRACE_CRED,
  1972. "ValidateCredential: %ws: %ws: TargetAlias must be wildcard if TargetName is.\n",
  1973. InputCredential->TargetName,
  1974. TargetAlias ));
  1975. goto Cleanup;
  1976. }
  1977. break;
  1978. }
  1979. //
  1980. // The target alias must be a netbios name.
  1981. //
  1982. if ( !NetpIsDomainNameValid( RealTargetAlias ) ) {
  1983. DebugLog(( DEB_TRACE_CRED,
  1984. "ValidateCredential: %ws: TargetAlias '%ws' must be a netbios name.\n",
  1985. InputCredential->TargetName,
  1986. TargetAlias ));
  1987. Status = STATUS_INVALID_PARAMETER;
  1988. goto Cleanup;
  1989. }
  1990. //
  1991. // The target name must be a DNS name
  1992. //
  1993. if ( !CredpValidateDnsString( NonWildcardedTargetName.Buffer,
  1994. FALSE,
  1995. DnsNameDomain,
  1996. &TempTargetNameSize ) ) {
  1997. Status = STATUS_INVALID_PARAMETER;
  1998. DebugLog(( DEB_TRACE_CRED,
  1999. "ValidateCredential: %ws: TargetName for domain or server must be a DNS name if target alias specified.\n",
  2000. InputCredential->TargetName ));
  2001. goto Cleanup;
  2002. }
  2003. }
  2004. } else {
  2005. DebugLog(( DEB_TRACE_CRED,
  2006. "ValidateCredential: %ws: Type %ld not valid\n",
  2007. InputCredential->TargetName,
  2008. InputCredential->Type ));
  2009. Status = STATUS_INVALID_PARAMETER;
  2010. goto Cleanup;
  2011. }
  2012. //
  2013. // Validate the persistence.
  2014. //
  2015. switch ( InputCredential->Persist ) {
  2016. case CRED_PERSIST_SESSION:
  2017. case CRED_PERSIST_LOCAL_MACHINE:
  2018. case CRED_PERSIST_ENTERPRISE:
  2019. break;
  2020. default:
  2021. DebugLog(( DEB_TRACE_CRED,
  2022. "ValidateCredential: %ws: Invalid persistance: %ld.\n",
  2023. InputCredential->TargetName,
  2024. InputCredential->Persist ));
  2025. Status = STATUS_INVALID_PARAMETER;
  2026. goto Cleanup;
  2027. }
  2028. //
  2029. // Allocate a buffer for the canonicalized credential.
  2030. //
  2031. AllocatedSize = (ULONG)(ROUND_UP_COUNT( sizeof(CANONICAL_CREDENTIAL), ALIGN_WORST) +
  2032. TargetNameSize +
  2033. (NonWildcardedTargetName.Buffer == NULL ?
  2034. 0 :
  2035. NonWildcardedTargetName.MaximumLength ) +
  2036. CommentSize +
  2037. ROUND_UP_COUNT( CredBlobSizeToAlloc, ALIGN_WORST) +
  2038. InputCredential->AttributeCount * sizeof(CREDENTIAL_ATTRIBUTE) +
  2039. VariableAttributeSize +
  2040. TargetAliasSize +
  2041. UserNameSize);
  2042. ReturnCredential = (PCANONICAL_CREDENTIAL) LsapAllocateLsaHeap( AllocatedSize );
  2043. if ( ReturnCredential == NULL ) {
  2044. Status = STATUS_NO_MEMORY;
  2045. goto Cleanup;
  2046. }
  2047. OldWhere = (PUCHAR)(ReturnCredential+1);
  2048. Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
  2049. RtlZeroMemory( OldWhere, Where-OldWhere );
  2050. ReturnCredential->AllocatedSize = AllocatedSize;
  2051. ReturnCredential->WildcardType = WildcardType;
  2052. //
  2053. // Copy the fixed size data
  2054. //
  2055. ReturnCredential->Cred.Flags = InputCredential->Flags;
  2056. ReturnCredential->Cred.Type = InputCredential->Type;
  2057. ReturnCredential->Cred.Persist = InputCredential->Persist;
  2058. ReturnCredential->Cred.LastWritten = InputCredential->LastWritten;
  2059. RtlZeroMemory( &ReturnCredential->Next, sizeof(ReturnCredential->Next) );
  2060. //
  2061. // Copy the 8-byte aligned data.
  2062. // (We don't know the format of the credential blob, but our in-process callers
  2063. // may have alignment requirements.)
  2064. //
  2065. if ( EncryptedInputCredential->ClearCredentialBlobSize != 0 ) {
  2066. //
  2067. // Put the credential blob in the buffer.
  2068. //
  2069. ReturnCredential->Cred.CredentialBlob = Where;
  2070. ReturnCredential->Cred.CredentialBlobSize = InputCredential->CredentialBlobSize;
  2071. ReturnCredential->ClearCredentialBlobSize = EncryptedInputCredential->ClearCredentialBlobSize;
  2072. RtlCopyMemory( Where, InputCredential->CredentialBlob, InputCredential->CredentialBlobSize );
  2073. Where += InputCredential->CredentialBlobSize;
  2074. //
  2075. // Align the running pointer again
  2076. //
  2077. OldWhere = Where;
  2078. // Always leave an extra trailing byte for encrypting the buffer in place
  2079. Where += CredBlobSizeToAlloc - InputCredential->CredentialBlobSize;
  2080. Where = (PUCHAR) ROUND_UP_POINTER( Where, ALIGN_WORST );
  2081. RtlZeroMemory( OldWhere, Where-OldWhere );
  2082. //
  2083. // If the blob isn't encrypted correctly for the cache,
  2084. // convert it.
  2085. //
  2086. if ( (CredFlags & CREDP_FLAGS_IN_PROCESS) == 0 ) {
  2087. //
  2088. // Only decode data if it is there
  2089. //
  2090. if ( ReturnCredential->Cred.CredentialBlobSize != 0 ) {
  2091. ULONG PaddingSize;
  2092. //
  2093. // If the password isn't already in the clear,
  2094. // convert it to the clear.
  2095. //
  2096. if ( (CredFlags & CREDP_FLAGS_CLEAR_PASSWORD) == 0 ) {
  2097. // Ensure we can cast between the types
  2098. ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, Cred) == offsetof( _CANONICAL_CREDENTIAL, Cred));
  2099. ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, ClearCredentialBlobSize) == offsetof( _CANONICAL_CREDENTIAL, ClearCredentialBlobSize));
  2100. if ( !CredpDecodeCredential( (PENCRYPTED_CREDENTIALW)ReturnCredential ) ) {
  2101. DebugLog(( DEB_TRACE_CRED,
  2102. "ValidateCredential: %ws: Cannot decode cred blob\n",
  2103. InputCredential->TargetName ));
  2104. Status = STATUS_INVALID_PARAMETER;
  2105. goto Cleanup;
  2106. }
  2107. }
  2108. //
  2109. // Obfuscate the sensitive data
  2110. // A large enough space was already pushed above.
  2111. //
  2112. ReturnCredential->Cred.CredentialBlobSize = CredBlobSizeToAlloc;
  2113. // Clear the padding at the end to ensure we can compare encrypted blobs
  2114. PaddingSize = CredBlobSizeToAlloc -
  2115. ReturnCredential->ClearCredentialBlobSize;
  2116. if ( PaddingSize != 0 ) {
  2117. RtlZeroMemory( &ReturnCredential->Cred.CredentialBlob[ReturnCredential->ClearCredentialBlobSize],
  2118. PaddingSize );
  2119. }
  2120. LsaProtectMemory( ReturnCredential->Cred.CredentialBlob,
  2121. ReturnCredential->Cred.CredentialBlobSize );
  2122. }
  2123. }
  2124. }
  2125. //
  2126. // Copy the 4 byte aligned data
  2127. //
  2128. ReturnCredential->Cred.AttributeCount = InputCredential->AttributeCount;
  2129. if ( InputCredential->AttributeCount != 0 ) {
  2130. ReturnCredential->Cred.Attributes = (PCREDENTIAL_ATTRIBUTEW) Where;
  2131. Where += ReturnCredential->Cred.AttributeCount * sizeof(CREDENTIAL_ATTRIBUTE);
  2132. } else {
  2133. ReturnCredential->Cred.Attributes = NULL;
  2134. }
  2135. //
  2136. // Copy the 2 byte aligned data
  2137. //
  2138. ReturnCredential->Cred.TargetName = (LPWSTR) Where;
  2139. RtlCopyMemory( Where, InputCredential->TargetName, TargetNameSize );
  2140. Where += TargetNameSize;
  2141. ReturnCredential->TargetName.Buffer = ReturnCredential->Cred.TargetName;
  2142. ReturnCredential->TargetName.MaximumLength = (USHORT)TargetNameSize;
  2143. ReturnCredential->TargetName.Length = ReturnCredential->TargetName.MaximumLength - sizeof(WCHAR);
  2144. if ( NonWildcardedTargetName.Buffer != NULL ) {
  2145. ReturnCredential->NonWildcardedTargetName.Buffer = (LPWSTR)Where;
  2146. RtlCopyMemory( Where, NonWildcardedTargetName.Buffer, NonWildcardedTargetName.MaximumLength );
  2147. Where += NonWildcardedTargetName.MaximumLength;
  2148. ReturnCredential->NonWildcardedTargetName.MaximumLength = NonWildcardedTargetName.MaximumLength;
  2149. ReturnCredential->NonWildcardedTargetName.Length = NonWildcardedTargetName.Length;
  2150. } else {
  2151. ReturnCredential->NonWildcardedTargetName = ReturnCredential->TargetName;
  2152. }
  2153. if ( CommentSize != 0 ) {
  2154. ReturnCredential->Cred.Comment = (LPWSTR) Where;
  2155. RtlCopyMemory( Where, InputCredential->Comment, CommentSize );
  2156. Where += CommentSize;
  2157. } else {
  2158. ReturnCredential->Cred.Comment = NULL;
  2159. }
  2160. if ( TargetAliasSize != 0 ) {
  2161. ReturnCredential->Cred.TargetAlias = (LPWSTR) Where;
  2162. RtlCopyMemory( Where, TargetAlias, TargetAliasSize );
  2163. Where += TargetAliasSize;
  2164. ReturnCredential->TargetAlias.Buffer = ReturnCredential->Cred.TargetAlias;
  2165. ReturnCredential->TargetAlias.MaximumLength = (USHORT)TargetAliasSize;
  2166. ReturnCredential->TargetAlias.Length = ReturnCredential->TargetAlias.MaximumLength - sizeof(WCHAR);
  2167. } else {
  2168. ReturnCredential->Cred.TargetAlias = NULL;
  2169. RtlInitUnicodeString( &ReturnCredential->TargetAlias, NULL );
  2170. }
  2171. if ( UserNameSize != 0 ) {
  2172. ReturnCredential->Cred.UserName = (LPWSTR) Where;
  2173. RtlCopyMemory( Where, UserName, UserNameSize );
  2174. Where += UserNameSize;
  2175. ReturnCredential->UserName.Buffer = ReturnCredential->Cred.UserName;
  2176. ReturnCredential->UserName.MaximumLength = (USHORT)UserNameSize;
  2177. ReturnCredential->UserName.Length = ReturnCredential->UserName.MaximumLength - sizeof(WCHAR);
  2178. } else {
  2179. ReturnCredential->Cred.UserName = NULL;
  2180. RtlInitUnicodeString( &ReturnCredential->UserName, NULL );
  2181. }
  2182. for ( i=0; i<InputCredential->AttributeCount; i++ ) {
  2183. TempSize = (wcslen( InputCredential->Attributes[i].Keyword ) + 1) * sizeof(WCHAR);
  2184. ReturnCredential->Cred.Attributes[i].Keyword = (LPWSTR) Where;
  2185. RtlCopyMemory( Where, InputCredential->Attributes[i].Keyword, TempSize );
  2186. Where += TempSize;
  2187. ReturnCredential->Cred.Attributes[i].Flags = InputCredential->Attributes[i].Flags;
  2188. }
  2189. //
  2190. // Copy the 1 byte aligned data
  2191. //
  2192. for ( i=0; i<InputCredential->AttributeCount; i++ ) {
  2193. if ( InputCredential->Attributes[i].ValueSize != 0 ) {
  2194. ReturnCredential->Cred.Attributes[i].ValueSize =
  2195. InputCredential->Attributes[i].ValueSize;
  2196. ReturnCredential->Cred.Attributes[i].Value = Where;
  2197. RtlCopyMemory( Where,
  2198. InputCredential->Attributes[i].Value,
  2199. InputCredential->Attributes[i].ValueSize );
  2200. Where += InputCredential->Attributes[i].ValueSize;
  2201. } else {
  2202. ReturnCredential->Cred.Attributes[i].ValueSize = 0;
  2203. ReturnCredential->Cred.Attributes[i].Value = NULL;
  2204. }
  2205. ReturnCredential->Cred.Attributes[i].Flags = InputCredential->Attributes[i].Flags;
  2206. }
  2207. //
  2208. // If we have target information,
  2209. // use it to do an extra validation.
  2210. //
  2211. if ( TargetInfo != NULL ) {
  2212. ULONG AliasIndex;
  2213. if (!CredpCompareCredToTargetInfo( TargetInfo, ReturnCredential, &AliasIndex ) ) {
  2214. Status = STATUS_INVALID_PARAMETER;
  2215. DebugLog(( DEB_TRACE_CRED,
  2216. "ValidateCredential: %ws: TargetInfo doesn't match credential.\n",
  2217. InputCredential->TargetName ));
  2218. goto Cleanup;
  2219. }
  2220. }
  2221. //
  2222. // Return the credential to the caller.
  2223. //
  2224. *ValidatedCredential = ReturnCredential;
  2225. ReturnCredential = NULL;
  2226. Status = STATUS_SUCCESS;
  2227. Cleanup:
  2228. if ( ReturnCredential != NULL ) {
  2229. LsapFreeLsaHeap( ReturnCredential );
  2230. }
  2231. if ( NonWildcardedTargetName.Buffer != NULL ) {
  2232. LsapFreeLsaHeap( NonWildcardedTargetName.Buffer );
  2233. }
  2234. if ( AllocatedTargetAlias != NULL ) {
  2235. SafeAllocaFree( AllocatedTargetAlias );
  2236. }
  2237. if ( AllocatedUserName != NULL ) {
  2238. MIDL_user_free( AllocatedUserName );
  2239. }
  2240. return Status;
  2241. }
  2242. BOOLEAN
  2243. CredpValidateNames(
  2244. IN OUT PCREDENTIAL_TARGET_INFORMATION InputTargetInfo,
  2245. IN BOOLEAN DoServer,
  2246. OUT PULONG NetbiosNameSize,
  2247. OUT PULONG DnsNameSize
  2248. )
  2249. /*++
  2250. Routine Description:
  2251. This routine validates the server names (or domain names) in a target info structure.
  2252. It handles the CRED_TI_*_FORMAT_UNKNOWN bit. If specified, the routine ensures only the
  2253. "dns" field of the name is specified. Also, the specified name is syntax checked.
  2254. If the specified name only matches one of the name formats, the "FORMAT_UNKNOWN" bit is
  2255. turned off and the appropriate name is set in InputTargetInfo
  2256. Arguments:
  2257. InputTargetInfo - Specifies the target info to validate.
  2258. On return, the Flags and name fields my be updated to reflect the true name formats.
  2259. DoServer - If TRUE the server names are validated.
  2260. If FALSE, the domain names are validated.
  2261. NetbiosNameSize - Returns the size in bytes of the netbios name
  2262. DnsNameSize - Returns the size in bytes of the DNS name
  2263. Return Values:
  2264. FALSE - if the names are invalid.
  2265. --*/
  2266. {
  2267. DWORD FlagBit;
  2268. LPWSTR *NetbiosNamePtr;
  2269. LPWSTR *DnsNamePtr;
  2270. DNS_NAME_FORMAT DnsNameFormat;
  2271. //
  2272. // Set up some locals identifying whether we're doing the server or domain.
  2273. //
  2274. if ( DoServer ) {
  2275. FlagBit = CRED_TI_SERVER_FORMAT_UNKNOWN;
  2276. NetbiosNamePtr = &InputTargetInfo->NetbiosServerName;
  2277. DnsNamePtr = &InputTargetInfo->DnsServerName;
  2278. DnsNameFormat = DnsNameHostnameFull;
  2279. } else {
  2280. FlagBit = CRED_TI_DOMAIN_FORMAT_UNKNOWN;
  2281. NetbiosNamePtr = &InputTargetInfo->NetbiosDomainName;
  2282. DnsNamePtr = &InputTargetInfo->DnsDomainName;
  2283. DnsNameFormat = DnsNameDomain;
  2284. }
  2285. //
  2286. // If the format unknown bit is set,
  2287. // try to determine the formation.
  2288. //
  2289. if ( InputTargetInfo->Flags & FlagBit ) {
  2290. BOOLEAN IsNetbios;
  2291. BOOLEAN IsDns;
  2292. ULONG LocalNetbiosNameSize;
  2293. ULONG LocalDnsNameSize;
  2294. //
  2295. // Caller must pass the unknown name as the DNS name
  2296. //
  2297. if ( *NetbiosNamePtr != NULL) {
  2298. DebugLog(( DEB_TRACE_CRED,
  2299. "CredpValidateNames: Netbios name must be null.\n" ));
  2300. return FALSE;
  2301. }
  2302. //
  2303. // Determine the syntax of the passed in name.
  2304. //
  2305. IsNetbios = CredpValidateString( *DnsNamePtr, CNLEN, FALSE, &LocalNetbiosNameSize );
  2306. IsDns = CredpValidateDnsString( *DnsNamePtr, FALSE, DnsNameFormat, &LocalDnsNameSize );
  2307. //
  2308. // If the name simply isn't valid,
  2309. // we're done.
  2310. //
  2311. if ( !IsNetbios && !IsDns ) {
  2312. DebugLog(( DEB_TRACE_CRED,
  2313. "CredpValidateNames: Invalid DNS Buffer: %ws (may not be fatal).\n",
  2314. *DnsNamePtr ));
  2315. return FALSE;
  2316. //
  2317. // If the name is only a valid DNS name
  2318. // use it as such
  2319. //
  2320. } else if ( !IsNetbios && IsDns ) {
  2321. // Turn off the bit since the name is not ambiguous.
  2322. InputTargetInfo->Flags &= ~FlagBit;
  2323. *NetbiosNameSize = 0;
  2324. *DnsNameSize = LocalDnsNameSize;
  2325. //
  2326. // If the name is only a valid netbios name
  2327. // use it as such
  2328. //
  2329. } else if ( IsNetbios && !IsDns ) {
  2330. // Turn off the bit since the name is not ambiguous.
  2331. InputTargetInfo->Flags &= ~FlagBit;
  2332. *NetbiosNameSize = LocalNetbiosNameSize;
  2333. *NetbiosNamePtr = *DnsNamePtr;
  2334. *DnsNamePtr = NULL;
  2335. *DnsNameSize = 0;
  2336. //
  2337. // If the name is valid for both formats,
  2338. // leave it ambiguous
  2339. //
  2340. } else {
  2341. *NetbiosNameSize = 0;
  2342. *DnsNameSize = LocalDnsNameSize;
  2343. }
  2344. } else {
  2345. if ( !CredpValidateString( *NetbiosNamePtr, CNLEN, TRUE, NetbiosNameSize ) ||
  2346. !CredpValidateDnsString( *DnsNamePtr, TRUE, DnsNameFormat, DnsNameSize ) ) {
  2347. DebugLog(( DEB_TRACE_CRED,
  2348. "CredpValidateNames: Invalid Buffer.\n" ));
  2349. return FALSE;
  2350. }
  2351. }
  2352. return TRUE;
  2353. }
  2354. NTSTATUS
  2355. CredpValidateTargetInfo(
  2356. IN PCREDENTIAL_TARGET_INFORMATION InputTargetInfo,
  2357. OUT PCANONICAL_TARGET_INFO *ValidatedTargetInfo
  2358. )
  2359. /*++
  2360. Routine Description:
  2361. This routine validates a passed in target info structure. It returns
  2362. a canonicalized version of the target info.
  2363. All DNS names have the trailing . stripped.
  2364. Arguments:
  2365. InputTargetInfo - Specifies the target info to validate.
  2366. ValidatedTargetInfo - Returns a pointer to the canonicalized target info.
  2367. The caller should free this structure by calling LsapFreeLsaHeap.
  2368. Return Values:
  2369. The following status codes may be returned:
  2370. STATUS_INVALID_PARMETER - The input target info is not valid.
  2371. --*/
  2372. {
  2373. NTSTATUS Status;
  2374. PCANONICAL_TARGET_INFO ReturnTargetInfo = NULL;
  2375. ULONG TempSize;
  2376. // Local copy of InputTargetInfo that this routine can modify.
  2377. CREDENTIAL_TARGET_INFORMATION LocalTargetInfo;
  2378. ULONG TargetNameSize;
  2379. ULONG NetbiosServerNameSize;
  2380. WCHAR NetbiosServerName[CNLEN+1];
  2381. ULONG DnsServerNameSize;
  2382. ULONG NetbiosDomainNameSize;
  2383. ULONG DnsDomainNameSize;
  2384. ULONG DnsTreeNameSize;
  2385. ULONG PackageNameSize;
  2386. ULONG CredTypeSize;
  2387. ULONG AllocatedSize;
  2388. BOOLEAN FictitiousServerName = FALSE;
  2389. LPBYTE Where;
  2390. LPBYTE OldWhere;
  2391. ULONG i;
  2392. //
  2393. // Validate the pointer itself.
  2394. //
  2395. if ( InputTargetInfo == NULL ) {
  2396. Status = STATUS_INVALID_PARAMETER;
  2397. DebugLog(( DEB_TRACE_CRED,
  2398. "ValidateTargetInfo: TargetInfo NULL.\n" ));
  2399. goto Cleanup;
  2400. }
  2401. //
  2402. // Validate Flags
  2403. //
  2404. LocalTargetInfo = *InputTargetInfo;
  2405. if ( (LocalTargetInfo.Flags & ~CRED_TI_VALID_FLAGS) != 0 ) {
  2406. Status = STATUS_INVALID_PARAMETER;
  2407. DebugLog(( DEB_TRACE_CRED,
  2408. "ValidateTargetInfo: Invalid flags 0x%lx.\n",
  2409. LocalTargetInfo.Flags ));
  2410. goto Cleanup;
  2411. }
  2412. //
  2413. // All share level machines are workgroup members
  2414. //
  2415. if ( LocalTargetInfo.Flags & CRED_TI_ONLY_PASSWORD_REQUIRED ) {
  2416. LocalTargetInfo.Flags |= CRED_TI_WORKGROUP_MEMBER;
  2417. }
  2418. //
  2419. // Validate the contained strings.
  2420. //
  2421. if ( !CredpValidateString( LocalTargetInfo.TargetName, CRED_MAX_DOMAIN_TARGET_NAME_LENGTH, TRUE, &TargetNameSize ) ||
  2422. !CredpValidateDnsString( LocalTargetInfo.DnsTreeName, TRUE, DnsNameDomain, &DnsTreeNameSize ) ||
  2423. !CredpValidateString( LocalTargetInfo.PackageName, CRED_MAX_STRING_LENGTH, TRUE, &PackageNameSize ) ) {
  2424. Status = STATUS_INVALID_PARAMETER;
  2425. DebugLog(( DEB_TRACE_CRED,
  2426. "ValidateTargetInfo: Invalid Buffer.\n" ));
  2427. goto Cleanup;
  2428. }
  2429. //
  2430. // Supply a server name if none is present.
  2431. //
  2432. // pre-NTLM-V2 servers don't supply a server name in the negotiate response. NTLM supplies
  2433. // the TargetName from the SPN. In that case, the TargetName makes a better server name than
  2434. // none at all.
  2435. //
  2436. if ( (LocalTargetInfo.NetbiosServerName == NULL || *(LocalTargetInfo.NetbiosServerName) == L'\0') &&
  2437. (LocalTargetInfo.DnsServerName == NULL || *(LocalTargetInfo.DnsServerName) == L'\0') &&
  2438. TargetNameSize != 0 ) {
  2439. LocalTargetInfo.DnsServerName = LocalTargetInfo.TargetName;
  2440. LocalTargetInfo.Flags |= CRED_TI_SERVER_FORMAT_UNKNOWN;
  2441. FictitiousServerName = TRUE;
  2442. }
  2443. //
  2444. // Validate the server and domain names.
  2445. //
  2446. if ( !CredpValidateNames( &LocalTargetInfo, TRUE, &NetbiosServerNameSize, &DnsServerNameSize ) ) {
  2447. //
  2448. // Don't bail out if we made up the server name
  2449. //
  2450. if ( FictitiousServerName ) {
  2451. LocalTargetInfo.DnsServerName = NULL;
  2452. LocalTargetInfo.Flags &= ~CRED_TI_SERVER_FORMAT_UNKNOWN;
  2453. FictitiousServerName = FALSE;
  2454. NetbiosServerNameSize = 0;
  2455. DnsServerNameSize = 0;
  2456. } else {
  2457. Status = STATUS_INVALID_PARAMETER;
  2458. DebugLog(( DEB_TRACE_CRED,
  2459. "ValidateTargetInfo: Invalid server buffer.\n" ));
  2460. goto Cleanup;
  2461. }
  2462. }
  2463. if ( !CredpValidateNames( &LocalTargetInfo, FALSE, &NetbiosDomainNameSize, &DnsDomainNameSize ) ) {
  2464. Status = STATUS_INVALID_PARAMETER;
  2465. DebugLog(( DEB_TRACE_CRED,
  2466. "ValidateTargetInfo: Invalid domain buffer.\n" ));
  2467. goto Cleanup;
  2468. }
  2469. //
  2470. // Validate the attribute list.
  2471. // Ensure RPC passed us sane information.
  2472. //
  2473. if ( LocalTargetInfo.CredTypeCount != 0 &&
  2474. LocalTargetInfo.CredTypes == NULL ) {
  2475. DebugLog((DEB_TRACE_CRED, "ValidateTargetInfo: Invalid cred type buffer.\n" ));
  2476. Status = STATUS_INVALID_PARAMETER;
  2477. goto Cleanup;
  2478. }
  2479. //
  2480. // Validate the cred types
  2481. //
  2482. for ( i=0; i<LocalTargetInfo.CredTypeCount; i ++ ) {
  2483. switch ( LocalTargetInfo.CredTypes[i] ) {
  2484. case CRED_TYPE_GENERIC:
  2485. case CRED_TYPE_DOMAIN_PASSWORD:
  2486. case CRED_TYPE_DOMAIN_CERTIFICATE:
  2487. case CRED_TYPE_DOMAIN_VISIBLE_PASSWORD:
  2488. break;
  2489. default:
  2490. DebugLog((DEB_TRACE_CRED, "ValidateTargetInfo: Invalid cred type %ld %ld.\n", i, LocalTargetInfo.CredTypes[i] ));
  2491. Status = STATUS_INVALID_PARAMETER;
  2492. goto Cleanup;
  2493. }
  2494. }
  2495. CredTypeSize = LocalTargetInfo.CredTypeCount * sizeof(DWORD);
  2496. //
  2497. // Supply a netbios server name if none is present and DNS server name is known.
  2498. //
  2499. if ( NetbiosServerNameSize == 0 &&
  2500. DnsServerNameSize != 0 &&
  2501. (LocalTargetInfo.Flags & CRED_TI_SERVER_FORMAT_UNKNOWN) == 0 ) {
  2502. DWORD Size = CNLEN+1;
  2503. if ( !DnsHostnameToComputerNameW( LocalTargetInfo.DnsServerName,
  2504. NetbiosServerName,
  2505. &Size ) ) {
  2506. DebugLog(( DEB_TRACE_CRED,
  2507. "ValidateTargetInfo: Cannot DnsHostNameToComputerName: %ld.\n",
  2508. GetLastError() ));
  2509. Status = STATUS_INVALID_PARAMETER;
  2510. goto Cleanup;
  2511. }
  2512. LocalTargetInfo.NetbiosServerName = NetbiosServerName;
  2513. NetbiosServerNameSize = (Size + 1) * sizeof(WCHAR);
  2514. }
  2515. //
  2516. // Supply a target name if none is present.
  2517. //
  2518. // NTLM authentication to Unix servers return target info. However, the callers of SSPI
  2519. // don't supply an SPN. NTLM builds the TargetName from the SPN.
  2520. //
  2521. if ( TargetNameSize == 0 ) {
  2522. //
  2523. // If there's a DNS server name,
  2524. // use it.
  2525. //
  2526. if ( DnsServerNameSize != 0 ) {
  2527. LocalTargetInfo.TargetName = LocalTargetInfo.DnsServerName;
  2528. TargetNameSize = DnsServerNameSize;
  2529. //
  2530. // If there's a netbios server name,
  2531. // use it.
  2532. } else if ( NetbiosServerNameSize != 0 ) {
  2533. LocalTargetInfo.TargetName = LocalTargetInfo.NetbiosServerName;
  2534. TargetNameSize = NetbiosServerNameSize;
  2535. //
  2536. // If there's a DNS Domain name,
  2537. // use it.
  2538. //
  2539. } else if ( DnsDomainNameSize != 0 ) {
  2540. LocalTargetInfo.TargetName = LocalTargetInfo.DnsDomainName;
  2541. TargetNameSize = DnsDomainNameSize;
  2542. //
  2543. // If there's a netbios Domain name,
  2544. // use it.
  2545. } else if ( NetbiosDomainNameSize != 0 ) {
  2546. LocalTargetInfo.TargetName = LocalTargetInfo.NetbiosDomainName;
  2547. TargetNameSize = NetbiosDomainNameSize;
  2548. //
  2549. // If there's a DNS tree name,
  2550. // use it.
  2551. //
  2552. } else if ( DnsTreeNameSize != 0 ) {
  2553. LocalTargetInfo.TargetName = LocalTargetInfo.DnsTreeName;
  2554. TargetNameSize = DnsTreeNameSize;
  2555. }
  2556. }
  2557. //
  2558. // Canonicalize the target info.
  2559. //
  2560. // There are several cases where authentication packages are sent invalid target info.
  2561. // This section clears up those cases.
  2562. //
  2563. if ( NetbiosServerNameSize != 0 ) {
  2564. UNICODE_STRING NetbiosServerNameString;
  2565. NetbiosServerNameString.Buffer = LocalTargetInfo.NetbiosServerName;
  2566. NetbiosServerNameString.MaximumLength = (USHORT) NetbiosServerNameSize;
  2567. NetbiosServerNameString.Length = NetbiosServerNameString.MaximumLength - sizeof(WCHAR);
  2568. //
  2569. // Machines that are a member of a workgroup indicate that their NetbiosDomainName
  2570. // equals their NetbiosServerName.
  2571. // Instead, indicate workgroup membership by the lack of a NetbiosDomainName.
  2572. //
  2573. if ( NetbiosDomainNameSize != 0 ) {
  2574. UNICODE_STRING NetbiosDomainNameString;
  2575. NetbiosDomainNameString.Buffer = LocalTargetInfo.NetbiosDomainName;
  2576. NetbiosDomainNameString.MaximumLength = (USHORT) NetbiosDomainNameSize;
  2577. NetbiosDomainNameString.Length = NetbiosDomainNameString.MaximumLength - sizeof(WCHAR);
  2578. if ( RtlEqualUnicodeString( &NetbiosServerNameString,
  2579. &NetbiosDomainNameString,
  2580. TRUE ) ) {
  2581. NetbiosDomainNameSize = 0;
  2582. LocalTargetInfo.Flags |= CRED_TI_WORKGROUP_MEMBER;
  2583. }
  2584. }
  2585. //
  2586. // Machines that are a member of an NT 4 domain indicate that their DnsDomainName
  2587. // equals their NetbiosServerName.
  2588. // Instead, zap the DnsDomainName.
  2589. //
  2590. if ( DnsDomainNameSize != 0 ) {
  2591. UNICODE_STRING DnsDomainNameString;
  2592. DnsDomainNameString.Buffer = LocalTargetInfo.DnsDomainName;
  2593. DnsDomainNameString.MaximumLength = (USHORT) DnsDomainNameSize;
  2594. DnsDomainNameString.Length = DnsDomainNameString.MaximumLength - sizeof(WCHAR);
  2595. if ( RtlEqualUnicodeString( &NetbiosServerNameString,
  2596. &DnsDomainNameString,
  2597. TRUE ) ) {
  2598. DnsDomainNameSize = 0;
  2599. }
  2600. }
  2601. //
  2602. // Some machines in a workgroup also return the DnsDomainName set to the DnsServerName.
  2603. // Instead, zap the DnsDomainName.
  2604. if ( DnsDomainNameSize != 0 && DnsServerNameSize != 0 ) {
  2605. UNICODE_STRING DnsServerNameString;
  2606. UNICODE_STRING DnsDomainNameString;
  2607. DnsServerNameString.Buffer = LocalTargetInfo.DnsServerName;
  2608. DnsServerNameString.MaximumLength = (USHORT) DnsServerNameSize;
  2609. DnsServerNameString.Length = DnsServerNameString.MaximumLength - sizeof(WCHAR);
  2610. DnsDomainNameString.Buffer = LocalTargetInfo.DnsDomainName;
  2611. DnsDomainNameString.MaximumLength = (USHORT) DnsDomainNameSize;
  2612. DnsDomainNameString.Length = DnsDomainNameString.MaximumLength - sizeof(WCHAR);
  2613. if ( RtlEqualUnicodeString( &DnsServerNameString,
  2614. &DnsDomainNameString,
  2615. TRUE ) ) {
  2616. DnsDomainNameSize = 0;
  2617. }
  2618. }
  2619. }
  2620. //
  2621. // Allocate a buffer for the canonicalized TargetInfo.
  2622. //
  2623. AllocatedSize = sizeof(CANONICAL_TARGET_INFO) +
  2624. TargetNameSize +
  2625. NetbiosServerNameSize +
  2626. DnsServerNameSize +
  2627. NetbiosDomainNameSize +
  2628. DnsDomainNameSize +
  2629. DnsTreeNameSize +
  2630. PackageNameSize +
  2631. CredTypeSize;
  2632. ReturnTargetInfo = (PCANONICAL_TARGET_INFO) LsapAllocateLsaHeap( AllocatedSize );
  2633. if ( ReturnTargetInfo == NULL ) {
  2634. Status = STATUS_NO_MEMORY;
  2635. goto Cleanup;
  2636. }
  2637. Where = (PUCHAR)(ReturnTargetInfo+1);
  2638. //
  2639. // Copy fixed size data
  2640. //
  2641. ReturnTargetInfo->Flags = LocalTargetInfo.Flags;
  2642. //
  2643. // Copy the DWORD aligned data
  2644. //
  2645. ReturnTargetInfo->CredTypeCount = LocalTargetInfo.CredTypeCount;
  2646. if ( ReturnTargetInfo->CredTypeCount != 0 ) {
  2647. ReturnTargetInfo->CredTypes = (LPDWORD)Where;
  2648. RtlCopyMemory( Where, LocalTargetInfo.CredTypes, CredTypeSize );
  2649. Where += CredTypeSize;
  2650. } else {
  2651. ReturnTargetInfo->CredTypes = NULL;
  2652. }
  2653. //
  2654. // Copy the 2 byte aligned data
  2655. //
  2656. if ( TargetNameSize != 0 ) {
  2657. ReturnTargetInfo->TargetName.Buffer = (LPWSTR) Where;
  2658. ReturnTargetInfo->TargetName.MaximumLength = (USHORT) TargetNameSize;
  2659. ReturnTargetInfo->TargetName.Length = (USHORT)(ReturnTargetInfo->TargetName.MaximumLength - sizeof(WCHAR));
  2660. RtlCopyMemory( Where, LocalTargetInfo.TargetName, TargetNameSize );
  2661. Where += TargetNameSize;
  2662. } else {
  2663. RtlInitUnicodeString( &ReturnTargetInfo->TargetName, NULL );
  2664. }
  2665. if ( NetbiosServerNameSize != 0 ) {
  2666. ReturnTargetInfo->NetbiosServerName.Buffer = (LPWSTR) Where;
  2667. ReturnTargetInfo->NetbiosServerName.MaximumLength = (USHORT) NetbiosServerNameSize;
  2668. ReturnTargetInfo->NetbiosServerName.Length = (USHORT)(ReturnTargetInfo->NetbiosServerName.MaximumLength - sizeof(WCHAR));
  2669. RtlCopyMemory( Where, LocalTargetInfo.NetbiosServerName, NetbiosServerNameSize );
  2670. Where += NetbiosServerNameSize;
  2671. } else {
  2672. RtlInitUnicodeString( &ReturnTargetInfo->NetbiosServerName, NULL );
  2673. }
  2674. if ( DnsServerNameSize != 0 ) {
  2675. ReturnTargetInfo->DnsServerName.Buffer = (LPWSTR) Where;
  2676. ReturnTargetInfo->DnsServerName.MaximumLength = (USHORT) DnsServerNameSize;
  2677. ReturnTargetInfo->DnsServerName.Length = (USHORT)(ReturnTargetInfo->DnsServerName.MaximumLength - sizeof(WCHAR));
  2678. RtlCopyMemory( Where, LocalTargetInfo.DnsServerName, DnsServerNameSize );
  2679. Where += DnsServerNameSize;
  2680. } else {
  2681. RtlInitUnicodeString( &ReturnTargetInfo->DnsServerName, NULL );
  2682. }
  2683. if ( NetbiosDomainNameSize != 0 ) {
  2684. ReturnTargetInfo->NetbiosDomainName.Buffer = (LPWSTR) Where;
  2685. ReturnTargetInfo->NetbiosDomainName.MaximumLength = (USHORT)(NetbiosDomainNameSize);
  2686. ReturnTargetInfo->NetbiosDomainName.Length = (USHORT)(ReturnTargetInfo->NetbiosDomainName.MaximumLength - sizeof(WCHAR));
  2687. RtlCopyMemory( Where, LocalTargetInfo.NetbiosDomainName, NetbiosDomainNameSize );
  2688. Where += NetbiosDomainNameSize;
  2689. } else {
  2690. RtlInitUnicodeString( &ReturnTargetInfo->NetbiosDomainName, NULL );
  2691. }
  2692. if ( DnsDomainNameSize != 0 ) {
  2693. ReturnTargetInfo->DnsDomainName.Buffer = (LPWSTR) Where;
  2694. ReturnTargetInfo->DnsDomainName.MaximumLength = (USHORT)(DnsDomainNameSize);
  2695. ReturnTargetInfo->DnsDomainName.Length = (USHORT)(ReturnTargetInfo->DnsDomainName.MaximumLength - sizeof(WCHAR));
  2696. RtlCopyMemory( Where, LocalTargetInfo.DnsDomainName, DnsDomainNameSize );
  2697. Where += DnsDomainNameSize;
  2698. } else {
  2699. RtlInitUnicodeString( &ReturnTargetInfo->DnsDomainName, NULL );
  2700. }
  2701. if ( DnsTreeNameSize != 0 ) {
  2702. ReturnTargetInfo->DnsTreeName.Buffer = (LPWSTR) Where;
  2703. ReturnTargetInfo->DnsTreeName.MaximumLength = (USHORT)(DnsTreeNameSize);
  2704. ReturnTargetInfo->DnsTreeName.Length = (USHORT)(ReturnTargetInfo->DnsTreeName.MaximumLength - sizeof(WCHAR));
  2705. RtlCopyMemory( Where, LocalTargetInfo.DnsTreeName, DnsTreeNameSize );
  2706. Where += DnsTreeNameSize;
  2707. } else {
  2708. RtlInitUnicodeString( &ReturnTargetInfo->DnsTreeName, NULL );
  2709. }
  2710. if ( PackageNameSize != 0 ) {
  2711. ReturnTargetInfo->PackageName.Buffer = (LPWSTR) Where;
  2712. ReturnTargetInfo->PackageName.MaximumLength = (USHORT)(PackageNameSize);
  2713. ReturnTargetInfo->PackageName.Length = (USHORT)(ReturnTargetInfo->PackageName.MaximumLength - sizeof(WCHAR));
  2714. RtlCopyMemory( Where, LocalTargetInfo.PackageName, PackageNameSize );
  2715. Where += PackageNameSize;
  2716. } else {
  2717. RtlInitUnicodeString( &ReturnTargetInfo->PackageName, NULL );
  2718. }
  2719. //
  2720. // Return the TargetInfo to the caller.
  2721. //
  2722. *ValidatedTargetInfo = ReturnTargetInfo;
  2723. ReturnTargetInfo = NULL;
  2724. Status = STATUS_SUCCESS;
  2725. Cleanup:
  2726. if ( ReturnTargetInfo != NULL ) {
  2727. LsapFreeLsaHeap( ReturnTargetInfo );
  2728. }
  2729. return Status;
  2730. }
  2731. PPROMPT_DATA
  2732. CredpFindPromptData(
  2733. IN PCREDENTIAL_SETS CredentialSets,
  2734. IN PUNICODE_STRING TargetName,
  2735. IN ULONG Type,
  2736. IN ULONG Persist
  2737. )
  2738. /*++
  2739. Routine Description:
  2740. This routine finds a the prompt data for a credential in a credential set.
  2741. On entry, UserCredentialSets->CritSect must be locked.
  2742. Arguments:
  2743. CredentialSet - Credential set list to find the credential in.
  2744. TargetName - Name of the credential whose prompt data is to be found.
  2745. Type - Type of the credential whose prompt data is to be found.
  2746. Persist - Peristence of the credential whose prompt data is to be found.
  2747. Return Values:
  2748. Returns a pointer to the prompt data. This pointer my be used as long as
  2749. UserCredentialSets->CritSect remains locked.
  2750. NULL: There is no such credential or there is no prompt data for the credential.
  2751. --*/
  2752. {
  2753. PPROMPT_DATA PromptData;
  2754. PLIST_ENTRY ListEntry;
  2755. //
  2756. // Loop through the list of prompt data trying to find this one.
  2757. //
  2758. for ( ListEntry = CredentialSets->SessionCredSets->PromptData.Flink ;
  2759. ListEntry != &CredentialSets->SessionCredSets->PromptData;
  2760. ListEntry = ListEntry->Flink) {
  2761. PromptData = CONTAINING_RECORD( ListEntry, PROMPT_DATA, Next );
  2762. if ( Type == PromptData->Type &&
  2763. Persist == PromptData->Persist &&
  2764. RtlEqualUnicodeString( TargetName,
  2765. &PromptData->TargetName,
  2766. TRUE ) ) {
  2767. return PromptData;
  2768. }
  2769. }
  2770. return NULL;
  2771. }
  2772. PPROMPT_DATA
  2773. CredpAllocatePromptData(
  2774. IN PCANONICAL_CREDENTIAL Credential
  2775. )
  2776. /*++
  2777. Routine Description:
  2778. This routine allocates a prompt data structure that corresponds to the passed in
  2779. credential.
  2780. Arguments:
  2781. Credential - Specifies the credential to allocate the prompt data structure for.
  2782. Return Values:
  2783. Returns a pointer to the prompt data.
  2784. The buffer should be freed via LsapFreeLsaHeap.
  2785. NULL: Memory could not be allocated.
  2786. --*/
  2787. {
  2788. PPROMPT_DATA PromptData;
  2789. LPBYTE Where;
  2790. //
  2791. // Allocate the PromptData structure.
  2792. //
  2793. PromptData = (PPROMPT_DATA) LsapAllocateLsaHeap(
  2794. sizeof(PROMPT_DATA) +
  2795. Credential->TargetName.Length );
  2796. if ( PromptData == NULL ) {
  2797. return NULL;
  2798. }
  2799. Where = (LPBYTE)(PromptData+1);
  2800. //
  2801. // Fill it in
  2802. //
  2803. PromptData->Type = Credential->Cred.Type;
  2804. PromptData->Persist = Credential->Cred.Persist;
  2805. PromptData->Written = FALSE;
  2806. PromptData->TargetName.Buffer = (LPWSTR)Where;
  2807. PromptData->TargetName.Length = Credential->TargetName.Length;
  2808. PromptData->TargetName.MaximumLength = Credential->TargetName.Length;
  2809. RtlCopyMemory( Where,
  2810. Credential->TargetName.Buffer,
  2811. PromptData->TargetName.Length );
  2812. return PromptData;
  2813. }
  2814. PENCRYPTED_CREDENTIALW
  2815. CredpCloneCredential(
  2816. IN PCREDENTIAL_SETS CredentialSets,
  2817. IN ULONG CredFlags,
  2818. IN PCANONICAL_CREDENTIAL InputCredential
  2819. )
  2820. /*++
  2821. Routine Description:
  2822. This routine creates a credential suitable for returning out of the
  2823. credential manager.
  2824. On entry, UserCredentialSets->CritSect must be locked.
  2825. Arguments:
  2826. CredentialSets - A pointer to the referenced redential sets the cloned credential is in.
  2827. CredFlags - Flags changing the behavior of the routine:
  2828. CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
  2829. CREDP_FLAGS_USE_MIDL_HEAP - If specified, use MIDL_user_allocate to allocate memory.
  2830. InputCredential - Specifies the credential to clone
  2831. Return Values:
  2832. Credential to return.
  2833. The returned credential may container pointers. All pointer are to addresses within
  2834. the returned allocated block.
  2835. The caller should free this memory using LsapFreeLsaHeap.
  2836. If CREDP_FLAGS_USE_MIDL_HEAP was specified, use MIDL_user_free.
  2837. NULL: Memory could not be allocated
  2838. --*/
  2839. {
  2840. PCREDENTIAL Credential;
  2841. PENCRYPTED_CREDENTIALW EncryptedCredential;
  2842. DWORD_PTR Offset;
  2843. ULONG i;
  2844. //
  2845. // Allocate memory for the returned credential
  2846. //
  2847. if ( CredFlags & CREDP_FLAGS_USE_MIDL_HEAP ) {
  2848. Credential = (PCREDENTIALW) MIDL_user_allocate( InputCredential->AllocatedSize );
  2849. } else {
  2850. Credential = (PCREDENTIALW) LsapAllocateLsaHeap( InputCredential->AllocatedSize );
  2851. }
  2852. if ( Credential == NULL ) {
  2853. return NULL;
  2854. }
  2855. EncryptedCredential = (PENCRYPTED_CREDENTIALW) Credential;
  2856. //
  2857. // Copy the credential
  2858. // Note that the "fixed size" part of the structure copied below is copying a
  2859. // CANONICAL_CREDENTIAL to a ENCRYPTED_CREDENTIALW. We rely on the following
  2860. // asserted conditions.
  2861. //
  2862. ASSERT( sizeof(ENCRYPTED_CREDENTIALW) <= sizeof(CANONICAL_CREDENTIAL) );
  2863. ASSERT( offsetof( _ENCRYPTED_CREDENTIALW, Cred) == 0 );
  2864. ASSERT( offsetof( _CANONICAL_CREDENTIAL, Cred) == 0 );
  2865. RtlCopyMemory( Credential, InputCredential, InputCredential->AllocatedSize );
  2866. //
  2867. // Grab the clear text size of the blob
  2868. //
  2869. EncryptedCredential->ClearCredentialBlobSize = InputCredential->ClearCredentialBlobSize;
  2870. ASSERT( InputCredential->ClearCredentialBlobSize <= InputCredential->Cred.CredentialBlobSize );
  2871. //
  2872. // Relocate any pointers
  2873. //
  2874. #define RELOCATE_ONE( _x, _type ) if ( _x != NULL ) { _x = (_type) ((LPBYTE)(_x) + Offset); }
  2875. Offset = ((LPBYTE)Credential) - ((LPBYTE)InputCredential);
  2876. RELOCATE_ONE( Credential->TargetName, LPWSTR );
  2877. RELOCATE_ONE( Credential->Comment, LPWSTR );
  2878. RELOCATE_ONE( Credential->CredentialBlob, LPBYTE );
  2879. RELOCATE_ONE( Credential->Attributes, PCREDENTIAL_ATTRIBUTEW );
  2880. for ( i=0; i<Credential->AttributeCount; i++ ) {
  2881. RELOCATE_ONE( Credential->Attributes[i].Keyword, LPWSTR );
  2882. RELOCATE_ONE( Credential->Attributes[i].Value, LPBYTE );
  2883. }
  2884. RELOCATE_ONE( Credential->TargetAlias, LPWSTR );
  2885. RELOCATE_ONE( Credential->UserName, LPWSTR );
  2886. #undef RELOCATE_ONE
  2887. //
  2888. // If we're leaving the process,
  2889. // handle the private data.
  2890. //
  2891. if ( (CredFlags & CREDP_FLAGS_IN_PROCESS) == 0 ) {
  2892. //
  2893. // Domain passwords or cert pins never leave the process
  2894. //
  2895. // "Visible Password" is for user mode implementation auth package implementations.
  2896. // So allow "Visible Password" credentials out of process.
  2897. //
  2898. if ( Credential->Type != CRED_TYPE_GENERIC &&
  2899. Credential->Type != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD ) {
  2900. if ( Credential->CredentialBlob != NULL &&
  2901. Credential->CredentialBlobSize != 0 ) {
  2902. RtlZeroMemory( Credential->CredentialBlob, Credential->CredentialBlobSize );
  2903. Credential->CredentialBlob = NULL;
  2904. Credential->CredentialBlobSize = 0;
  2905. EncryptedCredential->ClearCredentialBlobSize = 0;
  2906. }
  2907. //
  2908. // Other credentials should be protected for transit on the wire.
  2909. //
  2910. } else {
  2911. //
  2912. // Only encode data if it is there
  2913. //
  2914. if ( Credential->CredentialBlobSize != 0 ) {
  2915. //
  2916. // First unprotect the memory
  2917. //
  2918. LsaUnprotectMemory( Credential->CredentialBlob,
  2919. Credential->CredentialBlobSize );
  2920. //
  2921. // Encrypt for transit on the wire (always LPC)
  2922. //
  2923. if ( !CredpEncodeCredential( EncryptedCredential )) {
  2924. // Clear the possibly clear password
  2925. RtlZeroMemory( Credential->CredentialBlob, Credential->CredentialBlobSize );
  2926. if ( CredFlags & CREDP_FLAGS_USE_MIDL_HEAP ) {
  2927. MIDL_user_free( Credential );
  2928. } else {
  2929. LsapFreeLsaHeap( Credential );
  2930. }
  2931. return NULL;
  2932. }
  2933. }
  2934. }
  2935. }
  2936. //
  2937. // Return the PromptNow bit if the credential hasn't been refreshed recently.
  2938. //
  2939. Credential->Flags &= ~CRED_FLAGS_PROMPT_NOW;
  2940. if ( !PersistCredBlob( Credential ) ) {
  2941. PPROMPT_DATA PromptData;
  2942. //
  2943. // Get the prompt data for this credential
  2944. //
  2945. PromptData = CredpFindPromptData( CredentialSets,
  2946. &InputCredential->TargetName,
  2947. Credential->Type,
  2948. Credential->Persist );
  2949. //
  2950. // If we've never prompted for this credential since logon,
  2951. // Prompt now
  2952. //
  2953. if ( ShouldPromptNow( PromptData )) {
  2954. Credential->Flags |= CRED_FLAGS_PROMPT_NOW;
  2955. }
  2956. }
  2957. return EncryptedCredential;
  2958. }
  2959. BOOLEAN
  2960. CredpMarshalCredentials(
  2961. IN PCREDENTIAL_SET CredentialSet,
  2962. OUT LPDWORD BufferSize,
  2963. OUT LPBYTE *Buffer
  2964. )
  2965. /*++
  2966. Routine Description:
  2967. This routine grabs all of the credentials from a Credential Set and
  2968. marshals them into a single buffer.
  2969. On entry, UserCredentialSets->CritSect must be locked.
  2970. Arguments:
  2971. CredentialSet - Credential set containing the credentials to marshal
  2972. BufferSize - Returns the size of the marshaled credentials
  2973. Returns zero if there are no credentials.
  2974. Buffer - Returns a buffer containing the marshaled credentials
  2975. The buffer must be freed using LsapFreeLsaHeap.
  2976. Return Values:
  2977. TRUE - Buffer was sucessfully marshaled
  2978. FALSE - Buffer could not be allocated
  2979. --*/
  2980. {
  2981. PLIST_ENTRY ListEntry;
  2982. PCANONICAL_CREDENTIAL Credential;
  2983. ULONG ReturnBufferSize;
  2984. PMARSHALED_CREDENTIAL_SET ReturnBuffer;
  2985. ULONG i;
  2986. LPBYTE Where;
  2987. LPBYTE OldWhere;
  2988. LPBYTE LocalCredBlob = NULL;
  2989. ULONG LocalCredBlobSize = 0;
  2990. //
  2991. // Loop through the list of credentials computing the size of the return buffer
  2992. //
  2993. ReturnBufferSize = 0;
  2994. for ( ListEntry = CredentialSet->Credentials.Flink ;
  2995. ListEntry != &CredentialSet->Credentials;
  2996. ListEntry = ListEntry->Flink) {
  2997. Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  2998. ReturnBufferSize += sizeof(MARSHALED_CREDENTIAL);
  2999. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
  3000. ReturnBufferSize += sizeof(ULONG) + Credential->TargetName.MaximumLength;
  3001. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
  3002. ReturnBufferSize += sizeof(ULONG) + (Credential->Cred.Comment == NULL ? 0 : ((wcslen( Credential->Cred.Comment ) + 1) * sizeof(WCHAR)));
  3003. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
  3004. ReturnBufferSize += sizeof(ULONG) + Credential->TargetAlias.MaximumLength;
  3005. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
  3006. ReturnBufferSize += sizeof(ULONG) + Credential->UserName.MaximumLength;
  3007. if ( PersistCredBlob( &Credential->Cred ) ) {
  3008. ReturnBufferSize += sizeof(ULONG) + Credential->ClearCredentialBlobSize;
  3009. LocalCredBlobSize = max( LocalCredBlobSize, Credential->Cred.CredentialBlobSize );
  3010. } else {
  3011. ReturnBufferSize += sizeof(ULONG);
  3012. }
  3013. for ( i=0; i<Credential->Cred.AttributeCount; i++ ) {
  3014. ReturnBufferSize += sizeof(ULONG);
  3015. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WCHAR );
  3016. ReturnBufferSize += sizeof(ULONG) + (Credential->Cred.Attributes[i].Keyword == NULL ? 0 : (wcslen( Credential->Cred.Attributes[i].Keyword ) + 1 ) * sizeof(WCHAR) );
  3017. ReturnBufferSize += sizeof(ULONG) + Credential->Cred.Attributes[i].ValueSize;
  3018. }
  3019. ReturnBufferSize = ROUND_UP_COUNT( ReturnBufferSize, ALIGN_WORST );
  3020. }
  3021. //
  3022. // If there is nothing to marshal,
  3023. // we're done.
  3024. //
  3025. if ( ReturnBufferSize == 0 ) {
  3026. *Buffer = NULL;
  3027. *BufferSize = 0;
  3028. return TRUE;
  3029. }
  3030. //
  3031. // Allocate a buffer to return to the caller.
  3032. //
  3033. ReturnBufferSize += ROUND_UP_COUNT( sizeof(MARSHALED_CREDENTIAL_SET), ALIGN_WORST );
  3034. ReturnBuffer = (PMARSHALED_CREDENTIAL_SET) LsapAllocateLsaHeap( ReturnBufferSize );
  3035. if ( ReturnBuffer == NULL) {
  3036. return FALSE;
  3037. }
  3038. //
  3039. // Allocate a buffer to decrypt the credential blob into
  3040. //
  3041. SafeAllocaAllocate( LocalCredBlob, LocalCredBlobSize );
  3042. if ( LocalCredBlob == NULL ) {
  3043. LsapFreeLsaHeap( ReturnBuffer );
  3044. return FALSE;
  3045. }
  3046. //
  3047. // Create the buffer header.
  3048. //
  3049. ReturnBuffer->Version = MARSHALED_CREDENTIAL_SET_VERSION;
  3050. ReturnBuffer->Size = ReturnBufferSize;
  3051. OldWhere = (PUCHAR)(ReturnBuffer+1);
  3052. Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
  3053. RtlZeroMemory( OldWhere, Where-OldWhere );
  3054. //
  3055. // Copy the individual credentials
  3056. //
  3057. for ( ListEntry = CredentialSet->Credentials.Flink ;
  3058. ListEntry != &CredentialSet->Credentials;
  3059. ListEntry = ListEntry->Flink) {
  3060. PMARSHALED_CREDENTIAL CredEntry;
  3061. ULONG CommentSize;
  3062. Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  3063. CredEntry = (PMARSHALED_CREDENTIAL)Where;
  3064. //
  3065. // Copy the fixed size fields into the buffer.
  3066. //
  3067. CredEntry->Flags = Credential->Cred.Flags;
  3068. CredEntry->Type = Credential->Cred.Type;
  3069. CredEntry->LastWritten = Credential->Cred.LastWritten;
  3070. CredEntry->Persist = Credential->Cred.Persist;
  3071. CredEntry->AttributeCount = Credential->Cred.AttributeCount;
  3072. CredEntry->Expansion1 = 0;
  3073. CredEntry->Expansion2 = 0;
  3074. Where = (LPBYTE)(CredEntry+1);
  3075. //
  3076. // Copy the strings
  3077. //
  3078. #define CredpMarshalBytes( _Ptr, _Size, _Align ) \
  3079. OldWhere = Where; \
  3080. Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, _Align ); \
  3081. RtlZeroMemory( OldWhere, Where-OldWhere ); \
  3082. SmbPutUlong( Where, (_Size) ); \
  3083. Where += sizeof(ULONG); \
  3084. if ( _Size != 0 ) { \
  3085. RtlCopyMemory( Where, (_Ptr), (_Size) ); \
  3086. Where += (_Size); \
  3087. }
  3088. CredpMarshalBytes( Credential->TargetName.Buffer, Credential->TargetName.MaximumLength, ALIGN_WCHAR );
  3089. CommentSize = Credential->Cred.Comment == NULL ? 0 : (wcslen( Credential->Cred.Comment ) + 1 ) * sizeof(WCHAR);
  3090. CredpMarshalBytes( Credential->Cred.Comment, CommentSize, ALIGN_WCHAR );
  3091. CredpMarshalBytes( Credential->TargetAlias.Buffer, Credential->TargetAlias.MaximumLength, ALIGN_WCHAR );
  3092. CredpMarshalBytes( Credential->UserName.Buffer, Credential->UserName.MaximumLength, ALIGN_WCHAR );
  3093. //
  3094. // Marshal the (decrypted) credential itself
  3095. //
  3096. if ( PersistCredBlob( &Credential->Cred ) ) {
  3097. //
  3098. // Grab a local copy of the cred blob to decrypt into
  3099. //
  3100. if ( Credential->Cred.CredentialBlobSize != 0 ) {
  3101. RtlCopyMemory( LocalCredBlob,
  3102. Credential->Cred.CredentialBlob,
  3103. Credential->Cred.CredentialBlobSize );
  3104. LsaUnprotectMemory( LocalCredBlob,
  3105. Credential->Cred.CredentialBlobSize );
  3106. }
  3107. CredpMarshalBytes( LocalCredBlob, Credential->ClearCredentialBlobSize, ALIGN_BYTE );
  3108. } else {
  3109. CredpMarshalBytes( NULL, 0, ALIGN_BYTE );
  3110. }
  3111. //
  3112. // Marshal the attributes
  3113. //
  3114. for ( i=0; i<CredEntry->AttributeCount; i++ ) {
  3115. ULONG KeywordSize;
  3116. SmbPutUlong( Where, Credential->Cred.Attributes[i].Flags );
  3117. Where += sizeof(ULONG);
  3118. KeywordSize = Credential->Cred.Attributes[i].Keyword == NULL ? 0 : (wcslen( Credential->Cred.Attributes[i].Keyword ) + 1) * sizeof(WCHAR);
  3119. CredpMarshalBytes( Credential->Cred.Attributes[i].Keyword, KeywordSize, ALIGN_WCHAR );
  3120. CredpMarshalBytes( Credential->Cred.Attributes[i].Value,
  3121. Credential->Cred.Attributes[i].ValueSize,
  3122. ALIGN_BYTE );
  3123. }
  3124. //
  3125. // Zero any padding bytes
  3126. //
  3127. OldWhere = Where;
  3128. Where = (PUCHAR) ROUND_UP_POINTER( OldWhere, ALIGN_WORST );
  3129. RtlZeroMemory( OldWhere, Where-OldWhere );
  3130. //
  3131. // Remember the size of this credential.
  3132. //
  3133. CredEntry->EntrySize = (ULONG)(Where - ((LPBYTE)CredEntry));
  3134. }
  3135. //
  3136. // Sanity check
  3137. //
  3138. if ( ReturnBufferSize != (ULONG)(Where - ((LPBYTE)ReturnBuffer))) {
  3139. DebugLog(( DEB_TRACE_CRED,
  3140. "CredpMarshalCredentials: Marshaled %ld bytes into a buffer %ld long.\n",
  3141. Where - ((LPBYTE)ReturnBuffer),
  3142. ReturnBufferSize ));
  3143. }
  3144. //
  3145. // Return the buffer to the caller
  3146. //
  3147. *Buffer = (LPBYTE) ReturnBuffer;
  3148. *BufferSize = ReturnBufferSize;
  3149. //
  3150. // Free the temp buffer
  3151. //
  3152. if ( LocalCredBlob != NULL ) {
  3153. RtlZeroMemory( LocalCredBlob, LocalCredBlobSize );
  3154. SafeAllocaFree( LocalCredBlob );
  3155. }
  3156. return TRUE;
  3157. }
  3158. DWORD
  3159. CreateNestedDirectories(
  3160. IN LPWSTR szFullPath,
  3161. IN LPWSTR szCreationStartPoint // must point in null-terminated range of szFullPath
  3162. )
  3163. /*++
  3164. Create all subdirectories if they do not exists starting at
  3165. szCreationStartPoint.
  3166. szCreationStartPoint must point to a character within the null terminated
  3167. buffer specified by the szFullPath parameter.
  3168. Note that szCreationStartPoint should not point at the first character
  3169. of a drive root, eg:
  3170. d:\foo\bar\bilge\water
  3171. \\server\share\foo\bar
  3172. \\?\d:\big\path\bilge\water
  3173. Instead, szCreationStartPoint should point beyond these components, eg:
  3174. bar\bilge\water
  3175. foo\bar
  3176. big\path\bilge\water
  3177. This function does not implement logic for adjusting to compensate for these
  3178. inputs because the environment it was design to be used in causes the input
  3179. szCreationStartPoint to point well into the szFullPath input buffer.
  3180. This function stolen from crypto api.
  3181. --*/
  3182. {
  3183. DWORD i;
  3184. DWORD cchRemaining;
  3185. DWORD LastError = ERROR_SUCCESS;
  3186. BOOL fSuccess = FALSE;
  3187. if( szCreationStartPoint < szFullPath ||
  3188. szCreationStartPoint > (lstrlenW(szFullPath) + szFullPath)
  3189. )
  3190. return ERROR_INVALID_PARAMETER;
  3191. cchRemaining = lstrlenW( szCreationStartPoint );
  3192. //
  3193. // scan from left to right in the szCreationStartPoint string
  3194. // looking for directory delimiter.
  3195. //
  3196. for ( i = 0 ; i < cchRemaining ; i++ ) {
  3197. WCHAR charReplaced = szCreationStartPoint[ i ];
  3198. if( charReplaced == L'\\' || charReplaced == L'/' ) {
  3199. szCreationStartPoint[ i ] = L'\0';
  3200. fSuccess = CreateDirectoryW( szFullPath, NULL );
  3201. szCreationStartPoint[ i ] = charReplaced;
  3202. if( !fSuccess ) {
  3203. LastError = GetLastError();
  3204. if( LastError != ERROR_ALREADY_EXISTS ) {
  3205. //
  3206. // continue onwards, trying to create specified subdirectories.
  3207. // this is done to address the obscure scenario where
  3208. // the Bypass Traverse Checking Privilege allows the caller
  3209. // to create directories below an existing path where one
  3210. // component denies the user access.
  3211. // we just keep trying and the last CreateDirectory()
  3212. // result is returned to the caller.
  3213. //
  3214. continue;
  3215. }
  3216. }
  3217. LastError = ERROR_SUCCESS;
  3218. }
  3219. }
  3220. //
  3221. // check if the last directory creation actually succeeded.
  3222. // if it did, we need to adjust the file attributes on that directory
  3223. // and its parent.
  3224. //
  3225. if( fSuccess ) {
  3226. SetFileAttributesW( szFullPath, FILE_ATTRIBUTE_SYSTEM );
  3227. //
  3228. // now, scan from right to left looking for the prior directory
  3229. // de-limiter.
  3230. //
  3231. if( cchRemaining < 2 )
  3232. return LastError;
  3233. for ( i = (cchRemaining-2) ; i > 0 ; i-- ) {
  3234. WCHAR charReplaced = szCreationStartPoint[ i ];
  3235. if( charReplaced == L'\\' || charReplaced == L'/' ) {
  3236. szCreationStartPoint[ i ] = L'\0';
  3237. SetFileAttributesW( szFullPath, FILE_ATTRIBUTE_SYSTEM );
  3238. szCreationStartPoint[ i ] = charReplaced;
  3239. break;
  3240. }
  3241. }
  3242. }
  3243. return LastError;
  3244. }
  3245. DWORD
  3246. GetUserStorageArea(
  3247. IN int csidl,
  3248. IN PSID UserSid,
  3249. OUT LPWSTR *UserStorageArea
  3250. )
  3251. /*++
  3252. Routine Description:
  3253. Get the path to user's roaming profile. The subdirectories are created as needed.
  3254. Caller must be impersonating the user.
  3255. Arguments:
  3256. csidl - Specifies which system directory to use as a root.
  3257. UserSid - Sid of the user
  3258. UserStorageArea - Returns the path of the user storage area
  3259. The buffer must be freed using LsapFreeLsaHeap
  3260. Return Values:
  3261. NO_ERROR: Path returned properly
  3262. ERROR_CANTOPEN: Path could not be found
  3263. --*/
  3264. {
  3265. DWORD WinStatus;
  3266. NTSTATUS Status;
  3267. WCHAR szUserStorageRoot[MAX_PATH+1];
  3268. DWORD cbUserStorageRoot;
  3269. const WCHAR szProductString[] = L"\\Microsoft\\Credentials\\";
  3270. DWORD cbProductString = sizeof(szProductString) - sizeof(WCHAR);
  3271. HANDLE hFile = INVALID_HANDLE_VALUE;
  3272. PBYTE Where;
  3273. UNICODE_STRING UserSidString;
  3274. typedef HRESULT (WINAPI *SHGETFOLDERPATHW)(
  3275. HWND hwnd,
  3276. int csidl,
  3277. HANDLE hToken,
  3278. DWORD dwFlags,
  3279. LPWSTR pszPath
  3280. );
  3281. static SHGETFOLDERPATHW _SHGetFolderPathW;
  3282. HANDLE hToken;
  3283. //
  3284. // Initialization
  3285. //
  3286. *UserStorageArea = NULL;
  3287. RtlInitUnicodeString( &UserSidString, NULL );
  3288. //
  3289. // Load shell32.dll
  3290. //
  3291. if(_SHGetFolderPathW == NULL) {
  3292. HMODULE hShell32 = LoadLibraryW( L"shell32.dll" );
  3293. if(hShell32 == NULL) {
  3294. WinStatus = ERROR_CANTOPEN;
  3295. goto Cleanup;
  3296. }
  3297. _SHGetFolderPathW = (SHGETFOLDERPATHW)GetProcAddress(hShell32, "SHGetFolderPathW");
  3298. if( _SHGetFolderPathW == NULL ) {
  3299. WinStatus = ERROR_CANTOPEN;
  3300. goto Cleanup;
  3301. }
  3302. }
  3303. //
  3304. // Get the path of the "Application Data" folder
  3305. //
  3306. if( !OpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken )) {
  3307. WinStatus = GetLastError();
  3308. goto Cleanup;
  3309. }
  3310. WinStatus = (DWORD)_SHGetFolderPathW( NULL, csidl | CSIDL_FLAG_CREATE, hToken, 0, szUserStorageRoot );
  3311. CloseHandle( hToken );
  3312. if( WinStatus != ERROR_SUCCESS ) {
  3313. goto Cleanup;
  3314. }
  3315. cbUserStorageRoot = wcslen( szUserStorageRoot ) * sizeof(WCHAR);
  3316. //
  3317. // An empty string is not legal as the root component of the per-user
  3318. // storage area.
  3319. //
  3320. if( cbUserStorageRoot == 0 ) {
  3321. WinStatus = ERROR_CANTOPEN;
  3322. goto Cleanup;
  3323. }
  3324. //
  3325. // Ensure returned string does not have trailing \
  3326. //
  3327. if( szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] == L'\\' ) {
  3328. szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] = L'\0';
  3329. cbUserStorageRoot -= sizeof(WCHAR);
  3330. }
  3331. //
  3332. // Convert the SID to a text string
  3333. //
  3334. Status = RtlConvertSidToUnicodeString( &UserSidString, UserSid, TRUE );
  3335. if ( !NT_SUCCESS(Status) ) {
  3336. WinStatus = ERROR_NOT_ENOUGH_MEMORY;
  3337. goto Cleanup;
  3338. }
  3339. //
  3340. // Allocate memory for the return string
  3341. //
  3342. *UserStorageArea = (LPWSTR)LsapAllocateLsaHeap(
  3343. cbUserStorageRoot +
  3344. cbProductString +
  3345. UserSidString.Length +
  3346. (2 * sizeof(WCHAR)) // trailing slash and NULL
  3347. );
  3348. if( *UserStorageArea == NULL ) {
  3349. WinStatus = ERROR_NOT_ENOUGH_MEMORY;
  3350. goto Cleanup;
  3351. }
  3352. //
  3353. // Build the name as of the path.
  3354. //
  3355. Where = (PBYTE)*UserStorageArea;
  3356. RtlCopyMemory(Where, szUserStorageRoot, cbUserStorageRoot);
  3357. Where += cbUserStorageRoot;
  3358. RtlCopyMemory(Where, szProductString, cbProductString);
  3359. Where += cbProductString;
  3360. RtlCopyMemory(Where, UserSidString.Buffer, UserSidString.Length);
  3361. Where += UserSidString.Length; // note: does not include terminal NULL
  3362. if( *((LPWSTR)Where - 1) != L'\\' ) {
  3363. *(LPWSTR)Where = L'\\';
  3364. Where += sizeof(WCHAR);
  3365. }
  3366. *(LPWSTR)Where = L'\0';
  3367. //
  3368. // Ensure the directory exists
  3369. //
  3370. WinStatus = CreateNestedDirectories(
  3371. *UserStorageArea,
  3372. (LPWSTR)((LPBYTE)*UserStorageArea + cbUserStorageRoot + sizeof(WCHAR)) );
  3373. Cleanup:
  3374. RtlFreeUnicodeString( &UserSidString );
  3375. if( WinStatus != ERROR_SUCCESS && *UserStorageArea ) {
  3376. LsapFreeLsaHeap( *UserStorageArea );
  3377. *UserStorageArea = NULL;
  3378. }
  3379. return WinStatus;
  3380. }
  3381. DWORD
  3382. OpenFileInStorageArea(
  3383. IN DWORD dwDesiredAccess,
  3384. IN LPCWSTR szUserStorageArea,
  3385. IN LPCWSTR szFileName,
  3386. IN OUT HANDLE *phFile
  3387. )
  3388. // Routine stolen from crypto API
  3389. {
  3390. LPWSTR szFilePath = NULL;
  3391. DWORD cbUserStorageArea;
  3392. DWORD cbFileName;
  3393. DWORD dwShareMode = 0;
  3394. DWORD dwCreationDistribution = OPEN_EXISTING;
  3395. DWORD LastError = ERROR_SUCCESS;
  3396. *phFile = INVALID_HANDLE_VALUE;
  3397. if( dwDesiredAccess & GENERIC_READ ) {
  3398. dwShareMode |= FILE_SHARE_READ;
  3399. dwCreationDistribution = OPEN_EXISTING;
  3400. }
  3401. if( dwDesiredAccess & GENERIC_WRITE ) {
  3402. dwShareMode = 0;
  3403. dwCreationDistribution = CREATE_ALWAYS;
  3404. }
  3405. cbUserStorageArea = wcslen( szUserStorageArea ) * sizeof(WCHAR);
  3406. cbFileName = wcslen( szFileName ) * sizeof(WCHAR);
  3407. SafeAllocaAllocate( szFilePath, cbUserStorageArea + cbFileName + sizeof(WCHAR) );
  3408. if( szFilePath == NULL )
  3409. return ERROR_NOT_ENOUGH_MEMORY;
  3410. CopyMemory(szFilePath, szUserStorageArea, cbUserStorageArea);
  3411. CopyMemory((LPBYTE)szFilePath+cbUserStorageArea, szFileName, cbFileName + sizeof(WCHAR));
  3412. if( LastError == ERROR_SUCCESS ) {
  3413. //
  3414. // TODO:
  3415. // apply security descriptor to file.
  3416. //
  3417. *phFile = CreateFileW(
  3418. szFilePath,
  3419. dwDesiredAccess,
  3420. dwShareMode,
  3421. NULL,
  3422. dwCreationDistribution,
  3423. FILE_ATTRIBUTE_HIDDEN |
  3424. FILE_ATTRIBUTE_SYSTEM |
  3425. FILE_FLAG_SEQUENTIAL_SCAN,
  3426. NULL
  3427. );
  3428. if( *phFile == INVALID_HANDLE_VALUE ) {
  3429. LastError = GetLastError();
  3430. }
  3431. }
  3432. if(szFilePath)
  3433. SafeAllocaFree(szFilePath);
  3434. return LastError;
  3435. }
  3436. DWORD
  3437. DeleteFileInStorageArea(
  3438. IN LPCWSTR szUserStorageArea,
  3439. IN LPCWSTR szFileName
  3440. )
  3441. // Routine stolen from crypto API
  3442. {
  3443. LPWSTR szFilePath = NULL;
  3444. DWORD cbUserStorageArea;
  3445. DWORD cbFileName;
  3446. DWORD LastError = ERROR_SUCCESS;
  3447. cbUserStorageArea = wcslen( szUserStorageArea ) * sizeof(WCHAR);
  3448. cbFileName = wcslen( szFileName ) * sizeof(WCHAR);
  3449. SafeAllocaAllocate( szFilePath, cbUserStorageArea + cbFileName + sizeof(WCHAR) );
  3450. if( szFilePath == NULL )
  3451. return ERROR_NOT_ENOUGH_MEMORY;
  3452. CopyMemory(szFilePath, szUserStorageArea, cbUserStorageArea);
  3453. CopyMemory((LPBYTE)szFilePath+cbUserStorageArea, szFileName, cbFileName + sizeof(WCHAR));
  3454. if ( !DeleteFileW( szFilePath ) ) {
  3455. LastError = GetLastError();
  3456. }
  3457. if(szFilePath)
  3458. SafeAllocaFree(szFilePath);
  3459. return LastError;
  3460. }
  3461. BOOL
  3462. CredpGetUnicodeString(
  3463. IN LPBYTE BufferEnd,
  3464. IN OUT LPBYTE *Where,
  3465. OUT LPWSTR *String
  3466. )
  3467. /*++
  3468. Routine Description:
  3469. Determine if a UNICODE string in a message buffer is valid.
  3470. UNICODE strings always appear at a 2-byte boundary in the message.
  3471. Arguments:
  3472. BufferEnd - A pointer to the first byte beyond the end of the buffer.
  3473. Where - Indirectly points to the current location in the buffer. The
  3474. string at the current location is validated (i.e., checked to ensure
  3475. its length is within the bounds of the message buffer and not too
  3476. long). If the string is valid, this current location is updated
  3477. to point to the byte following the zero byte in the message buffer.
  3478. String - Returns a pointer to the validated string.
  3479. Pointer is to the buffer *Where points to.
  3480. Returns NULL for empty strings.
  3481. Return Value:
  3482. TRUE - the string is valid.
  3483. FALSE - the string is invalid.
  3484. --*/
  3485. {
  3486. DWORD Size;
  3487. LPWSTR ZeroPtr;
  3488. //
  3489. // Align the unicode string on a WCHAR boundary.
  3490. //
  3491. *Where = (LPBYTE) ROUND_UP_POINTER( *Where, ALIGN_WCHAR );
  3492. if ( (*Where) + sizeof(ULONG) > BufferEnd ) {
  3493. DebugLog(( DEB_TRACE_CRED,
  3494. "CredpGetUnicodeString: String size after buffer end: %lx %lx.\n",
  3495. *Where,
  3496. BufferEnd ));
  3497. return FALSE;
  3498. }
  3499. //
  3500. // Get the string size (in bytes)
  3501. //
  3502. Size = SmbGetUlong( *Where );
  3503. *Where += sizeof(ULONG);
  3504. if ( Size == 0 ) {
  3505. *String = NULL;
  3506. return TRUE;
  3507. }
  3508. if ( *Where >= BufferEnd ) {
  3509. DebugLog(( DEB_TRACE_CRED,
  3510. "CredpGetUnicodeString: String after buffer end: %lx %lx.\n",
  3511. *Where,
  3512. BufferEnd ));
  3513. return FALSE;
  3514. }
  3515. //
  3516. // Ensure the size is aligned
  3517. //
  3518. if ( Size != ROUND_UP_COUNT( Size, ALIGN_WCHAR) ) {
  3519. DebugLog(( DEB_TRACE_CRED,
  3520. "CredpGetUnicodeString: Size not aligned: %lx.\n",
  3521. Size ));
  3522. return FALSE;
  3523. }
  3524. //
  3525. // Limit the string to the number of bytes remaining in the message buffer.
  3526. //
  3527. if ( Size > (ULONG)(BufferEnd - (*Where)) ) {
  3528. DebugLog(( DEB_TRACE_CRED,
  3529. "CredpGetUnicodeString: String too big: %lx %lx %lx.\n",
  3530. *Where,
  3531. BufferEnd,
  3532. Size ));
  3533. return FALSE;
  3534. }
  3535. //
  3536. // Ensure the trailing zero exists
  3537. //
  3538. if ( ((LPWSTR)(*Where))[(Size/sizeof(WCHAR))-1] != L'\0' ) {
  3539. DebugLog(( DEB_TRACE_CRED,
  3540. "CredpGetUnicodeString: No trailing zero: %lx.\n",
  3541. Size ));
  3542. return FALSE;
  3543. }
  3544. //
  3545. // Ensure there aren't extra zero bytes.
  3546. //
  3547. ZeroPtr = wcschr( (LPWSTR)(*Where), L'\0' );
  3548. if ( ZeroPtr < &((LPWSTR)(*Where))[(Size/sizeof(WCHAR))-1] ) {
  3549. DebugLog(( DEB_TRACE_CRED,
  3550. "Trailing zero in middle of string: %lx.\n",
  3551. Size ));
  3552. return FALSE;
  3553. }
  3554. //
  3555. // Position 'Where' past the end of the string.
  3556. //
  3557. *String = (LPWSTR)(*Where);
  3558. *Where += Size;
  3559. return TRUE;
  3560. }
  3561. BOOL
  3562. CredpGetBytes(
  3563. IN LPBYTE BufferEnd,
  3564. IN OUT LPBYTE *Where,
  3565. OUT LPDWORD BufferSize,
  3566. OUT LPBYTE *Buffer
  3567. )
  3568. /*++
  3569. Routine Description:
  3570. Unmarshal an array of bytes from a message buffer.
  3571. Arguments:
  3572. BufferEnd - A pointer to the first byte beyond the end of the buffer.
  3573. Where - Indirectly points to the current location in the buffer. The
  3574. string at the current location is validated (i.e., checked to ensure
  3575. its length is within the bounds of the message buffer and not too
  3576. long). If the string is valid, this current location is updated
  3577. to point to the byte following the data bytes in the message buffer.
  3578. BufferSize - Returns the size (in bytes) of the data.
  3579. Buffer - Returns a pointer to the validated data.
  3580. Pointer is to the buffer *Where points to.
  3581. Returns NULL for zero length data.
  3582. Return Value:
  3583. TRUE - the string is valid.
  3584. FALSE - the string is invalid.
  3585. --*/
  3586. {
  3587. DWORD Size;
  3588. //
  3589. // Get the string size (in bytes)
  3590. //
  3591. Size = SmbGetUlong( *Where );
  3592. *Where += sizeof(ULONG);
  3593. if ( Size == 0 ) {
  3594. *Buffer = NULL;
  3595. *BufferSize = 0;
  3596. return TRUE;
  3597. }
  3598. if ( *Where >= BufferEnd ) {
  3599. DebugLog(( DEB_TRACE_CRED,
  3600. "CredpGetBytes: String after buffer end: %lx %lx.\n",
  3601. *Where,
  3602. BufferEnd ));
  3603. return FALSE;
  3604. }
  3605. //
  3606. // Limit the string to the number of bytes remaining in the message buffer.
  3607. //
  3608. if ( Size > (ULONG)(BufferEnd - (*Where)) ) {
  3609. DebugLog(( DEB_TRACE_CRED,
  3610. "CredpGetBytes: String too big: %lx %lx %lx.\n",
  3611. *Where,
  3612. BufferEnd,
  3613. Size ));
  3614. return FALSE;
  3615. }
  3616. //
  3617. // Position 'Where' past the end of the string.
  3618. //
  3619. *Buffer = *Where;
  3620. *BufferSize = Size;
  3621. *Where += Size;
  3622. return TRUE;
  3623. }
  3624. VOID
  3625. CredpMarkDirty(
  3626. IN PCREDENTIAL_SETS CredentialSets,
  3627. IN ULONG Persist,
  3628. IN PCANONICAL_CREDENTIAL Credential OPTIONAL
  3629. )
  3630. /*++
  3631. Routine Description:
  3632. The routine marks a credential set dirty and updates the last written
  3633. time on a credential.
  3634. On entry, UserCredentialSets->CritSect must be locked.
  3635. Arguments:
  3636. CredentialSets - Credential set to mark.
  3637. Persist - Persistence of the credential set to mark
  3638. Credential - Specifies the modified credential.
  3639. If NULL, no specific credential was modified.
  3640. Return Values:
  3641. None.
  3642. --*/
  3643. {
  3644. //
  3645. // Mark the credential set dirty.
  3646. //
  3647. PersistToCredentialSet( CredentialSets, Persist )->Dirty = TRUE;
  3648. //
  3649. // Mark the credential as modified
  3650. //
  3651. if ( Credential != NULL ) {
  3652. LsapQuerySystemTime( &Credential->Cred.LastWritten );
  3653. }
  3654. }
  3655. DWORD
  3656. CredpReadCredSet(
  3657. IN PCREDENTIAL_SETS CredentialSets,
  3658. IN ULONG Persist
  3659. )
  3660. /*++
  3661. Routine Description:
  3662. The routine reads a credential set from disk
  3663. On entry, UserCredentialSets->CritSect must be locked.
  3664. Arguments:
  3665. CredentialSets - Credential sets to read into
  3666. Persist - Persistence of the credential set to read
  3667. Return Values:
  3668. NO_ERROR - Credential set read successfully
  3669. --*/
  3670. {
  3671. DWORD WinStatus;
  3672. NTSTATUS Status;
  3673. PCREDENTIAL_SET CredentialSet;
  3674. PMARSHALED_CREDENTIAL_SET CredSetBuffer = NULL;
  3675. ULONG CredSetBufferSize = 0;
  3676. LPBYTE CredSetBufferEnd;
  3677. PMARSHALED_CREDENTIAL CredEntry;
  3678. LIST_ENTRY CredentialList;
  3679. PCANONICAL_CREDENTIAL TempCredential;
  3680. LPWSTR FilePath = NULL;
  3681. LPWSTR FileName = CREDENTIAL_FILE_NAME;
  3682. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  3683. PCREDENTIAL_ATTRIBUTEW Attributes = NULL;
  3684. BOOLEAN CritSectLocked = TRUE;
  3685. ULONG i;
  3686. ULONG BytesRead;
  3687. PVOID EncryptedBlob = NULL;
  3688. ULONG EncryptedBlobSize;
  3689. LPBYTE LocalCredBlob = NULL;
  3690. ULONG LocalCredBlobSize = 0;
  3691. PLIST_ENTRY ListEntry;
  3692. LPBYTE Where;
  3693. //
  3694. // Initialization
  3695. //
  3696. InitializeListHead( &CredentialList );
  3697. //
  3698. // Ignore non-persistent credential sets
  3699. //
  3700. if ( Persist == CRED_PERSIST_SESSION ) {
  3701. return NO_ERROR;
  3702. }
  3703. CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
  3704. //
  3705. // Drop the lock while we're doing the read. DPAPI accesses network resources
  3706. // while encrypting. We don't want to hold up cred set access just because the
  3707. // network is slow.
  3708. //
  3709. RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  3710. CritSectLocked = FALSE;
  3711. //
  3712. // Get the name of the path to read the cred set from
  3713. //
  3714. WinStatus = GetUserStorageArea(
  3715. (Persist == CRED_PERSIST_ENTERPRISE ? CSIDL_APPDATA : CSIDL_LOCAL_APPDATA),
  3716. CredentialSets->UserCredentialSets->UserSid,
  3717. &FilePath );
  3718. if ( WinStatus != NO_ERROR ) {
  3719. if ( WinStatus == ERROR_CANTOPEN ) {
  3720. WinStatus = NO_ERROR;
  3721. } else {
  3722. DebugLog(( DEB_TRACE_CRED,
  3723. "CredpReadCredSet: Cannot determine path to profile: %ld.\n",
  3724. WinStatus ));
  3725. }
  3726. goto Cleanup;
  3727. }
  3728. //
  3729. // Open the file
  3730. //
  3731. WinStatus = OpenFileInStorageArea(
  3732. GENERIC_READ,
  3733. FilePath,
  3734. FileName,
  3735. &FileHandle );
  3736. if ( WinStatus != NO_ERROR ) {
  3737. if ( WinStatus == ERROR_FILE_NOT_FOUND ||
  3738. WinStatus == ERROR_PATH_NOT_FOUND ) {
  3739. WinStatus = NO_ERROR;
  3740. } else {
  3741. DebugLog(( DEB_TRACE_CRED,
  3742. "CredpReadCredSet: Cannot open file %ls\\%ls: %ld.\n",
  3743. FilePath,
  3744. FileName,
  3745. WinStatus ));
  3746. }
  3747. goto Cleanup;
  3748. }
  3749. //
  3750. // Get the size of the file.
  3751. //
  3752. EncryptedBlobSize = GetFileSize( FileHandle, NULL );
  3753. if ( EncryptedBlobSize == 0xFFFFFFFF ) {
  3754. WinStatus = GetLastError();
  3755. DebugLog(( DEB_TRACE_CRED,
  3756. "CredpReadCredSet: Cannot GetFileSize %ls\\%ls: %ld.\n",
  3757. FilePath,
  3758. FileName,
  3759. WinStatus ));
  3760. WinStatus = ERROR_INVALID_DATA;
  3761. goto Cleanup;
  3762. }
  3763. if ( EncryptedBlobSize < 1 ) {
  3764. DebugLog(( DEB_TRACE_CRED,
  3765. "CredpReadCredSet: Size too small %ls\\%ls: %ld.\n",
  3766. FilePath,
  3767. FileName,
  3768. EncryptedBlobSize ));
  3769. WinStatus = ERROR_INVALID_DATA;
  3770. goto Cleanup;
  3771. }
  3772. //
  3773. // Allocate a buffer to read the file into.
  3774. //
  3775. SafeAllocaAllocate( EncryptedBlob, EncryptedBlobSize );
  3776. if ( EncryptedBlob == NULL ) {
  3777. WinStatus = ERROR_NOT_ENOUGH_MEMORY;
  3778. goto Cleanup;
  3779. }
  3780. //
  3781. // Read the file into the buffer.
  3782. //
  3783. if ( !ReadFile( FileHandle,
  3784. EncryptedBlob,
  3785. EncryptedBlobSize,
  3786. &BytesRead,
  3787. NULL ) ) { // Not Overlapped
  3788. WinStatus = GetLastError();
  3789. DebugLog(( DEB_TRACE_CRED,
  3790. "CredpReadCredSet: Cannot ReadFile %ls\\%ls: %ld.\n",
  3791. FilePath,
  3792. FileName,
  3793. WinStatus ));
  3794. WinStatus = ERROR_INVALID_DATA;
  3795. goto Cleanup;
  3796. }
  3797. if ( BytesRead != EncryptedBlobSize ) {
  3798. DebugLog(( DEB_TRACE_CRED,
  3799. "CredpReadCredSet: Cannot read entire file %ls\\%ls: %ld %ld.\n",
  3800. FilePath,
  3801. FileName,
  3802. BytesRead,
  3803. EncryptedBlobSize ));
  3804. WinStatus = ERROR_INVALID_DATA;
  3805. goto Cleanup;
  3806. }
  3807. //
  3808. // Decrypt the data
  3809. //
  3810. if ( !LsaICryptUnprotectData(
  3811. EncryptedBlob,
  3812. EncryptedBlobSize,
  3813. NULL, // No additional entropy
  3814. 0,
  3815. NULL, // Must be NULL
  3816. NULL, // Must be NULL
  3817. CRYPTPROTECT_SYSTEM | // Cannot be decrypted by usermode app
  3818. CRYPTPROTECT_VERIFY_PROTECTION | // Tell us if the encryption algorithm needs to change
  3819. CRYPTPROTECT_UI_FORBIDDEN, // No UI allowed
  3820. NULL, // No description of the data
  3821. (PVOID *)&CredSetBuffer,
  3822. &CredSetBufferSize ) ) {
  3823. WinStatus = GetLastError();
  3824. DebugLog(( DEB_TRACE_CRED,
  3825. "CredpReadCredSet: Cannot CryptUnprotectData: 0x%lx.\n",
  3826. WinStatus ));
  3827. WinStatus = ERROR_INVALID_DATA;
  3828. goto Cleanup;
  3829. }
  3830. //
  3831. // If the encryption algorithm changed,
  3832. // encrypt the data with the new algorithm
  3833. //
  3834. WinStatus = GetLastError();
  3835. if ( WinStatus == CRYPT_I_NEW_PROTECTION_REQUIRED ) {
  3836. DebugLog(( DEB_TRACE_CRED,
  3837. "CredpReadCredSet: Need to change encryption algorithm: 0x%lx.\n",
  3838. WinStatus ));
  3839. // The easiest way to do that is to simply mark the cred set as dirty.
  3840. CredpMarkDirty( CredentialSets, Persist, NULL );
  3841. }
  3842. //
  3843. // Validate the returned data.
  3844. //
  3845. if ( CredSetBufferSize < sizeof(MARSHALED_CREDENTIAL_SET) ) {
  3846. DebugLog(( DEB_TRACE_CRED,
  3847. "CredpReadCredSet: Size too small %ls\\%ls: %ld.\n",
  3848. FilePath,
  3849. FileName,
  3850. CredSetBufferSize ));
  3851. WinStatus = ERROR_INVALID_DATA;
  3852. goto Cleanup;
  3853. }
  3854. if ( CredSetBuffer->Version != MARSHALED_CREDENTIAL_SET_VERSION ) {
  3855. DebugLog(( DEB_TRACE_CRED,
  3856. "CredpReadCredSet: Version wrong %ls\\%ls: %ld %ld.\n",
  3857. FilePath,
  3858. FileName,
  3859. CredSetBuffer->Version,
  3860. MARSHALED_CREDENTIAL_SET_VERSION ));
  3861. WinStatus = ERROR_INVALID_DATA;
  3862. goto Cleanup;
  3863. }
  3864. if ( CredSetBuffer->Size != CredSetBufferSize ) {
  3865. DebugLog(( DEB_TRACE_CRED,
  3866. "CredpReadCredSet: Size wrong %ls\\%ls: %ld %ld.\n",
  3867. FilePath,
  3868. FileName,
  3869. CredSetBuffer->Size,
  3870. CredSetBufferSize ));
  3871. WinStatus = ERROR_INVALID_DATA;
  3872. goto Cleanup;
  3873. }
  3874. //
  3875. // Loop through each log entry.
  3876. //
  3877. CredSetBufferEnd = ((LPBYTE)CredSetBuffer) + CredSetBufferSize;
  3878. CredEntry = (PMARSHALED_CREDENTIAL)ROUND_UP_POINTER( (CredSetBuffer + 1), ALIGN_WORST );
  3879. while ( (LPBYTE)(CredEntry+1) <= CredSetBufferEnd ) {
  3880. LPBYTE CredEntryEnd;
  3881. ENCRYPTED_CREDENTIALW LocalCredential;
  3882. CredEntryEnd = ((LPBYTE)CredEntry) + CredEntry->EntrySize;
  3883. //
  3884. // Cleanup from a previous iteration.
  3885. //
  3886. if ( Attributes != NULL ) {
  3887. LsapFreeLsaHeap( Attributes );
  3888. Attributes = NULL;
  3889. }
  3890. //
  3891. // Ensure this entry is entirely within the allocated buffer.
  3892. //
  3893. if ( CredEntryEnd > CredSetBufferEnd ) {
  3894. DebugLog(( DEB_TRACE_CRED,
  3895. "CredpReadCredSet: Entry too big %ls\\%ls: %ld %ld.\n",
  3896. FilePath,
  3897. FileName,
  3898. ((LPBYTE)CredEntry)-((LPBYTE)CredSetBuffer),
  3899. CredEntry->EntrySize ));
  3900. WinStatus = ERROR_INVALID_DATA;
  3901. goto Cleanup;
  3902. }
  3903. //
  3904. // Validate the entry
  3905. //
  3906. if ( !COUNT_IS_ALIGNED(CredEntry->EntrySize, ALIGN_WORST) ) {
  3907. DebugLog(( DEB_TRACE_CRED,
  3908. "CredpReadCredSet: EntrySize not aligned %ls\\%ls: %ld.\n",
  3909. FilePath,
  3910. FileName,
  3911. CredEntry->EntrySize ));
  3912. WinStatus = ERROR_INVALID_DATA;
  3913. goto Cleanup;
  3914. }
  3915. //
  3916. // Grab the Position past the fixed size data for the entry.
  3917. //
  3918. Where = (LPBYTE) (CredEntry+1);
  3919. if ( Where >= CredEntryEnd ) {
  3920. DebugLog(( DEB_TRACE_CRED,
  3921. "CredpReadCredSet: Data after record missing %ls\\%ls: %lx %lx.\n",
  3922. FilePath,
  3923. FileName,
  3924. Where,
  3925. CredEntryEnd ));
  3926. WinStatus = ERROR_INVALID_DATA;
  3927. goto Cleanup;
  3928. }
  3929. //
  3930. // Copy the fixed size fields into the buffer.
  3931. //
  3932. RtlZeroMemory( &LocalCredential, sizeof(LocalCredential) );
  3933. LocalCredential.Cred.Flags = CredEntry->Flags;
  3934. LocalCredential.Cred.Type = CredEntry->Type;
  3935. LocalCredential.Cred.LastWritten = CredEntry->LastWritten;
  3936. LocalCredential.Cred.Persist = Persist;
  3937. LocalCredential.Cred.AttributeCount = CredEntry->AttributeCount;
  3938. //
  3939. // Copy the strings
  3940. //
  3941. if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.TargetName ) ) {
  3942. DebugLog(( DEB_TRACE_CRED,
  3943. "CredpReadCredSet: TargetName string broken %ls\\%ls: %lx %lx.\n",
  3944. FilePath,
  3945. FileName,
  3946. Where,
  3947. CredEntryEnd ));
  3948. WinStatus = ERROR_INVALID_DATA;
  3949. goto Cleanup;
  3950. }
  3951. if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.Comment ) ) {
  3952. DebugLog(( DEB_TRACE_CRED,
  3953. "CredpReadCredSet: Comment string broken %ls\\%ls: %lx %lx.\n",
  3954. FilePath,
  3955. FileName,
  3956. Where,
  3957. CredEntryEnd ));
  3958. WinStatus = ERROR_INVALID_DATA;
  3959. goto Cleanup;
  3960. }
  3961. if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.TargetAlias ) ) {
  3962. DebugLog(( DEB_TRACE_CRED,
  3963. "CredpReadCredSet: TargetAlias string broken %ls\\%ls: %lx %lx.\n",
  3964. FilePath,
  3965. FileName,
  3966. Where,
  3967. CredEntryEnd ));
  3968. WinStatus = ERROR_INVALID_DATA;
  3969. goto Cleanup;
  3970. }
  3971. if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.UserName ) ) {
  3972. DebugLog(( DEB_TRACE_CRED,
  3973. "CredpReadCredSet: UserName string broken %ls\\%ls: %lx %lx.\n",
  3974. FilePath,
  3975. FileName,
  3976. Where,
  3977. CredEntryEnd ));
  3978. WinStatus = ERROR_INVALID_DATA;
  3979. goto Cleanup;
  3980. }
  3981. //
  3982. // Unmarshal the clear credential itself
  3983. //
  3984. if ( !CredpGetBytes( CredEntryEnd,
  3985. &Where,
  3986. &LocalCredential.Cred.CredentialBlobSize,
  3987. &LocalCredential.Cred.CredentialBlob ) ) {
  3988. DebugLog(( DEB_TRACE_CRED,
  3989. "CredpReadCredSet: Credential broken %ls\\%ls: %lx %lx.\n",
  3990. FilePath,
  3991. FileName,
  3992. Where,
  3993. CredEntryEnd ));
  3994. WinStatus = ERROR_INVALID_DATA;
  3995. goto Cleanup;
  3996. }
  3997. // Clear cred have both buffers the same size.
  3998. LocalCredential.ClearCredentialBlobSize = LocalCredential.Cred.CredentialBlobSize;
  3999. //
  4000. // Marshal the attributes
  4001. //
  4002. if ( CredEntry->AttributeCount != 0) {
  4003. //
  4004. // Allocate an array to point to the aliases.
  4005. //
  4006. Attributes = (PCREDENTIAL_ATTRIBUTEW) LsapAllocateLsaHeap(
  4007. CredEntry->AttributeCount * sizeof(CREDENTIAL_ATTRIBUTEW) );
  4008. if ( Attributes == NULL ) {
  4009. WinStatus = ERROR_NOT_ENOUGH_MEMORY;
  4010. goto Cleanup;
  4011. }
  4012. LocalCredential.Cred.Attributes = Attributes;
  4013. //
  4014. // Loop unmarshaling the aliases.
  4015. //
  4016. for ( i=0; i<CredEntry->AttributeCount; i++ ) {
  4017. //
  4018. // Get the flags
  4019. //
  4020. LocalCredential.Cred.Attributes[i].Flags = SmbGetUlong( Where );
  4021. Where += sizeof(ULONG);
  4022. //
  4023. // Get the keyword
  4024. //
  4025. if ( !CredpGetUnicodeString( CredEntryEnd, &Where, &LocalCredential.Cred.Attributes[i].Keyword ) ) {
  4026. DebugLog(( DEB_TRACE_CRED,
  4027. "CredpReadCredSet: Keyword %ld broken %ls\\%ls: %lx %lx.\n",
  4028. i,
  4029. FilePath,
  4030. FileName,
  4031. Where,
  4032. CredEntryEnd ));
  4033. WinStatus = ERROR_INVALID_DATA;
  4034. goto Cleanup;
  4035. }
  4036. //
  4037. // Get the value
  4038. //
  4039. if ( !CredpGetBytes( CredEntryEnd,
  4040. &Where,
  4041. &LocalCredential.Cred.Attributes[i].ValueSize,
  4042. &LocalCredential.Cred.Attributes[i].Value ) ) {
  4043. DebugLog(( DEB_TRACE_CRED,
  4044. "CredpReadCredSet: Value %ld broken %ls\\%ls: %lx %lx.\n",
  4045. i,
  4046. FilePath,
  4047. FileName,
  4048. Where,
  4049. CredEntryEnd ));
  4050. WinStatus = ERROR_INVALID_DATA;
  4051. goto Cleanup;
  4052. }
  4053. }
  4054. }
  4055. //
  4056. // Canonicalize the credential.
  4057. //
  4058. Status = CredpValidateCredential(
  4059. CREDP_FLAGS_CLEAR_PASSWORD,
  4060. NULL, // No target info
  4061. &LocalCredential,
  4062. &TempCredential );
  4063. if ( !NT_SUCCESS(Status) ) {
  4064. if ( Status == ERROR_INVALID_PARAMETER ) {
  4065. // This isn't fatal. Just ignore this one credential
  4066. } else {
  4067. WinStatus = RtlNtStatusToDosError( Status );
  4068. goto Cleanup;
  4069. }
  4070. //
  4071. // Put it on a local list until we finish reading the entire log file
  4072. //
  4073. } else {
  4074. InsertTailList( &CredentialList, &TempCredential->Next );
  4075. }
  4076. //
  4077. // Move to the next entry.
  4078. //
  4079. CredEntry = (PMARSHALED_CREDENTIAL)(((LPBYTE)CredEntry) + CredEntry->EntrySize);
  4080. }
  4081. WinStatus = NO_ERROR;
  4082. //
  4083. // Be tidy.
  4084. //
  4085. Cleanup:
  4086. if ( !CritSectLocked ) {
  4087. RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  4088. }
  4089. //
  4090. // Free the temp buffer
  4091. //
  4092. if ( LocalCredBlob != NULL ) {
  4093. RtlZeroMemory( LocalCredBlob, LocalCredBlobSize );
  4094. SafeAllocaFree( LocalCredBlob );
  4095. }
  4096. //
  4097. // If the file cannot be read,
  4098. // that's not really an error.
  4099. //
  4100. if ( WinStatus == ERROR_INVALID_DATA ) {
  4101. // Leave it lying around to allow debugging.
  4102. WinStatus = NO_ERROR;
  4103. }
  4104. //
  4105. // If we're successful so far,
  4106. // add the temporary credential list to the in-memory credential set.
  4107. //
  4108. if ( WinStatus == NO_ERROR ) {
  4109. //
  4110. // Loop through the credentials adding them to the in-memory credential set.
  4111. //
  4112. while ( !IsListEmpty( &CredentialList ) ) {
  4113. ListEntry = RemoveHeadList( &CredentialList );
  4114. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  4115. //
  4116. // Write it to the credential set
  4117. //
  4118. Status = CredpWriteCredential( CredentialSets,
  4119. &TempCredential,
  4120. TRUE, // Creds are from persisted file
  4121. FALSE, // Don't update pin in CSP
  4122. FALSE, // Creds have not been prompted for
  4123. 0, // No flags
  4124. NULL,
  4125. NULL,
  4126. NULL );
  4127. if ( !NT_SUCCESS( Status )) {
  4128. LsapFreeLsaHeap( TempCredential );
  4129. //
  4130. // Remember the status. But continue on.
  4131. //
  4132. WinStatus = RtlNtStatusToDosError( Status );
  4133. }
  4134. }
  4135. }
  4136. //
  4137. // Cleanup temporary credential list
  4138. //
  4139. while ( !IsListEmpty( &CredentialList ) ) {
  4140. ListEntry = RemoveHeadList( &CredentialList );
  4141. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  4142. LsapFreeLsaHeap( TempCredential );
  4143. }
  4144. //
  4145. // Clear the buffer with clear text passwords in it
  4146. //
  4147. if ( CredSetBuffer != NULL ) {
  4148. RtlZeroMemory( CredSetBuffer, CredSetBufferSize );
  4149. LocalFree( CredSetBuffer );
  4150. }
  4151. if ( EncryptedBlob != NULL ) {
  4152. SafeAllocaFree( EncryptedBlob );
  4153. }
  4154. if ( FilePath != NULL ) {
  4155. LsapFreeLsaHeap( FilePath );
  4156. }
  4157. if ( FileHandle != INVALID_HANDLE_VALUE ) {
  4158. CloseHandle( FileHandle );
  4159. }
  4160. if ( Attributes != NULL ) {
  4161. LsapFreeLsaHeap( Attributes );
  4162. Attributes = NULL;
  4163. }
  4164. return WinStatus;
  4165. }
  4166. VOID
  4167. CredpLockCredSets(
  4168. IN PCREDENTIAL_SETS CredentialSets
  4169. )
  4170. /*++
  4171. Routine Description:
  4172. This routine locks a set of credential sets.
  4173. Arguments:
  4174. CredentialSets - Credential set to lock
  4175. Return Values:
  4176. None.
  4177. --*/
  4178. {
  4179. DWORD WinStatus;
  4180. //
  4181. // Lock the credential set.
  4182. //
  4183. // There's one crit sect the proctects all of the cred sets for the user.
  4184. // We could introduce one crit sect per credential set, but the user-wide crit
  4185. // sect would always be locked along with one of the session crit sects.
  4186. // So, the session crit sect would be wasted.
  4187. //
  4188. RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  4189. }
  4190. VOID
  4191. CredpUnlockAndFlushCredSets(
  4192. IN PCREDENTIAL_SETS CredentialSets
  4193. )
  4194. /*++
  4195. Routine Description:
  4196. This routine unlocks a set of credential sets and flushes it to disk if dirty.
  4197. Arguments:
  4198. CredentialSets - Credential set to lock
  4199. Return Values:
  4200. None.
  4201. --*/
  4202. {
  4203. PCREDENTIAL_SET CredentialSet;
  4204. PCANONICAL_CREDENTIAL Credential;
  4205. PLIST_ENTRY ListEntry;
  4206. ULONG Persist;
  4207. //
  4208. // Loop through the list of credential sets persisting each
  4209. //
  4210. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  4211. //
  4212. // Ignore non-persistent credential sets
  4213. //
  4214. if ( Persist == CRED_PERSIST_SESSION ) {
  4215. continue;
  4216. }
  4217. CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
  4218. //
  4219. // If the credential set is dirty,
  4220. // flush it.
  4221. //
  4222. if ( CredentialSet->Dirty ) {
  4223. //
  4224. // Indicate that the credential set is no longer dirty.
  4225. // But increment that count of times that it has been dirty.
  4226. //
  4227. CredentialSet->Dirty = FALSE;
  4228. CredentialSet->WriteCount ++;
  4229. //
  4230. // If no other thread is already writing it,
  4231. // we'll take on that responsibility.
  4232. //
  4233. // Otherwise, let that thread write it again.
  4234. //
  4235. if ( !CredentialSet->BeingWritten ) {
  4236. ULONG WriteCount;
  4237. //
  4238. // Tell other threads that we're writing
  4239. //
  4240. CredentialSet->BeingWritten = TRUE;
  4241. //
  4242. // Loop writing the credentials
  4243. //
  4244. do {
  4245. LPBYTE CredSetBuffer;
  4246. ULONG CredSetBufferSize;
  4247. LPWSTR FilePath;
  4248. LPWSTR FileName = CREDENTIAL_FILE_NAME;
  4249. DWORD WinStatus;
  4250. //
  4251. // Remember which snapshot we're writing.
  4252. //
  4253. WriteCount = CredentialSet->WriteCount;
  4254. FilePath = NULL;
  4255. //
  4256. // Grab a marshaled copy of the credential set
  4257. //
  4258. if ( !CredpMarshalCredentials( CredentialSet,
  4259. &CredSetBufferSize,
  4260. &CredSetBuffer ) ) {
  4261. //
  4262. // If we can't, mark them dirty for the next caller to flush
  4263. //
  4264. CredentialSet->Dirty = TRUE;
  4265. continue;
  4266. }
  4267. //
  4268. // Drop the lock while we're doing the write. DPAPI accesses network resources
  4269. // while encrypting. We don't want to hold up cred set access just because the
  4270. // network is slow.
  4271. //
  4272. RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  4273. //
  4274. // Get the name of the path to write the cred set to
  4275. //
  4276. WinStatus = GetUserStorageArea(
  4277. (Persist == CRED_PERSIST_ENTERPRISE ? CSIDL_APPDATA : CSIDL_LOCAL_APPDATA),
  4278. CredentialSets->UserCredentialSets->UserSid,
  4279. &FilePath );
  4280. if ( WinStatus != NO_ERROR ) {
  4281. DebugLog(( DEB_TRACE_CRED,
  4282. "CredpUnlockAndFlushCredSets: Cannot determine path to profile: %ld.\n",
  4283. WinStatus ));
  4284. } else {
  4285. //
  4286. // If the buffer is zero length,
  4287. // just delete the file
  4288. //
  4289. if ( CredSetBufferSize == 0 ) {
  4290. WinStatus = DeleteFileInStorageArea( FilePath, FileName );
  4291. if ( WinStatus != NO_ERROR ) {
  4292. if ( WinStatus == ERROR_FILE_NOT_FOUND ||
  4293. WinStatus == ERROR_PATH_NOT_FOUND ) {
  4294. WinStatus = NO_ERROR;
  4295. } else {
  4296. DebugLog(( DEB_TRACE_CRED,
  4297. "CredpUnlockAndFlushCredSets: Cannot delete %ls\\%ls: %ld.\n",
  4298. FilePath,
  4299. FileName,
  4300. WinStatus ));
  4301. }
  4302. }
  4303. //
  4304. // Otherwise, write data to the file.
  4305. //
  4306. } else {
  4307. PVOID EncryptedBlob;
  4308. ULONG EncryptedBlobSize;
  4309. UNICODE_STRING DescriptionString;
  4310. //
  4311. // Encrypt the data
  4312. //
  4313. RtlInitUnicodeString( &DescriptionString,
  4314. Persist == CRED_PERSIST_ENTERPRISE ?
  4315. L"Enterprise Credential Set" :
  4316. L"Local Credential Set" );
  4317. if ( !LsaICryptProtectData(
  4318. CredSetBuffer,
  4319. CredSetBufferSize,
  4320. &DescriptionString,
  4321. NULL, // No additional entropy
  4322. 0,
  4323. NULL, // Must be NULL
  4324. NULL, // Must be NULL
  4325. CRYPTPROTECT_SYSTEM | // Cannot be decrypted by usermode app
  4326. CRYPTPROTECT_UI_FORBIDDEN, // No UI allowed
  4327. &EncryptedBlob,
  4328. &EncryptedBlobSize ) ) {
  4329. WinStatus = GetLastError();
  4330. DebugLog(( DEB_TRACE_CRED,
  4331. "CredpUnlockAndFlushCredSets: Cannot CryptProtectData: 0x%lx.\n",
  4332. WinStatus ));
  4333. } else {
  4334. HANDLE FileHandle;
  4335. //
  4336. // Open the file
  4337. //
  4338. WinStatus = OpenFileInStorageArea(
  4339. GENERIC_WRITE,
  4340. FilePath,
  4341. FileName,
  4342. &FileHandle );
  4343. if ( WinStatus == NO_ERROR ) {
  4344. ULONG BytesWritten;
  4345. //
  4346. // Write the file
  4347. //
  4348. if ( !WriteFile( FileHandle,
  4349. EncryptedBlob,
  4350. EncryptedBlobSize,
  4351. &BytesWritten,
  4352. NULL ) ) { // Not Overlapped
  4353. WinStatus = GetLastError();
  4354. DebugLog(( DEB_TRACE_CRED,
  4355. "CredpUnlockAndFlushCredSets: Cannot write %ls\\%ls: %ld.\n",
  4356. FilePath,
  4357. FileName,
  4358. WinStatus ));
  4359. } else {
  4360. if ( BytesWritten != EncryptedBlobSize ) {
  4361. DebugLog(( DEB_TRACE_CRED,
  4362. "CredpUnlockAndFlushCredSets: Cannot write all of %ls\\%ls: %ld %ld.\n",
  4363. FilePath,
  4364. FileName,
  4365. EncryptedBlobSize,
  4366. BytesWritten ));
  4367. WinStatus = ERROR_INSUFFICIENT_BUFFER;
  4368. }
  4369. }
  4370. CloseHandle( FileHandle );
  4371. }
  4372. LocalFree( EncryptedBlob );
  4373. }
  4374. }
  4375. }
  4376. //
  4377. // If we failed to write for any reason,
  4378. // ensure the next caller does the flush.
  4379. //
  4380. if ( WinStatus != NO_ERROR ) {
  4381. CredentialSet->Dirty = FALSE;
  4382. }
  4383. //
  4384. // Clear the buffer with clear text passwords in it
  4385. //
  4386. if ( CredSetBufferSize != 0 ) {
  4387. RtlZeroMemory( CredSetBuffer, CredSetBufferSize );
  4388. LsapFreeLsaHeap( CredSetBuffer );
  4389. }
  4390. //
  4391. // Free any other resources
  4392. //
  4393. if ( FilePath != NULL ) {
  4394. LsapFreeLsaHeap( FilePath );
  4395. }
  4396. //
  4397. // Grab the lock again to see if we need to write again
  4398. //
  4399. RtlEnterCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  4400. } while ( CredentialSet->WriteCount != WriteCount );
  4401. //
  4402. // Tell other threads that we're no longer writing
  4403. //
  4404. CredentialSet->BeingWritten = FALSE;
  4405. }
  4406. }
  4407. }
  4408. //
  4409. // Unlock the credential set.
  4410. //
  4411. RtlLeaveCriticalSection( &CredentialSets->UserCredentialSets->CritSect );
  4412. }
  4413. PCANONICAL_CREDENTIAL
  4414. CredpFindCredential(
  4415. IN PCREDENTIAL_SETS CredentialSets,
  4416. IN PUNICODE_STRING TargetName,
  4417. IN ULONG Type
  4418. )
  4419. /*++
  4420. Routine Description:
  4421. This routine finds a named credential in a credential set.
  4422. On entry, UserCredentialSets->CritSect must be locked.
  4423. Arguments:
  4424. CredentialSet - Credential set list to find the credential in.
  4425. TargetName - Name of the credential to find.
  4426. Type - Type of the credential to find.
  4427. Return Values:
  4428. Returns a pointer to the named credential. This pointer my be used as long as
  4429. UserCredentialSets->CritSect remains locked.
  4430. NULL: There is no such credential.
  4431. --*/
  4432. {
  4433. PCREDENTIAL_SET CredentialSet;
  4434. PCANONICAL_CREDENTIAL Credential;
  4435. PLIST_ENTRY ListEntry;
  4436. ULONG Persist;
  4437. //
  4438. // Ignore queries for unsupported cred types
  4439. //
  4440. if ( CredDisableDomainCreds &&
  4441. CredpIsDomainCredential( Type ) ) {
  4442. return NULL;
  4443. }
  4444. //
  4445. // Loop through the list of credentials trying to find this one.
  4446. //
  4447. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  4448. //
  4449. // If the profile has not yet been loaded by this session,
  4450. // ignore any credentials loaded by another session.
  4451. //
  4452. if ( Persist != CRED_PERSIST_SESSION &&
  4453. !CredentialSets->SessionCredSets->ProfileLoaded ) {
  4454. continue;
  4455. }
  4456. CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
  4457. for ( ListEntry = CredentialSet->Credentials.Flink ;
  4458. ListEntry != &CredentialSet->Credentials;
  4459. ListEntry = ListEntry->Flink) {
  4460. Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  4461. if ( Type == Credential->Cred.Type &&
  4462. RtlEqualUnicodeString( TargetName,
  4463. &Credential->TargetName,
  4464. TRUE ) ) {
  4465. return Credential;
  4466. }
  4467. }
  4468. }
  4469. return NULL;
  4470. }
  4471. NTSTATUS
  4472. CredpWritePinToCsp(
  4473. IN PCREDENTIAL_SETS CredentialSets,
  4474. IN PCANONICAL_CREDENTIAL Credential
  4475. )
  4476. /*++
  4477. Routine Description:
  4478. This routine write a PIN to the CSP. CSPs implement the rules for the lifetime of
  4479. the PIN. Cred manager therefore doesn't hold onto the PIN, but rather gives it to
  4480. the CSP to manage.
  4481. The caller must be impersonating the logged on user owning the cred set.
  4482. Arguments:
  4483. CredentialSets - Credential set the credential is in.
  4484. Credential - Specifies the credential whose PIN is to be written.
  4485. Return Values:
  4486. The following status codes may be returned:
  4487. STATUS_SUCCESS - The PIN was stored successfully.
  4488. Or the credential isn't a cert credential.
  4489. STATUS_INVALID_PARAMETER
  4490. STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
  4491. existing credential. If such a field does not match the value
  4492. specified in the existing credential, this error is returned.
  4493. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  4494. Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
  4495. --*/
  4496. {
  4497. NTSTATUS Status;
  4498. DWORD WinStatus;
  4499. CRED_MARSHAL_TYPE CredType;
  4500. PCERT_CREDENTIAL_INFO CredInfo;
  4501. CRYPT_HASH_BLOB HashBlob;
  4502. HCERTSTORE CertStoreHandle = NULL;
  4503. PCCERT_CONTEXT CertContext = NULL;
  4504. ULONG ProviderInfoSize;
  4505. PCRYPT_KEY_PROV_INFO ProviderInfo = NULL;
  4506. HCRYPTPROV ProviderHandle = NULL;
  4507. UNICODE_STRING UnicodePin;
  4508. ANSI_STRING AnsiPin;
  4509. BOOLEAN ClearPin = FALSE;
  4510. CERT_KEY_CONTEXT CertKeyContext;
  4511. //
  4512. // Initialization
  4513. //
  4514. RtlInitAnsiString( &AnsiPin, NULL );
  4515. //
  4516. // Ignore credentials that aren't certificate credentials
  4517. //
  4518. if ( Credential->Cred.Type != CRED_TYPE_DOMAIN_CERTIFICATE ) {
  4519. Status = STATUS_SUCCESS;
  4520. goto Cleanup;
  4521. }
  4522. //
  4523. // Ignore pins of zero length.
  4524. //
  4525. // Either the caller is writing the cred and doesn't know the PIN yet.
  4526. // Or this cert has no PIN.
  4527. //
  4528. if ( Credential->Cred.CredentialBlobSize == 0 ) {
  4529. Status = STATUS_SUCCESS;
  4530. goto Cleanup;
  4531. }
  4532. //
  4533. // If the profile hasn't been loaded,
  4534. // fail now.
  4535. //
  4536. // The "My" store open will fail below and it'd be better to give a good error code.
  4537. //
  4538. ClearPin = TRUE;
  4539. if ( !CredentialSets->SessionCredSets->ProfileLoaded ) {
  4540. Status = SCARD_E_NO_SUCH_CERTIFICATE;
  4541. DebugLog(( DEB_TRACE_CRED,
  4542. "CredpWritePinToCsp: %ws: Cannot write PIN to cert since profile isn't loaded: 0x%lx.\n",
  4543. Credential->Cred.UserName,
  4544. Status ));
  4545. goto Cleanup;
  4546. }
  4547. //
  4548. // Convert the UserName of the credential to a hash of the cert
  4549. //
  4550. if (!CredUnmarshalCredentialW(
  4551. Credential->Cred.UserName,
  4552. &CredType,
  4553. (PVOID *)&CredInfo ) ) {
  4554. WinStatus = GetLastError();
  4555. if ( WinStatus == ERROR_INVALID_PARAMETER ) {
  4556. Status = STATUS_INVALID_PARAMETER;
  4557. } else {
  4558. Status = NetpApiStatusToNtStatus(WinStatus);
  4559. }
  4560. DebugLog(( DEB_TRACE_CRED,
  4561. "CredpWritePinToCsp: %ws: Cannot unmarshal user name of cert cred: 0x%lx.\n",
  4562. Credential->Cred.UserName,
  4563. Status ));
  4564. goto Cleanup;
  4565. }
  4566. if ( CredType != CertCredential ) {
  4567. DebugLog(( DEB_TRACE_CRED,
  4568. "CredpWritePinToCsp: %ws: cred isn't cert cred: %ld.\n",
  4569. Credential->Cred.UserName,
  4570. CredType ));
  4571. Status = STATUS_INVALID_PARAMETER;
  4572. goto Cleanup;
  4573. }
  4574. //
  4575. // Open a cert store
  4576. //
  4577. CertStoreHandle = CertOpenStore(
  4578. CERT_STORE_PROV_SYSTEM_W,
  4579. 0,
  4580. 0,
  4581. CERT_SYSTEM_STORE_CURRENT_USER,
  4582. L"MY");
  4583. if ( CertStoreHandle == NULL ) {
  4584. WinStatus = GetLastError();
  4585. if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
  4586. Status = WinStatus;
  4587. } else {
  4588. Status = NetpApiStatusToNtStatus(WinStatus);
  4589. }
  4590. DebugLog(( DEB_TRACE_CRED,
  4591. "CredpWritePinToCsp: %ws: cannot open cert store: %ld 0x%lx.\n",
  4592. Credential->Cred.UserName,
  4593. WinStatus,
  4594. WinStatus ));
  4595. goto Cleanup;
  4596. }
  4597. //
  4598. // Find the cert in the store which meets this hash
  4599. //
  4600. HashBlob.cbData = sizeof(CredInfo->rgbHashOfCert);
  4601. HashBlob.pbData = CredInfo->rgbHashOfCert;
  4602. CertContext = CertFindCertificateInStore(
  4603. CertStoreHandle,
  4604. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  4605. 0,
  4606. CERT_FIND_HASH,
  4607. &HashBlob,
  4608. NULL );
  4609. if ( CertContext == NULL ) {
  4610. WinStatus = GetLastError();
  4611. if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
  4612. Status = WinStatus;
  4613. } else if ( WinStatus == CRYPT_E_NOT_FOUND ) {
  4614. Status = SCARD_E_NO_SUCH_CERTIFICATE;
  4615. } else {
  4616. Status = NetpApiStatusToNtStatus(WinStatus);
  4617. }
  4618. DebugLog(( DEB_TRACE_CRED,
  4619. "CredpWritePinToCsp: %ws: cannot find cert: %ld 0x%lx.\n",
  4620. Credential->Cred.UserName,
  4621. WinStatus,
  4622. WinStatus ));
  4623. goto Cleanup;
  4624. }
  4625. //
  4626. // Get a handle to the private key
  4627. //
  4628. if ( !CryptAcquireCertificatePrivateKey(
  4629. CertContext,
  4630. CRYPT_ACQUIRE_SILENT_FLAG, // Ensure it doesn't prompt
  4631. NULL, // reserved
  4632. &ProviderHandle,
  4633. NULL,
  4634. NULL ) ) {
  4635. WinStatus = GetLastError();
  4636. if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
  4637. Status = WinStatus;
  4638. } else if ( WinStatus == NTE_BAD_KEYSET ) {
  4639. // Some CSPs (Schlumberger Cryptoflex) return the wrong status
  4640. Status = SCARD_W_REMOVED_CARD;
  4641. } else {
  4642. Status = NetpApiStatusToNtStatus(WinStatus);
  4643. }
  4644. DebugLog(( DEB_TRACE_CRED,
  4645. "CredpWritePinToCsp: %ws: cannot CryptAcquireCertificatePrivateKey: %ld 0x%lx.\n",
  4646. Credential->Cred.UserName,
  4647. WinStatus,
  4648. WinStatus ));
  4649. goto Cleanup;
  4650. }
  4651. //
  4652. // Convert the pin to ANSI
  4653. //
  4654. LsaUnprotectMemory( Credential->Cred.CredentialBlob,
  4655. Credential->Cred.CredentialBlobSize );
  4656. UnicodePin.Buffer = (LPWSTR) Credential->Cred.CredentialBlob;
  4657. UnicodePin.Length = (USHORT) Credential->ClearCredentialBlobSize;
  4658. UnicodePin.MaximumLength = (USHORT) Credential->ClearCredentialBlobSize;
  4659. Status = RtlUnicodeStringToAnsiString( &AnsiPin,
  4660. &UnicodePin,
  4661. TRUE );
  4662. LsaProtectMemory( Credential->Cred.CredentialBlob,
  4663. Credential->Cred.CredentialBlobSize );
  4664. if ( !NT_SUCCESS( Status )) {
  4665. DebugLog(( DEB_TRACE_CRED,
  4666. "CredpWritePinToCsp: %ws: Cannot convert PIN '%wZ' to ANSI: 0x%lx.\n",
  4667. Credential->Cred.UserName,
  4668. &UnicodePin,
  4669. Status ));
  4670. goto Cleanup;
  4671. }
  4672. //
  4673. // Set the pin in the provider
  4674. //
  4675. if (!CryptSetProvParam(
  4676. ProviderHandle,
  4677. PP_KEYEXCHANGE_PIN,
  4678. (LPBYTE)AnsiPin.Buffer,
  4679. 0 )) {
  4680. WinStatus = GetLastError();
  4681. //
  4682. // Some certs don't require a PIN
  4683. //
  4684. if ( WinStatus == NTE_BAD_TYPE ) {
  4685. //
  4686. // If the caller didn't pass us a PIN,
  4687. // we're fine.
  4688. //
  4689. if ( AnsiPin.Length == 0 ) {
  4690. WinStatus = NO_ERROR;
  4691. //
  4692. // If the caller did pass us a PIN,
  4693. // tell him.
  4694. //
  4695. } else {
  4696. WinStatus = ERROR_INVALID_PASSWORD;
  4697. }
  4698. }
  4699. if ( HRESULT_FACILITY(WinStatus) == FACILITY_SCARD ) {
  4700. Status = WinStatus;
  4701. } else if ( WinStatus == ERROR_ACCOUNT_DISABLED ) {
  4702. // Some CSPs (Schlumberger Cryptoflex) return the wrong status
  4703. Status = SCARD_W_CHV_BLOCKED;
  4704. } else {
  4705. Status = NetpApiStatusToNtStatus(WinStatus);
  4706. }
  4707. DebugLog(( DEB_TRACE_CRED,
  4708. "CredpWritePinToCsp: %ws: cannot CryptSetProvParam: %ld 0x%lx.\n",
  4709. Credential->Cred.UserName,
  4710. WinStatus,
  4711. WinStatus ));
  4712. goto Cleanup;
  4713. }
  4714. Status = STATUS_SUCCESS;
  4715. //
  4716. // Free any locally used resources
  4717. //
  4718. Cleanup:
  4719. //
  4720. // The whole reason we wrote the PIN to the CSP was so that cred man wouldn't store it.
  4721. // So clear it.
  4722. //
  4723. if ( ClearPin ) {
  4724. if ( Credential->Cred.CredentialBlob != NULL &&
  4725. Credential->Cred.CredentialBlobSize != 0 ) {
  4726. RtlZeroMemory( Credential->Cred.CredentialBlob,
  4727. Credential->Cred.CredentialBlobSize );
  4728. }
  4729. Credential->Cred.CredentialBlob = NULL;
  4730. Credential->Cred.CredentialBlobSize = 0;
  4731. Credential->ClearCredentialBlobSize = 0;
  4732. // Cred it already marked dirty
  4733. }
  4734. if ( AnsiPin.Buffer != NULL ) {
  4735. RtlZeroMemory( AnsiPin.Buffer, AnsiPin.Length );
  4736. RtlFreeAnsiString( &AnsiPin );
  4737. }
  4738. if ( ProviderHandle != NULL ) {
  4739. CryptReleaseContext( ProviderHandle, 0 );
  4740. }
  4741. if (NULL != ProviderInfo) {
  4742. LsapFreeLsaHeap( ProviderInfo );
  4743. }
  4744. if ( CertContext != NULL ) {
  4745. CertFreeCertificateContext( CertContext );
  4746. }
  4747. if ( CertStoreHandle != NULL ) {
  4748. CertCloseStore( CertStoreHandle, 0 );
  4749. }
  4750. return Status;
  4751. }
  4752. NTSTATUS
  4753. CredpWriteCredential(
  4754. IN PCREDENTIAL_SETS CredentialSets,
  4755. IN OUT PCANONICAL_CREDENTIAL *NewCredential,
  4756. IN BOOLEAN FromPersistedFile,
  4757. IN BOOLEAN WritePinToCsp,
  4758. IN BOOLEAN PromptedFor,
  4759. IN DWORD Flags,
  4760. OUT PCANONICAL_CREDENTIAL *OldCredential OPTIONAL,
  4761. OUT PPROMPT_DATA *OldPromptData OPTIONAL,
  4762. OUT PPROMPT_DATA *NewPromptData OPTIONAL
  4763. )
  4764. /*++
  4765. Routine Description:
  4766. The routine writes a credential to a credential set. If the credential
  4767. replaces an existing credential, the existing credential is delinked and
  4768. returned to the caller.
  4769. On entry, UserCredentialSets->CritSect must be locked. (This design ensures
  4770. Credential is valid upon return from the routine.)
  4771. Arguments:
  4772. CredentialSets - Credential set to write the credential in.
  4773. NewCredential - Specifies the credential to be written.
  4774. If CRED_PRESERVE_CREDENTIAL_BLOB is specified, the Credential returned in this
  4775. parameter will contain the preserved credential blob. The original credential will be
  4776. deleted.
  4777. If CRED_PRESERVE_CREDENTIAL_BLOB is not specified, this field will not be modified.
  4778. FromPersistedFile - True if the credential is from a persisted file.
  4779. Less validation is done on the credential.
  4780. WritePinToCsp - True if the password is explicitly being set.
  4781. If true, the PIN of certificate credentials is to be written to the CSP.
  4782. Callers that are simply updating the in-memory credential will set this false.
  4783. PromptedFor - Specifies whether Credential has already been prompted for
  4784. Flags - Specifies flags to control the operation of the API.
  4785. The following flags are defined:
  4786. CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
  4787. already existing credential with the same credential name and credential type.
  4788. OldCredential - If specified, returns a pointer to the old credential
  4789. Returns NULL if there was no old credential.
  4790. OldCredential should be freed using LsapFreeLsaHeap.
  4791. OldPromptData - If specified, returns a pointer to the old prompt data for the credential
  4792. Returns NULL if there is no old prompt data.
  4793. OldPromptData should be freed using LsapFreeLsaHeap.
  4794. NewPromptData - If specified, returns a pointer to the new prompt data for the credential
  4795. Returns NULL if there is no new prompt data.
  4796. NewPromptData is properly linked into the prompt data list and should only be freed
  4797. (using LsapFreeLsaHeap) if it is delinked by the caller.
  4798. Return Values:
  4799. The following status codes may be returned:
  4800. STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
  4801. existing credential. If such a field does not match the value
  4802. specified in the existing credential, this error is returned.
  4803. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  4804. Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
  4805. --*/
  4806. {
  4807. NTSTATUS Status;
  4808. PCANONICAL_CREDENTIAL Credential = *NewCredential;
  4809. PCANONICAL_CREDENTIAL TempCredential;
  4810. PPROMPT_DATA TempPromptData;
  4811. PPROMPT_DATA PromptData = NULL;
  4812. //
  4813. // Initialization
  4814. //
  4815. if ( OldCredential != NULL ) {
  4816. *OldCredential = NULL;
  4817. }
  4818. if ( OldPromptData != NULL ) {
  4819. *OldPromptData = NULL;
  4820. }
  4821. if ( NewPromptData != NULL ) {
  4822. *NewPromptData = NULL;
  4823. }
  4824. //
  4825. // If the profile hasn't been loaded yet,
  4826. // don't allow writing to persistent credential sets.
  4827. //
  4828. if ( Credential->Cred.Persist != CRED_PERSIST_SESSION &&
  4829. !FromPersistedFile &&
  4830. !CredentialSets->SessionCredSets->ProfileLoaded ) {
  4831. Status = STATUS_NO_SUCH_LOGON_SESSION;
  4832. DebugLog(( DEB_TRACE_CRED,
  4833. "CredpWriteCredential: Cannot write persistent cred set until profile loaded: %ld.\n",
  4834. Credential->Cred.Persist ));
  4835. goto Cleanup;
  4836. }
  4837. //
  4838. // If domain creds have been disabled,
  4839. // don't allow them to be written.
  4840. //
  4841. if ( CredDisableDomainCreds &&
  4842. !FromPersistedFile &&
  4843. CredpIsDomainCredential( Credential->Cred.Type ) ) {
  4844. Status = STATUS_NO_SUCH_LOGON_SESSION;
  4845. DebugLog(( DEB_TRACE_CRED,
  4846. "CredpWriteCredential: Cannot write domain cred when feature is disabled: %ld.\n",
  4847. Credential->Cred.Type ));
  4848. goto Cleanup;
  4849. }
  4850. //
  4851. // If this is a personal system,
  4852. // and this is a domain credential other than the *Session cred,
  4853. // don't allow them to be written.
  4854. //
  4855. if ( CredIsPersonal &&
  4856. CredpIsDomainCredential( Credential->Cred.Type ) &&
  4857. Credential->Cred.Type != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD &&
  4858. Credential->WildcardType != WcUniversalSessionWildcard ) {
  4859. Status = STATUS_NO_SUCH_LOGON_SESSION;
  4860. DebugLog(( DEB_TRACE_CRED,
  4861. "CredpWriteCredential: Cannot write domain cred on personal: %ld.\n",
  4862. Credential->Cred.Type ));
  4863. goto Cleanup;
  4864. }
  4865. //
  4866. // Don't allow * credential if the user is logged onto a domain account.
  4867. // In that case, the system uses the logon session credential for several purposes
  4868. // and the * credential masks that.
  4869. //
  4870. if ( Credential->WildcardType == WcUniversalWildcard &&
  4871. (CredentialSets->Flags & CREDSETS_FLAGS_LOCAL_ACCOUNT) == 0 ) {
  4872. Status = STATUS_INVALID_PARAMETER;
  4873. DebugLog(( DEB_TRACE_CRED,
  4874. "CredpWriteCredential: Cannot write '*' cred when logged onto domain account.\n" ));
  4875. goto Cleanup;
  4876. }
  4877. //
  4878. // Allocate PromptData if user is writing an always prompt credential
  4879. // Assume that the user only writes such a credential because he was prompted for a password.
  4880. //
  4881. if ( !FromPersistedFile &&
  4882. !PersistCredBlob( &Credential->Cred ) ) {
  4883. PromptData = CredpAllocatePromptData( Credential );
  4884. if ( PromptData == NULL ) {
  4885. Status = STATUS_NO_MEMORY;
  4886. goto Cleanup;
  4887. }
  4888. }
  4889. //
  4890. // Find any existing credential by the same name.
  4891. //
  4892. TempCredential = CredpFindCredential(
  4893. CredentialSets,
  4894. &Credential->TargetName,
  4895. Credential->Cred.Type );
  4896. //
  4897. // Find any prompt data for the existing credential
  4898. //
  4899. // There are cases where there is prompt data and no credential. That'd be the case
  4900. // if a machine (or enterprise) credential is deleted from another session.
  4901. //
  4902. TempPromptData = CredpFindPromptData(
  4903. CredentialSets,
  4904. &Credential->TargetName,
  4905. Credential->Cred.Type,
  4906. TempCredential == NULL ?
  4907. Credential->Cred.Persist :
  4908. TempCredential->Cred.Persist );
  4909. //
  4910. // Preserve the credential blob from this credential by the same name.
  4911. //
  4912. if ( Flags & CRED_PRESERVE_CREDENTIAL_BLOB ) {
  4913. ENCRYPTED_CREDENTIALW LocalCredential;
  4914. PCANONICAL_CREDENTIAL CompleteCredential;
  4915. //
  4916. // Don't allow the caller to specify a credential blob if he asked us to preserve the existing one
  4917. if ( Credential->Cred.CredentialBlobSize != 0 ) {
  4918. DebugLog(( DEB_TRACE_CRED,
  4919. "CredpWriteCredential: %ws: Trying to preserve credential blob AND specify a new one.\n",
  4920. Credential->Cred.TargetName ));
  4921. Status = STATUS_INVALID_PARAMETER;
  4922. goto Cleanup;
  4923. }
  4924. //
  4925. // If we need to preserve the blob,
  4926. // fail if there is no blob to preserve.
  4927. //
  4928. if ( TempCredential == NULL ) {
  4929. DebugLog(( DEB_TRACE_CRED,
  4930. "CredpWriteCredential: %ws: Cannot write credential with preserved blob.\n",
  4931. Credential->Cred.TargetName ));
  4932. Status = STATUS_NOT_FOUND;
  4933. goto Cleanup;
  4934. }
  4935. //
  4936. // Since we're preserving the existing credential blob,
  4937. // we know better than the caller whether the blob has been prompted for.
  4938. //
  4939. // If the existing credential has a persisted credential blob,
  4940. // then it has been prompted for.
  4941. //
  4942. // Otherwise, check whether we've already cached the credential in memory.
  4943. //
  4944. // (Use the PromptData for that to prevent leakage from another logon session.)
  4945. //
  4946. if ( PersistCredBlob( &TempCredential->Cred ) ) {
  4947. PromptedFor = TRUE;
  4948. } else {
  4949. PromptedFor = !ShouldPromptNow( TempPromptData );
  4950. }
  4951. //
  4952. // If the new credential isn't an always prompt credential, and
  4953. // there isn't a existing password on the credential,
  4954. // then the caller should have prompted for the credential and not asked us to preserve the old one.
  4955. //
  4956. if ( PromptData == NULL && !PromptedFor ) {
  4957. DebugLog(( DEB_TRACE_CRED,
  4958. "CredpWriteCredential: %ws: Trying to preserve credential blob AND it hasn't been prompted for.\n",
  4959. Credential->Cred.TargetName ));
  4960. Status = STATUS_INVALID_PARAMETER;
  4961. goto Cleanup;
  4962. }
  4963. //
  4964. // Build a new credential with the preserved credential blob in it.
  4965. //
  4966. LocalCredential.Cred = Credential->Cred;
  4967. LocalCredential.Cred.CredentialBlob = TempCredential->Cred.CredentialBlob;
  4968. LocalCredential.Cred.CredentialBlobSize = TempCredential->Cred.CredentialBlobSize;
  4969. LocalCredential.ClearCredentialBlobSize = TempCredential->ClearCredentialBlobSize;
  4970. //
  4971. // Get a marshaled copy of the new credential
  4972. //
  4973. Status = CredpValidateCredential(
  4974. CREDP_FLAGS_IN_PROCESS,
  4975. NULL, // Don't need to use TargetInfo again
  4976. &LocalCredential,
  4977. &CompleteCredential );
  4978. if ( !NT_SUCCESS(Status) ) {
  4979. goto Cleanup;
  4980. }
  4981. //
  4982. // Use this new credential for the rest of the operation
  4983. //
  4984. LsapFreeLsaHeap( Credential );
  4985. Credential = CompleteCredential;
  4986. *NewCredential = Credential;
  4987. }
  4988. //
  4989. // If this is a cert credential,
  4990. // write the pin into the CSP.
  4991. //
  4992. if ( WritePinToCsp ) {
  4993. Status = CredpWritePinToCsp( CredentialSets, Credential );
  4994. if ( !NT_SUCCESS(Status) ) {
  4995. goto Cleanup;
  4996. }
  4997. }
  4998. //
  4999. // Delink the existing credential.
  5000. //
  5001. if ( TempCredential != NULL ) {
  5002. RemoveEntryList( &TempCredential->Next );
  5003. CredpMarkDirty( CredentialSets, TempCredential->Cred.Persist, NULL );
  5004. if ( OldCredential != NULL ) {
  5005. *OldCredential = TempCredential;
  5006. } else {
  5007. LsapFreeLsaHeap( TempCredential );
  5008. }
  5009. }
  5010. //
  5011. // Delink existing prompt data
  5012. //
  5013. if ( TempPromptData != NULL ) {
  5014. RemoveEntryList( &TempPromptData->Next );
  5015. if ( OldPromptData != NULL ) {
  5016. *OldPromptData = TempPromptData;
  5017. } else {
  5018. LsapFreeLsaHeap( TempPromptData );
  5019. }
  5020. }
  5021. //
  5022. // Mark the credential as modified.
  5023. // Don't change the LastWritten date on restored credentials
  5024. //
  5025. CredpMarkDirty( CredentialSets,
  5026. Credential->Cred.Persist,
  5027. FromPersistedFile ? NULL : Credential );
  5028. //
  5029. // Link the prompt data
  5030. //
  5031. if ( PromptData != NULL ) {
  5032. InsertHeadList( &CredentialSets->SessionCredSets->PromptData,
  5033. &PromptData->Next );
  5034. if ( NewPromptData != NULL ) {
  5035. *NewPromptData = PromptData;
  5036. }
  5037. PromptData->Written = PromptedFor;
  5038. PromptData = NULL;
  5039. }
  5040. //
  5041. // Link the new credential into the cred set.
  5042. //
  5043. if ( FromPersistedFile ) {
  5044. // Preserve the order of the credentials in the file.
  5045. InsertTailList(
  5046. &PersistToCredentialSet( CredentialSets, Credential->Cred.Persist )->Credentials,
  5047. &Credential->Next );
  5048. } else {
  5049. // Put new credentials early in the list where they can be found quickly
  5050. InsertHeadList(
  5051. &PersistToCredentialSet( CredentialSets, Credential->Cred.Persist )->Credentials,
  5052. &Credential->Next );
  5053. }
  5054. Status = STATUS_SUCCESS;
  5055. //
  5056. // Cleanup
  5057. //
  5058. Cleanup:
  5059. if ( PromptData != NULL ) {
  5060. LsapFreeLsaHeap( PromptData );
  5061. }
  5062. return Status;
  5063. }
  5064. NTSTATUS
  5065. CredpWriteMorphedCredential(
  5066. IN PCREDENTIAL_SETS CredentialSets,
  5067. IN BOOLEAN PromptedFor,
  5068. IN PCANONICAL_TARGET_INFO TargetInfo OPTIONAL,
  5069. IN PENCRYPTED_CREDENTIALW MorphedCredential,
  5070. OUT PCANONICAL_CREDENTIAL *WrittenCredential
  5071. )
  5072. /*++
  5073. Routine Description:
  5074. This routine write a morphed credential to the credential set.
  5075. Any credential by the same target name and type is delinked and deallocated.
  5076. On entry, UserCredentialSets->CritSect must be locked.
  5077. Arguments:
  5078. CredentialSets - A pointer to the referenced credential set.
  5079. PromptedFor - Specifies whether the MorphedCredential should be considered
  5080. as having been prompted for.
  5081. TargetInfo - A description of the various aliases for a target.
  5082. MorphedCredential - Pointer to a credential to add to the credential set.
  5083. WrittenCredential - On success, returns a pointer to the written credential.
  5084. Return Values:
  5085. Sundry credential write status codes.
  5086. --*/
  5087. {
  5088. NTSTATUS Status;
  5089. PCANONICAL_CREDENTIAL ValidatedCredential;
  5090. //
  5091. // Get a marshaled copy of the new credential
  5092. //
  5093. Status = CredpValidateCredential(
  5094. CREDP_FLAGS_IN_PROCESS,
  5095. TargetInfo,
  5096. MorphedCredential,
  5097. &ValidatedCredential );
  5098. if ( NT_SUCCESS(Status) ) {
  5099. //
  5100. // Write it to the credential set
  5101. //
  5102. Status = CredpWriteCredential( CredentialSets,
  5103. &ValidatedCredential,
  5104. FALSE, // Creds are not from persisted file
  5105. FALSE, // Don't update pin in CSP
  5106. PromptedFor, // Caller knows if cred has been prompted for
  5107. 0, // No flags
  5108. NULL,
  5109. NULL,
  5110. NULL );
  5111. if ( NT_SUCCESS( Status )) {
  5112. *WrittenCredential = ValidatedCredential;
  5113. } else {
  5114. LsapFreeLsaHeap( ValidatedCredential );
  5115. }
  5116. }
  5117. return Status;
  5118. }
  5119. VOID
  5120. CredpUpdatePassword2(
  5121. IN PCREDENTIAL_SETS CredentialSets,
  5122. IN PUNICODE_STRING UserName,
  5123. IN PUNICODE_STRING Password,
  5124. IN DWORD CredType
  5125. )
  5126. /*++
  5127. Routine Description:
  5128. This routine updates the password in all credentials for UserName.
  5129. On entry, UserCredentialSets->CritSect must be locked.
  5130. Arguments:
  5131. CredentialSets - A pointer to the referenced credential set.
  5132. UserName - The UserName of the user.
  5133. Same format as in credential.
  5134. Password - New password for the user.
  5135. This password should be passed in hidden via LsapProtectMemory.
  5136. Length - Clear text length of the password
  5137. MaximumLength - Encrypted length of the password
  5138. CredType - Specifies the credential type to change the password on.
  5139. This should be one of CRED_TYPE_DOMAIN_*
  5140. Return Values:
  5141. None
  5142. --*/
  5143. {
  5144. NTSTATUS Status;
  5145. ULONG Persist;
  5146. PCANONICAL_CREDENTIAL TempCredential;
  5147. PCANONICAL_CREDENTIAL WrittenCredential;
  5148. PLIST_ENTRY ListEntry;
  5149. ENCRYPTED_CREDENTIALW LocalCredential;
  5150. //
  5151. // Ignore writes for unsupported cred types
  5152. //
  5153. if ( CredDisableDomainCreds &&
  5154. CredpIsDomainCredential( CredType ) ) {
  5155. return;
  5156. }
  5157. //
  5158. // Loop through the credentials finding those that match.
  5159. //
  5160. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  5161. PCREDENTIAL_SET CredentialSet;
  5162. //
  5163. // If the profile has not yet been loaded by this session,
  5164. // ignore any credentials loaded by another session.
  5165. //
  5166. if ( Persist != CRED_PERSIST_SESSION &&
  5167. !CredentialSets->SessionCredSets->ProfileLoaded ) {
  5168. continue;
  5169. }
  5170. CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
  5171. for ( ListEntry = CredentialSet->Credentials.Flink ;
  5172. ListEntry != &CredentialSet->Credentials;
  5173. ) {
  5174. // Grab a pointer to the next entry since this one may be delinked
  5175. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  5176. ListEntry = ListEntry->Flink;
  5177. //
  5178. // Only do credentials of the type requested
  5179. //
  5180. if ( TempCredential->Cred.Type != CredType ) {
  5181. continue;
  5182. }
  5183. //
  5184. // Only do credentials with identical user names.
  5185. //
  5186. if ( TempCredential->UserName.Length == 0 ||
  5187. !RtlEqualUnicodeString( UserName,
  5188. &TempCredential->UserName,
  5189. TRUE ) ) {
  5190. continue;
  5191. }
  5192. //
  5193. // Only do credentials if the password doesn't match already
  5194. //
  5195. if ( Password->MaximumLength == TempCredential->Cred.CredentialBlobSize &&
  5196. Password->Length == TempCredential->ClearCredentialBlobSize &&
  5197. (Password->MaximumLength == 0 ||
  5198. RtlEqualMemory( Password->Buffer,
  5199. TempCredential->Cred.CredentialBlob,
  5200. Password->MaximumLength ) ) ) {
  5201. continue;
  5202. }
  5203. //
  5204. // Finally, update the credential to match the new one.
  5205. //
  5206. LocalCredential.Cred = TempCredential->Cred;
  5207. LocalCredential.Cred.CredentialBlob = (LPBYTE)Password->Buffer;
  5208. LocalCredential.Cred.CredentialBlobSize = Password->MaximumLength;
  5209. LocalCredential.ClearCredentialBlobSize = Password->Length;
  5210. //
  5211. // Get a marshaled copy of the new credential
  5212. //
  5213. Status = CredpWriteMorphedCredential(
  5214. CredentialSets,
  5215. TRUE, // Cred has been prompted for
  5216. NULL, // No Target info since cred not for this target
  5217. &LocalCredential,
  5218. &WrittenCredential );
  5219. //
  5220. // Simply note success
  5221. //
  5222. if ( NT_SUCCESS(Status) ) {
  5223. DebugLog(( DEB_TRACE_CRED,
  5224. "CredpUpdatePassword: %ws: Updated password for '%ws'.\n",
  5225. WrittenCredential->TargetName.Buffer,
  5226. UserName->Buffer ));
  5227. }
  5228. }
  5229. }
  5230. }
  5231. VOID
  5232. CredpUpdatePassword(
  5233. IN PCREDENTIAL_SETS CredentialSets,
  5234. IN PCANONICAL_CREDENTIAL NewCredential
  5235. )
  5236. /*++
  5237. Routine Description:
  5238. This routine updates the password in all credentials to match that of the new credential.
  5239. On entry, UserCredentialSets->CritSect must be locked.
  5240. Arguments:
  5241. CredentialSets - A pointer to the referenced credential set.
  5242. NewCredential - Pointer to a credential that was just added to the
  5243. credential set.
  5244. Return Values:
  5245. None
  5246. --*/
  5247. {
  5248. NTSTATUS Status;
  5249. UNICODE_STRING Password;
  5250. //
  5251. // If the credential isn't a domain credential,
  5252. // we're done.
  5253. //
  5254. if ( !CredpIsDomainCredential( NewCredential->Cred.Type ) ||
  5255. NewCredential->UserName.Length == 0 ) {
  5256. return;
  5257. }
  5258. //
  5259. // Update the password on all matching credentials.
  5260. //
  5261. Password.Buffer = (LPWSTR)NewCredential->Cred.CredentialBlob;
  5262. Password.Length = (USHORT)NewCredential->ClearCredentialBlobSize;
  5263. Password.MaximumLength = (USHORT)NewCredential->Cred.CredentialBlobSize;
  5264. CredpUpdatePassword2( CredentialSets,
  5265. &NewCredential->UserName,
  5266. &Password,
  5267. NewCredential->Cred.Type );
  5268. }
  5269. VOID
  5270. CredpNotifyPasswordChange(
  5271. IN PUNICODE_STRING NetbiosDomainName OPTIONAL,
  5272. IN PUNICODE_STRING UserName,
  5273. IN PUNICODE_STRING DnsDomainName OPTIONAL,
  5274. IN PUNICODE_STRING Upn OPTIONAL,
  5275. IN PUNICODE_STRING NewPassword
  5276. )
  5277. /*++
  5278. Routine Description:
  5279. This routine updates the password of a particular user in the credential manager.
  5280. This routine is called while impersonating the user changing the password. That
  5281. user isn't necessarily UserName.
  5282. Arguments:
  5283. NetbiosDomainName - Netbios domain name of the user whose password was changed
  5284. UserName - User name of the user whose password was changed
  5285. DnsDomainName - If known, Dns Domain Name of the user whose password was changed
  5286. Upn - If known, the Upn of the user whose password was changed
  5287. NewPassword - The new password for the user.
  5288. Return Values:
  5289. None
  5290. --*/
  5291. {
  5292. HANDLE ClientToken;
  5293. LUID LogonId;
  5294. NTSTATUS Status;
  5295. CREDENTIAL_SETS CredentialSets = { NULL };
  5296. BOOLEAN CritSectLocked = FALSE;
  5297. UNICODE_STRING LocalUserName;
  5298. UNICODE_STRING EncryptedNewPassword;
  5299. //
  5300. // Got to have at least Netbios DomainName OR DNSDomainName
  5301. //
  5302. if (!ARGUMENT_PRESENT(NetbiosDomainName) &&
  5303. !ARGUMENT_PRESENT(DnsDomainName)) {
  5304. return;
  5305. }
  5306. //
  5307. // Initialization
  5308. //
  5309. LocalUserName.Buffer = NULL;
  5310. EncryptedNewPassword.Buffer = NULL;
  5311. //
  5312. // The password needs to be protected when we put it on the credential
  5313. //
  5314. EncryptedNewPassword.MaximumLength = AllocatedCredBlobSize( NewPassword->Length );
  5315. EncryptedNewPassword.Length = NewPassword->Length;
  5316. SafeAllocaAllocate( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
  5317. if ( EncryptedNewPassword.Buffer == NULL ) {
  5318. goto Cleanup;
  5319. }
  5320. RtlZeroMemory( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
  5321. RtlCopyMemory( EncryptedNewPassword.Buffer,
  5322. NewPassword->Buffer,
  5323. NewPassword->Length );
  5324. LsaProtectMemory( EncryptedNewPassword.Buffer, EncryptedNewPassword.MaximumLength );
  5325. //
  5326. // Get the logon id from the token
  5327. //
  5328. Status = NtOpenThreadToken( NtCurrentThread(),
  5329. TOKEN_QUERY,
  5330. TRUE,
  5331. &ClientToken );
  5332. if ( !NT_SUCCESS( Status ) ) {
  5333. goto Cleanup;
  5334. } else {
  5335. TOKEN_STATISTICS TokenStats;
  5336. ULONG ReturnedSize;
  5337. //
  5338. // Get the LogonId
  5339. //
  5340. Status = NtQueryInformationToken( ClientToken,
  5341. TokenStatistics,
  5342. &TokenStats,
  5343. sizeof( TokenStats ),
  5344. &ReturnedSize );
  5345. if ( NT_SUCCESS( Status ) ) {
  5346. //
  5347. // Save the logon id
  5348. //
  5349. LogonId = TokenStats.AuthenticationId;
  5350. }
  5351. NtClose( ClientToken );
  5352. }
  5353. //
  5354. // Get the credential set.
  5355. //
  5356. Status = CredpReferenceCredSets( &LogonId, &CredentialSets );
  5357. if ( !NT_SUCCESS(Status) ) {
  5358. goto Cleanup;
  5359. }
  5360. CredpLockCredSets( &CredentialSets );
  5361. CritSectLocked = TRUE;
  5362. //
  5363. // Update the password for all <NetbiosDomainName>\<UserName> credentials
  5364. //
  5365. if ( NetbiosDomainName != NULL ) {
  5366. LocalUserName.Length = NetbiosDomainName->Length +
  5367. sizeof(WCHAR) +
  5368. UserName->Length;
  5369. LocalUserName.MaximumLength = LocalUserName.Length;
  5370. SafeAllocaAllocate( LocalUserName.Buffer, LocalUserName.MaximumLength );
  5371. if ( LocalUserName.Buffer == NULL ) {
  5372. goto Cleanup;
  5373. }
  5374. RtlCopyMemory( LocalUserName.Buffer, NetbiosDomainName->Buffer, NetbiosDomainName->Length );
  5375. LocalUserName.Buffer[NetbiosDomainName->Length/sizeof(WCHAR)] = '\\';
  5376. RtlCopyMemory( &LocalUserName.Buffer[(NetbiosDomainName->Length/sizeof(WCHAR)) + 1],
  5377. UserName->Buffer,
  5378. UserName->Length );
  5379. CredpUpdatePassword2( &CredentialSets,
  5380. &LocalUserName,
  5381. &EncryptedNewPassword,
  5382. CRED_TYPE_DOMAIN_PASSWORD );
  5383. }
  5384. //
  5385. // Update the password for all <DnsDomainName>\<UserName> credentials
  5386. //
  5387. if ( DnsDomainName != NULL ) {
  5388. if ( LocalUserName.Buffer != NULL ) {
  5389. SafeAllocaFree( LocalUserName.Buffer );
  5390. }
  5391. LocalUserName.Length = DnsDomainName->Length +
  5392. sizeof(WCHAR) +
  5393. UserName->Length;
  5394. LocalUserName.MaximumLength = LocalUserName.Length;
  5395. SafeAllocaAllocate( LocalUserName.Buffer, LocalUserName.MaximumLength );
  5396. if ( LocalUserName.Buffer == NULL ) {
  5397. goto Cleanup;
  5398. }
  5399. RtlCopyMemory( LocalUserName.Buffer, DnsDomainName->Buffer, DnsDomainName->Length );
  5400. LocalUserName.Buffer[DnsDomainName->Length/sizeof(WCHAR)] = '\\';
  5401. RtlCopyMemory( &LocalUserName.Buffer[(DnsDomainName->Length/sizeof(WCHAR)) + 1],
  5402. UserName->Buffer,
  5403. UserName->Length );
  5404. CredpUpdatePassword2( &CredentialSets,
  5405. &LocalUserName,
  5406. &EncryptedNewPassword,
  5407. CRED_TYPE_DOMAIN_PASSWORD );
  5408. }
  5409. //
  5410. // Update the password for all <Upn> credentials
  5411. //
  5412. if ( Upn != NULL ) {
  5413. CredpUpdatePassword2( &CredentialSets,
  5414. Upn,
  5415. &EncryptedNewPassword,
  5416. CRED_TYPE_DOMAIN_PASSWORD );
  5417. }
  5418. //
  5419. // Cleanup
  5420. //
  5421. Cleanup:
  5422. if ( LocalUserName.Buffer != NULL ) {
  5423. SafeAllocaFree( LocalUserName.Buffer );
  5424. }
  5425. if ( CritSectLocked ) {
  5426. CredpUnlockAndFlushCredSets( &CredentialSets );
  5427. }
  5428. CredpDereferenceCredSets( &CredentialSets );
  5429. if ( EncryptedNewPassword.Buffer != NULL ) {
  5430. SafeAllocaFree( EncryptedNewPassword.Buffer );
  5431. }
  5432. }
  5433. NTSTATUS
  5434. CredpFindBestCredentials(
  5435. IN PLUID LogonId,
  5436. IN PCREDENTIAL_SETS CredentialSets,
  5437. IN PCANONICAL_TARGET_INFO TargetInfo,
  5438. OUT PCANONICAL_CREDENTIAL *BestCredentials,
  5439. OUT PULONG BestCredentialCount
  5440. )
  5441. /*++
  5442. Routine Description:
  5443. This routine finds the best credentials given a description of the target.
  5444. On entry, UserCredentialSets->CritSect must be locked.
  5445. Arguments:
  5446. LogonId - LogonId of the session to associate the credential with.
  5447. CredentialSets - A pointer to the referenced credential set.
  5448. TargetInfo - A description of the various aliases for a target.
  5449. BestCredentials - Returns the best credential for each credential type.
  5450. BestCredentialCount - Returns the number of elements returned in BestCredentials.
  5451. Return Values:
  5452. STATUS_SUCCESS
  5453. --*/
  5454. {
  5455. NTSTATUS Status;
  5456. PCANONICAL_CREDENTIAL Credential;
  5457. PLIST_ENTRY ListEntry;
  5458. ULONG TypeIndex;
  5459. ULONG AliasIndex;
  5460. ULONG Persist;
  5461. //
  5462. // This routine finds the best credentials for each credtype that match the TargetInfo.
  5463. // This array contains the pointers to the found credentials.
  5464. //
  5465. PCANONICAL_CREDENTIAL SavedCredentials[CRED_TYPE_MAXIMUM];
  5466. ULONG AliasIndices[CRED_TYPE_MAXIMUM];
  5467. ULONG CredTypeCount;
  5468. LPDWORD CredTypes;
  5469. //
  5470. // Clear our list of remembered credentials
  5471. //
  5472. RtlZeroMemory( &SavedCredentials, sizeof(SavedCredentials) );
  5473. for ( TypeIndex=0; TypeIndex<CRED_TYPE_MAXIMUM; TypeIndex++ ) {
  5474. AliasIndices[TypeIndex] = CRED_MAX_ALIASES;
  5475. }
  5476. //
  5477. // Loop through the list of credentials finding all that match the TargetInfo
  5478. //
  5479. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  5480. PCREDENTIAL_SET CredentialSet;
  5481. //
  5482. // If the profile has not yet been loaded by this session,
  5483. // ignore any credentials loaded by another session.
  5484. //
  5485. if ( Persist != CRED_PERSIST_SESSION &&
  5486. !CredentialSets->SessionCredSets->ProfileLoaded ) {
  5487. continue;
  5488. }
  5489. CredentialSet = PersistToCredentialSet( CredentialSets, Persist );
  5490. for ( ListEntry = CredentialSet->Credentials.Flink ;
  5491. ListEntry != &CredentialSet->Credentials;
  5492. ListEntry = ListEntry->Flink) {
  5493. Credential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  5494. //
  5495. // Ignore unsupported cred types
  5496. //
  5497. if ( CredDisableDomainCreds &&
  5498. CredpIsDomainCredential( Credential->Cred.Type ) ) {
  5499. continue;
  5500. }
  5501. //
  5502. // If the credential matches,
  5503. // save it.
  5504. //
  5505. if ( CredpCompareCredToTargetInfo( TargetInfo,
  5506. Credential,
  5507. &AliasIndex ) ) {
  5508. //
  5509. // Compute indices into the array of saved credentials.
  5510. //
  5511. // Rely on the following:
  5512. // CompareCred* validated that the cred is a domain cred
  5513. // CompareCred* only returns TRUE for well known CredType values.
  5514. TypeIndex = Credential->Cred.Type;
  5515. //
  5516. // If we've already found a credential in this category,
  5517. // keep the "better" one.
  5518. //
  5519. if ( SavedCredentials[TypeIndex] != NULL ) {
  5520. DebugLog(( DEB_TRACE_CRED,
  5521. "CredpFindBestCredentials: %ws: %ws: Two credentials match same target info criterias.\n",
  5522. SavedCredentials[TypeIndex]->TargetName.Buffer,
  5523. Credential->TargetName.Buffer ));
  5524. //
  5525. // If the existing credential is more specific than this one,
  5526. // Keep the existing.
  5527. //
  5528. if ( AliasIndices[TypeIndex] < AliasIndex ) {
  5529. DebugLog(( DEB_TRACE_CRED,
  5530. "CredpFindBestCredentials: %ws: Use this one (AI1).\n",
  5531. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5532. /* nothing to do here */
  5533. //
  5534. // If this credential is more specific than the saved one,
  5535. // save this one.
  5536. //
  5537. } else if ( AliasIndices[TypeIndex] > AliasIndex ) {
  5538. SavedCredentials[TypeIndex] = Credential;
  5539. AliasIndices[TypeIndex] = AliasIndex;
  5540. DebugLog(( DEB_TRACE_CRED,
  5541. "CredpFindBestCredentials: %ws: Use this one (AI1).\n",
  5542. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5543. //
  5544. // If the existing credential has a more specific wildcard,
  5545. // keep the existing.
  5546. //
  5547. } else if ( AliasIndex == CRED_WILDCARD_SERVER_NAME &&
  5548. SavedCredentials[TypeIndex]->TargetName.Length >
  5549. Credential->TargetName.Length ) {
  5550. DebugLog(( DEB_TRACE_CRED,
  5551. "CredpFindBestCredentials: %ws: Use this one (WC1).\n",
  5552. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5553. /* nothing to do here */
  5554. //
  5555. // If the existing credential has a less specific wildcard,
  5556. // save this one.
  5557. //
  5558. } else if ( AliasIndex == CRED_WILDCARD_SERVER_NAME &&
  5559. SavedCredentials[TypeIndex]->TargetName.Length <
  5560. Credential->TargetName.Length ) {
  5561. SavedCredentials[TypeIndex] = Credential;
  5562. AliasIndices[TypeIndex] = AliasIndex;
  5563. DebugLog(( DEB_TRACE_CRED,
  5564. "CredpFindBestCredentials: %ws: Use this one (WC2).\n",
  5565. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5566. /* nothing to do here */
  5567. //
  5568. // If one credential has a target alias and the other doesn't,
  5569. // keep the one without an alias,
  5570. // this is the case when the target info passes in a netbios name
  5571. // and there is both a netbios credential and a DNS credential.
  5572. // Prefer the netbios credential.
  5573. //
  5574. } else if ( SavedCredentials[TypeIndex]->TargetAlias.Length == 0 ) {
  5575. DebugLog(( DEB_TRACE_CRED,
  5576. "CredpFindBestCredentials: %ws: Use this one (AL).\n",
  5577. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5578. /* nothing to do here */
  5579. //
  5580. // If one credential is session specific,
  5581. // keep it (allowing the user to override persistent credentials).
  5582. //
  5583. } else if ( SavedCredentials[TypeIndex]->Cred.Persist == CRED_PERSIST_SESSION ) {
  5584. DebugLog(( DEB_TRACE_CRED,
  5585. "CredpFindBestCredentials: %ws: Use this one (PE).\n",
  5586. SavedCredentials[TypeIndex]->TargetName.Buffer ));
  5587. /* nothing to do here */
  5588. //
  5589. // Otherwise, just save this one
  5590. //
  5591. } else {
  5592. SavedCredentials[TypeIndex] = Credential;
  5593. AliasIndices[TypeIndex] = AliasIndex;
  5594. DebugLog(( DEB_TRACE_CRED,
  5595. "CredpFindBestCredentials: %ws: Use this one (default).\n",
  5596. Credential->TargetName.Buffer ));
  5597. }
  5598. //
  5599. // Otherwise, just save this one
  5600. //
  5601. } else {
  5602. SavedCredentials[TypeIndex] = Credential;
  5603. AliasIndices[TypeIndex] = AliasIndex;
  5604. }
  5605. }
  5606. }
  5607. }
  5608. //
  5609. // Don't return a *Session credential if our logon creds should work
  5610. //
  5611. // The *Session cred is a hack to force us to always use RAS dial creds on all
  5612. // connections to the dialed up network. However, we don't know what network a
  5613. // server is on. We still want to be able to use logon creds on the LAN. Our
  5614. // best bet is to use logon creds if the server is in the same domain as our logon
  5615. // creds.
  5616. //
  5617. // If we found a password cred,
  5618. // and that cred is the *Session cred,
  5619. // and the logon creds should work,
  5620. // ignore the *Session cred.
  5621. //
  5622. if ( SavedCredentials[CRED_TYPE_DOMAIN_PASSWORD] != NULL &&
  5623. AliasIndices[CRED_TYPE_DOMAIN_PASSWORD] == CRED_UNIVERSAL_SESSION_NAME ) {
  5624. Status = CredpLogonCredsMatchTargetInfo( LogonId, TargetInfo );
  5625. if ( NT_SUCCESS( Status )) {
  5626. SavedCredentials[CRED_TYPE_DOMAIN_PASSWORD] = NULL;
  5627. } else if ( Status != STATUS_NO_MATCH ) {
  5628. return Status;
  5629. }
  5630. }
  5631. //
  5632. // Decide the order to return the credentials in
  5633. //
  5634. if ( TargetInfo->CredTypeCount != 0 ) {
  5635. // Use the order specified by the auth package
  5636. CredTypeCount = TargetInfo->CredTypeCount;
  5637. CredTypes = TargetInfo->CredTypes;
  5638. } else {
  5639. // Use the default order
  5640. CredTypeCount = sizeof(CredTypeDefaultOrder)/sizeof(CredTypeDefaultOrder[0]);
  5641. CredTypes = CredTypeDefaultOrder;
  5642. }
  5643. //
  5644. // Return the credentials to the caller.
  5645. // Only return the ones requested by the caller
  5646. //
  5647. *BestCredentialCount = 0;
  5648. for ( TypeIndex=0; TypeIndex<CredTypeCount; TypeIndex++ ) {
  5649. ASSERT( CredTypes[TypeIndex] < CRED_TYPE_MAXIMUM );
  5650. if ( CredTypes[TypeIndex] < CRED_TYPE_MAXIMUM ) {
  5651. //
  5652. // If we have a saved credential,
  5653. // return it.
  5654. //
  5655. if ( SavedCredentials[CredTypes[TypeIndex]] != NULL ) {
  5656. BestCredentials[*BestCredentialCount] = SavedCredentials[CredTypes[TypeIndex]];
  5657. *BestCredentialCount += 1;
  5658. }
  5659. }
  5660. }
  5661. Status = STATUS_SUCCESS;
  5662. return Status;
  5663. }
  5664. extern "C"
  5665. NTSTATUS
  5666. CrediWrite(
  5667. IN PLUID LogonId,
  5668. IN ULONG CredFlags,
  5669. IN PENCRYPTED_CREDENTIALW Credential,
  5670. IN ULONG Flags
  5671. )
  5672. /*++
  5673. Routine Description:
  5674. The CredWrite API creates a new credential or modifies an existing
  5675. credential in the user's credential set. The new credential is
  5676. associated with the logon session of the current token. The token
  5677. must not have the user's SID disabled.
  5678. The CredWrite API creates a credential if none already exists by the
  5679. specified TargetName. If the specified TargetName already exists, the
  5680. specified credential replaces the existing one.
  5681. Arguments:
  5682. LogonId - LogonId of the session to associate the credential with.
  5683. CredFlags - Flags changing the behavior of the routine:
  5684. Reserved. Must be zero.
  5685. Note: The CredFlags parameter is internal to the LSA process.
  5686. The Flags parameter is external to the process.
  5687. Credential - Specifies the credential to be written.
  5688. Flags - Specifies flags to control the operation of the API.
  5689. The following flags are defined:
  5690. CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
  5691. already existing credential with the same credential name and credential type.
  5692. Return Values:
  5693. The following status codes may be returned:
  5694. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  5695. there is no credential set associated with this logon session.
  5696. Network logon sessions do not have an associated credential set.
  5697. STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
  5698. existing credential. If such a field does not match the value
  5699. specified in the existing credential, this error is returned.
  5700. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  5701. Returned only if CRED_PRESERVE_CREDENTIAL_BLOB was specified.
  5702. --*/
  5703. {
  5704. NTSTATUS Status;
  5705. CREDENTIAL_SETS CredentialSets = { NULL };
  5706. PCANONICAL_CREDENTIAL PassedCredential = NULL;
  5707. BOOLEAN CritSectLocked = FALSE;
  5708. //
  5709. // Validate the flags
  5710. //
  5711. #define CREDP_WRITE_VALID_FLAGS CRED_PRESERVE_CREDENTIAL_BLOB
  5712. if ( (Flags & ~CREDP_WRITE_VALID_FLAGS) != 0 ) {
  5713. Status = STATUS_INVALID_PARAMETER_1;
  5714. goto Cleanup;
  5715. }
  5716. //
  5717. // Validate the passed in credential
  5718. //
  5719. Status = CredpValidateCredential( CredFlags,
  5720. NULL, // No TargetInfo
  5721. Credential,
  5722. &PassedCredential );
  5723. if ( !NT_SUCCESS(Status) ) {
  5724. goto Cleanup;
  5725. }
  5726. //
  5727. // Get the credential set.
  5728. //
  5729. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  5730. if ( !NT_SUCCESS(Status) ) {
  5731. goto Cleanup;
  5732. }
  5733. CredpLockCredSets( &CredentialSets );
  5734. CritSectLocked = TRUE;
  5735. //
  5736. // Write the credential to the credential set.
  5737. //
  5738. Status = CredpWriteCredential( &CredentialSets,
  5739. &PassedCredential,
  5740. FALSE, // Creds are not from persisted file
  5741. TRUE, // Update pin in CSP
  5742. TRUE, // Cred has been prompted for
  5743. Flags,
  5744. NULL,
  5745. NULL,
  5746. NULL );
  5747. if ( !NT_SUCCESS(Status) ) {
  5748. goto Cleanup;
  5749. }
  5750. //
  5751. // Update the password for this user on all credentials.
  5752. //
  5753. CredpUpdatePassword( &CredentialSets,
  5754. PassedCredential );
  5755. PassedCredential = NULL;
  5756. Status = STATUS_SUCCESS;
  5757. //
  5758. // Cleanup
  5759. //
  5760. Cleanup:
  5761. if ( CritSectLocked ) {
  5762. CredpUnlockAndFlushCredSets( &CredentialSets );
  5763. }
  5764. if ( PassedCredential != NULL ) {
  5765. LsapFreeLsaHeap( PassedCredential );
  5766. }
  5767. CredpDereferenceCredSets( &CredentialSets );
  5768. return Status;
  5769. }
  5770. extern "C"
  5771. NTSTATUS
  5772. CrediRead (
  5773. IN PLUID LogonId,
  5774. IN ULONG CredFlags,
  5775. IN LPWSTR TargetName,
  5776. IN ULONG Type,
  5777. IN ULONG Flags,
  5778. OUT PENCRYPTED_CREDENTIALW *Credential
  5779. )
  5780. /*++
  5781. Routine Description:
  5782. The CredRead API reads a credential from the user's credential set.
  5783. The credential set used is the one associated with the logon session
  5784. of the current token. The token must not have the user's SID disabled.
  5785. Arguments:
  5786. LogonId - LogonId of the session to associate the credential with.
  5787. CredFlags - Flags changing the behavior of the routine:
  5788. CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
  5789. CREDP_FLAGS_USE_MIDL_HEAP - If specified, use MIDL_user_allocate to allocate memory.
  5790. Note: The CredFlags parameter is internal to the LSA process.
  5791. The Flags parameter is external to the process.
  5792. TargetName - Specifies the name of the credential to read.
  5793. Type - Specifies the Type of the credential to find.
  5794. One of the CRED_TYPE_* values should be specified.
  5795. Flags - Specifies flags to control the operation of the API.
  5796. Reserved. Must be zero.
  5797. Credential - Returns a pointer to the credential. The returned buffer
  5798. must be freed by calling LsapFreeLsaHeap.
  5799. If CREDP_FLAGS_USE_MIDL_HEAP was specified, use MIDL_user_free.
  5800. Return Values:
  5801. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  5802. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  5803. there is no credential set associated with this logon session.
  5804. Network logon sessions do not have an associated credential set.
  5805. --*/
  5806. {
  5807. NTSTATUS Status;
  5808. CREDENTIAL_SETS CredentialSets = { NULL };
  5809. PCANONICAL_CREDENTIAL TempCredential;
  5810. UNICODE_STRING TargetNameString;
  5811. DWORD TargetNameSize;
  5812. BOOLEAN CritSectLocked = FALSE;
  5813. //
  5814. // Validate the flags
  5815. //
  5816. if ( Flags != 0 ) {
  5817. Status = STATUS_INVALID_PARAMETER_1;
  5818. goto Cleanup;
  5819. }
  5820. //
  5821. // Validate the input parameters
  5822. //
  5823. Status = CredpValidateTargetName( TargetName,
  5824. Type,
  5825. MightBeUsernameTarget,
  5826. NULL, // Don't know user name
  5827. NULL, // Don't know persist
  5828. &TargetNameSize,
  5829. NULL, // Don't care about name type
  5830. NULL ); // Don't care about non-wilcarded form of name
  5831. if ( !NT_SUCCESS(Status ) ) {
  5832. goto Cleanup;
  5833. }
  5834. //
  5835. // Get the credential set.
  5836. //
  5837. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  5838. if ( !NT_SUCCESS(Status) ) {
  5839. goto Cleanup;
  5840. }
  5841. //
  5842. // Find the credential
  5843. //
  5844. TargetNameString.Buffer = TargetName;
  5845. TargetNameString.MaximumLength = (USHORT) TargetNameSize;
  5846. TargetNameString.Length = TargetNameString.MaximumLength - sizeof(WCHAR);
  5847. CredpLockCredSets( &CredentialSets );
  5848. CritSectLocked = TRUE;
  5849. TempCredential = CredpFindCredential(
  5850. &CredentialSets,
  5851. &TargetNameString,
  5852. Type );
  5853. if ( TempCredential == NULL ) {
  5854. Status = STATUS_NOT_FOUND;
  5855. goto Cleanup;
  5856. }
  5857. //
  5858. // Grab a copy of the credential to return to the caller.
  5859. //
  5860. *Credential = CredpCloneCredential( &CredentialSets, CredFlags, TempCredential );
  5861. if ( *Credential == NULL ) {
  5862. Status = STATUS_NO_MEMORY;
  5863. goto Cleanup;
  5864. }
  5865. Status = STATUS_SUCCESS;
  5866. //
  5867. // Cleanup
  5868. //
  5869. Cleanup:
  5870. if ( CritSectLocked ) {
  5871. CredpUnlockAndFlushCredSets( &CredentialSets );
  5872. }
  5873. CredpDereferenceCredSets( &CredentialSets );
  5874. return Status;
  5875. }
  5876. extern "C"
  5877. VOID
  5878. CrediFreeCredentials (
  5879. IN ULONG Count,
  5880. IN PENCRYPTED_CREDENTIALW *Credentials OPTIONAL
  5881. )
  5882. /*++
  5883. Routine Description:
  5884. This routine frees the buffers allocated by CrediEnumerate or
  5885. CrediReadDomainCredentials.
  5886. Arguments:
  5887. Count - Specifies the number of credentials in Credentials.
  5888. Credentials - A pointer to an array of pointers to credentials.
  5889. Return Values:
  5890. None.
  5891. --*/
  5892. {
  5893. ULONG i;
  5894. if ( Credentials != NULL ) {
  5895. for ( i=0; i<Count; i++ ) {
  5896. if ( Credentials[i] != NULL ) {
  5897. MIDL_user_free( Credentials[i] );
  5898. }
  5899. }
  5900. MIDL_user_free( Credentials );
  5901. }
  5902. }
  5903. extern "C"
  5904. NTSTATUS
  5905. CrediEnumerate (
  5906. IN PLUID LogonId,
  5907. IN ULONG CredFlags,
  5908. IN LPWSTR Filter,
  5909. IN ULONG Flags,
  5910. OUT PULONG Count,
  5911. OUT PENCRYPTED_CREDENTIALW **Credentials
  5912. )
  5913. /*++
  5914. Routine Description:
  5915. The CredEnumerate API enumerates the credentials from the user's credential set.
  5916. The credential set used is the one associated with the logon session
  5917. of the current token. The token must not have the user's SID disabled.
  5918. Arguments:
  5919. LogonId - LogonId of the session to associate the credential with.
  5920. CredFlags - Flags changing the behavior of the routine:
  5921. CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
  5922. Note: The CredFlags parameter is internal to the LSA process.
  5923. The Flags parameter is external to the process.
  5924. Filter - Specifies a filter for the returned credentials. Only credentials
  5925. with a TargetName matching the filter will be returned. The filter specifies
  5926. a name prefix followed by an asterisk. For instance, the filter "FRED*" will
  5927. return all credentials with a TargetName beginning with the string "FRED".
  5928. If NULL is specified, all credentials will be returned.
  5929. Flags - Specifies flags to control the operation of the API.
  5930. Reserved. Must be zero.
  5931. Count - Returns a count of the number of credentials returned in Credentials.
  5932. Credentials - Returns a pointer to an array of pointers to credentials.
  5933. The returned buffer must be freed by calling CrediFreeCredentials
  5934. Return Values:
  5935. On success, TRUE is returned. On failure, FALSE is returned.
  5936. GetLastError() may be called to get a more specific status code.
  5937. The following status codes may be returned:
  5938. STATUS_NOT_FOUND - There is no credentials matching the specified Filter.
  5939. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  5940. there is no credential set associated with this logon session.
  5941. Network logon sessions do not have an associated credential set.
  5942. --*/
  5943. {
  5944. NTSTATUS Status;
  5945. CREDENTIAL_SETS CredentialSets = { NULL };
  5946. ULONG Persist;
  5947. PCANONICAL_CREDENTIAL TempCredential;
  5948. PENCRYPTED_CREDENTIALW *TempCredentials = NULL;
  5949. DWORD FilterSize;
  5950. ULONG CredentialCount = 0;
  5951. ULONG CredentialIndex;
  5952. PLIST_ENTRY ListEntry;
  5953. BOOLEAN CritSectLocked = FALSE;
  5954. UNICODE_STRING PassedFilter;
  5955. BOOLEAN Wildcarded = FALSE;
  5956. //
  5957. // Validate the flags
  5958. //
  5959. if ( Flags != 0 ) {
  5960. Status = STATUS_INVALID_PARAMETER_1;
  5961. goto Cleanup;
  5962. }
  5963. //
  5964. // Validate the input parameters
  5965. //
  5966. if ( !CredpValidateString( Filter,
  5967. max(CRED_MAX_GENERIC_TARGET_NAME_LENGTH, CRED_MAX_DOMAIN_TARGET_NAME_LENGTH),
  5968. TRUE, // NULL is OK
  5969. &FilterSize ) ) {
  5970. DebugLog(( DEB_TRACE_CRED,
  5971. "CrediEnumerate: Invalid Filter buffer.\n" ));
  5972. Status = STATUS_INVALID_PARAMETER;
  5973. goto Cleanup;
  5974. }
  5975. //
  5976. // Get the credential set.
  5977. //
  5978. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  5979. if ( !NT_SUCCESS(Status) ) {
  5980. goto Cleanup;
  5981. }
  5982. //
  5983. // Canonicalize the filter
  5984. //
  5985. if ( FilterSize != 0 ) {
  5986. PassedFilter.Buffer = Filter;
  5987. PassedFilter.Length = (USHORT)(FilterSize - sizeof(WCHAR));
  5988. PassedFilter.MaximumLength = (USHORT)FilterSize;
  5989. if ( Filter[(PassedFilter.Length-sizeof(WCHAR))/sizeof(WCHAR)] == '*' ) {
  5990. PassedFilter.Length -= sizeof(WCHAR);
  5991. PassedFilter.MaximumLength -= sizeof(WCHAR);
  5992. Wildcarded = TRUE;
  5993. }
  5994. } else {
  5995. RtlInitUnicodeString( &PassedFilter, NULL );
  5996. }
  5997. //
  5998. // Count the number of credentials the match the filter
  5999. //
  6000. CredentialCount = 0;
  6001. CredpLockCredSets( &CredentialSets );
  6002. CritSectLocked = TRUE;
  6003. //
  6004. // Walk each credential set
  6005. //
  6006. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  6007. PCREDENTIAL_SET CredentialSet;
  6008. //
  6009. // If the profile has not yet been loaded by this session,
  6010. // ignore any credentials loaded by another session.
  6011. //
  6012. if ( Persist != CRED_PERSIST_SESSION &&
  6013. !CredentialSets.SessionCredSets->ProfileLoaded ) {
  6014. continue;
  6015. }
  6016. CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
  6017. for ( ListEntry = CredentialSet->Credentials.Flink ;
  6018. ListEntry != &CredentialSet->Credentials;
  6019. ListEntry = ListEntry->Flink) {
  6020. UNICODE_STRING TempTargetName;
  6021. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  6022. //
  6023. // Ignore unsupported cred types
  6024. //
  6025. if ( CredDisableDomainCreds &&
  6026. CredpIsDomainCredential( TempCredential->Cred.Type ) ) {
  6027. continue;
  6028. }
  6029. //
  6030. // If wildcarding,
  6031. // compare the filter to the prefix of the target name.
  6032. //
  6033. TempTargetName = TempCredential->TargetName;
  6034. if ( Wildcarded && TempTargetName.Length > PassedFilter.Length ) {
  6035. TempTargetName.Length = PassedFilter.Length;
  6036. TempTargetName.MaximumLength = PassedFilter.MaximumLength;
  6037. }
  6038. if ( FilterSize == 0 ||
  6039. RtlEqualUnicodeString( &PassedFilter,
  6040. &TempTargetName,
  6041. TRUE ) ) {
  6042. CredentialCount++;
  6043. TempCredential->ReturnMe = TRUE;
  6044. } else {
  6045. TempCredential->ReturnMe = FALSE;
  6046. }
  6047. }
  6048. }
  6049. if ( CredentialCount == 0 ) {
  6050. Status = STATUS_NOT_FOUND;
  6051. goto Cleanup;
  6052. }
  6053. //
  6054. // Allocate a buffer to return the credentials in
  6055. //
  6056. TempCredentials = (PENCRYPTED_CREDENTIALW *) MIDL_user_allocate( CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
  6057. if ( TempCredentials == NULL ) {
  6058. Status = STATUS_NO_MEMORY;
  6059. goto Cleanup;
  6060. }
  6061. RtlZeroMemory( TempCredentials, CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
  6062. //
  6063. // Grab a copy of each of the credentials to return to the caller.
  6064. //
  6065. CredentialIndex = 0;
  6066. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  6067. PCREDENTIAL_SET CredentialSet;
  6068. //
  6069. // If the profile has not yet been loaded by this session,
  6070. // ignore any credentials loaded by another session.
  6071. //
  6072. if ( Persist != CRED_PERSIST_SESSION &&
  6073. !CredentialSets.SessionCredSets->ProfileLoaded ) {
  6074. continue;
  6075. }
  6076. CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
  6077. for ( ListEntry = CredentialSet->Credentials.Flink ;
  6078. ListEntry != &CredentialSet->Credentials;
  6079. ListEntry = ListEntry->Flink) {
  6080. TempCredential = CONTAINING_RECORD( ListEntry, CANONICAL_CREDENTIAL, Next );
  6081. //
  6082. // Ignore unsupported cred types
  6083. //
  6084. if ( CredDisableDomainCreds &&
  6085. CredpIsDomainCredential( TempCredential->Cred.Type ) ) {
  6086. continue;
  6087. }
  6088. if ( TempCredential->ReturnMe ) {
  6089. TempCredentials[CredentialIndex] =
  6090. CredpCloneCredential( &CredentialSets,
  6091. CredFlags | CREDP_FLAGS_USE_MIDL_HEAP,
  6092. TempCredential );
  6093. if ( TempCredentials[CredentialIndex] == NULL ) {
  6094. Status = STATUS_NO_MEMORY;
  6095. goto Cleanup;
  6096. }
  6097. CredentialIndex ++;
  6098. }
  6099. }
  6100. }
  6101. *Count = CredentialCount;
  6102. *Credentials = TempCredentials;
  6103. TempCredentials = NULL;
  6104. Status = STATUS_SUCCESS;
  6105. //
  6106. // Cleanup
  6107. //
  6108. Cleanup:
  6109. if ( CritSectLocked ) {
  6110. CredpUnlockAndFlushCredSets( &CredentialSets );
  6111. }
  6112. if ( TempCredentials != NULL ) {
  6113. CrediFreeCredentials( CredentialCount, TempCredentials );
  6114. }
  6115. CredpDereferenceCredSets( &CredentialSets );
  6116. return Status;
  6117. }
  6118. extern "C"
  6119. NTSTATUS
  6120. CrediWriteDomainCredentials (
  6121. IN PLUID LogonId,
  6122. IN ULONG CredFlags,
  6123. IN PCREDENTIAL_TARGET_INFORMATIONW TargetInfo,
  6124. IN PENCRYPTED_CREDENTIALW Credential,
  6125. IN ULONG Flags
  6126. )
  6127. /*++
  6128. Routine Description:
  6129. The CredWriteDomainCredentials API writes a new domain
  6130. credential to the user's credential set. The new credential is
  6131. associated with the logon session of the current token. The token
  6132. must not have the user's SID disabled.
  6133. CredWriteDomainCredentials differs from CredWrite in that it handles
  6134. the idiosyncrasies of domain (CRED_TYPE_DOMAIN_*)
  6135. credentials. Domain credentials contain more than one target field.
  6136. At least one of the naming parameters must be specified: NetbiosServerName,
  6137. DnsServerName, NetbiosDomainName, DnsDomainName or DnsTreeName.
  6138. Arguments:
  6139. LogonId - LogonId of the session to associate the credential with.
  6140. CredFlags - Flags changing the behavior of the routine:
  6141. No flags are currently defined.
  6142. Note: The CredFlags parameter is internal to the LSA process.
  6143. The Flags parameter is external to the process.
  6144. TargetInfo - Specifies the target information identifying the target server.
  6145. Credential - Specifies the credential to be written.
  6146. Flags - Specifies flags to control the operation of the API.
  6147. The following flags are defined:
  6148. CRED_PRESERVE_CREDENTIAL_BLOB: The credential blob should be preserved from the
  6149. already existing credential with the same credential name and credential type.
  6150. Return Values:
  6151. The following status codes may be returned:
  6152. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  6153. there is no credential set associated with this logon session.
  6154. Network logon sessions do not have an associated credential set.
  6155. STATUS_INVALID_PARAMETER - Certain fields may not be changed in an
  6156. existing credential. If such a field does not match the value
  6157. specified in the existing credential, this error is returned.
  6158. STATUS_INVALID_PARAMETER - None of the naming parameters were specified
  6159. or the credential specified did not have the Type field set to
  6160. CRED_TYPE_DOMAIN_PASSWORD, CRED_TYPE_DOMAIN_CERTIFICATE or CRED_TYPE_DOMAIN_VISIBLE_PASSWORD.
  6161. --*/
  6162. {
  6163. NTSTATUS Status;
  6164. CREDENTIAL_SETS CredentialSets = {NULL};
  6165. PCANONICAL_CREDENTIAL OldCredential = NULL;
  6166. PPROMPT_DATA OldPromptData = NULL;
  6167. PPROMPT_DATA NewPromptData = NULL;
  6168. PCANONICAL_CREDENTIAL PassedCredential = NULL;
  6169. PCANONICAL_TARGET_INFO CanonicalTargetInfo = NULL;
  6170. ULONG AliasIndex;
  6171. BOOLEAN CritSectLocked = FALSE;
  6172. ULONG CredentialCount = 0;
  6173. PCANONICAL_CREDENTIAL BestCredentials[CRED_TYPE_MAXIMUM];
  6174. ULONG CredentialIndex;
  6175. //
  6176. // Validate the flags
  6177. //
  6178. #define CREDP_WRITE_DOM_VALID_FLAGS CRED_PRESERVE_CREDENTIAL_BLOB
  6179. if ( (Flags & ~CREDP_WRITE_DOM_VALID_FLAGS) != 0 ) {
  6180. Status = STATUS_INVALID_PARAMETER_1;
  6181. goto Cleanup;
  6182. }
  6183. //
  6184. // Validate the TargetInfo
  6185. //
  6186. Status = CredpValidateTargetInfo( TargetInfo,
  6187. &CanonicalTargetInfo );
  6188. if ( !NT_SUCCESS(Status) ) {
  6189. goto Cleanup;
  6190. }
  6191. //
  6192. // Validate the passed in credential
  6193. //
  6194. Status = CredpValidateCredential( CredFlags,
  6195. CanonicalTargetInfo,
  6196. Credential,
  6197. &PassedCredential );
  6198. if ( !NT_SUCCESS(Status) ) {
  6199. goto Cleanup;
  6200. }
  6201. if ( !CredpIsDomainCredential(PassedCredential->Cred.Type) ) {
  6202. DebugLog(( DEB_TRACE_CRED,
  6203. "CrediWriteDomainCredential: Only allow for domain credentials.\n" ));
  6204. Status = STATUS_INVALID_PARAMETER;
  6205. goto Cleanup;
  6206. }
  6207. //
  6208. // Get the credential set.
  6209. //
  6210. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  6211. if ( !NT_SUCCESS(Status) ) {
  6212. goto Cleanup;
  6213. }
  6214. CredpLockCredSets( &CredentialSets );
  6215. CritSectLocked = TRUE;
  6216. //
  6217. // Write the credential to the credential set.
  6218. //
  6219. Status = CredpWriteCredential( &CredentialSets,
  6220. &PassedCredential,
  6221. FALSE, // Creds are not from persisted file
  6222. TRUE, // Update pin in CSP
  6223. TRUE, // Cred has been prompted for
  6224. Flags,
  6225. &OldCredential,
  6226. &OldPromptData,
  6227. &NewPromptData );
  6228. if ( !NT_SUCCESS(Status) ) {
  6229. goto Cleanup;
  6230. }
  6231. //
  6232. // Ensure the written credential is the best credential for this target info
  6233. //
  6234. Status = CredpFindBestCredentials( LogonId,
  6235. &CredentialSets,
  6236. CanonicalTargetInfo,
  6237. BestCredentials,
  6238. &CredentialCount );
  6239. if ( NT_SUCCESS(Status) ) {
  6240. for ( CredentialIndex = 0; CredentialIndex<CredentialCount; CredentialIndex++ ) {
  6241. if ( PassedCredential == BestCredentials[CredentialIndex] ) {
  6242. break;
  6243. }
  6244. }
  6245. if ( CredentialIndex >= CredentialCount ) {
  6246. DebugLog(( DEB_TRACE_CRED,
  6247. "CrediWriteDomainCredential: Credential isn't best credential for target info.\n" ));
  6248. Status = STATUS_INVALID_PARAMETER;
  6249. }
  6250. }
  6251. if ( !NT_SUCCESS(Status) ) {
  6252. //
  6253. // Remove the new credential from the cred set.
  6254. //
  6255. RemoveEntryList( &PassedCredential->Next );
  6256. //
  6257. // Remove the new prompt data from the cred set.
  6258. //
  6259. if ( NewPromptData != NULL ) {
  6260. RemoveEntryList( &NewPromptData->Next );
  6261. LsapFreeLsaHeap( NewPromptData );
  6262. }
  6263. //
  6264. // Put the old credential back in the cred set.
  6265. //
  6266. if ( OldCredential != NULL ) {
  6267. InsertHeadList(
  6268. &PersistToCredentialSet( &CredentialSets, OldCredential->Cred.Persist )->Credentials,
  6269. &OldCredential->Next );
  6270. OldCredential = NULL;
  6271. //
  6272. // Put the old prompt data back in the cred set.
  6273. //
  6274. if ( OldPromptData != NULL ) {
  6275. InsertHeadList(
  6276. &CredentialSets.SessionCredSets->PromptData,
  6277. &OldPromptData->Next );
  6278. OldPromptData = NULL;
  6279. }
  6280. }
  6281. goto Cleanup;
  6282. }
  6283. //
  6284. // Update the password for this user on all credentials.
  6285. //
  6286. CredpUpdatePassword( &CredentialSets,
  6287. PassedCredential );
  6288. PassedCredential = NULL;
  6289. Status = STATUS_SUCCESS;
  6290. //
  6291. // Cleanup
  6292. //
  6293. Cleanup:
  6294. if ( CritSectLocked ) {
  6295. CredpUnlockAndFlushCredSets( &CredentialSets );
  6296. }
  6297. if ( PassedCredential != NULL ) {
  6298. LsapFreeLsaHeap( PassedCredential );
  6299. }
  6300. if ( OldCredential != NULL ) {
  6301. LsapFreeLsaHeap( OldCredential );
  6302. }
  6303. if ( OldPromptData != NULL ) {
  6304. LsapFreeLsaHeap( OldPromptData );
  6305. }
  6306. if ( CanonicalTargetInfo != NULL ) {
  6307. LsapFreeLsaHeap( CanonicalTargetInfo );
  6308. }
  6309. CredpDereferenceCredSets( &CredentialSets );
  6310. return Status;
  6311. }
  6312. extern "C"
  6313. NTSTATUS
  6314. CrediReadDomainCredentials (
  6315. IN PLUID LogonId,
  6316. IN ULONG CredFlags,
  6317. IN PCREDENTIAL_TARGET_INFORMATIONW TargetInfo,
  6318. IN ULONG Flags,
  6319. OUT PULONG Count,
  6320. OUT PENCRYPTED_CREDENTIALW **Credentials
  6321. )
  6322. /*++
  6323. Routine Description:
  6324. The CredReadDomainCredentials API reads the domain credentials from the user's credential set.
  6325. The credential set used is the one associated with the logon session
  6326. of the current token. The token must not have the user's SID disabled.
  6327. CredReadDomainCredentials differs from CredRead in that it handles the
  6328. idiosyncrasies of domain (CRED_TYPE_DOMAIN_*)
  6329. credentials. Domain credentials contain more than one target field.
  6330. At least one of the naming parameters must be specified: NetbiosServerName,
  6331. DnsServerName, NetbiosDomainName, DnsDomainName or DnsTreeName. This API returns
  6332. the most specific credentials that match the naming parameters. That is, if there
  6333. is a credential that matches the target server name and a credential that matches
  6334. the target domain name, only the server specific credential is returned. This is
  6335. the credential that would be used.
  6336. Arguments:
  6337. LogonId - LogonId of the session to associate the credential with.
  6338. CredFlags - Flags changing the behavior of the routine:
  6339. CREDP_FLAGS_IN_PROCESS - Caller is in-process. Password data may be returned
  6340. CREDP_FLAGS_DONT_CACHE_TI - TargetInformation shouldn't be cached for CredGetTargetInfo
  6341. Note: The CredFlags parameter is internal to the LSA process.
  6342. The Flags parameter is external to the process.
  6343. TargetInfo - Specifies the target information identifying the target server.
  6344. Flags - Specifies flags to control the operation of the API.
  6345. Reserved. Must be zero.
  6346. Count - Returns a count of the number of credentials returned in Credentials.
  6347. Credentials - Returns a pointer to an array of pointers to credentials.
  6348. The returned buffer must be freed by calling CrediFreeCredentials
  6349. Return Values:
  6350. The following status codes may be returned:
  6351. STATUS_INVALID_PARAMETER - None of the naming parameters were specified.
  6352. STATUS_NOT_FOUND - There are no credentials matching the specified naming parameters.
  6353. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  6354. there is no credential set associated with this logon session.
  6355. Network logon sessions do not have an associated credential set.
  6356. --*/
  6357. {
  6358. NTSTATUS Status;
  6359. CREDENTIAL_SETS CredentialSets = {NULL};
  6360. PENCRYPTED_CREDENTIALW *TempCredentials = NULL;
  6361. PCANONICAL_TARGET_INFO CanonicalTargetInfo = NULL;
  6362. ULONG CredentialCount = 0;
  6363. PCANONICAL_CREDENTIAL BestCredentials[CRED_TYPE_MAXIMUM];
  6364. ULONG CredentialIndex;
  6365. PLIST_ENTRY ListEntry;
  6366. BOOLEAN CritSectLocked = FALSE;
  6367. //
  6368. // Validate the flags
  6369. //
  6370. if ( Flags != 0 ) {
  6371. Status = STATUS_INVALID_PARAMETER_1;
  6372. goto Cleanup;
  6373. }
  6374. //
  6375. // Validate the TargetInfo
  6376. //
  6377. Status = CredpValidateTargetInfo( TargetInfo,
  6378. &CanonicalTargetInfo );
  6379. if ( !NT_SUCCESS(Status) ) {
  6380. goto Cleanup;
  6381. }
  6382. //
  6383. // Get the credential set.
  6384. //
  6385. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  6386. if ( !NT_SUCCESS(Status) ) {
  6387. goto Cleanup;
  6388. }
  6389. //
  6390. // Determine the best credential of each type.
  6391. //
  6392. CredpLockCredSets( &CredentialSets );
  6393. CritSectLocked = TRUE;
  6394. Status = CredpFindBestCredentials( LogonId,
  6395. &CredentialSets,
  6396. CanonicalTargetInfo,
  6397. BestCredentials,
  6398. &CredentialCount );
  6399. if ( !NT_SUCCESS(Status) ) {
  6400. goto Cleanup;
  6401. }
  6402. if ( CredentialCount == 0 ) {
  6403. Status = STATUS_NOT_FOUND;
  6404. goto Cleanup;
  6405. }
  6406. //
  6407. // Allocate a buffer to return the credentials in
  6408. //
  6409. TempCredentials = (PENCRYPTED_CREDENTIALW *) MIDL_user_allocate( CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
  6410. if ( TempCredentials == NULL ) {
  6411. Status = STATUS_NO_MEMORY;
  6412. goto Cleanup;
  6413. }
  6414. RtlZeroMemory( TempCredentials, CredentialCount * sizeof(PENCRYPTED_CREDENTIALW) );
  6415. //
  6416. // Grab a copy of each of the credentials to return to the caller.
  6417. //
  6418. for ( CredentialIndex = 0; CredentialIndex<CredentialCount; CredentialIndex++ ) {
  6419. TempCredentials[CredentialIndex] =
  6420. CredpCloneCredential( &CredentialSets,
  6421. CredFlags | CREDP_FLAGS_USE_MIDL_HEAP,
  6422. BestCredentials[CredentialIndex] );
  6423. if ( TempCredentials[CredentialIndex] == NULL ) {
  6424. Status = STATUS_NO_MEMORY;
  6425. goto Cleanup;
  6426. }
  6427. }
  6428. *Count = CredentialCount;
  6429. *Credentials = TempCredentials;
  6430. TempCredentials = NULL;
  6431. Status = STATUS_SUCCESS;
  6432. //
  6433. // Cleanup
  6434. //
  6435. Cleanup:
  6436. if ( CanonicalTargetInfo != NULL ) {
  6437. //
  6438. // Cache this target info
  6439. //
  6440. if ( CritSectLocked &&
  6441. (CredFlags & CREDP_FLAGS_DONT_CACHE_TI) == 0 &&
  6442. (CanonicalTargetInfo->Flags & CRED_TI_USERNAME_TARGET) == 0 ) {
  6443. if ( CredpCacheTargetInfo( &CredentialSets, CanonicalTargetInfo )) {
  6444. CanonicalTargetInfo = NULL;
  6445. }
  6446. }
  6447. //
  6448. // Free the target info if it is still around
  6449. //
  6450. if ( CanonicalTargetInfo != NULL ) {
  6451. LsapFreeLsaHeap( CanonicalTargetInfo );
  6452. }
  6453. }
  6454. if ( CritSectLocked ) {
  6455. CredpUnlockAndFlushCredSets( &CredentialSets );
  6456. }
  6457. if ( TempCredentials != NULL ) {
  6458. CrediFreeCredentials( CredentialCount, TempCredentials );
  6459. }
  6460. CredpDereferenceCredSets( &CredentialSets );
  6461. return Status;
  6462. }
  6463. extern "C"
  6464. NTSTATUS
  6465. CrediDelete (
  6466. IN PLUID LogonId,
  6467. IN ULONG CredFlags,
  6468. IN LPWSTR TargetName,
  6469. IN ULONG Type,
  6470. IN ULONG Flags
  6471. )
  6472. /*++
  6473. Routine Description:
  6474. The CredDelete API deletes a credential from the user's credential set.
  6475. The credential set used is the one associated with the logon session
  6476. of the current token. The token must not have the user's SID disabled.
  6477. Arguments:
  6478. LogonId - LogonId of the session to associate the credential with.
  6479. CredFlags - Flags changing the behavior of the routine:
  6480. Must be zero.
  6481. Note: The CredFlags parameter is internal to the LSA process.
  6482. The Flags parameter is external to the process.
  6483. TargetName - Specifies the name of the credential to delete.
  6484. Type - Specifies the Type of the credential to find.
  6485. One of the CRED_TYPE_* values should be specified.
  6486. Flags - Specifies flags to control the operation of the API.
  6487. Reserved. Must be zero.
  6488. Return Values:
  6489. The following status codes may be returned:
  6490. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  6491. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  6492. there is no credential set associated with this logon session.
  6493. Network logon sessions do not have an associated credential set.
  6494. --*/
  6495. {
  6496. NTSTATUS Status;
  6497. CREDENTIAL_SETS CredentialSets = {NULL};
  6498. PCANONICAL_CREDENTIAL TempCredential;
  6499. PPROMPT_DATA PromptData;
  6500. UNICODE_STRING TargetNameString;
  6501. DWORD TargetNameSize;
  6502. BOOLEAN CritSectLocked = FALSE;
  6503. //
  6504. // Validate the flags
  6505. //
  6506. if ( Flags != 0 ) {
  6507. Status = STATUS_INVALID_PARAMETER_1;
  6508. goto Cleanup;
  6509. }
  6510. //
  6511. // Validate the input parameters
  6512. //
  6513. Status = CredpValidateTargetName( TargetName,
  6514. Type,
  6515. MightBeUsernameTarget,
  6516. NULL, // Don't know user name
  6517. NULL, // Don't know persist
  6518. &TargetNameSize,
  6519. NULL, // Don't care about name type
  6520. NULL ); // Don't care about non-wilcarded form of name
  6521. if ( !NT_SUCCESS(Status ) ) {
  6522. goto Cleanup;
  6523. }
  6524. //
  6525. // Get the credential set.
  6526. //
  6527. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  6528. if ( !NT_SUCCESS(Status) ) {
  6529. goto Cleanup;
  6530. }
  6531. //
  6532. // Find the credential
  6533. //
  6534. TargetNameString.Buffer = TargetName;
  6535. TargetNameString.MaximumLength = (USHORT) TargetNameSize;
  6536. TargetNameString.Length = TargetNameString.MaximumLength - sizeof(WCHAR);
  6537. CredpLockCredSets( &CredentialSets );
  6538. CritSectLocked = TRUE;
  6539. TempCredential = CredpFindCredential(
  6540. &CredentialSets,
  6541. &TargetNameString,
  6542. Type );
  6543. if ( TempCredential == NULL ) {
  6544. Status = STATUS_NOT_FOUND;
  6545. goto Cleanup;
  6546. }
  6547. //
  6548. // Find the prompt data (if any)
  6549. //
  6550. PromptData = CredpFindPromptData(
  6551. &CredentialSets,
  6552. &TargetNameString,
  6553. Type,
  6554. TempCredential->Cred.Persist );
  6555. //
  6556. // Mark the credential set as modified.
  6557. //
  6558. CredpMarkDirty( &CredentialSets, TempCredential->Cred.Persist, NULL );
  6559. //
  6560. // Delink and delete the credential.
  6561. //
  6562. RemoveEntryList( &TempCredential->Next );
  6563. LsapFreeLsaHeap( TempCredential );
  6564. //
  6565. // Delink and delete the prompt data
  6566. //
  6567. if ( PromptData != NULL ) {
  6568. RemoveEntryList( &PromptData->Next );
  6569. LsapFreeLsaHeap( PromptData );
  6570. }
  6571. Status = STATUS_SUCCESS;
  6572. //
  6573. // Cleanup
  6574. //
  6575. Cleanup:
  6576. if ( CritSectLocked ) {
  6577. CredpUnlockAndFlushCredSets( &CredentialSets );
  6578. }
  6579. CredpDereferenceCredSets( &CredentialSets );
  6580. return Status;
  6581. }
  6582. NTSTATUS
  6583. CrediRename (
  6584. IN PLUID LogonId,
  6585. IN LPWSTR OldTargetName,
  6586. IN LPWSTR NewTargetName,
  6587. IN ULONG Type,
  6588. IN ULONG Flags
  6589. )
  6590. /*++
  6591. Routine Description:
  6592. The CredRename API renames a credential in the user's credential set.
  6593. The credential set used is the one associated with the logon session
  6594. of the current token. The token must not have the user's SID disabled.
  6595. Arguments:
  6596. LogonId - LogonId of the session to associate the credential with.
  6597. OldTargetName - Specifies the current name of the credential to rename.
  6598. NewTargetName - Specifies the new name of the credential.
  6599. Type - Specifies the Type of the credential to rename
  6600. One of the CRED_TYPE_* values should be specified.
  6601. Flags - Specifies flags to control the operation of the API.
  6602. Reserved. Must be zero.
  6603. Return Values:
  6604. The following status codes may be returned:
  6605. STATUS_NOT_FOUND - There is no credential with the specified OldTargetName.
  6606. STATUS_OBJECT_NAME_COLLISION - There is already a credential named NewTargetName.
  6607. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  6608. there is no credential set associated with this logon session.
  6609. Network logon sessions do not have an associated credential set.
  6610. --*/
  6611. {
  6612. NTSTATUS Status;
  6613. CREDENTIAL_SETS CredentialSets = {NULL};
  6614. BOOLEAN CritSectLocked = FALSE;
  6615. PPROMPT_DATA OldPromptData;
  6616. UNICODE_STRING OldTargetNameString;
  6617. DWORD OldTargetNameSize;
  6618. PCANONICAL_CREDENTIAL OldCredential;
  6619. UNICODE_STRING NewTargetNameString;
  6620. DWORD NewTargetNameSize;
  6621. PCANONICAL_CREDENTIAL NewCredential;
  6622. ENCRYPTED_CREDENTIALW LocalCredential;
  6623. PCANONICAL_CREDENTIAL WrittenCredential;
  6624. //
  6625. // Validate the flags
  6626. //
  6627. if ( Flags != 0 ) {
  6628. Status = STATUS_INVALID_PARAMETER_1;
  6629. goto Cleanup;
  6630. }
  6631. //
  6632. // Get the credential set.
  6633. //
  6634. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  6635. if ( !NT_SUCCESS(Status) ) {
  6636. goto Cleanup;
  6637. }
  6638. CredpLockCredSets( &CredentialSets );
  6639. CritSectLocked = TRUE;
  6640. //
  6641. // Find the old credential
  6642. //
  6643. Status = CredpValidateTargetName( OldTargetName,
  6644. Type,
  6645. MightBeUsernameTarget,
  6646. NULL, // Don't know user name
  6647. NULL, // Don't know persist
  6648. &OldTargetNameSize,
  6649. NULL, // Don't care about name type
  6650. NULL ); // Don't care about non-wilcarded form of name
  6651. if ( !NT_SUCCESS(Status ) ) {
  6652. goto Cleanup;
  6653. }
  6654. OldTargetNameString.Buffer = OldTargetName;
  6655. OldTargetNameString.MaximumLength = (USHORT) OldTargetNameSize;
  6656. OldTargetNameString.Length = OldTargetNameString.MaximumLength - sizeof(WCHAR);
  6657. OldCredential = CredpFindCredential(
  6658. &CredentialSets,
  6659. &OldTargetNameString,
  6660. Type );
  6661. if ( OldCredential == NULL ) {
  6662. Status = STATUS_NOT_FOUND;
  6663. goto Cleanup;
  6664. }
  6665. //
  6666. // Find the New credential
  6667. //
  6668. Status = CredpValidateTargetName( NewTargetName,
  6669. Type,
  6670. (OldCredential->Cred.Flags & CRED_FLAGS_USERNAME_TARGET) ?
  6671. IsUsernameTarget :
  6672. IsNotUsernameTarget,
  6673. &OldCredential->Cred.UserName,
  6674. &OldCredential->Cred.Persist,
  6675. &NewTargetNameSize,
  6676. NULL, // Don't care about name type
  6677. NULL ); // Don't care about non-wilcarded form of name
  6678. if ( !NT_SUCCESS(Status ) ) {
  6679. goto Cleanup;
  6680. }
  6681. NewTargetNameString.Buffer = NewTargetName;
  6682. NewTargetNameString.MaximumLength = (USHORT) NewTargetNameSize;
  6683. NewTargetNameString.Length = NewTargetNameString.MaximumLength - sizeof(WCHAR);
  6684. NewCredential = CredpFindCredential(
  6685. &CredentialSets,
  6686. &NewTargetNameString,
  6687. Type );
  6688. if ( NewCredential != NULL ) {
  6689. Status = STATUS_OBJECT_NAME_COLLISION;
  6690. goto Cleanup;
  6691. }
  6692. //
  6693. // Find the prompt data (if any)
  6694. //
  6695. OldPromptData = CredpFindPromptData(
  6696. &CredentialSets,
  6697. &OldTargetNameString,
  6698. Type,
  6699. OldCredential->Cred.Persist );
  6700. //
  6701. // Write the new credential
  6702. //
  6703. // Clear the TargetAlias. It'll either be syntactically invalid and fail. Or
  6704. // worse, it'll be syntactically valid but semantic nonsense.
  6705. //
  6706. LocalCredential.Cred = OldCredential->Cred;
  6707. LocalCredential.ClearCredentialBlobSize = OldCredential->ClearCredentialBlobSize;
  6708. LocalCredential.Cred.TargetName = NewTargetName;
  6709. LocalCredential.Cred.TargetAlias = NULL;
  6710. Status = CredpWriteMorphedCredential(
  6711. &CredentialSets,
  6712. !ShouldPromptNow( OldPromptData ), // Preserve the prompted for state
  6713. NULL, // No Target info
  6714. &LocalCredential,
  6715. &WrittenCredential );
  6716. if ( !NT_SUCCESS(Status) ) {
  6717. goto Cleanup;
  6718. }
  6719. //
  6720. // Mark the credential set as modified.
  6721. //
  6722. CredpMarkDirty( &CredentialSets, OldCredential->Cred.Persist, NULL );
  6723. //
  6724. // Delink and delete the old credential.
  6725. //
  6726. RemoveEntryList( &OldCredential->Next );
  6727. LsapFreeLsaHeap( OldCredential );
  6728. //
  6729. // Delink and delete the prompt data
  6730. //
  6731. if ( OldPromptData != NULL ) {
  6732. RemoveEntryList( &OldPromptData->Next );
  6733. LsapFreeLsaHeap( OldPromptData );
  6734. }
  6735. Status = STATUS_SUCCESS;
  6736. //
  6737. // Cleanup
  6738. //
  6739. Cleanup:
  6740. if ( CritSectLocked ) {
  6741. CredpUnlockAndFlushCredSets( &CredentialSets );
  6742. }
  6743. CredpDereferenceCredSets( &CredentialSets );
  6744. return Status;
  6745. }
  6746. extern "C"
  6747. NTSTATUS
  6748. CrediGetTargetInfo (
  6749. IN PLUID LogonId,
  6750. IN LPWSTR TargetServerName,
  6751. IN ULONG Flags,
  6752. OUT PCREDENTIAL_TARGET_INFORMATIONW *TargetInfo
  6753. )
  6754. /*++
  6755. Routine Description:
  6756. The CredGetTargetInfo API gets all of the known target name information
  6757. for the named target machine. This API is executed locally
  6758. and does not need any particular privilege. The information returned is expected
  6759. to be passed to the CredReadDomainCredentials and CredWriteDomainCredentials APIs.
  6760. The information should not be used for any other purpose.
  6761. Authentication packages compute TargetInfo when attempting to authenticate to
  6762. ServerName. The authentication packages cache this target information to make it
  6763. available to CredGetTargetInfo. Therefore, the target information will only be
  6764. available if we've recently attempted to authenticate to ServerName.
  6765. Arguments:
  6766. LogonId - LogonId of the session the target info is associated with
  6767. TargetServerName - This parameter specifies the name of the machine to get the information
  6768. for.
  6769. Flags - Specifies flags to control the operation of the API.
  6770. CRED_ALLOW_NAME_RESOLUTION - Specifies that if no target info can be found for
  6771. TargetName, then name resolution should be done on TargetName to convert it
  6772. to other forms. If target info exists for any of those other forms, that
  6773. target info is returned. Currently only DNS name resolution is done.
  6774. This bit is useful if the application doesn't call the authentication package
  6775. directly. The application might pass the TargetName to another layer of software
  6776. to authenticate to the server. That layer of software might resolve the name and
  6777. pass the resolved name to the authentication package. As such, there will be no
  6778. target info for the original TargetName.
  6779. TargetInfo - Returns a pointer to the target information.
  6780. At least one of the returned fields of TargetInfo will be non-NULL.
  6781. Return Values:
  6782. The following status codes may be returned:
  6783. STATUS_NO_MEMORY - There isn't enough memory to complete the operation.
  6784. STATUS_NOT_FOUND - There is no credential with the specified TargetName.
  6785. --*/
  6786. {
  6787. NTSTATUS Status;
  6788. DWORD WinStatus;
  6789. PCANONICAL_TARGET_INFO CanonicalTargetInfo;
  6790. PCREDENTIAL_TARGET_INFORMATIONW LocalTargetInfo = NULL;
  6791. ULONG TargetServerNameSize;
  6792. LPBYTE Where;
  6793. CREDENTIAL_SETS CredentialSets = {NULL};
  6794. BOOLEAN CritSectLocked = FALSE;
  6795. PDNS_RECORD DnsARecords = NULL;
  6796. #define CREDP_GTI_VALID_FLAGS CRED_ALLOW_NAME_RESOLUTION
  6797. //
  6798. // Validate the flags
  6799. //
  6800. if ( (Flags & ~CREDP_GTI_VALID_FLAGS) != 0 ) {
  6801. Status = STATUS_INVALID_PARAMETER_1;
  6802. goto Cleanup;
  6803. }
  6804. //
  6805. // Validate the input parameters
  6806. //
  6807. if ( !CredpValidateString( TargetServerName,
  6808. CRED_MAX_DOMAIN_TARGET_NAME_LENGTH,
  6809. FALSE, // NULL not OK
  6810. &TargetServerNameSize ) ) {
  6811. Status = STATUS_INVALID_PARAMETER;
  6812. DebugLog(( DEB_TRACE_CRED,
  6813. "CrediGetTargetInfo: Invalid TargetServerName buffer.\n" ));
  6814. goto Cleanup;
  6815. }
  6816. //
  6817. // Get the credential set.
  6818. //
  6819. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  6820. if ( !NT_SUCCESS(Status) ) {
  6821. goto Cleanup;
  6822. }
  6823. //
  6824. // Lock the credential set.
  6825. //
  6826. CredpLockCredSets( &CredentialSets );
  6827. CritSectLocked = TRUE;
  6828. //
  6829. // See if the named server can be found directly
  6830. //
  6831. CanonicalTargetInfo = CredpFindTargetInfo( &CredentialSets, TargetServerName );
  6832. if ( CanonicalTargetInfo == NULL ) {
  6833. PDNS_RECORD DnsRecord;
  6834. //
  6835. // If the caller doesn't wants us to try name resolution in an attempt for find the target info
  6836. // we're done.
  6837. //
  6838. if ( (Flags & CRED_ALLOW_NAME_RESOLUTION) == 0 ) {
  6839. Status = STATUS_NOT_FOUND;
  6840. goto Cleanup;
  6841. }
  6842. //
  6843. // Query DNS for the name
  6844. //
  6845. WinStatus = DnsQuery_W(
  6846. TargetServerName,
  6847. DNS_TYPE_A,
  6848. 0, // No special flags
  6849. NULL, // No special DNS servers
  6850. &DnsARecords,
  6851. NULL );
  6852. if ( WinStatus != NO_ERROR ) {
  6853. // Don't confuse the caller with a more specific status
  6854. Status = STATUS_NOT_FOUND;
  6855. goto Cleanup;
  6856. }
  6857. //
  6858. // Find the first A or AAAA record
  6859. //
  6860. // Only look up the first name returned from DNS. That's the name the redir used when
  6861. // creating the SPN that the authentication packages pass us in the TargetName.
  6862. //
  6863. for ( DnsRecord = DnsARecords;
  6864. DnsRecord != NULL;
  6865. DnsRecord = DnsRecord->pNext ) {
  6866. if ( DnsRecord->wType == DNS_TYPE_A ||
  6867. DnsRecord->wType == DNS_TYPE_AAAA ) {
  6868. //
  6869. // See if the named server can be found directly
  6870. //
  6871. CanonicalTargetInfo = CredpFindTargetInfo( &CredentialSets, DnsRecord->pName );
  6872. break;
  6873. }
  6874. }
  6875. if ( CanonicalTargetInfo == NULL ) {
  6876. Status = STATUS_NOT_FOUND;
  6877. goto Cleanup;
  6878. }
  6879. }
  6880. //
  6881. // Allocate a structure to return to the caller
  6882. //
  6883. LocalTargetInfo = (PCREDENTIAL_TARGET_INFORMATIONW) MIDL_user_allocate(
  6884. sizeof(*LocalTargetInfo) +
  6885. CanonicalTargetInfo->CredTypeCount * sizeof(DWORD) +
  6886. CanonicalTargetInfo->TargetName.MaximumLength +
  6887. CanonicalTargetInfo->NetbiosServerName.MaximumLength +
  6888. CanonicalTargetInfo->DnsServerName.MaximumLength +
  6889. CanonicalTargetInfo->NetbiosDomainName.MaximumLength +
  6890. CanonicalTargetInfo->DnsDomainName.MaximumLength +
  6891. CanonicalTargetInfo->DnsTreeName.MaximumLength +
  6892. CanonicalTargetInfo->PackageName.MaximumLength );
  6893. if ( LocalTargetInfo == NULL) {
  6894. Status = STATUS_NO_MEMORY;
  6895. goto Cleanup;
  6896. }
  6897. RtlZeroMemory( LocalTargetInfo, sizeof(*LocalTargetInfo) );
  6898. Where = (LPBYTE) (LocalTargetInfo+1);
  6899. //
  6900. // Copy the constant size data
  6901. //
  6902. LocalTargetInfo->Flags = CanonicalTargetInfo->Flags;
  6903. //
  6904. // Copy the DWORD aligned data
  6905. //
  6906. LocalTargetInfo->CredTypeCount = CanonicalTargetInfo->CredTypeCount;
  6907. if ( LocalTargetInfo->CredTypeCount != 0 ) {
  6908. LocalTargetInfo->CredTypes = (LPDWORD)Where;
  6909. RtlCopyMemory( Where, CanonicalTargetInfo->CredTypes, CanonicalTargetInfo->CredTypeCount * sizeof(DWORD) );
  6910. Where += CanonicalTargetInfo->CredTypeCount * sizeof(DWORD);
  6911. } else {
  6912. LocalTargetInfo->CredTypes = NULL;
  6913. }
  6914. //
  6915. // Copy the 2-byte aligned data
  6916. //
  6917. if ( CanonicalTargetInfo->TargetName.Length != 0 ) {
  6918. LocalTargetInfo->TargetName = (LPWSTR) Where;
  6919. RtlCopyMemory( LocalTargetInfo->TargetName,
  6920. CanonicalTargetInfo->TargetName.Buffer,
  6921. CanonicalTargetInfo->TargetName.MaximumLength );
  6922. Where += CanonicalTargetInfo->TargetName.MaximumLength;
  6923. }
  6924. if ( CanonicalTargetInfo->NetbiosServerName.Length != 0 ) {
  6925. LocalTargetInfo->NetbiosServerName = (LPWSTR) Where;
  6926. RtlCopyMemory( LocalTargetInfo->NetbiosServerName,
  6927. CanonicalTargetInfo->NetbiosServerName.Buffer,
  6928. CanonicalTargetInfo->NetbiosServerName.MaximumLength );
  6929. Where += CanonicalTargetInfo->NetbiosServerName.MaximumLength;
  6930. }
  6931. if ( CanonicalTargetInfo->DnsServerName.Length != 0 ) {
  6932. LocalTargetInfo->DnsServerName = (LPWSTR) Where;
  6933. RtlCopyMemory( LocalTargetInfo->DnsServerName,
  6934. CanonicalTargetInfo->DnsServerName.Buffer,
  6935. CanonicalTargetInfo->DnsServerName.MaximumLength );
  6936. Where += CanonicalTargetInfo->DnsServerName.MaximumLength;
  6937. }
  6938. if ( CanonicalTargetInfo->NetbiosDomainName.Length != 0 ) {
  6939. LocalTargetInfo->NetbiosDomainName = (LPWSTR) Where;
  6940. RtlCopyMemory( LocalTargetInfo->NetbiosDomainName,
  6941. CanonicalTargetInfo->NetbiosDomainName.Buffer,
  6942. CanonicalTargetInfo->NetbiosDomainName.MaximumLength );
  6943. Where += CanonicalTargetInfo->NetbiosDomainName.MaximumLength;
  6944. }
  6945. if ( CanonicalTargetInfo->DnsDomainName.Length != 0 ) {
  6946. LocalTargetInfo->DnsDomainName = (LPWSTR) Where;
  6947. RtlCopyMemory( LocalTargetInfo->DnsDomainName,
  6948. CanonicalTargetInfo->DnsDomainName.Buffer,
  6949. CanonicalTargetInfo->DnsDomainName.MaximumLength );
  6950. Where += CanonicalTargetInfo->DnsDomainName.MaximumLength;
  6951. }
  6952. if ( CanonicalTargetInfo->DnsTreeName.Length != 0 ) {
  6953. LocalTargetInfo->DnsTreeName = (LPWSTR) Where;
  6954. RtlCopyMemory( LocalTargetInfo->DnsTreeName,
  6955. CanonicalTargetInfo->DnsTreeName.Buffer,
  6956. CanonicalTargetInfo->DnsTreeName.MaximumLength );
  6957. Where += CanonicalTargetInfo->DnsTreeName.MaximumLength;
  6958. }
  6959. if ( CanonicalTargetInfo->PackageName.Length != 0 ) {
  6960. LocalTargetInfo->PackageName = (LPWSTR) Where;
  6961. RtlCopyMemory( LocalTargetInfo->PackageName,
  6962. CanonicalTargetInfo->PackageName.Buffer,
  6963. CanonicalTargetInfo->PackageName.MaximumLength );
  6964. Where += CanonicalTargetInfo->PackageName.MaximumLength;
  6965. }
  6966. //
  6967. // Return the information to the caller
  6968. //
  6969. *TargetInfo = LocalTargetInfo;
  6970. Status = STATUS_SUCCESS;
  6971. //
  6972. // Cleanup
  6973. //
  6974. Cleanup:
  6975. if ( CritSectLocked ) {
  6976. CredpUnlockAndFlushCredSets( &CredentialSets );
  6977. }
  6978. if ( DnsARecords != NULL ) {
  6979. DnsRecordListFree( DnsARecords, DnsFreeRecordListDeep );
  6980. }
  6981. CredpDereferenceCredSets( &CredentialSets );
  6982. return Status;
  6983. }
  6984. NTSTATUS
  6985. CrediGetSessionTypes (
  6986. IN PLUID LogonId,
  6987. IN DWORD MaximumPersistCount,
  6988. OUT LPDWORD MaximumPersist
  6989. )
  6990. /*++
  6991. Routine Description:
  6992. CredGetSessionTypes returns the maximum persistence supported by the current logon
  6993. session.
  6994. For whistler, CRED_PERSIST_LOCAL_MACHINE and CRED_PERSIST_ENTERPRISE credentials can not
  6995. be stored for sessions where the profile is not loaded. If future releases, credentials
  6996. might not be associated with the user's profile.
  6997. Arguments:
  6998. LogonId - LogonId of the session to associate the credential with.
  6999. MaximumPersistCount - Specifies the number of elements in the MaximumPersist array.
  7000. The caller should specify CRED_TYPE_MAXIMUM for this parameter.
  7001. MaximumPersist - Returns the maximum persistance supported by the current logon session for
  7002. each credential type. Index into the array with one of the CRED_TYPE_* defines.
  7003. Returns CRED_PERSIST_NONE if no credential of this type can be stored.
  7004. Returns CRED_PERSIST_SESSION if only session specific credential may be stored.
  7005. Returns CRED_PERSIST_LOCAL_MACHINE if session specific and machine specific credentials
  7006. may be stored.
  7007. Returns CRED_PERSIST_ENTERPRISE if any credential may be stored.
  7008. Return Values:
  7009. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  7010. there is no credential set associated with this logon session.
  7011. Network logon sessions do not have an associated credential set.
  7012. --*/
  7013. {
  7014. NTSTATUS Status;
  7015. CREDENTIAL_SETS CredentialSets = {NULL};
  7016. BOOLEAN CritSectLocked = FALSE;
  7017. ULONG i;
  7018. PCREDENTIAL_SET CredentialSet;
  7019. ULONG Persist;
  7020. //
  7021. // Validate the parameters
  7022. //
  7023. if ( MaximumPersistCount != 0 && MaximumPersist == NULL ) {
  7024. Status = STATUS_INVALID_PARAMETER;
  7025. goto Cleanup;
  7026. }
  7027. //
  7028. // Avoid huge buffers
  7029. // Allow some leeway to allow new applications to run on old OSes.
  7030. //
  7031. if ( MaximumPersistCount > CRED_TYPE_MAXIMUM+1000 ) {
  7032. Status = STATUS_INVALID_PARAMETER;
  7033. goto Cleanup;
  7034. }
  7035. //
  7036. // Get the credential set.
  7037. //
  7038. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  7039. if ( !NT_SUCCESS(Status) ) {
  7040. goto Cleanup;
  7041. }
  7042. //
  7043. // Lock the credential set.
  7044. //
  7045. CredpLockCredSets( &CredentialSets );
  7046. CritSectLocked = TRUE;
  7047. //
  7048. // Loop through the list filling in each element
  7049. //
  7050. for ( i=0; i<MaximumPersistCount; i++ ) {
  7051. //
  7052. // The default value depends on whether the profile is loaded.
  7053. //
  7054. if ( CredentialSets.SessionCredSets->ProfileLoaded ) {
  7055. MaximumPersist[i] = CRED_PERSIST_ENTERPRISE;
  7056. } else {
  7057. MaximumPersist[i] = CRED_PERSIST_SESSION;
  7058. }
  7059. //
  7060. // Some types can be disabled
  7061. //
  7062. if ( i == CRED_TYPE_GENERIC ) {
  7063. /* Nothing to do here */
  7064. //
  7065. // Disable domain credentials based on policy
  7066. //
  7067. } else if ( CredpIsDomainCredential(i) ) {
  7068. if ( CredDisableDomainCreds ) {
  7069. MaximumPersist[i] = CRED_PERSIST_NONE;
  7070. } else if ( CredIsPersonal &&
  7071. i != CRED_TYPE_DOMAIN_VISIBLE_PASSWORD ) {
  7072. MaximumPersist[i] = CRED_PERSIST_NONE;
  7073. }
  7074. //
  7075. // Disable all types we don't understand
  7076. //
  7077. } else {
  7078. MaximumPersist[i] = CRED_PERSIST_NONE;
  7079. }
  7080. }
  7081. Status = STATUS_SUCCESS;
  7082. //
  7083. // Cleanup
  7084. //
  7085. Cleanup:
  7086. if ( CritSectLocked ) {
  7087. CredpUnlockAndFlushCredSets( &CredentialSets );
  7088. }
  7089. CredpDereferenceCredSets( &CredentialSets );
  7090. return Status;
  7091. }
  7092. NTSTATUS
  7093. CrediProfileLoaded (
  7094. IN PLUID LogonId
  7095. )
  7096. /*++
  7097. Routine Description:
  7098. The CredProfileLoaded API is a private API used by LoadUserProfile to notify the
  7099. credential manager that the profile for the current user has been loaded.
  7100. The caller must be impersonating the logged on user.
  7101. Arguments:
  7102. LogonId - LogonId of the session to associate the credential with.
  7103. Return Values:
  7104. The following status codes may be returned:
  7105. STATUS_NO_SUCH_LOGON_SESSION - The logon session does not exist or
  7106. there is no credential set associated with this logon session.
  7107. Network logon sessions do not have an associated credential set.
  7108. --*/
  7109. {
  7110. NTSTATUS Status;
  7111. DWORD WinStatus;
  7112. CREDENTIAL_SETS CredentialSets = {NULL};
  7113. BOOLEAN CritSectLocked = FALSE;
  7114. PCREDENTIAL_SET CredentialSet;
  7115. ULONG Persist;
  7116. //
  7117. // Get the credential set.
  7118. //
  7119. Status = CredpReferenceCredSets( LogonId, &CredentialSets );
  7120. if ( !NT_SUCCESS(Status) ) {
  7121. //
  7122. // This is a notification API. Just because there are no credentials, doesn't
  7123. // mean the profile load should fail. Indeed, it is already finished.
  7124. //
  7125. if ( Status == STATUS_NO_SUCH_LOGON_SESSION ) {
  7126. Status = STATUS_SUCCESS;
  7127. }
  7128. goto Cleanup;
  7129. }
  7130. //
  7131. // Lock the credential set.
  7132. //
  7133. CredpLockCredSets( &CredentialSets );
  7134. CritSectLocked = TRUE;
  7135. //
  7136. // Mark it that the profile is now loaded.
  7137. //
  7138. CredentialSets.SessionCredSets->ProfileLoaded = TRUE;
  7139. //
  7140. // Loop through the list of credential sets reading each
  7141. //
  7142. for ( Persist=CRED_PERSIST_MIN; Persist <= CRED_PERSIST_MAX; Persist++ ) {
  7143. //
  7144. // Ignore non-persistent credential sets
  7145. //
  7146. if ( Persist == CRED_PERSIST_SESSION ) {
  7147. continue;
  7148. }
  7149. CredentialSet = PersistToCredentialSet( &CredentialSets, Persist );
  7150. //
  7151. // If this cred set hasn't been read yet,
  7152. // read it now.
  7153. //
  7154. if ( !CredentialSet->FileRead ) {
  7155. WinStatus = CredpReadCredSet( &CredentialSets, Persist );
  7156. if ( WinStatus == NO_ERROR ) {
  7157. CredentialSet->FileRead = TRUE;
  7158. }
  7159. }
  7160. }
  7161. Status = STATUS_SUCCESS;
  7162. //
  7163. // Cleanup
  7164. //
  7165. Cleanup:
  7166. if ( CritSectLocked ) {
  7167. CredpUnlockAndFlushCredSets( &CredentialSets );
  7168. }
  7169. CredpDereferenceCredSets( &CredentialSets );
  7170. return Status;
  7171. }