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.

5150 lines
143 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: logonapi.cxx
  8. //
  9. // Contents: Code for logon and logoff for the Kerberos package
  10. //
  11. //
  12. // History: 16-April-1996 MikeSw Created
  13. // 15-June-2000 t-ryanj Added event tracing support
  14. //
  15. //------------------------------------------------------------------------
  16. #include <kerb.hxx>
  17. #include <kerbp.h>
  18. #include <utils.hxx>
  19. #ifdef DEBUG_SUPPORT
  20. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  21. #endif
  22. #define FILENO FILENO_LOGONAPI
  23. //+-------------------------------------------------------------------------
  24. //
  25. // Function: KerbFindCommonPaEtype
  26. //
  27. // Synopsis: Finds an encryption type in common between KDC and client.
  28. //
  29. // Effects:
  30. //
  31. // Arguments: Credentials - Client's credentials, must be locked
  32. // InputPaData - PA data from an error return from the KDC
  33. // UseOldPassword - if TRUE, use the old password instead of current password
  34. // UserKey - receives key for common encryption type
  35. //
  36. // Requires:
  37. //
  38. // Returns:
  39. //
  40. // Notes:
  41. //
  42. //
  43. //--------------------------------------------------------------------------
  44. NTSTATUS
  45. KerbFindCommonPaEtype(
  46. IN PKERB_PRIMARY_CREDENTIAL Credentials,
  47. IN OPTIONAL PKERB_PA_DATA_LIST InputPaData,
  48. IN BOOLEAN UseOldPassword,
  49. IN BOOLEAN IgnoreSaltFailures,
  50. OUT PKERB_ENCRYPTION_KEY * UserKey
  51. )
  52. {
  53. NTSTATUS Status = STATUS_SUCCESS;
  54. KERBERR KerbErr;
  55. PKERB_PA_DATA InputData = NULL;
  56. ULONG PasswordTypes[KERB_MAX_CRYPTO_SYSTEMS];
  57. ULONG PasswordCount;
  58. ULONG KdcEtypes[KERB_MAX_CRYPTO_SYSTEMS];
  59. ULONG KdcEtypeCount = 0;
  60. PKERB_ETYPE_INFO * EtypeInfo = NULL;
  61. PKERB_ETYPE_INFO EtypeEntry;
  62. ULONG CommonCryptSystem;
  63. ULONG Index;
  64. PKERB_STORED_CREDENTIAL Passwords;
  65. BOOLEAN UseDES = FALSE;
  66. *UserKey = NULL;
  67. //
  68. // Check to see if the input had any interesting PA data
  69. //
  70. if ((InputPaData != NULL) && (!UseOldPassword))
  71. {
  72. InputData = KerbFindPreAuthDataEntry(
  73. KRB5_PADATA_ETYPE_INFO,
  74. InputPaData
  75. );
  76. if (InputData == NULL)
  77. {
  78. //
  79. // If no etype-info was provided, then we are out of luck.
  80. // Change this to allow for utilizing default DES etype if no
  81. // etypeinfo specified for Heimdel KDC interop. Bug#87960
  82. //
  83. //Status = STATUS_NO_PA_DATA;
  84. //goto Cleanup;
  85. UseDES = TRUE;
  86. }
  87. else
  88. {
  89. //
  90. // Unpack the etype info
  91. //
  92. KerbErr = KerbUnpackData(
  93. InputData->preauth_data.value,
  94. InputData->preauth_data.length,
  95. PKERB_ETYPE_INFO_PDU,
  96. (PVOID *) &EtypeInfo
  97. );
  98. if (!KERB_SUCCESS(KerbErr))
  99. {
  100. D_DebugLog((DEB_ERROR,"Failed to unpack ETYPE INFO: 0x%x. %ws, line%d\n",KerbErr, THIS_FILE, __LINE__));
  101. Status = STATUS_INSUFFICIENT_RESOURCES;
  102. goto Cleanup;
  103. }
  104. //
  105. // Build a new set of passwords
  106. //
  107. Status = KerbChangeCredentialsPassword(
  108. Credentials,
  109. NULL, // no password
  110. *EtypeInfo,
  111. UnknownAccount,
  112. PRIMARY_CRED_CLEAR_PASSWORD
  113. );
  114. if (!NT_SUCCESS(Status))
  115. {
  116. D_DebugLog((DEB_ERROR,"Failed to update primary creds with new salt: 0x%x, file %ws %d\n",
  117. Status, THIS_FILE, __LINE__ ));
  118. if (!IgnoreSaltFailures)
  119. {
  120. //
  121. // Remap the error, as we want to return a more useful error
  122. //
  123. if (Status == STATUS_INVALID_PARAMETER)
  124. {
  125. Status = STATUS_WRONG_PASSWORD;
  126. }
  127. goto Cleanup;
  128. }
  129. else
  130. {
  131. Status = STATUS_SUCCESS;
  132. }
  133. }
  134. //
  135. // Build a list of crypt types from the etype info
  136. //
  137. KdcEtypeCount = 0;
  138. EtypeEntry = *EtypeInfo;
  139. while (EtypeEntry != NULL)
  140. {
  141. KdcEtypes[KdcEtypeCount++] = EtypeEntry->value.encryption_type;
  142. EtypeEntry = EtypeEntry->next;
  143. if (KdcEtypeCount == KERB_MAX_CRYPTO_SYSTEMS)
  144. {
  145. break;
  146. }
  147. }
  148. }
  149. } else {
  150. ULONG OldFirstEtype;
  151. //
  152. // Include all our crypt types as supported
  153. //
  154. Status = CDBuildIntegrityVect(
  155. &KdcEtypeCount,
  156. KdcEtypes
  157. );
  158. DsysAssert(NT_SUCCESS(Status));
  159. DsysAssert(KdcEtypeCount >= 1);
  160. //
  161. // replace the first etype with the default
  162. //
  163. if (KdcEtypes[0] != KerbGlobalDefaultPreauthEtype)
  164. {
  165. OldFirstEtype = KdcEtypes[0];
  166. KdcEtypes[0] = KerbGlobalDefaultPreauthEtype;
  167. for (Index = 1; Index < KdcEtypeCount ; Index++ )
  168. {
  169. if ( KdcEtypes[Index] == KerbGlobalDefaultPreauthEtype)
  170. {
  171. KdcEtypes[Index] = OldFirstEtype;
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. // Heimdal KDC compat gives us no supported EType info, so
  178. // we've got to rely upon SPEC'd default, DES encryption.
  179. // See bug 87960 for more info. NOTE: We'll try this
  180. // 2 times... Should work to avoid this..
  181. if (UseDES) {
  182. ULONG OldFirstEtype;
  183. //
  184. // Include all our crypt types as supported
  185. //
  186. Status = CDBuildIntegrityVect(
  187. &KdcEtypeCount,
  188. KdcEtypes
  189. );
  190. DsysAssert(NT_SUCCESS(Status));
  191. DsysAssert(KdcEtypeCount >= 1);
  192. //
  193. // Use **only** DES, as it should be supported by all
  194. // KDCs, and w/o preauth ETYPEINFO data, we would have
  195. // to hit every ETYPE.
  196. // TBD: When Heimdal supports RC4, or if they fix their
  197. // padata, then pull this code.
  198. if (KdcEtypes[0] != KERB_ETYPE_DES_CBC_MD5)
  199. {
  200. OldFirstEtype = KdcEtypes[0];
  201. KdcEtypes[0] = KERB_ETYPE_DES_CBC_MD5;
  202. for (Index = 1; Index < KdcEtypeCount ; Index++ )
  203. {
  204. if ( KdcEtypes[Index] == KERB_ETYPE_DES_CBC_MD5)
  205. {
  206. KdcEtypes[Index] = OldFirstEtype;
  207. break;
  208. }
  209. }
  210. }
  211. }
  212. //
  213. // Build the list of passwords
  214. //
  215. if (UseOldPassword)
  216. {
  217. Passwords = Credentials->OldPasswords;
  218. if (Passwords == NULL)
  219. {
  220. Status = STATUS_WRONG_PASSWORD;
  221. goto Cleanup;
  222. }
  223. }
  224. else
  225. {
  226. Passwords = Credentials->Passwords;
  227. }
  228. PasswordCount = Passwords->CredentialCount;
  229. if (PasswordCount > KERB_MAX_CRYPTO_SYSTEMS)
  230. {
  231. DsysAssert(PasswordCount < KERB_MAX_CRYPTO_SYSTEMS);
  232. Status = STATUS_INTERNAL_ERROR;
  233. goto Cleanup;
  234. }
  235. for (Index = 0; Index < PasswordCount ; Index++ )
  236. {
  237. PasswordTypes[Index] = (ULONG) Passwords->Credentials[Index].Key.keytype;
  238. }
  239. //
  240. // Now find the common crypt system
  241. //
  242. Status = CDFindCommonCSystemWithKey(
  243. KdcEtypeCount,
  244. KdcEtypes,
  245. PasswordCount,
  246. PasswordTypes,
  247. &CommonCryptSystem
  248. );
  249. if (!NT_SUCCESS(Status))
  250. {
  251. goto Cleanup;
  252. }
  253. //
  254. // Get the key for the common crypt type
  255. //
  256. *UserKey = KerbGetKeyFromList(
  257. Passwords,
  258. CommonCryptSystem
  259. );
  260. DsysAssert(*UserKey != NULL);
  261. //
  262. // If we were using etype info, and not an old password, and the
  263. // etype doesn't use salt, then fail the operation, as we aren't
  264. // really generating a new key.
  265. //
  266. if (!UseOldPassword &&
  267. (CommonCryptSystem == KerbGlobalDefaultPreauthEtype) &&
  268. ARGUMENT_PRESENT(InputPaData))
  269. {
  270. PCRYPTO_SYSTEM CryptoSystem = NULL;
  271. Status = CDLocateCSystem(CommonCryptSystem, &CryptoSystem);
  272. if (!NT_SUCCESS(Status))
  273. {
  274. D_DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",CommonCryptSystem,Status ));
  275. goto Cleanup;
  276. }
  277. DsysAssert(CryptoSystem != NULL);
  278. if ((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) == 0)
  279. {
  280. if (!IgnoreSaltFailures)
  281. {
  282. D_DebugLog((DEB_WARN,"Tried to update password with new salt, but keytype 0x%x doesn't use salt.\n",
  283. CommonCryptSystem));
  284. *UserKey = NULL;
  285. Status = STATUS_WRONG_PASSWORD;
  286. goto Cleanup;
  287. }
  288. }
  289. }
  290. Cleanup:
  291. if (EtypeInfo != NULL)
  292. {
  293. KerbFreeData(PKERB_ETYPE_INFO_PDU, EtypeInfo);
  294. }
  295. return(Status);
  296. }
  297. //+-------------------------------------------------------------------------
  298. //
  299. // Function: KerbBuildPreAuthData
  300. //
  301. // Synopsis: Builds pre-auth data for type the specified pre-auth types
  302. //
  303. // Effects:
  304. //
  305. // Arguments: Credentials - Client's credentials, must be locked
  306. // RealmName - Name of target realm
  307. // ServiceName - Name of target server
  308. // PaTypeCount - count of pre-auth types to build
  309. // PaTypes - list of pre-auth types to build
  310. // InputPaData - any PA data returned by a previous (failed)
  311. // AS request
  312. // TimeSkew - Time Skew with KDC
  313. // UseOldPassword - Use the old password instead of current one
  314. // PreAuthData - receives list of pre-auth data
  315. // Done - don't call again on pre-auth failure
  316. //
  317. // Requires:
  318. //
  319. // Returns:
  320. //
  321. // Notes:
  322. //
  323. //
  324. //--------------------------------------------------------------------------
  325. NTSTATUS
  326. KerbBuildPreAuthData(
  327. IN PKERB_PRIMARY_CREDENTIAL Credentials,
  328. IN PUNICODE_STRING RealmName,
  329. IN PKERB_INTERNAL_NAME ServiceName,
  330. IN ULONG PaTypeCount,
  331. IN PULONG PaTypes,
  332. IN OPTIONAL PKERB_PA_DATA_LIST InputPaData,
  333. IN PTimeStamp TimeSkew,
  334. IN BOOLEAN UseOldPassword,
  335. IN ULONG Nonce,
  336. IN KERBERR ErrorCode,
  337. OUT PKERB_PA_DATA_LIST * PreAuthData,
  338. OUT PKERB_ENCRYPTION_KEY EncryptionKey,
  339. OUT PKERB_CRYPT_LIST * CryptList,
  340. OUT PBOOLEAN Done
  341. )
  342. {
  343. KERBERR KerbErr = KDC_ERR_NONE;
  344. NTSTATUS Status = STATUS_SUCCESS;
  345. PKERB_PA_DATA_LIST ListElement = NULL;
  346. PKERB_PA_DATA_LIST OutputList = NULL;
  347. ULONG Index;
  348. BOOLEAN FoundPreauth = FALSE;
  349. //
  350. // Initialize outputs
  351. //
  352. *PreAuthData = NULL;
  353. *Done = FALSE;
  354. for (Index = 0 ; Index < PaTypeCount ; Index++ )
  355. {
  356. switch(PaTypes[Index]) {
  357. case KRB5_PADATA_ENC_TIMESTAMP:
  358. {
  359. KERB_ENCRYPTED_TIMESTAMP Timestamp = {0};
  360. TimeStamp CurrentTime;
  361. PBYTE EncryptedTime = NULL;
  362. ULONG EncryptedTimeSize = 0;
  363. KERB_ENCRYPTED_DATA EncryptedData;
  364. PKERB_ENCRYPTION_KEY UserKey = NULL;
  365. FoundPreauth = TRUE;
  366. //
  367. // Check for encryption hints in the incoming pa-data
  368. //
  369. Status = KerbFindCommonPaEtype(
  370. Credentials,
  371. InputPaData,
  372. UseOldPassword,
  373. ErrorCode == KDC_ERR_PREAUTH_REQUIRED, // ignore salt problems on preauth-req errors
  374. &UserKey
  375. );
  376. if (!NT_SUCCESS(Status))
  377. {
  378. goto Cleanup;
  379. }
  380. //
  381. // If there was any input PA data, we don't want to try again.
  382. //
  383. if (InputPaData != NULL)
  384. {
  385. *Done = TRUE;
  386. }
  387. //
  388. // Build the output element
  389. //
  390. ListElement = (PKERB_PA_DATA_LIST) KerbAllocate(sizeof(KERB_PA_DATA_LIST));
  391. if (ListElement == NULL)
  392. {
  393. Status = STATUS_INSUFFICIENT_RESOURCES;
  394. goto Cleanup;
  395. }
  396. //
  397. // Now build the encrypted timestamp
  398. //
  399. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  400. //
  401. // Adjust for time skew with KDC
  402. //
  403. KerbSetTime(&CurrentTime, KerbGetTime(CurrentTime) + KerbGetTime(*TimeSkew));
  404. KerbConvertLargeIntToGeneralizedTimeWrapper(
  405. &Timestamp.timestamp,
  406. &Timestamp.KERB_ENCRYPTED_TIMESTAMP_usec,
  407. &CurrentTime
  408. );
  409. Timestamp.bit_mask = KERB_ENCRYPTED_TIMESTAMP_usec_present;
  410. KerbErr = KerbPackEncryptedTime(
  411. &Timestamp,
  412. &EncryptedTimeSize,
  413. &EncryptedTime
  414. );
  415. if (!KERB_SUCCESS(KerbErr))
  416. {
  417. Status = STATUS_INSUFFICIENT_RESOURCES;
  418. goto Cleanup;
  419. }
  420. //
  421. // Now encrypt the time
  422. //
  423. KerbErr = KerbAllocateEncryptionBufferWrapper(
  424. UserKey->keytype,
  425. EncryptedTimeSize,
  426. &EncryptedData.cipher_text.length,
  427. &EncryptedData.cipher_text.value
  428. );
  429. if (!KERB_SUCCESS(KerbErr))
  430. {
  431. D_DebugLog((DEB_ERROR,"\n\nFailed to get encryption overhead. %ws, line %d\n\n", THIS_FILE, __LINE__));
  432. KerbFree(EncryptedTime);
  433. Status = KerbMapKerbError(KerbErr);
  434. goto Cleanup;
  435. }
  436. KerbErr = KerbEncryptDataEx(
  437. &EncryptedData,
  438. EncryptedTimeSize,
  439. EncryptedTime,
  440. UserKey->keytype,
  441. KERB_ENC_TIMESTAMP_SALT,
  442. UserKey
  443. );
  444. KerbFree(EncryptedTime);
  445. if (!KERB_SUCCESS(KerbErr))
  446. {
  447. MIDL_user_free(EncryptedData.cipher_text.value);
  448. D_DebugLog((DEB_ERROR,"Failed to encrypt PA data. %ws, line %d\n", THIS_FILE, __LINE__));
  449. Status = STATUS_INSUFFICIENT_RESOURCES;
  450. goto Cleanup;
  451. }
  452. //
  453. // Now pack the encrypted data
  454. //
  455. KerbErr = KerbPackEncryptedData(
  456. &EncryptedData,
  457. (PULONG) &ListElement->value.preauth_data.length,
  458. (PUCHAR *) &ListElement->value.preauth_data.value
  459. );
  460. MIDL_user_free(EncryptedData.cipher_text.value);
  461. if (!KERB_SUCCESS(KerbErr))
  462. {
  463. Status = STATUS_INSUFFICIENT_RESOURCES;
  464. goto Cleanup;
  465. }
  466. ListElement->value.preauth_data_type = KRB5_PADATA_ENC_TIMESTAMP;
  467. ListElement->next = OutputList;
  468. OutputList = ListElement;
  469. ListElement = NULL;
  470. break;
  471. }
  472. #ifndef WIN32_CHICAGO
  473. case KRB5_PADATA_PK_AS_REQ:
  474. FoundPreauth = TRUE;
  475. Status = KerbBuildPkinitPreauthData(
  476. Credentials,
  477. InputPaData,
  478. TimeSkew,
  479. ServiceName,
  480. RealmName,
  481. Nonce,
  482. &OutputList,
  483. EncryptionKey,
  484. CryptList,
  485. Done
  486. );
  487. break;
  488. #endif // WIN32_CHICAGO
  489. default:
  490. continue;
  491. }
  492. }
  493. if (!FoundPreauth)
  494. {
  495. DebugLog((DEB_ERROR,"NO known pa data type passed to KerbBuildPreAuthData. %ws, line %d\n",
  496. THIS_FILE, __LINE__ ));
  497. Status = STATUS_UNSUPPORTED_PREAUTH;
  498. goto Cleanup;
  499. }
  500. *PreAuthData = OutputList;
  501. OutputList = NULL;
  502. Cleanup:
  503. if (OutputList != NULL)
  504. {
  505. KerbFreePreAuthData(
  506. OutputList
  507. );
  508. }
  509. if (ListElement != NULL)
  510. {
  511. KerbFreePreAuthData(
  512. ListElement
  513. );
  514. }
  515. return(Status);
  516. }
  517. //+-------------------------------------------------------------------------
  518. //
  519. // Function: KerbGetPreAuthDataForRealm
  520. //
  521. // Synopsis: Gets the appropriate pre-auth data for the specified realm.
  522. // Right now it always returns KRB_ENC_TIMESTAMP pre-auth data
  523. // but at some point it might do different things based on
  524. // the realm.
  525. //
  526. // Effects:
  527. //
  528. // Arguments: Credentials - Client's credentials
  529. // TargetRealm - realm from which the client is requesting a ticket
  530. // OldPreAuthData - any pre-auth data returned from the KDC on
  531. // the last AS request.
  532. // TimeSkew - Time skew with KDC
  533. // UseOldPassword - if TRUE, use the old password instead of current
  534. // PreAuthData - Receives the new pre-auth data
  535. // Done - if TRUE, don't bother trying again on a pre-auth required
  536. // failure
  537. //
  538. // Requires:
  539. //
  540. // Returns:
  541. //
  542. // Notes:
  543. //
  544. //
  545. //--------------------------------------------------------------------------
  546. NTSTATUS
  547. KerbGetPreAuthDataForRealm(
  548. IN PKERB_PRIMARY_CREDENTIAL Credentials,
  549. IN PUNICODE_STRING TargetRealm,
  550. IN PKERB_INTERNAL_NAME ServiceName,
  551. IN PKERB_PA_DATA_LIST OldPreAuthData,
  552. IN PTimeStamp TimeSkew,
  553. IN BOOLEAN UseOldPassword,
  554. IN ULONG Nonce,
  555. IN KERBERR ErrorCode,
  556. OUT PKERB_PA_DATA_LIST * PreAuthData,
  557. OUT PKERB_ENCRYPTION_KEY EncryptionKey,
  558. OUT PKERB_CRYPT_LIST * CryptList,
  559. OUT PBOOLEAN Done
  560. )
  561. {
  562. #define KERB_MAX_PA_DATA_TYPES 10
  563. NTSTATUS Status;
  564. ULONG PaTypeCount = 0;
  565. ULONG PaDataTypes[KERB_MAX_PA_DATA_TYPES];
  566. PKERB_MIT_REALM MitRealm;
  567. BOOLEAN UsedAlternateName;
  568. PKERB_PA_DATA_LIST PreAuthElement = NULL;
  569. //
  570. // If an error message was supplied, see if we can pull out the
  571. // list of supported pre-auth types from it.
  572. //
  573. if (ARGUMENT_PRESENT(OldPreAuthData) && (ErrorCode == KDC_ERR_PREAUTH_REQUIRED))
  574. {
  575. //
  576. // Pick the first type from the list as the type
  577. //
  578. PreAuthElement = OldPreAuthData;
  579. while ((PaTypeCount < KERB_MAX_PA_DATA_TYPES) && (PreAuthElement != NULL))
  580. {
  581. PaDataTypes[PaTypeCount++] = (ULONG) PreAuthElement->value.preauth_data_type;
  582. PreAuthElement = PreAuthElement->next;
  583. }
  584. }
  585. else
  586. {
  587. //
  588. // For MIT realms, check the list to see what kind of preauth to do.
  589. //
  590. if (KerbLookupMitRealm(
  591. TargetRealm,
  592. &MitRealm,
  593. &UsedAlternateName
  594. ))
  595. {
  596. //
  597. // There are some types of preauth returned from the KDC that we
  598. // need to log an event for. PA-PW-SALT (3) and PA-AFS3-SALT (10)
  599. // are not implemented in our client, so log an error to help admins,
  600. // and retry w/ default for realm.
  601. //
  602. while ((PaTypeCount < KERB_MAX_PA_DATA_TYPES) && (PreAuthElement != NULL))
  603. {
  604. if (PreAuthElement->value.preauth_data_type == KRB5_PADATA_PW_SALT ||
  605. PreAuthElement->value.preauth_data_type == KRB5_PADATA_AFS3_SALT)
  606. {
  607. Status = STATUS_UNSUPPORTED_PREAUTH;
  608. DebugLog((
  609. DEB_ERROR,
  610. "Unsupported Preauth type : %x\n",
  611. PreAuthElement->value.preauth_data_type
  612. ));
  613. goto Cleanup;
  614. }
  615. PaTypeCount++;
  616. PreAuthElement = PreAuthElement->next;
  617. }
  618. if (MitRealm->PreAuthType != 0)
  619. {
  620. PaDataTypes[0] = MitRealm->PreAuthType;
  621. PaTypeCount = 1;
  622. }
  623. else
  624. {
  625. return(STATUS_SUCCESS);
  626. }
  627. }
  628. //
  629. // Plug in fancier capabilities here.
  630. //
  631. //
  632. // If the caller has public key credentials, use pkinit rather than
  633. // encrypted timestamp
  634. //
  635. else if (Credentials->PublicKeyCreds != NULL)
  636. {
  637. PaDataTypes[0] = KRB5_PADATA_PK_AS_REQ;
  638. PaTypeCount = 1;
  639. }
  640. else
  641. {
  642. //
  643. // If we were succeful, ignore this preauth data
  644. //
  645. if ((ErrorCode == KDC_ERR_NONE) && (OldPreAuthData != NULL))
  646. {
  647. return(STATUS_SUCCESS);
  648. }
  649. PaDataTypes[0] = KRB5_PADATA_ENC_TIMESTAMP;
  650. PaTypeCount = 1;
  651. }
  652. }
  653. Status = KerbBuildPreAuthData(
  654. Credentials,
  655. TargetRealm,
  656. ServiceName,
  657. PaTypeCount,
  658. PaDataTypes,
  659. OldPreAuthData,
  660. TimeSkew,
  661. UseOldPassword,
  662. Nonce,
  663. ErrorCode,
  664. PreAuthData,
  665. EncryptionKey,
  666. CryptList,
  667. Done
  668. );
  669. Cleanup:
  670. return(Status);
  671. }
  672. //+-------------------------------------------------------------------------
  673. //
  674. // Function: KerbUnpackErrorPreauth
  675. //
  676. // Synopsis: Unpacks preauth data from a kerb_error message
  677. //
  678. // Effects:
  679. //
  680. // Arguments: ErrorMessage - ErrorMessage from an AS request that failed
  681. // with KDC_ERR_PREAUTH_REQUIRED
  682. // PreAuthData - returns any preauth data from the error message
  683. //
  684. // Requires:
  685. //
  686. // Returns:
  687. //
  688. // Notes:
  689. //
  690. //
  691. //--------------------------------------------------------------------------
  692. NTSTATUS
  693. KerbUnpackErrorPreauth(
  694. IN PKERB_ERROR ErrorMessage,
  695. OUT PKERB_PA_DATA_LIST ** PreAuthData
  696. )
  697. {
  698. KERBERR KerbErr = KDC_ERR_NONE;
  699. NTSTATUS Status = STATUS_SUCCESS;
  700. PKERB_PREAUTH_DATA_LIST * ErrorPreAuth = NULL;
  701. *PreAuthData = NULL;
  702. //
  703. // If there was no data, return now
  704. //
  705. if ((ErrorMessage->bit_mask & error_data_present) == 0)
  706. {
  707. //
  708. // If we weren't given any hints, we can't do any better so return
  709. // an error.
  710. //
  711. KerbErr = KDC_ERR_PREAUTH_REQUIRED;
  712. goto Cleanup;
  713. }
  714. KerbErr = KerbUnpackData(
  715. ErrorMessage->error_data.value,
  716. ErrorMessage->error_data.length,
  717. PKERB_PREAUTH_DATA_LIST_PDU,
  718. (PVOID *) &ErrorPreAuth
  719. );
  720. if (!KERB_SUCCESS(KerbErr))
  721. {
  722. D_DebugLog((DEB_ERROR,"Failed to unpack pre-auth data from error message. %ws, line %d\n", THIS_FILE, __LINE__));
  723. //
  724. // This error code isn't particularly informative but we were unable to get the
  725. // error information so this is the best we can do.
  726. //
  727. Status = STATUS_LOGON_FAILURE;
  728. goto Cleanup;
  729. }
  730. //
  731. // Make sure the two structures are similar
  732. //
  733. DsysAssert(FIELD_OFFSET(KERB_PREAUTH_DATA_LIST,next) == FIELD_OFFSET(KERB_PA_DATA_LIST,next));
  734. DsysAssert(FIELD_OFFSET(KERB_PREAUTH_DATA_LIST,value) == FIELD_OFFSET(KERB_PA_DATA_LIST,value));
  735. DsysAssert(sizeof(KERB_PREAUTH_DATA_LIST) == sizeof(KERB_PA_DATA_LIST));
  736. *PreAuthData = (PKERB_PA_DATA_LIST *) ErrorPreAuth;
  737. ErrorPreAuth = NULL;
  738. Cleanup:
  739. if (ErrorPreAuth != NULL)
  740. {
  741. KerbFreeData(PKERB_PREAUTH_DATA_LIST_PDU,ErrorPreAuth);
  742. }
  743. return(Status);
  744. }
  745. //+-------------------------------------------------------------------------
  746. //
  747. // Function: KerbAddPacRequestPreAuth
  748. //
  749. // Synopsis: Add the pac-request preauth data to either requst a pac
  750. // or request that no pac be included
  751. //
  752. // Effects:
  753. //
  754. // Arguments:
  755. //
  756. // Requires:
  757. //
  758. // Returns:
  759. //
  760. // Notes:
  761. //
  762. //
  763. //--------------------------------------------------------------------------
  764. NTSTATUS
  765. KerbAddPacRequestPreAuth(
  766. OUT PKERB_PA_DATA_LIST * PreAuthData,
  767. IN ULONG TicketFlags
  768. )
  769. {
  770. NTSTATUS Status = STATUS_SUCCESS;
  771. PKERB_PA_DATA_LIST ListElement = NULL;
  772. PKERB_PA_DATA_LIST LastElement = NULL;
  773. KERB_PA_PAC_REQUEST PacRequest = {0};
  774. ListElement = (PKERB_PA_DATA_LIST) KerbAllocate(sizeof(KERB_PA_DATA_LIST));
  775. if (ListElement == NULL)
  776. {
  777. Status = STATUS_INSUFFICIENT_RESOURCES;
  778. goto Cleanup;
  779. }
  780. if ((TicketFlags & KERB_GET_TICKET_NO_PAC) != 0 )
  781. {
  782. PacRequest.include_pac = FALSE;
  783. }
  784. else
  785. {
  786. PacRequest.include_pac = TRUE;
  787. }
  788. //
  789. // Marshall the type into the list element.
  790. //
  791. if (!KERB_SUCCESS(KerbPackData(
  792. &PacRequest,
  793. KERB_PA_PAC_REQUEST_PDU,
  794. (PULONG) &ListElement->value.preauth_data.length,
  795. (PUCHAR *) &ListElement->value.preauth_data.value
  796. )))
  797. {
  798. Status = STATUS_INSUFFICIENT_RESOURCES;
  799. goto Cleanup;
  800. }
  801. ListElement->value.preauth_data_type = KRB5_PADATA_PAC_REQUEST;
  802. //
  803. // We want this to go at the end, so that it will override any other
  804. // pa-data that may enable a PAC.
  805. //
  806. LastElement = *PreAuthData;
  807. if (LastElement != NULL)
  808. {
  809. while (LastElement->next != NULL)
  810. {
  811. LastElement = LastElement->next;
  812. }
  813. LastElement->next = ListElement;
  814. }
  815. else
  816. {
  817. *PreAuthData = ListElement;
  818. }
  819. ListElement->next = NULL;
  820. ListElement = NULL;
  821. Cleanup:
  822. if (ListElement != NULL)
  823. {
  824. KerbFreePreAuthData(
  825. ListElement
  826. );
  827. }
  828. return(Status);
  829. }
  830. //+-------------------------------------------------------------------------
  831. //
  832. // Function: KerbPingWlBalloon
  833. //
  834. // Synopsis: Opens and pulses winlogon event, so they can pop up the balloon,
  835. // informing user of bad pwd, or expired pwd
  836. //
  837. // Effects:
  838. //
  839. // Arguments: LogonSession - Logon session for which to acquire a ticket
  840. //
  841. // Requires:
  842. //
  843. // Returns: STATUS_SUCCESS on success
  844. //
  845. //
  846. //
  847. //
  848. #define KERBEROS_NOTIFICATION_EVENT_NAME L"WlballoonKerberosNotificationEventName"
  849. BOOLEAN
  850. KerbPingWlBalloon(
  851. PLUID Luid
  852. )
  853. {
  854. HANDLE EventHandle;
  855. WCHAR Event[512];
  856. wsprintfW(
  857. Event,
  858. L"Global\\%08x%08x_%s",
  859. Luid->HighPart,
  860. Luid->LowPart,
  861. KERBEROS_NOTIFICATION_EVENT_NAME
  862. );
  863. EventHandle = OpenEventW(EVENT_MODIFY_STATE, FALSE, Event);
  864. if (EventHandle == NULL)
  865. {
  866. DebugLog((DEB_ERROR, "Opening winlogon event %S failed %x\n", Event, GetLastError()));
  867. return FALSE;
  868. }
  869. if (!SetEvent(EventHandle))
  870. {
  871. DebugLog((DEB_ERROR, "SETTING winlogon event %S failed %x\n", Event, GetLastError()));
  872. }
  873. if (EventHandle != NULL)
  874. {
  875. CloseHandle(EventHandle);
  876. }
  877. return TRUE;
  878. }
  879. //+-------------------------------------------------------------------------
  880. //
  881. // Function: KerbGetAuthenticationTicket
  882. //
  883. // Synopsis: Gets an AS ticket for the specified logon session
  884. //
  885. // Effects:
  886. //
  887. // Arguments: LogonSession - Logon session for which to acquire a ticket
  888. //
  889. // Requires:
  890. //
  891. // Returns: STATUS_SUCCESS on success
  892. //
  893. //
  894. // Notes: The retry logic here is complex. The idea is that we
  895. // shouldn't retry more than once for any particular failure.
  896. //
  897. //
  898. //--------------------------------------------------------------------------
  899. #define KERB_RETRY_ETYPE_FAILURE 0x0001
  900. #define KERB_RETRY_TIME_FAILURE 0x0002
  901. #define KERB_RETRY_PASSWORD_FAILURE 0x0004
  902. #define KERB_RETRY_WRONG_PREAUTH 0x0008
  903. #define KERB_RETRY_USE_TCP 0x0010
  904. #define KERB_RETRY_CALL_PDC 0x0020
  905. #define KERB_RETRY_SALT_FAILURE 0x0040
  906. #define KERB_RETRY_WITH_ACCOUNT 0x0080
  907. #define KERB_RETRY_BAD_REALM 0x0100
  908. #define KERB_RETRY_PKINIT 0x0200
  909. #define KERB_RETRY_BAD_KDC 0x0400
  910. NTSTATUS
  911. KerbGetAuthenticationTicket(
  912. IN OUT PKERB_LOGON_SESSION LogonSession,
  913. IN OPTIONAL PKERB_CREDENTIAL Credential,
  914. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  915. IN PKERB_INTERNAL_NAME ServiceName,
  916. IN PUNICODE_STRING ServerRealm,
  917. IN PKERB_INTERNAL_NAME ClientFullName,
  918. IN ULONG TicketFlags,
  919. IN ULONG CacheFlags,
  920. OUT OPTIONAL PKERB_TICKET_CACHE_ENTRY * TicketCacheEntry,
  921. OUT OPTIONAL PKERB_ENCRYPTION_KEY CredentialKey,
  922. OUT PUNICODE_STRING CorrectRealm
  923. )
  924. {
  925. NTSTATUS Status = STATUS_SUCCESS;
  926. NTSTATUS OldStatus = STATUS_SUCCESS;
  927. NTSTATUS ExtendedStatus = STATUS_SUCCESS;
  928. KERBERR KerbErr = KDC_ERR_NONE;
  929. KERBERR LastKerbErr = KDC_ERR_NONE;
  930. KERB_KDC_REQUEST TicketRequest = {0};
  931. PKERB_KDC_REQUEST_BODY RequestBody;
  932. PULONG CryptVector = NULL;
  933. BOOLEAN LogonSessionsLocked = FALSE;
  934. PKERB_KDC_REPLY KdcReply = NULL;
  935. PKERB_ENCRYPTED_KDC_REPLY ReplyBody = NULL;
  936. PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL;
  937. PKERB_TICKET_CACHE_ENTRY NewTGT = NULL;
  938. TimeStamp TempTime;
  939. KERB_MESSAGE_BUFFER RequestMessage = {0, NULL};
  940. KERB_MESSAGE_BUFFER ReplyMessage = {0, NULL};
  941. UNICODE_STRING ClientName = NULL_UNICODE_STRING;
  942. PKERB_ENCRYPTION_KEY ClientKey;
  943. ULONG RetryFlags = 0;
  944. BOOLEAN CalledPDC = FALSE;
  945. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials;
  946. BOOLEAN PreAuthDone = FALSE;
  947. PKERB_ERROR ErrorMessage = NULL;
  948. PKERB_PA_DATA_LIST * OldPreAuthData = NULL;
  949. #ifndef WIN32_CHICAGO
  950. LARGE_INTEGER TimeSkew = {0,0};
  951. #else // WIN32_CHICAGO
  952. TimeStamp TimeSkew = 0;
  953. #endif // WIN32_CHICAGO
  954. ULONG NameType = KRB_NT_MS_PRINCIPAL;
  955. BOOLEAN UsedAlternateName = FALSE;
  956. PKERB_MIT_REALM MitRealm = NULL;
  957. PKERB_INTERNAL_NAME LocalServiceName = NULL;
  958. PKERB_EXT_ERROR pExtendedError = NULL;
  959. BOOLEAN UsedCredentials = FALSE;
  960. KERB_ENCRYPTION_KEY EncryptionKey = {0};
  961. PKERB_HOST_ADDRESSES HostAddresses = NULL;
  962. PKERB_CRYPT_LIST CryptList = NULL;
  963. UNICODE_STRING ClientRealm = {0};
  964. ULONG KdcOptions;
  965. ULONG KdcFlagOptions, AdditionalFlags = 0;
  966. BOOLEAN DoLogonRetry = FALSE;
  967. BOOLEAN DoTcpRetry = FALSE;
  968. BOOLEAN DoPreauthRetry = FALSE;
  969. BOOLEAN DoAccountLookup = FALSE;
  970. BOOLEAN IncludeIpAddresses = FALSE;
  971. BOOLEAN IncludeNetbiosAddresses = TRUE;
  972. D_DebugLog((DEB_TRACE,"Getting authentication ticket for client "));
  973. D_KerbPrintKdcName(DEB_TRACE, ClientFullName );
  974. D_DebugLog((DEB_TRACE," for service in realm %wZ : ", ServerRealm));
  975. D_KerbPrintKdcName(DEB_TRACE, ServiceName);
  976. //
  977. // Initialize variables to NULL
  978. //
  979. RequestBody = &TicketRequest.request_body;
  980. RtlInitUnicodeString(
  981. CorrectRealm,
  982. NULL
  983. );
  984. if ((ClientFullName->NameCount == 0) || (ClientFullName->Names[0].Length == 0))
  985. {
  986. D_DebugLog((DEB_WARN,"KerbGetServiceTicket: not requesting ticket for blank server name\n"));
  987. Status = STATUS_NO_SUCH_USER;
  988. goto Cleanup;
  989. }
  990. //
  991. // Build the request
  992. //
  993. KdcOptions = KERB_DEFAULT_TICKET_FLAGS;
  994. //
  995. // The domain name may be null - if so, use our domain for now.
  996. //
  997. //
  998. // Check to see if the domain is an MIT realm
  999. //
  1000. if (KerbLookupMitRealm(
  1001. ServerRealm,
  1002. &MitRealm,
  1003. &UsedAlternateName
  1004. ) ||
  1005. ((TicketFlags & KERB_GET_AUTH_TICKET_NO_CANONICALIZE) != 0))
  1006. {
  1007. DsysAssert(((TicketFlags & KERB_GET_AUTH_TICKET_NO_CANONICALIZE) != 0) ||
  1008. (MitRealm != NULL));
  1009. //
  1010. // So the user is getting a ticket from an MIT realm. This means
  1011. // we don't ask for name canonicalization.
  1012. //
  1013. KdcOptions &= ~KERB_KDC_OPTIONS_name_canonicalize;
  1014. if (MitRealm != NULL)
  1015. {
  1016. LogonSession->LogonSessionFlags |= KERB_LOGON_MIT_REALM;
  1017. }
  1018. }
  1019. KdcFlagOptions = KerbConvertUlongToFlagUlong(KdcOptions);
  1020. RequestBody->kdc_options.value = (PUCHAR) &KdcFlagOptions ;
  1021. RequestBody->kdc_options.length = sizeof(ULONG) * 8;
  1022. RequestBody->nonce = KerbAllocateNonce();
  1023. TempTime = KerbGlobalWillNeverTime;
  1024. KerbConvertLargeIntToGeneralizedTime(
  1025. &RequestBody->endtime,
  1026. NULL,
  1027. &TempTime
  1028. );
  1029. KerbConvertLargeIntToGeneralizedTime(
  1030. &RequestBody->KERB_KDC_REQUEST_BODY_renew_until,
  1031. NULL,
  1032. &TempTime
  1033. );
  1034. RequestBody->bit_mask |= KERB_KDC_REQUEST_BODY_renew_until_present;
  1035. //
  1036. // Lock down the logon session while we build the request.
  1037. //
  1038. KerbReadLockLogonSessions(LogonSession);
  1039. LogonSessionsLocked = TRUE;
  1040. //
  1041. // If credentials were supplied, use the primary creds from there
  1042. //
  1043. if (ARGUMENT_PRESENT(Credential) && (Credential->SuppliedCredentials != NULL))
  1044. {
  1045. UsedCredentials = TRUE;
  1046. PrimaryCredentials = Credential->SuppliedCredentials;
  1047. D_DebugLog((DEB_TRACE_CRED,"GetAuthTicket: Using supplied credentials %wZ\\%wZ to \n",
  1048. &PrimaryCredentials->DomainName,
  1049. &PrimaryCredentials->UserName
  1050. ));
  1051. D_KerbPrintKdcName( DEB_TRACE_CRED, ServiceName );
  1052. }
  1053. else if (ARGUMENT_PRESENT(CredManCredentials))
  1054. {
  1055. UsedCredentials = TRUE;
  1056. PrimaryCredentials = CredManCredentials->SuppliedCredentials;
  1057. D_DebugLog((DEB_TRACE_CRED,"GetAuthTicket: Using cred manager credentials %wZ\\%wZ to \n",
  1058. &PrimaryCredentials->DomainName,
  1059. &PrimaryCredentials->UserName
  1060. ));
  1061. D_KerbPrintKdcName( DEB_TRACE_CRED, ServiceName );
  1062. }
  1063. else
  1064. {
  1065. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  1066. D_DebugLog((DEB_TRACE_CRED,"GetAuthTicket: Using default credentials %wZ\\%wZ to ",
  1067. &PrimaryCredentials->DomainName,
  1068. &PrimaryCredentials->UserName
  1069. ));
  1070. D_KerbPrintKdcName(DEB_TRACE_CRED, ServiceName);
  1071. }
  1072. if ((PrimaryCredentials->Passwords == NULL) &&
  1073. (PrimaryCredentials->PublicKeyCreds == NULL))
  1074. {
  1075. D_DebugLog((DEB_ERROR,"Can't get AS ticket with no password. %ws, line %d\n", THIS_FILE, __LINE__));
  1076. Status = SEC_E_NO_CREDENTIALS;
  1077. goto Cleanup;
  1078. }
  1079. //
  1080. // Copy all the names into the request message
  1081. //
  1082. //
  1083. // Build the client name from the client domain & user name.
  1084. //
  1085. KerbErr = KerbConvertKdcNameToPrincipalName(
  1086. &RequestBody->KERB_KDC_REQUEST_BODY_client_name,
  1087. ClientFullName
  1088. );
  1089. if (!KERB_SUCCESS(KerbErr))
  1090. {
  1091. Status = STATUS_INSUFFICIENT_RESOURCES;
  1092. goto Cleanup;
  1093. }
  1094. RequestBody->bit_mask |= KERB_KDC_REQUEST_BODY_client_name_present;
  1095. //
  1096. // If we are talking to an NT Domain, or are using an MIT compatible
  1097. // name type, convert the service name as is is
  1098. //
  1099. KerbErr = KerbConvertKdcNameToPrincipalName(
  1100. &RequestBody->KERB_KDC_REQUEST_BODY_server_name,
  1101. ServiceName
  1102. );
  1103. if (!KERB_SUCCESS(KerbErr))
  1104. {
  1105. Status = STATUS_INSUFFICIENT_RESOURCES;
  1106. goto Cleanup;
  1107. }
  1108. RequestBody->bit_mask |= KERB_KDC_REQUEST_BODY_server_name_present;
  1109. //
  1110. // Build the list of host addresses. We don't do this for all
  1111. // MIT realms
  1112. //
  1113. if ( KerbGlobalUseClientIpAddresses ) {
  1114. IncludeIpAddresses = TRUE;
  1115. }
  1116. //
  1117. // MIT realms never care to see the NetBIOS addresses
  1118. //
  1119. if ( MitRealm != NULL ) {
  1120. IncludeNetbiosAddresses = FALSE;
  1121. if (( MitRealm->Flags & KERB_MIT_REALM_SEND_ADDRESS ) != 0 ) {
  1122. IncludeIpAddresses = TRUE;
  1123. }
  1124. }
  1125. //
  1126. // We always put the NetBIOS name of the client into the request,
  1127. // as this is how the workstations restriction is enforced.
  1128. // It is understood that the mechanism is bogus, as the client can spoof
  1129. // the netbios address, but that's okay -- we're only doing this for
  1130. // feature preservation, this is no worse than W2K.
  1131. //
  1132. // The IP address is only put in based on a registry setting or for MIT
  1133. // realms that explicity request it, as having them in the request would
  1134. // break us when going through NATs.
  1135. //
  1136. Status = KerbBuildHostAddresses(
  1137. IncludeIpAddresses,
  1138. IncludeNetbiosAddresses,
  1139. &HostAddresses
  1140. );
  1141. if (!NT_SUCCESS(Status))
  1142. {
  1143. goto Cleanup;
  1144. }
  1145. if ( HostAddresses )
  1146. {
  1147. RequestBody->addresses = HostAddresses;
  1148. RequestBody->bit_mask |= addresses_present;
  1149. }
  1150. TicketRequest.version = KERBEROS_VERSION;
  1151. TicketRequest.message_type = KRB_AS_REQ;
  1152. PreauthRestart:
  1153. //
  1154. // Lock down the logon session while we build the request.
  1155. // This is done so that when we try the second time around, the logon
  1156. // session list is locked
  1157. //
  1158. if (!LogonSessionsLocked)
  1159. {
  1160. KerbReadLockLogonSessions(LogonSession);
  1161. LogonSessionsLocked = TRUE;
  1162. }
  1163. DoPreauthRetry = FALSE;
  1164. if (RequestMessage.Buffer != NULL)
  1165. {
  1166. MIDL_user_free(RequestMessage.Buffer);
  1167. RequestMessage.Buffer = NULL;
  1168. }
  1169. // Free this in case we are doing a retry
  1170. KerbFreeRealm(
  1171. &RequestBody->realm
  1172. );
  1173. KerbErr = KerbConvertUnicodeStringToRealm(
  1174. &RequestBody->realm,
  1175. ServerRealm
  1176. );
  1177. if (!KERB_SUCCESS(KerbErr))
  1178. {
  1179. Status = STATUS_INSUFFICIENT_RESOURCES;
  1180. goto Cleanup;
  1181. }
  1182. //
  1183. // Stick the PA data in the request
  1184. //
  1185. DsysAssert(TicketRequest.KERB_KDC_REQUEST_preauth_data == NULL);
  1186. Status = KerbGetPreAuthDataForRealm(
  1187. PrimaryCredentials,
  1188. ServerRealm,
  1189. ServiceName,
  1190. (OldPreAuthData != NULL) ? *OldPreAuthData : NULL,
  1191. &TimeSkew,
  1192. (RetryFlags & KERB_RETRY_PASSWORD_FAILURE) != 0,
  1193. RequestBody->nonce,
  1194. LastKerbErr,
  1195. &TicketRequest.KERB_KDC_REQUEST_preauth_data,
  1196. &EncryptionKey,
  1197. &CryptList,
  1198. &PreAuthDone
  1199. );
  1200. if (!NT_SUCCESS(Status))
  1201. {
  1202. //
  1203. // If we couldn't build the preauth, try again
  1204. //
  1205. if (Status == STATUS_WRONG_PASSWORD)
  1206. {
  1207. if ((RetryFlags & KERB_RETRY_PASSWORD_FAILURE) == 0)
  1208. {
  1209. RetryFlags |= KERB_RETRY_PASSWORD_FAILURE;
  1210. goto PreauthRestart;
  1211. }
  1212. else if ((RetryFlags & KERB_RETRY_SALT_FAILURE) == 0)
  1213. {
  1214. RetryFlags |= KERB_RETRY_SALT_FAILURE;
  1215. RetryFlags &= ~KERB_RETRY_PASSWORD_FAILURE;
  1216. goto PreauthRestart;
  1217. }
  1218. } else if (Status == STATUS_UNSUPPORTED_PREAUTH)
  1219. {
  1220. // Log this, every time, as this is impossible to triage otherwise
  1221. KerbReportKerbError(
  1222. ServiceName,
  1223. ServerRealm,
  1224. NULL,
  1225. Credential,
  1226. KLIN(FILENO,__LINE__),
  1227. NULL,
  1228. KDC_ERR_PADATA_TYPE_NOSUPP,
  1229. NULL,
  1230. TRUE
  1231. );
  1232. }
  1233. DebugLog((DEB_ERROR,"GetAuthenticationTicket: Failed to build pre-auth data: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1234. goto Cleanup;
  1235. }
  1236. //
  1237. // Build crypt list
  1238. //
  1239. KerbFreeCryptList(
  1240. RequestBody->encryption_type
  1241. );
  1242. RequestBody->encryption_type = NULL;
  1243. if (PrimaryCredentials->Passwords != NULL) {
  1244. if (!KERB_SUCCESS(KerbConvertKeysToCryptList(
  1245. &RequestBody->encryption_type,
  1246. PrimaryCredentials->Passwords
  1247. )))
  1248. {
  1249. Status = STATUS_INSUFFICIENT_RESOURCES;
  1250. goto Cleanup;
  1251. }
  1252. } else {
  1253. ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
  1254. ULONG CryptTypeCount = KERB_MAX_CRYPTO_SYSTEMS;
  1255. //
  1256. // Include all our crypt types as supported
  1257. //
  1258. Status = CDBuildIntegrityVect(
  1259. &CryptTypeCount,
  1260. CryptTypes
  1261. );
  1262. DsysAssert(NT_SUCCESS(Status));
  1263. if (!KERB_SUCCESS(KerbConvertArrayToCryptList(
  1264. &RequestBody->encryption_type,
  1265. CryptTypes,
  1266. CryptTypeCount)))
  1267. {
  1268. Status = STATUS_INSUFFICIENT_RESOURCES;
  1269. goto Cleanup;
  1270. }
  1271. }
  1272. //
  1273. // Add in preauth generated encryption types
  1274. //
  1275. if (CryptList != NULL)
  1276. {
  1277. PKERB_CRYPT_LIST Next;
  1278. Next = CryptList;
  1279. while (Next != NULL)
  1280. {
  1281. if (Next->next == NULL)
  1282. {
  1283. Next->next = RequestBody->encryption_type;
  1284. RequestBody->encryption_type = CryptList;
  1285. CryptList = NULL;
  1286. break;
  1287. }
  1288. Next = Next->next;
  1289. }
  1290. }
  1291. //
  1292. // If the we need to either request the presence or absence of a PAC, do
  1293. // it here
  1294. //
  1295. if (MitRealm == NULL)
  1296. {
  1297. Status = KerbAddPacRequestPreAuth(
  1298. &TicketRequest.KERB_KDC_REQUEST_preauth_data,
  1299. TicketFlags
  1300. );
  1301. if (!NT_SUCCESS(Status))
  1302. {
  1303. goto Cleanup;
  1304. }
  1305. }
  1306. if (TicketRequest.KERB_KDC_REQUEST_preauth_data != NULL)
  1307. {
  1308. TicketRequest.bit_mask |= KERB_KDC_REQUEST_preauth_data_present;
  1309. }
  1310. //
  1311. // Pack the request
  1312. //
  1313. KerbErr = KerbPackAsRequest(
  1314. &TicketRequest,
  1315. &RequestMessage.BufferSize,
  1316. &RequestMessage.Buffer
  1317. );
  1318. if (!KERB_SUCCESS(KerbErr))
  1319. {
  1320. Status = STATUS_INSUFFICIENT_RESOURCES;
  1321. goto Cleanup;
  1322. }
  1323. RetryLogon:
  1324. DoLogonRetry = FALSE;
  1325. //
  1326. // Unlock the logon sessions and credential so we don't cause problems
  1327. // waiting for a network request to complete.
  1328. //
  1329. if (LogonSessionsLocked)
  1330. {
  1331. KerbUnlockLogonSessions(LogonSession);
  1332. LogonSessionsLocked = FALSE;
  1333. }
  1334. D_DebugLog((DEB_TRACE_KDC,"KerbGetAuthenticationTicket: Calling KDC\n"));
  1335. RetryWithTcp:
  1336. if (ReplyMessage.Buffer != NULL)
  1337. {
  1338. MIDL_user_free(ReplyMessage.Buffer);
  1339. ReplyMessage.Buffer = NULL;
  1340. }
  1341. DoTcpRetry = FALSE;
  1342. Status = KerbMakeKdcCall(
  1343. ServerRealm,
  1344. (DoAccountLookup && ClientFullName->NameType == KRB_NT_PRINCIPAL) ? &ClientFullName->Names[0] : NULL, // send the client name, if available
  1345. (RetryFlags & KERB_RETRY_CALL_PDC) != 0,
  1346. (RetryFlags & KERB_RETRY_USE_TCP) != 0,
  1347. &RequestMessage,
  1348. &ReplyMessage,
  1349. AdditionalFlags,
  1350. &CalledPDC
  1351. // FESTER: TBD: Tell us we made MIT call for AS, so we can update
  1352. // logonsession w/ MIT flag....
  1353. );
  1354. D_DebugLog((DEB_TRACE_KDC,"KerbGetAuthenticationTicket: Returned from KDC status 0x%x\n",
  1355. Status ));
  1356. if (!NT_SUCCESS(Status))
  1357. {
  1358. #if DBG
  1359. if (Status != STATUS_NO_LOGON_SERVERS)
  1360. {
  1361. DebugLog((DEB_ERROR,"Failed KerbMakeKdcCall for AS request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  1362. }
  1363. #endif
  1364. //
  1365. // If this is the second time around (on the PDC) and this fails,
  1366. // use the original error
  1367. //
  1368. if (OldStatus != STATUS_SUCCESS)
  1369. {
  1370. Status = OldStatus;
  1371. }
  1372. goto Cleanup;
  1373. }
  1374. //
  1375. // Free the preauth data now, as it is not necessary any more
  1376. //
  1377. KerbFreePreAuthData( TicketRequest.KERB_KDC_REQUEST_preauth_data );
  1378. TicketRequest.KERB_KDC_REQUEST_preauth_data = NULL;
  1379. KerbErr = KerbUnpackAsReply(
  1380. ReplyMessage.Buffer,
  1381. ReplyMessage.BufferSize,
  1382. &KdcReply
  1383. );
  1384. if (!KERB_SUCCESS(KerbErr))
  1385. {
  1386. D_DebugLog((DEB_WARN,"Failed to unpack KDC reply as AS: 0x%x\n", KerbErr ));
  1387. //
  1388. // Try to unpack it as kerb_error
  1389. //
  1390. if (ErrorMessage != NULL)
  1391. {
  1392. KerbFreeKerbError(ErrorMessage);
  1393. ErrorMessage = NULL;
  1394. }
  1395. KerbErr = KerbUnpackKerbError(
  1396. ReplyMessage.Buffer,
  1397. ReplyMessage.BufferSize,
  1398. &ErrorMessage
  1399. );
  1400. if (KERB_SUCCESS(KerbErr))
  1401. {
  1402. //
  1403. // Let's see if there's any extended error here
  1404. //
  1405. if (ErrorMessage->bit_mask & error_data_present)
  1406. {
  1407. if (NULL != pExtendedError) // might be a re-auth failure. Don't leak!
  1408. {
  1409. KerbFree(pExtendedError);
  1410. pExtendedError = NULL;
  1411. }
  1412. KerbErr = KerbUnpackErrorData(
  1413. ErrorMessage,
  1414. &pExtendedError
  1415. );
  1416. if (KERB_SUCCESS(KerbErr) && (EXT_CLIENT_INFO_PRESENT(pExtendedError)))
  1417. {
  1418. ExtendedStatus = pExtendedError->status;
  1419. }
  1420. }
  1421. KerbErr = (KERBERR) ErrorMessage->error_code;
  1422. LastKerbErr = KerbErr;
  1423. DebugLog((DEB_ERROR,"KerbCallKdc failed: error 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  1424. Status = KerbMapKerbError(KerbErr);
  1425. KerbReportKerbError(
  1426. ServiceName,
  1427. ServerRealm,
  1428. LogonSession,
  1429. Credential,
  1430. KLIN(FILENO,__LINE__),
  1431. ErrorMessage,
  1432. KerbErr,
  1433. pExtendedError,
  1434. FALSE
  1435. );
  1436. if (KerbErr == KRB_ERR_RESPONSE_TOO_BIG)
  1437. {
  1438. if ((RetryFlags & KERB_RETRY_USE_TCP) != 0)
  1439. {
  1440. D_DebugLog((DEB_ERROR,"Got response too big twice. %ws, %d\n",
  1441. THIS_FILE, __LINE__ ));
  1442. Status = STATUS_BUFFER_OVERFLOW;
  1443. goto Cleanup;
  1444. }
  1445. RetryFlags |= KERB_RETRY_USE_TCP;
  1446. DoTcpRetry = TRUE;
  1447. }
  1448. //
  1449. // If we didn't try the PDC, try it now.
  1450. //
  1451. else if (KerbErr == KDC_ERR_KEY_EXPIRED)
  1452. {
  1453. if (CalledPDC ||
  1454. ((RetryFlags & KERB_RETRY_CALL_PDC) != 0) ||
  1455. (!KerbGlobalRetryPdc))
  1456. {
  1457. // If we've already tried the PDC, then we should
  1458. // have some extended info w.r.t. what's up w/
  1459. // this password.
  1460. if (EXT_CLIENT_INFO_PRESENT(pExtendedError))
  1461. {
  1462. Status = ExtendedStatus;
  1463. }
  1464. else
  1465. {
  1466. Status = KerbMapKerbError(KerbErr);
  1467. }
  1468. goto Cleanup;
  1469. }
  1470. RetryFlags |= KERB_RETRY_CALL_PDC;
  1471. DoLogonRetry = TRUE;
  1472. }
  1473. //
  1474. // Check for time skew. If so, calculate the skew and retry
  1475. //
  1476. else if (KerbErr == KRB_AP_ERR_SKEW)
  1477. {
  1478. TimeStamp CurrentTime;
  1479. TimeStamp KdcTime;
  1480. if ((RetryFlags & KERB_RETRY_TIME_FAILURE) != 0)
  1481. {
  1482. Status = KerbMapKerbError(KerbErr);
  1483. goto Cleanup;
  1484. }
  1485. RetryFlags |= KERB_RETRY_TIME_FAILURE;
  1486. DoPreauthRetry = TRUE;
  1487. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime);
  1488. KerbConvertGeneralizedTimeToLargeInt(
  1489. &KdcTime,
  1490. &ErrorMessage->server_time,
  1491. ErrorMessage->server_usec
  1492. );
  1493. KerbSetTime(&TimeSkew, KerbGetTime(KdcTime) - KerbGetTime(CurrentTime));
  1494. KerbUpdateSkewTime(TRUE);
  1495. }
  1496. //
  1497. // Check for pre-authenication required
  1498. //
  1499. else if ((KerbErr == KDC_ERR_PREAUTH_FAILED) ||
  1500. (KerbErr == KRB_AP_ERR_BAD_INTEGRITY))
  1501. {
  1502. //
  1503. // This is a bad password failure.
  1504. //
  1505. if ((RetryFlags & KERB_RETRY_PASSWORD_FAILURE) == 0)
  1506. {
  1507. RetryFlags |= KERB_RETRY_PASSWORD_FAILURE;
  1508. }
  1509. else if ((RetryFlags & KERB_RETRY_SALT_FAILURE) != 0)
  1510. {
  1511. Status = KerbMapKerbError(KerbErr);
  1512. goto Cleanup;
  1513. }
  1514. else
  1515. {
  1516. RetryFlags |= KERB_RETRY_SALT_FAILURE;
  1517. RetryFlags &= ~KERB_RETRY_PASSWORD_FAILURE;
  1518. }
  1519. //
  1520. // In this case, there may be data in the error data
  1521. //
  1522. KerbFreeData(
  1523. PKERB_PREAUTH_DATA_LIST_PDU,
  1524. OldPreAuthData
  1525. );
  1526. OldPreAuthData = NULL;
  1527. (VOID) KerbUnpackErrorPreauth(
  1528. ErrorMessage,
  1529. &OldPreAuthData
  1530. );
  1531. DoPreauthRetry = TRUE;
  1532. }
  1533. else if ((KerbErr == KDC_ERR_ETYPE_NOTSUPP) ||
  1534. (KerbErr == KDC_ERR_PREAUTH_REQUIRED))
  1535. {
  1536. NTSTATUS TempStatus;
  1537. if (KerbErr == KDC_ERR_ETYPE_NOTSUPP)
  1538. {
  1539. if ((RetryFlags & KERB_RETRY_ETYPE_FAILURE) != 0)
  1540. {
  1541. Status = KerbMapKerbError(KerbErr);
  1542. goto Cleanup;
  1543. }
  1544. RetryFlags |= KERB_RETRY_ETYPE_FAILURE;
  1545. }
  1546. else
  1547. {
  1548. if ((RetryFlags & KERB_RETRY_WRONG_PREAUTH) != 0)
  1549. {
  1550. Status = KerbMapKerbError(KerbErr);
  1551. goto Cleanup;
  1552. }
  1553. RetryFlags |= KERB_RETRY_WRONG_PREAUTH;
  1554. }
  1555. //
  1556. // In this case, there should be data in the error data
  1557. //
  1558. KerbFreeData(
  1559. PKERB_PREAUTH_DATA_LIST_PDU,
  1560. OldPreAuthData
  1561. );
  1562. OldPreAuthData = NULL;
  1563. TempStatus = KerbUnpackErrorPreauth(
  1564. ErrorMessage,
  1565. &OldPreAuthData
  1566. );
  1567. if (!NT_SUCCESS(TempStatus))
  1568. {
  1569. D_DebugLog((DEB_ERROR,"GetAuthTicket: Failed to unpack error for preauth : 0x%x. %ws, line %d\n", TempStatus, THIS_FILE, __LINE__));
  1570. D_DebugLog((DEB_ERROR,"client was "));
  1571. D_KerbPrintKdcName(DEB_ERROR, ClientFullName );
  1572. D_DebugLog((DEB_ERROR," for service in realm %wZ : ", ServerRealm));
  1573. D_KerbPrintKdcName(DEB_ERROR, ServiceName);
  1574. DsysAssert(!NT_SUCCESS(Status));
  1575. goto Cleanup;
  1576. }
  1577. DoPreauthRetry = TRUE;
  1578. }
  1579. //
  1580. // There's something wrong w/ the client's account. In all cases
  1581. // this error should be accompanied by an extended error packet.
  1582. // and no retry should be attempted (see restrict.cxx)
  1583. //
  1584. else if ((KerbErr == KDC_ERR_CLIENT_REVOKED ||
  1585. KerbErr == KDC_ERR_POLICY ) &&
  1586. EXT_CLIENT_INFO_PRESENT(pExtendedError))
  1587. {
  1588. Status = pExtendedError->status;
  1589. goto Cleanup;
  1590. }
  1591. //
  1592. // For PKINIT, the client not trusted error indicates that SCLogon failed
  1593. // as the client certificate was bogus. Log an error here.
  1594. // NOTE: The extended status is a wincrypt error, so just return the
  1595. // normal status (
  1596. else if (KerbErr == KDC_ERR_CLIENT_NOT_TRUSTED)
  1597. {
  1598. ULONG PolicyStatus = ERROR_NOT_SUPPORTED; // w2k DCs won't likely have this data.
  1599. //
  1600. // WE may have trust status on the client certificate
  1601. // use this to create an event log. NOte: this is only
  1602. // going to happen if logon was against Whistler DC
  1603. //
  1604. // W2K Dcs returning errors will get mapped to generic
  1605. // error.
  1606. //
  1607. if (EXT_CLIENT_INFO_PRESENT(pExtendedError))
  1608. {
  1609. PolicyStatus = pExtendedError->status;
  1610. }
  1611. KerbReportPkinitError(PolicyStatus, NULL);
  1612. Status = KerbMapClientCertChainError(PolicyStatus);
  1613. DebugLog((DEB_ERROR, "Client certificate didn't validate on KDC - %x\n", PolicyStatus));
  1614. }
  1615. else if ((KerbErr == KDC_ERR_PADATA_TYPE_NOSUPP) &&
  1616. (EXT_CLIENT_INFO_PRESENT(pExtendedError)))
  1617. {
  1618. if ((RetryFlags & KERB_RETRY_PKINIT) != 0)
  1619. {
  1620. Status = KerbMapKerbError(KerbErr);
  1621. goto Cleanup;
  1622. }
  1623. //
  1624. // no KDC certificate error in edata, do a retry against
  1625. // another DC
  1626. //
  1627. if (pExtendedError->status == STATUS_PKINIT_FAILURE)
  1628. {
  1629. RetryFlags |= KERB_RETRY_PKINIT;
  1630. AdditionalFlags = DS_FORCE_REDISCOVERY;
  1631. DoLogonRetry = TRUE;
  1632. }
  1633. }
  1634. //
  1635. // Check if the server didn't know the client principal
  1636. //
  1637. else if (KerbErr == KDC_ERR_C_PRINCIPAL_UNKNOWN )
  1638. {
  1639. // fester
  1640. //D_DebugLog((DEB_ERROR, "Client principal unknown (realm %wZ) : ", ServerRealm));
  1641. //D_KerbPrintKdcName(DEB_ERROR, ClientFullName);
  1642. if ((RetryFlags & KERB_RETRY_WITH_ACCOUNT) != 0)
  1643. {
  1644. Status = KerbMapKerbError(KerbErr);
  1645. goto Cleanup;
  1646. }
  1647. RetryFlags |= KERB_RETRY_WITH_ACCOUNT;
  1648. DoAccountLookup = TRUE;
  1649. if ((ErrorMessage->bit_mask & client_realm_present) != 0)
  1650. {
  1651. UNICODE_STRING TempRealm;
  1652. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  1653. &TempRealm,
  1654. &ErrorMessage->client_realm
  1655. )))
  1656. {
  1657. Status = STATUS_INSUFFICIENT_RESOURCES;
  1658. goto Cleanup;
  1659. }
  1660. if (!RtlEqualUnicodeString(
  1661. ServerRealm,
  1662. &TempRealm,
  1663. TRUE // case insensitive
  1664. ))
  1665. {
  1666. D_DebugLog((DEB_TRACE,"Received UPN referral to another domain: %wZ\n",
  1667. &TempRealm ));
  1668. //
  1669. // Return the correct realm so the caller will retry
  1670. //
  1671. *CorrectRealm = TempRealm;
  1672. TempRealm.Buffer = NULL;
  1673. DoAccountLookup = FALSE;
  1674. }
  1675. KerbFreeString( &TempRealm );
  1676. }
  1677. if (DoAccountLookup)
  1678. {
  1679. DoLogonRetry = TRUE;
  1680. }
  1681. }
  1682. //
  1683. // Something's wrong w/ the KDC... Try another one, but 1 time only
  1684. //
  1685. else if (KerbErr == KDC_ERR_SVC_UNAVAILABLE)
  1686. {
  1687. if ((RetryFlags & KERB_RETRY_BAD_KDC) != 0)
  1688. {
  1689. Status = KerbMapKerbError(KerbErr);
  1690. goto Cleanup;
  1691. }
  1692. D_DebugLog((DEB_ERROR, "Retrying new KDC\n"));
  1693. AdditionalFlags = DS_FORCE_REDISCOVERY;
  1694. DoLogonRetry = TRUE;
  1695. RetryFlags |= KERB_RETRY_BAD_KDC;
  1696. }
  1697. else if (KerbErr == KDC_ERR_WRONG_REALM)
  1698. {
  1699. if ((RetryFlags & KERB_RETRY_BAD_REALM) != 0)
  1700. {
  1701. Status = KerbMapKerbError(KerbErr);
  1702. goto Cleanup;
  1703. }
  1704. RetryFlags |= KERB_RETRY_BAD_REALM;
  1705. AdditionalFlags = DS_FORCE_REDISCOVERY; // possibly bad cached DC
  1706. DoLogonRetry = TRUE;
  1707. if ((ErrorMessage->bit_mask & client_realm_present) != 0)
  1708. {
  1709. UNICODE_STRING TempRealm;
  1710. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  1711. &TempRealm,
  1712. &ErrorMessage->client_realm
  1713. )))
  1714. {
  1715. Status = STATUS_INSUFFICIENT_RESOURCES;
  1716. goto Cleanup;
  1717. }
  1718. if (!RtlEqualUnicodeString(
  1719. ServerRealm,
  1720. &TempRealm,
  1721. TRUE // case insensitive
  1722. ))
  1723. {
  1724. D_DebugLog((DEB_TRACE,"Received UPN referral to another domain: %wZ\n",
  1725. &TempRealm ));
  1726. //
  1727. // Return the correct realm so the caller will retry
  1728. //
  1729. *CorrectRealm = TempRealm;
  1730. TempRealm.Buffer = NULL;
  1731. DoLogonRetry = FALSE; // this is a referral, not a bad cache entry
  1732. }
  1733. KerbFreeString( &TempRealm );
  1734. }
  1735. }
  1736. //
  1737. // Retry if need be
  1738. //
  1739. if (DoPreauthRetry)
  1740. {
  1741. goto PreauthRestart;
  1742. }
  1743. else if (DoLogonRetry)
  1744. {
  1745. goto RetryLogon;
  1746. }
  1747. else if (DoTcpRetry)
  1748. {
  1749. goto RetryWithTcp;
  1750. }
  1751. }
  1752. else
  1753. {
  1754. D_DebugLog((DEB_WARN,"Failed to unpack KDC reply as AS or Error: 0x%x\n", KerbErr ));
  1755. Status = STATUS_INTERNAL_ERROR;
  1756. }
  1757. goto Cleanup;
  1758. }
  1759. //
  1760. // Update the skew counter if necessary
  1761. //
  1762. if ((RetryFlags & KERB_RETRY_TIME_FAILURE) == 0)
  1763. {
  1764. KerbUpdateSkewTime(FALSE);
  1765. }
  1766. //
  1767. // Now unpack the reply body:
  1768. //
  1769. KerbWriteLockLogonSessions(LogonSession);
  1770. LogonSessionsLocked = TRUE;
  1771. //
  1772. // if there was any pre auth data, process it now
  1773. //
  1774. if ((KdcReply->bit_mask & KERB_KDC_REPLY_preauth_data_present) != 0)
  1775. {
  1776. Status = KerbGetPreAuthDataForRealm(
  1777. PrimaryCredentials,
  1778. ServerRealm,
  1779. ServiceName,
  1780. (PKERB_PA_DATA_LIST) KdcReply->KERB_KDC_REPLY_preauth_data,
  1781. &TimeSkew,
  1782. (RetryFlags & KERB_RETRY_PASSWORD_FAILURE) != 0,
  1783. RequestBody->nonce,
  1784. KDC_ERR_NONE,
  1785. &TicketRequest.KERB_KDC_REQUEST_preauth_data,
  1786. &EncryptionKey,
  1787. &CryptList,
  1788. &PreAuthDone
  1789. );
  1790. if (!NT_SUCCESS(Status))
  1791. {
  1792. if (Status == STATUS_UNSUPPORTED_PREAUTH )
  1793. {
  1794. // Log this, every time, as this is impossible to triage otherwise
  1795. KerbReportKerbError(
  1796. ServiceName,
  1797. ServerRealm,
  1798. NULL,
  1799. Credential,
  1800. KLIN(FILENO,__LINE__),
  1801. NULL,
  1802. KDC_ERR_PADATA_TYPE_NOSUPP,
  1803. NULL,
  1804. TRUE
  1805. );
  1806. }
  1807. D_DebugLog((DEB_ERROR,"Failed to post process pre-auth data: 0x%x. %ws, %d\n",Status, THIS_FILE, __LINE__));
  1808. goto Cleanup;
  1809. }
  1810. KerbFreeCryptList(CryptList);
  1811. CryptList = NULL;
  1812. }
  1813. //
  1814. // If there is any preauth in the response, handle it
  1815. //
  1816. if (EncryptionKey.keyvalue.value == NULL)
  1817. {
  1818. ClientKey = KerbGetKeyFromList(
  1819. PrimaryCredentials->Passwords,
  1820. KdcReply->encrypted_part.encryption_type
  1821. );
  1822. DsysAssert(ClientKey != NULL);
  1823. if (ClientKey == NULL)
  1824. {
  1825. D_DebugLog((DEB_ERROR,"Kdc returned reply with encryption type we don't support: %d. %ws, line %d\n",
  1826. KdcReply->encrypted_part.encryption_type, THIS_FILE, __LINE__));
  1827. Status = STATUS_LOGON_FAILURE;
  1828. goto Cleanup;
  1829. }
  1830. }
  1831. else
  1832. {
  1833. //
  1834. // Use the encryption key we have from the pre-auth data
  1835. //
  1836. ClientKey = &EncryptionKey;
  1837. }
  1838. KerbErr = KerbUnpackKdcReplyBody(
  1839. &KdcReply->encrypted_part,
  1840. ClientKey,
  1841. KERB_ENCRYPTED_AS_REPLY_PDU,
  1842. &ReplyBody
  1843. );
  1844. //
  1845. // if we couldn't decrypt it and we have an old password around,
  1846. // give it a try too before heading to the PDC.
  1847. //
  1848. if ((KerbErr == KRB_AP_ERR_MODIFIED) &&
  1849. (PrimaryCredentials->OldPasswords != NULL) &&
  1850. (EncryptionKey.keyvalue.value == NULL))
  1851. {
  1852. ClientKey = KerbGetKeyFromList(
  1853. PrimaryCredentials->OldPasswords,
  1854. KdcReply->encrypted_part.encryption_type
  1855. );
  1856. if (ClientKey != NULL)
  1857. {
  1858. KerbErr = KerbUnpackKdcReplyBody(
  1859. &KdcReply->encrypted_part,
  1860. ClientKey,
  1861. KERB_ENCRYPTED_AS_REPLY_PDU,
  1862. &ReplyBody
  1863. );
  1864. }
  1865. }
  1866. if (!KERB_SUCCESS(KerbErr))
  1867. {
  1868. D_DebugLog((DEB_ERROR,"Failed to decrypt KDC reply body: 0x%x. %ws, line %d\n",KerbErr, THIS_FILE, __LINE__));
  1869. //
  1870. // If we didn't try the PDC, try it now.
  1871. //
  1872. if (((RetryFlags & KERB_RETRY_CALL_PDC) == 0) &&
  1873. (KerbErr == KRB_AP_ERR_MODIFIED) &&
  1874. (KerbGlobalRetryPdc))
  1875. {
  1876. RetryFlags |= KERB_RETRY_CALL_PDC;
  1877. KerbFreeAsReply(KdcReply);
  1878. KdcReply = NULL;
  1879. ReplyMessage.Buffer = NULL;
  1880. D_DebugLog((DEB_TRACE_CRED,"KerbGetAuthenticationTicket: Password wrong, trying PDC\n"));
  1881. goto RetryLogon;
  1882. }
  1883. Status = STATUS_LOGON_FAILURE;
  1884. goto Cleanup;
  1885. }
  1886. //
  1887. // Verify the nonce is correct:
  1888. //
  1889. if (RequestBody->nonce != ReplyBody->nonce)
  1890. {
  1891. D_DebugLog((DEB_ERROR,"AS Nonces don't match: 0x%x vs 0x%x. %ws, line %d\n",RequestBody->nonce, ReplyBody->nonce, THIS_FILE, __LINE__));
  1892. Status = STATUS_LOGON_FAILURE;
  1893. goto Cleanup;
  1894. }
  1895. //
  1896. // Update the logon session with the information if we didn't use
  1897. // supplied credentials.
  1898. //
  1899. {
  1900. UNICODE_STRING TempName;
  1901. UNICODE_STRING TempRealm;
  1902. //
  1903. // Get the new client realm & user name - if they are different
  1904. // we will update the logon session. This is in case the user
  1905. // logged on with a nickname (e.g. email name)
  1906. //
  1907. if (!KERB_SUCCESS(KerbConvertPrincipalNameToString(
  1908. &ClientName,
  1909. &NameType,
  1910. &KdcReply->client_name
  1911. )))
  1912. {
  1913. Status = STATUS_INSUFFICIENT_RESOURCES;
  1914. goto Cleanup;
  1915. }
  1916. //
  1917. // The KDC may hand back names with domains, so split the name
  1918. // now.
  1919. //
  1920. Status = KerbSplitFullServiceName(
  1921. &ClientName,
  1922. &TempRealm,
  1923. &TempName
  1924. );
  1925. if (!NT_SUCCESS(Status))
  1926. {
  1927. goto Cleanup;
  1928. }
  1929. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  1930. &ClientRealm,
  1931. &KdcReply->client_realm
  1932. )))
  1933. {
  1934. Status = STATUS_INSUFFICIENT_RESOURCES;
  1935. goto Cleanup;
  1936. }
  1937. if (!RtlEqualUnicodeString(
  1938. &PrimaryCredentials->UserName,
  1939. &TempName,
  1940. TRUE // case insensitive
  1941. )) {
  1942. D_DebugLog((DEB_TRACE_LSESS,"UserName different in logon session & AS ticket: %wZ vs %wZ\n",
  1943. &PrimaryCredentials->UserName,
  1944. &TempName
  1945. ));
  1946. KerbFreeString(
  1947. &PrimaryCredentials->UserName
  1948. );
  1949. Status = KerbDuplicateString(
  1950. &PrimaryCredentials->UserName,
  1951. &TempName
  1952. );
  1953. if (!NT_SUCCESS(Status))
  1954. {
  1955. goto Cleanup;
  1956. }
  1957. }
  1958. if (!RtlEqualUnicodeString(
  1959. &PrimaryCredentials->DomainName,
  1960. &ClientRealm,
  1961. FALSE // case sensitive, specially for ext chars.
  1962. )) {
  1963. D_DebugLog((DEB_TRACE_LSESS, "Domain name is different in logon session & as ticket: %wZ vs %wZ\n",
  1964. &PrimaryCredentials->DomainName,
  1965. &ClientRealm
  1966. ));
  1967. KerbFreeString(
  1968. &PrimaryCredentials->DomainName
  1969. );
  1970. PrimaryCredentials->DomainName = ClientRealm;
  1971. ClientRealm.Buffer = NULL;
  1972. }
  1973. }
  1974. //
  1975. // Cache the ticket
  1976. //
  1977. DsysAssert(LogonSessionsLocked);
  1978. //
  1979. // Free the cleartext password as we now have a ticket acquired with them.
  1980. //
  1981. if (PrimaryCredentials->ClearPassword.Buffer != NULL)
  1982. {
  1983. RtlZeroMemory(
  1984. PrimaryCredentials->ClearPassword.Buffer,
  1985. PrimaryCredentials->ClearPassword.Length
  1986. );
  1987. KerbFreeString(&PrimaryCredentials->ClearPassword);
  1988. }
  1989. Status = KerbCacheTicket(
  1990. &PrimaryCredentials->AuthenticationTicketCache,
  1991. KdcReply,
  1992. ReplyBody,
  1993. ServiceName,
  1994. ServerRealm,
  1995. CacheFlags,
  1996. TRUE,
  1997. &CacheEntry
  1998. );
  1999. if (!NT_SUCCESS(Status))
  2000. {
  2001. if (Status == STATUS_TIME_DIFFERENCE_AT_DC &&
  2002. ((RetryFlags & KERB_RETRY_TIME_FAILURE) == 0))
  2003. {
  2004. RetryFlags |= KERB_RETRY_TIME_FAILURE;
  2005. KerbUpdateSkewTime(TRUE);
  2006. D_DebugLog((DEB_WARN, "Retrying AS after trying to cache time invalid ticket\n"));
  2007. goto PreauthRestart;
  2008. }
  2009. goto Cleanup;
  2010. }
  2011. if (ARGUMENT_PRESENT(TicketCacheEntry))
  2012. {
  2013. *TicketCacheEntry = CacheEntry;
  2014. CacheEntry = NULL;
  2015. }
  2016. if (ARGUMENT_PRESENT(CredentialKey))
  2017. {
  2018. *CredentialKey = EncryptionKey;
  2019. EncryptionKey.keyvalue.value = NULL;
  2020. }
  2021. Cleanup:
  2022. if (HostAddresses != NULL)
  2023. {
  2024. KerbFreeHostAddresses(HostAddresses);
  2025. }
  2026. if (ErrorMessage != NULL)
  2027. {
  2028. KerbFreeKerbError(ErrorMessage);
  2029. }
  2030. if (pExtendedError)
  2031. {
  2032. KerbFreeData(KERB_EXT_ERROR_PDU, pExtendedError);
  2033. }
  2034. if (LogonSessionsLocked)
  2035. {
  2036. KerbUnlockLogonSessions(LogonSession);
  2037. }
  2038. if (CryptVector != NULL)
  2039. {
  2040. KerbFree(CryptVector);
  2041. }
  2042. if (OldPreAuthData != NULL)
  2043. {
  2044. KerbFreeData(
  2045. PKERB_PREAUTH_DATA_LIST_PDU,
  2046. OldPreAuthData
  2047. );
  2048. }
  2049. KerbFreePreAuthData( TicketRequest.KERB_KDC_REQUEST_preauth_data );
  2050. KerbFreeCryptList(
  2051. RequestBody->encryption_type
  2052. );
  2053. KerbFreeCryptList(
  2054. CryptList
  2055. );
  2056. KerbFreeString(
  2057. &ClientName
  2058. );
  2059. KerbFreeKdcName(
  2060. &LocalServiceName
  2061. );
  2062. KerbFreeString(
  2063. &ClientRealm
  2064. );
  2065. KerbFreePrincipalName(
  2066. &RequestBody->KERB_KDC_REQUEST_BODY_client_name
  2067. );
  2068. KerbFreePrincipalName(
  2069. &RequestBody->KERB_KDC_REQUEST_BODY_server_name
  2070. );
  2071. KerbFreeRealm(
  2072. &RequestBody->realm
  2073. );
  2074. KerbFreeKdcReplyBody( ReplyBody );
  2075. KerbFreeAsReply( KdcReply );
  2076. if (CacheEntry != NULL)
  2077. {
  2078. KerbDereferenceTicketCacheEntry(CacheEntry);
  2079. }
  2080. if (NewTGT != NULL)
  2081. {
  2082. KerbDereferenceTicketCacheEntry(NewTGT);
  2083. }
  2084. if (ReplyMessage.Buffer != NULL)
  2085. {
  2086. MIDL_user_free(ReplyMessage.Buffer);
  2087. }
  2088. if (RequestMessage.Buffer != NULL)
  2089. {
  2090. MIDL_user_free(RequestMessage.Buffer);
  2091. }
  2092. KerbFreeKey(&EncryptionKey);
  2093. return(Status);
  2094. }
  2095. //+-------------------------------------------------------------------------
  2096. //
  2097. // Function: KerbGetClientNameAndRealm
  2098. //
  2099. // Synopsis: Proceses the name & realm supplied by a client to determine
  2100. // a name & realm to be sent to the KDC
  2101. //
  2102. // Effects:
  2103. //
  2104. // Arguments:
  2105. //
  2106. // Requires:
  2107. //
  2108. // Returns:
  2109. //
  2110. // Notes:
  2111. //
  2112. //
  2113. //--------------------------------------------------------------------------
  2114. NTSTATUS
  2115. KerbGetClientNameAndRealm(
  2116. IN OPTIONAL LUID *pLogonId,
  2117. IN PKERB_PRIMARY_CREDENTIAL PrimaryCreds,
  2118. IN BOOLEAN SuppliedCreds,
  2119. IN OPTIONAL PUNICODE_STRING SuppRealm,
  2120. IN OUT OPTIONAL BOOLEAN * MitRealmUsed,
  2121. IN BOOLEAN UseWkstaRealm,
  2122. OUT PKERB_INTERNAL_NAME * ClientName,
  2123. OUT PUNICODE_STRING ClientRealm
  2124. )
  2125. {
  2126. NTSTATUS Status = STATUS_SUCCESS;
  2127. ULONG ParseFlags = 0;
  2128. ULONG ProcessFlags = 0;
  2129. PUNICODE_STRING UserName = NULL;
  2130. UNICODE_STRING LocalMachineServiceName;
  2131. LUID SystemLogonId = SYSTEM_LUID;
  2132. LocalMachineServiceName.Buffer = NULL;
  2133. //
  2134. // if the computer name has changed, we lie about the machineservicename,
  2135. // since existing creds contain the wrong username
  2136. //
  2137. if (( KerbGlobalMachineNameChanged ) &&
  2138. ( !SuppliedCreds ) &&
  2139. ( pLogonId != NULL ) &&
  2140. ( RtlEqualLuid(pLogonId, &SystemLogonId)))
  2141. {
  2142. D_DebugLog((DEB_WARN,"Netbios machine name change caused credential over-ride.\n"));
  2143. KerbGlobalReadLock();
  2144. Status = KerbDuplicateString( &LocalMachineServiceName, &KerbGlobalMachineServiceName );
  2145. KerbGlobalReleaseLock();
  2146. if(!NT_SUCCESS( Status ))
  2147. {
  2148. goto Cleanup;
  2149. }
  2150. UserName = &LocalMachineServiceName;
  2151. } else {
  2152. UserName = &PrimaryCreds->UserName;
  2153. }
  2154. //
  2155. // Compute the parse flags
  2156. //
  2157. if (PrimaryCreds->DomainName.Length != 0)
  2158. {
  2159. ParseFlags |= KERB_CRACK_NAME_REALM_SUPPLIED;
  2160. }
  2161. else if (UseWkstaRealm)
  2162. {
  2163. ParseFlags |= KERB_CRACK_NAME_USE_WKSTA_REALM;
  2164. }
  2165. Status = KerbProcessTargetNames(
  2166. UserName,
  2167. NULL,
  2168. ParseFlags,
  2169. &ProcessFlags,
  2170. ClientName,
  2171. ClientRealm,
  2172. NULL
  2173. );
  2174. if (!NT_SUCCESS(Status))
  2175. {
  2176. goto Cleanup;
  2177. }
  2178. //
  2179. // If we were not supplied a realm, use the one from the name
  2180. //
  2181. if (SuppRealm && (SuppRealm->Length != 0))
  2182. {
  2183. KerbFreeString(ClientRealm);
  2184. Status = KerbDuplicateString(
  2185. ClientRealm,
  2186. SuppRealm
  2187. );
  2188. if (!NT_SUCCESS(Status))
  2189. {
  2190. goto Cleanup;
  2191. }
  2192. }
  2193. else if (PrimaryCreds->DomainName.Length != 0)
  2194. {
  2195. KerbFreeString(ClientRealm);
  2196. Status = KerbDuplicateString(
  2197. ClientRealm,
  2198. &PrimaryCreds->DomainName
  2199. );
  2200. if (!NT_SUCCESS(Status))
  2201. {
  2202. goto Cleanup;
  2203. }
  2204. }
  2205. Cleanup:
  2206. #ifdef notdef
  2207. if (CacheEntry != NULL)
  2208. {
  2209. KerbDereferenceSidCacheEntry(
  2210. CacheEntry
  2211. );
  2212. CacheEntry = NULL;
  2213. }
  2214. #endif
  2215. if (ARGUMENT_PRESENT(MitRealmUsed))
  2216. {
  2217. *MitRealmUsed = ((ProcessFlags & KERB_MIT_REALM_USED) != 0);
  2218. }
  2219. KerbFreeString( &LocalMachineServiceName );
  2220. return(Status);
  2221. }
  2222. //+-------------------------------------------------------------------------
  2223. //
  2224. // Function: KerbGetTicketGrantingTicket
  2225. //
  2226. // Synopsis: Gets a TGT for a set of credentials
  2227. //
  2228. // Effects:
  2229. //
  2230. // Arguments:
  2231. //
  2232. // Requires:
  2233. //
  2234. // Returns:
  2235. //
  2236. // Notes:
  2237. //
  2238. //
  2239. //--------------------------------------------------------------------------
  2240. NTSTATUS
  2241. KerbGetTicketGrantingTicket(
  2242. IN OUT PKERB_LOGON_SESSION LogonSession,
  2243. IN OPTIONAL PKERB_CREDENTIAL Credential,
  2244. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  2245. IN OPTIONAL PUNICODE_STRING SuppRealm,
  2246. OUT OPTIONAL PKERB_TICKET_CACHE_ENTRY * TicketCacheEntry,
  2247. OUT OPTIONAL PKERB_ENCRYPTION_KEY CredentialKey
  2248. )
  2249. {
  2250. NTSTATUS Status = STATUS_SUCCESS, LookupStatus = STATUS_SUCCESS;
  2251. KERBERR KerbErr;
  2252. PKERB_INTERNAL_NAME KdcServiceKdcName = NULL;
  2253. PKERB_INTERNAL_NAME ClientName = NULL;
  2254. UNICODE_STRING UClientName = {0};
  2255. UNICODE_STRING ClientRealm = {0};
  2256. UNICODE_STRING CorrectRealm = {0};
  2257. ULONG RetryCount = KERB_CLIENT_REFERRAL_MAX;
  2258. PKERB_PRIMARY_CREDENTIAL PrimaryCreds;
  2259. PKERB_MIT_REALM MitRealm = NULL;
  2260. ULONG RequestFlags = 0;
  2261. BOOLEAN UsingSuppliedCreds = FALSE;
  2262. BOOLEAN UseWkstaRealm = TRUE;
  2263. BOOLEAN MitRealmLogon = FALSE;
  2264. BOOLEAN UsedPrimaryLogonCreds = FALSE;
  2265. LUID LogonId;
  2266. //
  2267. // Get the proper realm name
  2268. //
  2269. if (ARGUMENT_PRESENT(Credential) && (Credential->SuppliedCredentials != NULL))
  2270. {
  2271. PrimaryCreds = Credential->SuppliedCredentials;
  2272. if ((Credential->CredentialFlags & KERB_CRED_NO_PAC) != 0)
  2273. {
  2274. RequestFlags |= KERB_GET_TICKET_NO_PAC;
  2275. }
  2276. LogonId = Credential->LogonId;
  2277. UsingSuppliedCreds = TRUE;
  2278. }
  2279. else if (ARGUMENT_PRESENT(CredManCredentials))
  2280. {
  2281. PrimaryCreds = CredManCredentials->SuppliedCredentials;
  2282. LogonId = LogonSession->LogonId;
  2283. }
  2284. else
  2285. {
  2286. KerbWriteLockLogonSessions(LogonSession);
  2287. PrimaryCreds = &LogonSession->PrimaryCredentials;
  2288. LogonId = LogonSession->LogonId;
  2289. Status = KerbDuplicateString(
  2290. &UClientName,
  2291. &LogonSession->PrimaryCredentials.UserName
  2292. );
  2293. if (!NT_SUCCESS(Status))
  2294. {
  2295. goto Cleanup;
  2296. }
  2297. UsedPrimaryLogonCreds = TRUE;
  2298. }
  2299. //
  2300. // Parse the name
  2301. //
  2302. Status = KerbGetClientNameAndRealm(
  2303. &LogonId,
  2304. PrimaryCreds,
  2305. UsingSuppliedCreds,
  2306. SuppRealm,
  2307. &MitRealmLogon,
  2308. UseWkstaRealm,
  2309. &ClientName,
  2310. &ClientRealm
  2311. );
  2312. //
  2313. // If we're doing a MIT logon, add the MIT logon flag
  2314. //
  2315. if (MitRealmLogon && UsedPrimaryLogonCreds)
  2316. {
  2317. LogonSession->LogonSessionFlags |= KERB_LOGON_MIT_REALM;
  2318. }
  2319. // only needed lock if we're tinkering w/ primary creds
  2320. // in case updates the credentials for that logon id.
  2321. if (UsedPrimaryLogonCreds)
  2322. {
  2323. KerbUnlockLogonSessions(LogonSession);
  2324. }
  2325. if (!NT_SUCCESS(Status))
  2326. {
  2327. D_DebugLog((DEB_ERROR,"Failed to get client name & realm: 0x%x, %ws line %d\n",
  2328. Status, THIS_FILE, __LINE__ ));
  2329. goto Cleanup;
  2330. }
  2331. GetTicketRestart:
  2332. KerbErr = KerbBuildFullServiceKdcName(
  2333. &ClientRealm,
  2334. &KerbGlobalKdcServiceName,
  2335. KRB_NT_SRV_INST,
  2336. &KdcServiceKdcName
  2337. );
  2338. if (!KERB_SUCCESS(KerbErr))
  2339. {
  2340. Status = STATUS_INSUFFICIENT_RESOURCES;
  2341. goto Cleanup;
  2342. }
  2343. Status = KerbGetAuthenticationTicket(
  2344. LogonSession,
  2345. Credential,
  2346. CredManCredentials,
  2347. KdcServiceKdcName,
  2348. &ClientRealm,
  2349. ClientName,
  2350. RequestFlags,
  2351. KERB_TICKET_CACHE_PRIMARY_TGT,
  2352. TicketCacheEntry,
  2353. CredentialKey,
  2354. &CorrectRealm
  2355. );
  2356. //
  2357. // If it failed but gave us another realm to try, go there
  2358. //
  2359. if (!NT_SUCCESS(Status) && (CorrectRealm.Length != 0))
  2360. {
  2361. if (--RetryCount != 0)
  2362. {
  2363. KerbFreeKdcName(&KdcServiceKdcName);
  2364. KerbFreeString(&ClientRealm);
  2365. ClientRealm = CorrectRealm;
  2366. CorrectRealm.Buffer = NULL;
  2367. //
  2368. // Might be an MIT realm, in which case we'll need to adjust
  2369. // the client name. This will also populate the realm list
  2370. // with appropriate entries, so the KerbGetKdcBinding will not
  2371. // hit DNS again.
  2372. //
  2373. if (KerbLookupMitRealmWithSrvLookup(
  2374. &ClientRealm,
  2375. &MitRealm,
  2376. FALSE,
  2377. FALSE
  2378. ))
  2379. {
  2380. D_DebugLog((DEB_TRACE,"Reacquiring client name & realm after referral\n"));
  2381. UseWkstaRealm = FALSE;
  2382. KerbFreeKdcName(&ClientName);
  2383. Status = KerbGetClientNameAndRealm(
  2384. &LogonId,
  2385. PrimaryCreds,
  2386. UsingSuppliedCreds,
  2387. NULL,
  2388. NULL,
  2389. UseWkstaRealm,
  2390. &ClientName,
  2391. &ClientRealm
  2392. );
  2393. if (!NT_SUCCESS(Status))
  2394. {
  2395. goto Cleanup;
  2396. }
  2397. }
  2398. goto GetTicketRestart;
  2399. }
  2400. else
  2401. {
  2402. // Tbd: Log error here? Max referrals reached..
  2403. goto Cleanup;
  2404. }
  2405. }
  2406. else if ((Status == STATUS_NO_SUCH_USER) && UsingSuppliedCreds && UseWkstaRealm)
  2407. {
  2408. //
  2409. // We tried using the realm of the workstation and the account couldn't
  2410. // be found - try the realm from the UPN now.
  2411. //
  2412. if (KerbIsThisOurDomain(&ClientRealm))
  2413. {
  2414. UseWkstaRealm = FALSE;
  2415. KerbFreeKdcName(&ClientName);
  2416. KerbFreeString(&ClientRealm);
  2417. //
  2418. // Only do this if the caller did not supply a
  2419. // domain name
  2420. //
  2421. KerbReadLockLogonSessions(LogonSession);
  2422. if (PrimaryCreds->DomainName.Length == 0)
  2423. {
  2424. Status = KerbGetClientNameAndRealm(
  2425. &LogonId,
  2426. PrimaryCreds,
  2427. UsingSuppliedCreds,
  2428. NULL,
  2429. NULL,
  2430. UseWkstaRealm,
  2431. &ClientName,
  2432. &ClientRealm
  2433. );
  2434. }
  2435. KerbUnlockLogonSessions(LogonSession);
  2436. if (!NT_SUCCESS(Status))
  2437. {
  2438. goto Cleanup;
  2439. }
  2440. goto GetTicketRestart;
  2441. }
  2442. }
  2443. Cleanup:
  2444. if (Status == STATUS_ACCOUNT_DISABLED && UsedPrimaryLogonCreds)
  2445. {
  2446. D_DebugLog((DEB_ERROR, "Purging NLP Cache entry due to disabled acct.\n"));
  2447. KerbCacheLogonInformation(
  2448. &UClientName,
  2449. &ClientRealm,
  2450. NULL,
  2451. NULL,
  2452. NULL,
  2453. ((LogonSession->LogonSessionFlags & KERB_LOGON_MIT_REALM) != 0),
  2454. MSV1_0_CACHE_LOGON_DELETE_ENTRY,
  2455. NULL,
  2456. NULL, // no supplemental creds
  2457. 0
  2458. );
  2459. }
  2460. if (UsedPrimaryLogonCreds &&
  2461. ((Status == STATUS_WRONG_PASSWORD) ||
  2462. (Status == STATUS_SMARTCARD_NO_CARD) ||
  2463. (Status == STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED) ))
  2464. {
  2465. KerbPingWlBalloon(&LogonSession->LogonId);
  2466. }
  2467. KerbFreeString(&ClientRealm);
  2468. KerbFreeString(&CorrectRealm);
  2469. KerbFreeString(&UClientName);
  2470. KerbFreeKdcName(&KdcServiceKdcName);
  2471. KerbFreeKdcName(&ClientName);
  2472. return(Status);
  2473. }
  2474. //+-------------------------------------------------------------------------
  2475. //
  2476. // Function: KerbPurgeServiceTicketAndTgt
  2477. //
  2478. // Synopsis: Whacks the service ticket, and its associated TGT, usually as a
  2479. // result of some sort of error condition.
  2480. //
  2481. // Effects:
  2482. //
  2483. // Arguments:
  2484. //
  2485. // Requires:
  2486. //
  2487. // Returns:
  2488. //
  2489. // Notes:
  2490. //
  2491. //
  2492. //--------------------------------------------------------------------------
  2493. BOOLEAN
  2494. KerbPurgeServiceTicketAndTgt(
  2495. IN PKERB_CONTEXT Context,
  2496. IN LSA_SEC_HANDLE CredentialHandle,
  2497. IN OPTIONAL PKERB_CREDMAN_CRED CredManHandle
  2498. )
  2499. {
  2500. PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL;
  2501. PKERB_LOGON_SESSION LogonSession = NULL;
  2502. UNICODE_STRING RealmName[3];
  2503. PUNICODE_STRING pTmp = RealmName;
  2504. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  2505. PKERB_CREDENTIAL Credential = NULL;
  2506. BOOLEAN CacheLocked = FALSE, fRet = FALSE;
  2507. NTSTATUS Status;
  2508. // Validate in params
  2509. // If any of these fires, contact Todds
  2510. DsysAssert(NULL != CredentialHandle);
  2511. DsysAssert(NULL != Context->TicketCacheEntry );
  2512. DsysAssert(NULL != Context->TicketCacheEntry->TargetDomainName.Buffer);
  2513. Status = KerbReferenceCredential(
  2514. CredentialHandle,
  2515. 0,
  2516. FALSE,
  2517. &Credential
  2518. );
  2519. if (!NT_SUCCESS(Status) || Credential == NULL)
  2520. {
  2521. D_DebugLog((DEB_ERROR,"KerbPurgeServiceTicket supplied w/ bogus cred handle\n"));
  2522. goto Cleanup;
  2523. }
  2524. LogonSession = KerbReferenceLogonSession(&Credential->LogonId, FALSE);
  2525. if (NULL == LogonSession)
  2526. {
  2527. D_DebugLog((DEB_ERROR, "Couldn't find LUID %x\n", Credential->LogonId));
  2528. goto Cleanup;
  2529. }
  2530. KerbReadLockLogonSessions(&KerbLogonSessionList);
  2531. if (NULL != Credential && Credential->SuppliedCredentials != NULL)
  2532. {
  2533. PrimaryCredentials = Credential->SuppliedCredentials;
  2534. D_DebugLog((DEB_TRACE, "Purging tgt associated with SUPPLIED creds (%S\\%S)\n",
  2535. PrimaryCredentials->DomainName.Buffer,PrimaryCredentials->UserName.Buffer));
  2536. }
  2537. else if ARGUMENT_PRESENT(CredManHandle)
  2538. {
  2539. PrimaryCredentials = CredManHandle->SuppliedCredentials;
  2540. D_DebugLog((DEB_TRACE, "Purging tgt associated with CREDMAN creds (%S\\%S)\n",
  2541. PrimaryCredentials->DomainName.Buffer,PrimaryCredentials->UserName.Buffer));
  2542. }
  2543. else
  2544. {
  2545. PrimaryCredentials = &LogonSession->PrimaryCredentials;
  2546. D_DebugLog((DEB_TRACE, "Purging tgt associated with PRIMARY creds (%S\\%S)\n",
  2547. PrimaryCredentials->DomainName.Buffer,PrimaryCredentials->UserName.Buffer));
  2548. }
  2549. KerbWriteLockContexts();
  2550. TicketCacheEntry = Context->TicketCacheEntry;
  2551. Context->TicketCacheEntry = NULL;
  2552. KerbUnlockContexts();
  2553. KerbReadLockTicketCache();
  2554. CacheLocked = TRUE;
  2555. // Do some mem copy rather than block over ticket cache searches
  2556. RtlCopyMemory(RealmName, &TicketCacheEntry->TargetDomainName, sizeof(UNICODE_STRING));
  2557. RealmName[0].Buffer = (PWSTR) KerbAllocate(RealmName[0].MaximumLength);
  2558. if (NULL == RealmName[0].Buffer)
  2559. {
  2560. goto Cleanup;
  2561. }
  2562. RtlCopyUnicodeString(
  2563. &RealmName[0],
  2564. &TicketCacheEntry->TargetDomainName
  2565. );
  2566. RtlCopyMemory(&RealmName[1],&TicketCacheEntry->AltTargetDomainName, sizeof(UNICODE_STRING));
  2567. if (RealmName[1].Buffer != NULL && RealmName[1].MaximumLength != 0)
  2568. {
  2569. RealmName[1].Buffer = (PWSTR) KerbAllocate(RealmName[1].MaximumLength);
  2570. if (NULL == RealmName[1].Buffer)
  2571. {
  2572. goto Cleanup;
  2573. }
  2574. RtlCopyUnicodeString(
  2575. &RealmName[1],
  2576. &TicketCacheEntry->TargetDomainName
  2577. );
  2578. }
  2579. KerbUnlockTicketCache();
  2580. CacheLocked = FALSE;
  2581. RtlInitUnicodeString(
  2582. &RealmName[2],
  2583. NULL
  2584. );
  2585. // Kill the service ticket
  2586. KerbRemoveTicketCacheEntry(TicketCacheEntry);
  2587. do
  2588. {
  2589. PKERB_TICKET_CACHE_ENTRY DummyEntry = NULL;
  2590. DummyEntry = KerbLocateTicketCacheEntryByRealm(
  2591. &PrimaryCredentials->AuthenticationTicketCache,
  2592. pTmp,
  2593. KERB_TICKET_CACHE_PRIMARY_TGT
  2594. );
  2595. if (NULL == DummyEntry)
  2596. {
  2597. D_DebugLog((DEB_TRACE, "Didn't find primary TGT for %S \n", pTmp->Buffer));
  2598. }
  2599. else
  2600. {
  2601. KerbRemoveTicketCacheEntry(DummyEntry);
  2602. KerbDereferenceTicketCacheEntry(DummyEntry);
  2603. }
  2604. DummyEntry = KerbLocateTicketCacheEntryByRealm(
  2605. &PrimaryCredentials->AuthenticationTicketCache,
  2606. pTmp,
  2607. KERB_TICKET_CACHE_DELEGATION_TGT
  2608. );
  2609. if (NULL == DummyEntry)
  2610. {
  2611. D_DebugLog((DEB_TRACE, "Didn't find delegation TGT for %S\n", pTmp->Buffer));
  2612. }
  2613. else
  2614. {
  2615. KerbRemoveTicketCacheEntry(DummyEntry);
  2616. KerbDereferenceTicketCacheEntry(DummyEntry);
  2617. }
  2618. pTmp++;
  2619. } while (pTmp->Buffer != NULL);
  2620. fRet = TRUE;
  2621. Cleanup:
  2622. if (CacheLocked)
  2623. {
  2624. KerbUnlockTicketCache();
  2625. }
  2626. if (NULL != Credential)
  2627. {
  2628. KerbDereferenceCredential(Credential);
  2629. }
  2630. KerbUnlockLogonSessions(&KerbLogonSessionList);
  2631. if (NULL != LogonSession)
  2632. {
  2633. KerbDereferenceLogonSession(LogonSession);
  2634. }
  2635. if (RealmName[0].Buffer != NULL)
  2636. {
  2637. KerbFree(RealmName[0].Buffer);
  2638. }
  2639. if (RealmName[1].Buffer != NULL)
  2640. {
  2641. KerbFree(RealmName[1].Buffer);
  2642. }
  2643. return fRet;
  2644. }
  2645. //+-------------------------------------------------------------------------
  2646. //
  2647. // Function: KerbCopyTicketCache
  2648. //
  2649. // Synopsis: Copies the authentication ticket cache from one
  2650. // logon session to another.
  2651. //
  2652. // Effects:
  2653. //
  2654. // Arguments:
  2655. //
  2656. // Requires:
  2657. //
  2658. // Returns:
  2659. //
  2660. // Notes:
  2661. //
  2662. //
  2663. //--------------------------------------------------------------------------
  2664. VOID
  2665. KerbUpdateOldLogonSession(
  2666. IN PKERB_LOGON_SESSION LogonSession,
  2667. IN PLUID OldLogonId,
  2668. IN PKERB_TICKET_CACHE_ENTRY NewWorkstationTicket
  2669. )
  2670. {
  2671. PKERB_LOGON_SESSION OldLogonSession;
  2672. OldLogonSession = KerbReferenceLogonSession(
  2673. OldLogonId,
  2674. FALSE // don't unlink
  2675. );
  2676. if (OldLogonSession == NULL)
  2677. {
  2678. goto Cleanup;
  2679. }
  2680. KerbWriteLockLogonSessions(OldLogonSession);
  2681. KerbWriteLockLogonSessions(LogonSession);
  2682. KerbWriteLockTicketCache();
  2683. //
  2684. // Make sure the two accounts are the same before copying tickets
  2685. // around.
  2686. //
  2687. if ((RtlEqualUnicodeString(
  2688. &LogonSession->PrimaryCredentials.UserName,
  2689. &OldLogonSession->PrimaryCredentials.UserName,
  2690. TRUE // case insensitive
  2691. )) &&
  2692. (RtlEqualUnicodeString(
  2693. &LogonSession->PrimaryCredentials.DomainName,
  2694. &OldLogonSession->PrimaryCredentials.DomainName,
  2695. TRUE // case insensitive
  2696. )))
  2697. {
  2698. PKERB_TICKET_CACHE_ENTRY ASTicket = NULL;
  2699. //
  2700. // Search for the new TGT so we can put it in the old
  2701. // cache.
  2702. //
  2703. ASTicket = KerbLocateTicketCacheEntryByRealm(
  2704. &LogonSession->PrimaryCredentials.AuthenticationTicketCache,
  2705. NULL, // get initial ticket
  2706. KERB_TICKET_CACHE_PRIMARY_TGT
  2707. );
  2708. if (ASTicket != NULL)
  2709. {
  2710. OldLogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  2711. KerbRemoveTicketCacheEntry(ASTicket);
  2712. }
  2713. else
  2714. {
  2715. //
  2716. // Hold on to our old TGT if we didn't get one this time around..
  2717. //
  2718. D_DebugLog((DEB_ERROR, "Failed to find primary TGT on unlock logon session\n"));
  2719. ASTicket = KerbLocateTicketCacheEntryByRealm(
  2720. &OldLogonSession->PrimaryCredentials.AuthenticationTicketCache,
  2721. NULL,
  2722. KERB_TICKET_CACHE_PRIMARY_TGT
  2723. );
  2724. if (ASTicket != NULL)
  2725. {
  2726. //
  2727. // Copy into new logon session cache for later reuse.
  2728. //
  2729. KerbRemoveTicketCacheEntry(ASTicket);
  2730. OldLogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  2731. }
  2732. else
  2733. {
  2734. //
  2735. // No TGT in either new or old logonsession -- we're deferred...
  2736. //
  2737. D_DebugLog((DEB_ERROR, "Failed to find primary TGT on *OLD* logon session\n"));
  2738. OldLogonSession->LogonSessionFlags |= KERB_LOGON_DEFERRED;
  2739. }
  2740. }
  2741. if ((LogonSession->LogonSessionFlags & KERB_LOGON_SMARTCARD) != 0)
  2742. {
  2743. OldLogonSession->LogonSessionFlags |= KERB_LOGON_SMARTCARD;
  2744. }
  2745. //
  2746. // swap the primary creds
  2747. //
  2748. KerbFreePrimaryCredentials(&OldLogonSession->PrimaryCredentials, FALSE);
  2749. if ( NewWorkstationTicket != NULL )
  2750. {
  2751. KerbRemoveTicketCacheEntry(NewWorkstationTicket);
  2752. }
  2753. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
  2754. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);
  2755. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
  2756. RtlCopyMemory(
  2757. &OldLogonSession->PrimaryCredentials,
  2758. &LogonSession->PrimaryCredentials,
  2759. sizeof(KERB_PRIMARY_CREDENTIAL)
  2760. );
  2761. RtlZeroMemory(
  2762. &LogonSession->PrimaryCredentials,
  2763. sizeof(KERB_PRIMARY_CREDENTIAL)
  2764. );
  2765. // Fix up list entry pointers.
  2766. KerbInitTicketCache(&OldLogonSession->PrimaryCredentials.AuthenticationTicketCache);
  2767. KerbInitTicketCache(&OldLogonSession->PrimaryCredentials.S4UTicketCache);
  2768. KerbInitTicketCache(&OldLogonSession->PrimaryCredentials.ServerTicketCache);
  2769. KerbInitTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
  2770. KerbInitTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);
  2771. KerbInitTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
  2772. // insert new tickets into old logon session
  2773. if (ASTicket != NULL)
  2774. {
  2775. KerbInsertTicketCacheEntry(&OldLogonSession->PrimaryCredentials.AuthenticationTicketCache, ASTicket);
  2776. KerbDereferenceTicketCacheEntry(ASTicket); // for locate call above
  2777. }
  2778. if (NewWorkstationTicket != NULL)
  2779. {
  2780. KerbInsertTicketCacheEntry(&OldLogonSession->PrimaryCredentials.ServerTicketCache, NewWorkstationTicket);
  2781. }
  2782. }
  2783. KerbUnlockTicketCache();
  2784. KerbUnlockLogonSessions(LogonSession);
  2785. KerbUnlockLogonSessions(OldLogonSession);
  2786. KerbDereferenceLogonSession(OldLogonSession);
  2787. Cleanup:
  2788. return;
  2789. }
  2790. #ifndef WIN32_CHICAGO // later
  2791. //+-------------------------------------------------------------------------
  2792. //
  2793. // Function: KerbCheckDomainlessLogonPolicy
  2794. //
  2795. // Synopsis: If a machine is not a member of a domain or MIT realm,
  2796. // we've got to verify that the kerberos principal is mapped
  2797. // to a local account.
  2798. //
  2799. // Effects:
  2800. //
  2801. // Arguments:
  2802. //
  2803. // Requires:
  2804. //
  2805. // Returns:
  2806. //
  2807. // Notes:
  2808. //
  2809. //
  2810. //--------------------------------------------------------------------------
  2811. NTSTATUS NTAPI
  2812. KerbCheckRealmlessLogonPolicy(
  2813. IN PKERB_TICKET_CACHE_ENTRY AsTicket,
  2814. IN PKERB_INTERNAL_NAME ClientName,
  2815. IN PUNICODE_STRING ClientRealm
  2816. )
  2817. {
  2818. NTSTATUS Status = STATUS_SUCCESS;
  2819. //
  2820. // Sanity check to prevent identity spoofing for
  2821. // gaining access to a machine
  2822. //
  2823. // tbd: Credman support? Seems like we should be using credman
  2824. // credentials, if present, but for logon???
  2825. //
  2826. if (!KerbEqualKdcNames(
  2827. ClientName,
  2828. AsTicket->ClientName) ||
  2829. !RtlEqualUnicodeString(
  2830. &AsTicket->ClientDomainName,
  2831. ClientRealm,
  2832. TRUE
  2833. ))
  2834. {
  2835. D_DebugLog((DEB_ERROR, "Logon session and AS ticket identities don't match\n"));
  2836. // tbd: Log names?
  2837. Status = STATUS_NO_SUCH_USER;
  2838. }
  2839. return Status;
  2840. }
  2841. //+-------------------------------------------------------------------------
  2842. //
  2843. // Function: LsaApLogonUserEx2
  2844. //
  2845. // Synopsis: Handles service, batch, and interactive logons
  2846. //
  2847. // Effects:
  2848. //
  2849. // Arguments:
  2850. //
  2851. // Requires:
  2852. //
  2853. // Returns:
  2854. //
  2855. // Notes:
  2856. //
  2857. //
  2858. //--------------------------------------------------------------------------
  2859. NTSTATUS NTAPI
  2860. LsaApLogonUserEx2(
  2861. IN PLSA_CLIENT_REQUEST ClientRequest,
  2862. IN SECURITY_LOGON_TYPE LogonType,
  2863. IN PVOID ProtocolSubmitBuffer,
  2864. IN PVOID ClientBufferBase,
  2865. IN ULONG SubmitBufferSize,
  2866. OUT PVOID *ProfileBuffer,
  2867. OUT PULONG ProfileBufferLength,
  2868. OUT PLUID NewLogonId,
  2869. OUT PNTSTATUS ApiSubStatus,
  2870. OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
  2871. OUT PVOID *TokenInformation,
  2872. OUT PUNICODE_STRING *AccountName,
  2873. OUT PUNICODE_STRING *AuthenticatingAuthority,
  2874. OUT PUNICODE_STRING *MachineName,
  2875. OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
  2876. OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * CachedCredentials
  2877. )
  2878. {
  2879. NTSTATUS Status = STATUS_SUCCESS;
  2880. PKERB_LOGON_SESSION LogonSession = NULL;
  2881. PKERB_INTERACTIVE_LOGON LogonInfo = NULL;
  2882. UCHAR Seed;
  2883. UNICODE_STRING TempName = NULL_UNICODE_STRING;
  2884. UNICODE_STRING TempAuthority = NULL_UNICODE_STRING;
  2885. UNICODE_STRING NullAuthority = NULL_UNICODE_STRING;
  2886. UNICODE_STRING MappedClientName = NULL_UNICODE_STRING;
  2887. LUID LogonId;
  2888. LUID OldLogonId;
  2889. BOOLEAN DoingUnlock = FALSE, WkstaAccount = FALSE;
  2890. PKERB_TICKET_CACHE_ENTRY WorkstationTicket = NULL;
  2891. PKERB_TICKET_CACHE_ENTRY AsTicket = NULL;
  2892. KERB_MESSAGE_BUFFER ForwardedTgt = {0};
  2893. KERBEROS_MACHINE_ROLE Role;
  2894. PKERB_MIT_REALM MitRealm = NULL;
  2895. BOOLEAN LocalLogon = FALSE, RealmlessWkstaLogon = FALSE;
  2896. BOOLEAN UsedAlternateName = FALSE;
  2897. BOOLEAN MitRealmLogon = FALSE;
  2898. KERB_ENCRYPTION_KEY CredentialKey = {0};
  2899. UNICODE_STRING DomainName = {0};
  2900. PKERB_INTERNAL_NAME ClientName = NULL;
  2901. UNICODE_STRING ClientRealm = {0};
  2902. PKERB_INTERNAL_NAME MachineServiceName = {0};
  2903. PKERB_INTERNAL_NAME S4UClientName = {0};
  2904. UNICODE_STRING S4UClientRealm = {0};
  2905. UNICODE_STRING CorrectRealm = {0};
  2906. PLSAPR_CR_CIPHER_VALUE SecretCurrent = NULL;
  2907. SECPKG_CLIENT_INFO ClientInfo;
  2908. UNICODE_STRING Prefix, SavedPassword = {0};
  2909. BOOLEAN ServiceSecretLogon = FALSE;
  2910. ULONG ProcessFlags = 0;
  2911. PCERT_CONTEXT CertContext = NULL;
  2912. BOOLEAN fSuppliedCertCred = FALSE;
  2913. PVOID pTempSubmitBuffer = ProtocolSubmitBuffer;
  2914. PNETLOGON_VALIDATION_SAM_INFO4 ValidationInfo = NULL;
  2915. PNETLOGON_VALIDATION_SAM_INFO4 SCValidationInfo = NULL;
  2916. GUID LogonGuid = { 0 };
  2917. //
  2918. // Credential manager stored credentials.
  2919. //
  2920. UNICODE_STRING CredmanUserName;
  2921. UNICODE_STRING CredmanDomainName;
  2922. UNICODE_STRING CredmanPassword;
  2923. #if _WIN64
  2924. BOOL fAllocatedSubmitBuffer = FALSE;
  2925. #endif // _WIN64
  2926. KERB_LOGON_INFO LogonTraceInfo;
  2927. if (LogonType == CachedInteractive) {
  2928. //
  2929. // We don't support cached logons.
  2930. //
  2931. return STATUS_INVALID_LOGON_TYPE;
  2932. }
  2933. if( KerbEventTraceFlag ) // Event Trace: KerbLogonUserStart {No Data}
  2934. {
  2935. // Set trace parameters
  2936. LogonTraceInfo.EventTrace.Guid = KerbLogonGuid;
  2937. LogonTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START;
  2938. LogonTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID;
  2939. LogonTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER);
  2940. TraceEvent(
  2941. KerbTraceLoggerHandle,
  2942. (PEVENT_TRACE_HEADER)&LogonTraceInfo);
  2943. }
  2944. //
  2945. // First initialize all the output parameters to NULL.
  2946. //
  2947. *ProfileBuffer = NULL;
  2948. *ApiSubStatus = STATUS_SUCCESS;
  2949. *TokenInformation = NULL;
  2950. *AccountName = NULL;
  2951. *AuthenticatingAuthority = NULL;
  2952. *MachineName = NULL;
  2953. *CachedCredentials = NULL;
  2954. CredmanUserName.Buffer = NULL;
  2955. CredmanDomainName.Buffer = NULL;
  2956. CredmanPassword.Buffer = NULL;
  2957. RtlZeroMemory(
  2958. PrimaryCredentials,
  2959. sizeof(SECPKG_PRIMARY_CRED)
  2960. );
  2961. if (!KerbGlobalInitialized)
  2962. {
  2963. Status = STATUS_INVALID_SERVER_STATE;
  2964. goto Cleanup;
  2965. }
  2966. //
  2967. // Make sure we have at least tcp as a xport, unless we are doing
  2968. // cached sc logon, in which case we'll let them get through.
  2969. //
  2970. KerbGlobalReadLock();
  2971. if (KerbGlobalNoTcpUdp)
  2972. {
  2973. Status = STATUS_NETWORK_UNREACHABLE;
  2974. }
  2975. KerbGlobalReleaseLock();
  2976. if (!NT_SUCCESS(Status))
  2977. {
  2978. goto Cleanup;
  2979. }
  2980. Role = KerbGetGlobalRole();
  2981. *AccountName = (PUNICODE_STRING) KerbAllocate(sizeof(UNICODE_STRING));
  2982. if (*AccountName == NULL)
  2983. {
  2984. Status = STATUS_INSUFFICIENT_RESOURCES;
  2985. goto Cleanup;
  2986. }
  2987. (*AccountName)->Buffer = NULL;
  2988. *AuthenticatingAuthority = (PUNICODE_STRING) KerbAllocate(sizeof(UNICODE_STRING));
  2989. if (*AuthenticatingAuthority == NULL)
  2990. {
  2991. Status = STATUS_INSUFFICIENT_RESOURCES;
  2992. goto Cleanup;
  2993. }
  2994. (*AuthenticatingAuthority)->Buffer = NULL;
  2995. //
  2996. // Initialize local pointers to NULL
  2997. //
  2998. LogonId.LowPart = 0;
  2999. LogonId.HighPart = 0;
  3000. *NewLogonId = LogonId;
  3001. //
  3002. // Check the logon type
  3003. //
  3004. switch (LogonType) {
  3005. case Service:
  3006. case Interactive:
  3007. case Batch:
  3008. case Network:
  3009. case NetworkCleartext:
  3010. case RemoteInteractive:
  3011. PSECURITY_SEED_AND_LENGTH SeedAndLength;
  3012. LogonInfo = (PKERB_INTERACTIVE_LOGON) pTempSubmitBuffer;
  3013. if (SubmitBufferSize < sizeof(KERB_LOGON_SUBMIT_TYPE))
  3014. {
  3015. Status = STATUS_INVALID_PARAMETER;
  3016. goto Cleanup;
  3017. }
  3018. #if _WIN64
  3019. SECPKG_CALL_INFO CallInfo;
  3020. //
  3021. // Expand the ProtocolSubmitBuffer to 64-bit pointers if this
  3022. // call came from a WOW client.
  3023. //
  3024. if(!LsaFunctions->GetCallInfo(&CallInfo))
  3025. {
  3026. Status = STATUS_INTERNAL_ERROR;
  3027. goto Cleanup;
  3028. }
  3029. if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
  3030. {
  3031. Status = KerbConvertWOWLogonBuffer(ProtocolSubmitBuffer,
  3032. ClientBufferBase,
  3033. &SubmitBufferSize,
  3034. LogonInfo->MessageType,
  3035. &pTempSubmitBuffer);
  3036. if (!NT_SUCCESS(Status))
  3037. {
  3038. goto Cleanup;
  3039. }
  3040. fAllocatedSubmitBuffer = TRUE;
  3041. //
  3042. // Some macros below expand out to use ProtocolSubmitBuffer directly.
  3043. // We've secretly replaced their usual ProtocolSubmitBuffer with
  3044. // pTempSubmitBuffer -- let's see if they can tell the difference.
  3045. //
  3046. ProtocolSubmitBuffer = pTempSubmitBuffer;
  3047. LogonInfo = (PKERB_INTERACTIVE_LOGON) pTempSubmitBuffer;
  3048. }
  3049. #endif // _WIN64
  3050. if ((LogonInfo->MessageType == KerbInteractiveLogon) ||
  3051. (LogonInfo->MessageType == KerbWorkstationUnlockLogon))
  3052. {
  3053. //
  3054. // Pull the interesting information out of the submit buffer
  3055. //
  3056. if (LogonInfo->MessageType == KerbInteractiveLogon)
  3057. {
  3058. if (SubmitBufferSize < sizeof(KERB_INTERACTIVE_LOGON))
  3059. {
  3060. D_DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
  3061. Status = STATUS_INVALID_PARAMETER;
  3062. goto Cleanup;
  3063. }
  3064. }
  3065. else
  3066. {
  3067. if (SubmitBufferSize < sizeof(KERB_INTERACTIVE_UNLOCK_LOGON))
  3068. {
  3069. D_DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
  3070. Status = STATUS_INVALID_PARAMETER;
  3071. goto Cleanup;
  3072. }
  3073. PKERB_INTERACTIVE_UNLOCK_LOGON UnlockInfo = (PKERB_INTERACTIVE_UNLOCK_LOGON) LogonInfo;
  3074. OldLogonId = UnlockInfo->LogonId;
  3075. DoingUnlock = TRUE;
  3076. }
  3077. //
  3078. // If the password length is greater than 255 (i.e., the
  3079. // upper byte of the length is non-zero) then the password
  3080. // has been run-encoded for privacy reasons. Get the
  3081. // run-encode seed out of the upper-byte of the length
  3082. // for later use.
  3083. //
  3084. SeedAndLength = (PSECURITY_SEED_AND_LENGTH)
  3085. &LogonInfo->Password.Length;
  3086. Seed = SeedAndLength->Seed;
  3087. SeedAndLength->Seed = 0;
  3088. //
  3089. // Enforce length restrictions on username and password.
  3090. //
  3091. if ( LogonInfo->UserName.Length > (UNLEN * sizeof(WCHAR)) ||
  3092. LogonInfo->Password.Length > (PWLEN * sizeof(WCHAR)) ) {
  3093. D_DebugLog((DEB_ERROR,"LsaApLogonUserEx2: Name or password too long. %ws, line%d\n", THIS_FILE, __LINE__));
  3094. Status = STATUS_NAME_TOO_LONG;
  3095. goto Cleanup;
  3096. }
  3097. //
  3098. // Relocate any pointers to be relative to 'LogonInfo'
  3099. //
  3100. RELOCATE_ONE(&LogonInfo->UserName);
  3101. NULL_RELOCATE_ONE(&LogonInfo->LogonDomainName);
  3102. NULL_RELOCATE_ONE(&LogonInfo->Password);
  3103. if( (LogonInfo->LogonDomainName.Length <= sizeof(WCHAR)) &&
  3104. (LogonInfo->Password.Length <= sizeof(WCHAR))
  3105. )
  3106. {
  3107. if(KerbProcessUserNameCredential(
  3108. &LogonInfo->UserName,
  3109. &CredmanUserName,
  3110. &CredmanDomainName,
  3111. &CredmanPassword
  3112. ) == STATUS_SUCCESS)
  3113. {
  3114. LogonInfo->UserName = CredmanUserName;
  3115. LogonInfo->LogonDomainName = CredmanDomainName;
  3116. LogonInfo->Password = CredmanPassword;
  3117. Seed = 0;
  3118. }
  3119. }
  3120. if ( LogonType == Service )
  3121. {
  3122. SECPKG_CALL_INFO CallInfo;
  3123. //
  3124. // If we have a service logon, the password we got is likely the name of the
  3125. // secret that is holding the account password. Make sure to read that secret
  3126. // here
  3127. //
  3128. RtlInitUnicodeString( &Prefix, L"_SC_" );
  3129. if ( (RtlPrefixUnicodeString( &Prefix, &LogonInfo->Password, TRUE )) &&
  3130. (LsaFunctions->GetCallInfo(&CallInfo)) &&
  3131. (CallInfo.Attributes & SECPKG_CALL_IS_TCB)
  3132. )
  3133. {
  3134. LSAPR_HANDLE SecretHandle = NULL;
  3135. Status = LsarOpenSecret( KerbGlobalPolicyHandle,
  3136. ( PLSAPR_UNICODE_STRING )&LogonInfo->Password,
  3137. SECRET_QUERY_VALUE,
  3138. &SecretHandle );
  3139. if ( NT_SUCCESS( Status ) ) {
  3140. Status = LsarQuerySecret( SecretHandle,
  3141. &SecretCurrent,
  3142. NULL,
  3143. NULL,
  3144. NULL );
  3145. if ( NT_SUCCESS( Status ) && (SecretCurrent != NULL) ) {
  3146. RtlCopyMemory( &SavedPassword,
  3147. &LogonInfo->Password,
  3148. sizeof( UNICODE_STRING ) );
  3149. LogonInfo->Password.Length = ( USHORT )SecretCurrent->Length;
  3150. LogonInfo->Password.MaximumLength =
  3151. ( USHORT )SecretCurrent->MaximumLength;
  3152. LogonInfo->Password.Buffer = ( USHORT * )SecretCurrent->Buffer;
  3153. ServiceSecretLogon = TRUE;
  3154. }
  3155. LsarClose( &SecretHandle );
  3156. }
  3157. }
  3158. if ( !NT_SUCCESS( Status ) ) {
  3159. goto Cleanup;
  3160. }
  3161. }
  3162. D_DebugLog((DEB_TRACE,"Logging on user %wZ, domain %wZ\n",
  3163. &LogonInfo->UserName,
  3164. &LogonInfo->LogonDomainName
  3165. ));
  3166. KerbGlobalReadLock();
  3167. WkstaAccount = RtlEqualUnicodeString(
  3168. &KerbGlobalMachineName,
  3169. &LogonInfo->LogonDomainName,
  3170. TRUE
  3171. );
  3172. KerbGlobalReleaseLock();
  3173. // In the case where we're doing a realmless wksta
  3174. // logon, then run see if there's a client mapping,
  3175. // but only for interactive logons
  3176. if ((!WkstaAccount) &&
  3177. (Role == KerbRoleRealmlessWksta) &&
  3178. (LogonType == Interactive ))
  3179. {
  3180. Status = KerbProcessTargetNames(
  3181. &LogonInfo->UserName,
  3182. NULL,
  3183. 0,
  3184. &ProcessFlags,
  3185. &ClientName,
  3186. &ClientRealm,
  3187. NULL
  3188. );
  3189. if (NT_SUCCESS(Status))
  3190. {
  3191. Status = KerbMapClientName(
  3192. &MappedClientName,
  3193. ClientName,
  3194. &ClientRealm
  3195. );
  3196. }
  3197. if (NT_SUCCESS(Status))
  3198. {
  3199. D_DebugLog((DEB_WARN, "Mapping user to MIT principal\n"));
  3200. RealmlessWkstaLogon = TRUE;
  3201. }
  3202. }
  3203. if (WkstaAccount ||
  3204. (KerbGlobalDomainIsPreNT5 && !RealmlessWkstaLogon) ||
  3205. (KerbGlobalSafeModeBootOptionPresent))
  3206. {
  3207. D_DebugLog(( DEB_TRACE, "Local Logon, bailing out now\n" ));
  3208. Status = STATUS_NO_LOGON_SERVERS;
  3209. goto Cleanup ;
  3210. }
  3211. //
  3212. // Now decode the password, if necessary
  3213. //
  3214. if (Seed != 0 ) {
  3215. __try {
  3216. RtlRunDecodeUnicodeString( Seed, &LogonInfo->Password);
  3217. } __except(EXCEPTION_EXECUTE_HANDLER) {
  3218. Status = STATUS_ILL_FORMED_PASSWORD;
  3219. goto Cleanup;
  3220. }
  3221. }
  3222. //
  3223. // Check if the user name holds a cert context thumbprint
  3224. //
  3225. Status = KerbCheckUserNameForCert(
  3226. NULL,
  3227. TRUE,
  3228. &LogonInfo->UserName,
  3229. &CertContext
  3230. );
  3231. if (NT_SUCCESS(Status))
  3232. {
  3233. if (NULL != CertContext)
  3234. {
  3235. fSuppliedCertCred = TRUE;
  3236. }
  3237. else
  3238. {
  3239. //
  3240. // Copy out the user name and Authenticating Authority so we can audit them.
  3241. //
  3242. Status = KerbDuplicateString(
  3243. &TempName,
  3244. &LogonInfo->UserName
  3245. );
  3246. if (!NT_SUCCESS(Status))
  3247. {
  3248. goto Cleanup;
  3249. }
  3250. }
  3251. }
  3252. else
  3253. {
  3254. goto Cleanup;
  3255. }
  3256. if ( LogonInfo->LogonDomainName.Buffer != NULL ) {
  3257. Status = KerbDuplicateString(
  3258. &TempAuthority,
  3259. &LogonInfo->LogonDomainName
  3260. );
  3261. if (!NT_SUCCESS(Status))
  3262. {
  3263. goto Cleanup;
  3264. }
  3265. }
  3266. //
  3267. // Allocate a locally unique ID for this logon session. We will
  3268. // create it in the LSA just before returning.
  3269. //
  3270. Status = NtAllocateLocallyUniqueId( &LogonId );
  3271. if (!NT_SUCCESS(Status))
  3272. {
  3273. D_DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  3274. goto Cleanup;
  3275. }
  3276. if (fSuppliedCertCred)
  3277. {
  3278. //
  3279. // Build a logon session to hold all this information
  3280. // for a smart card logon
  3281. //
  3282. Status = KerbCreateSmartCardLogonSessionFromCertContext(
  3283. &CertContext,
  3284. &LogonId,
  3285. &NullAuthority,
  3286. &LogonInfo->Password,
  3287. NULL, // no CSP data
  3288. 0, // no CSP data
  3289. &LogonSession,
  3290. &TempName
  3291. );
  3292. if (!NT_SUCCESS(Status))
  3293. {
  3294. goto Cleanup;
  3295. }
  3296. }
  3297. else
  3298. {
  3299. //
  3300. // Build a logon session to hold all this information
  3301. //
  3302. Status = KerbCreateLogonSession(
  3303. &LogonId,
  3304. &TempName,
  3305. &TempAuthority,
  3306. &LogonInfo->Password,
  3307. NULL, // no old password
  3308. PRIMARY_CRED_CLEAR_PASSWORD,
  3309. LogonType,
  3310. &LogonSession
  3311. );
  3312. if (!NT_SUCCESS(Status))
  3313. {
  3314. goto Cleanup;
  3315. }
  3316. }
  3317. }
  3318. else if ((LogonInfo->MessageType == KerbSmartCardLogon) ||
  3319. (LogonInfo->MessageType == KerbSmartCardUnlockLogon))
  3320. {
  3321. if (LogonInfo->MessageType == KerbSmartCardUnlockLogon)
  3322. {
  3323. if (SubmitBufferSize < sizeof(KERB_SMART_CARD_UNLOCK_LOGON))
  3324. {
  3325. D_DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
  3326. Status = STATUS_INVALID_PARAMETER;
  3327. goto Cleanup;
  3328. }
  3329. PKERB_SMART_CARD_UNLOCK_LOGON UnlockInfo = (PKERB_SMART_CARD_UNLOCK_LOGON) LogonInfo;
  3330. OldLogonId = UnlockInfo->LogonId;
  3331. DoingUnlock = TRUE;
  3332. }
  3333. Status = KerbCreateSmartCardLogonSession(
  3334. pTempSubmitBuffer,
  3335. ClientBufferBase,
  3336. SubmitBufferSize,
  3337. LogonType,
  3338. &LogonSession,
  3339. &LogonId,
  3340. &TempName,
  3341. &TempAuthority
  3342. );
  3343. if (!NT_SUCCESS(Status))
  3344. {
  3345. goto Cleanup;
  3346. }
  3347. }
  3348. else if ((LogonInfo->MessageType == KerbTicketLogon) ||
  3349. (LogonInfo->MessageType == KerbTicketUnlockLogon))
  3350. {
  3351. if (LogonInfo->MessageType == KerbTicketUnlockLogon)
  3352. {
  3353. if (SubmitBufferSize < sizeof(KERB_TICKET_UNLOCK_LOGON))
  3354. {
  3355. D_DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
  3356. Status = STATUS_INVALID_PARAMETER;
  3357. goto Cleanup;
  3358. }
  3359. PKERB_TICKET_UNLOCK_LOGON UnlockInfo = (PKERB_TICKET_UNLOCK_LOGON) LogonInfo;
  3360. OldLogonId = UnlockInfo->LogonId;
  3361. DoingUnlock = TRUE;
  3362. }
  3363. //
  3364. // Ticket logons *must* have TCB. This prevents arbitrary users from
  3365. // gathering service tickets for given hosts, and presenting them in order
  3366. // to spoof the client of those tickets.
  3367. //
  3368. Status = LsaFunctions->GetClientInfo( &ClientInfo );
  3369. if ( !NT_SUCCESS( Status ))
  3370. {
  3371. goto Cleanup;
  3372. }
  3373. if ( !ClientInfo.HasTcbPrivilege )
  3374. {
  3375. DebugLog((DEB_ERROR, "Calling ticket logon / unlock without TCB\n"));
  3376. Status = STATUS_PRIVILEGE_NOT_HELD;
  3377. goto Cleanup;
  3378. }
  3379. Status = KerbCreateTicketLogonSession(
  3380. pTempSubmitBuffer,
  3381. ClientBufferBase,
  3382. SubmitBufferSize,
  3383. LogonType,
  3384. &LogonSession,
  3385. &LogonId,
  3386. &WorkstationTicket,
  3387. &ForwardedTgt
  3388. );
  3389. if (!NT_SUCCESS(Status))
  3390. {
  3391. goto Cleanup;
  3392. }
  3393. }
  3394. //
  3395. // The S4UToSelf Logon really is quite different
  3396. // than any other form of logon. As such, we're
  3397. // going to branch out into the S4UToSelf protocol
  3398. // code.
  3399. //
  3400. else if (LogonInfo->MessageType == KerbS4ULogon)
  3401. {
  3402. if (SubmitBufferSize < sizeof(KERB_S4U_LOGON))
  3403. {
  3404. D_DebugLog((DEB_ERROR,"Submit buffer to logon too small: %d. %ws, line %d\n",SubmitBufferSize, THIS_FILE, __LINE__));
  3405. Status = STATUS_INVALID_PARAMETER;
  3406. goto Cleanup;
  3407. }
  3408. if ( LogonType != Network )
  3409. {
  3410. D_DebugLog((DEB_ERROR, "LogonType must be network for S4ULogon\n"));
  3411. Status = STATUS_INVALID_LOGON_TYPE;
  3412. goto Cleanup;
  3413. }
  3414. Status = KerbS4UToSelfLogon(
  3415. pTempSubmitBuffer,
  3416. ClientBufferBase,
  3417. SubmitBufferSize,
  3418. &LogonSession,
  3419. &LogonId,
  3420. &WorkstationTicket,
  3421. &S4UClientName,
  3422. &S4UClientRealm
  3423. );
  3424. if (!NT_SUCCESS(Status))
  3425. {
  3426. DebugLog((DEB_ERROR, "KerbS4UToSelfLogon failed - %x\n", Status));
  3427. goto Cleanup;
  3428. }
  3429. }
  3430. else
  3431. {
  3432. D_DebugLog((DEB_ERROR,"Invalid info class to logon: %d. %ws, line %d\n",
  3433. LogonInfo->MessageType, THIS_FILE, __LINE__));
  3434. Status = STATUS_INVALID_INFO_CLASS;
  3435. goto Cleanup;
  3436. }
  3437. break;
  3438. default:
  3439. //
  3440. // No other logon types are supported.
  3441. //
  3442. Status = STATUS_INVALID_LOGON_TYPE;
  3443. D_DebugLog((DEB_ERROR, "Invalid logon type passed to LsaApLogonUserEx2: %d. %ws, line %d\n",LogonType, THIS_FILE, __LINE__));
  3444. goto Cleanup;
  3445. }
  3446. D_DebugLog((DEB_TRACE,"LogonUser: Attempting to logon user %wZ\\%wZ\n",
  3447. &TempAuthority,
  3448. &TempName
  3449. ));
  3450. //
  3451. // If the KDC is not yet started, start it now.
  3452. //
  3453. #ifndef WIN32_CHICAGO
  3454. if ((KerbGlobalRole == KerbRoleDomainController) && !KerbKdcStarted)
  3455. {
  3456. D_DebugLog((DEB_TRACE_LOGON,"Waiting for KDC to start\n"));
  3457. Status = KerbWaitForKdc( KerbGlobalKdcWaitTime );
  3458. if (!NT_SUCCESS(Status))
  3459. {
  3460. D_DebugLog((DEB_TRACE_LOGON,"Failed to wait for KDC to start\n"));
  3461. goto Cleanup;
  3462. }
  3463. }
  3464. #endif // WIN32_CHICAGO
  3465. //
  3466. // If we don't have a workstation ticket already, attempt to get one now.
  3467. //
  3468. // Per bug 94726, if we're not part of an NT domain, then we should not require
  3469. // the workstation ticket to perform the logon successfully.
  3470. if (WorkstationTicket == NULL)
  3471. {
  3472. //
  3473. // Get the initial TGT for the user. This routine figures out the real
  3474. // principal names & realm names
  3475. //
  3476. Status = KerbGetTicketGrantingTicket(
  3477. LogonSession,
  3478. NULL,
  3479. NULL,
  3480. NULL, // no credential
  3481. (Role == KerbRoleRealmlessWksta ? &AsTicket : NULL),
  3482. &CredentialKey
  3483. );
  3484. if ( Status == STATUS_NO_TRUST_SAM_ACCOUNT )
  3485. {
  3486. Status = STATUS_NO_LOGON_SERVERS ;
  3487. }
  3488. if (NT_SUCCESS(Status))
  3489. {
  3490. if (Role != KerbRoleRealmlessWksta) // joined machine
  3491. {
  3492. KerbWriteLockLogonSessions(LogonSession);
  3493. LogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  3494. KerbUnlockLogonSessions(LogonSession);
  3495. //
  3496. // Check to see if the client is from an MIT realm
  3497. //
  3498. KerbGlobalReadLock();
  3499. (VOID) KerbLookupMitRealm(
  3500. &KerbGlobalDnsDomainName,
  3501. &MitRealm,
  3502. &UsedAlternateName
  3503. );
  3504. Status = KerbDuplicateKdcName(
  3505. &MachineServiceName,
  3506. KerbGlobalMitMachineServiceName
  3507. );
  3508. KerbGlobalReleaseLock();
  3509. if (!NT_SUCCESS(Status))
  3510. {
  3511. goto Cleanup;
  3512. }
  3513. Status = KerbGetOurDomainName(
  3514. &DomainName
  3515. );
  3516. if (!NT_SUCCESS(Status))
  3517. {
  3518. goto Cleanup;
  3519. }
  3520. //
  3521. // Now that we have a TGT, we need to build a token. The PAC is currently
  3522. // hidden inside the TGT, so we need to get a ticket to this workstation
  3523. //
  3524. D_DebugLog((DEB_TRACE_LOGON,"Getting outbound ticket to "));
  3525. D_KerbPrintKdcName(DEB_TRACE_LOGON, MachineServiceName );
  3526. Status = KerbGetServiceTicket(
  3527. LogonSession,
  3528. NULL, // no credential
  3529. NULL,
  3530. MachineServiceName,
  3531. &DomainName,
  3532. NULL,
  3533. ProcessFlags,
  3534. 0, // no options
  3535. 0, // no enc type
  3536. NULL, // no error message
  3537. NULL, // no authorization data
  3538. NULL, // no TGT reply
  3539. &WorkstationTicket,
  3540. &LogonGuid
  3541. );
  3542. if (!NT_SUCCESS(Status))
  3543. {
  3544. DebugLog((DEB_ERROR,"LogonUser: Failed to get workstation ticket for %wZ\\%wZ: 0x%x. %ws, line %d\n",
  3545. &TempAuthority, &TempName, Status, THIS_FILE, __LINE__ ));
  3546. goto Cleanup;
  3547. }
  3548. }
  3549. else
  3550. {
  3551. D_DebugLog((DEB_ERROR, "Nonjoined workstation\n"));
  3552. DsysAssert(AsTicket != NULL);
  3553. DsysAssert(MappedClientName.Buffer != NULL);
  3554. Status = KerbCheckRealmlessLogonPolicy(
  3555. AsTicket,
  3556. ClientName,
  3557. &ClientRealm
  3558. );
  3559. if (!NT_SUCCESS(Status))
  3560. {
  3561. D_DebugLog((DEB_ERROR, "LogonUser: Failed check for domainless logon\n"));
  3562. goto Cleanup;
  3563. }
  3564. }
  3565. }
  3566. else
  3567. {
  3568. DebugLog((DEB_WARN, "LogonUser: Failed to get TGT for %wZ\\%wZ : 0x%x\n",
  3569. &TempAuthority,
  3570. &TempName,
  3571. Status ));
  3572. //
  3573. // If this was a smart card logon, try logging on locally for
  3574. // non-definitive errors:
  3575. //
  3576. if ( ( (LogonInfo->MessageType == KerbSmartCardLogon ) ||
  3577. (LogonInfo->MessageType == KerbSmartCardUnlockLogon ) ) &&
  3578. ( ( Status == STATUS_NO_LOGON_SERVERS ) ||
  3579. ( Status == STATUS_NETWORK_UNREACHABLE ) ||// From DsGetdcName
  3580. ( Status == STATUS_NETLOGON_NOT_STARTED ) ) )
  3581. {
  3582. Status = KerbDoLocalSmartCardLogon(
  3583. LogonSession,
  3584. TokenInformationType,
  3585. TokenInformation,
  3586. ProfileBufferLength,
  3587. ProfileBuffer,
  3588. PrimaryCredentials,
  3589. CachedCredentials,
  3590. &SCValidationInfo
  3591. );
  3592. if (!NT_SUCCESS(Status))
  3593. {
  3594. DebugLog((DEB_ERROR,"Failed smart card local logon: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  3595. goto Cleanup;
  3596. }
  3597. LocalLogon = TRUE;
  3598. }
  3599. else
  3600. {
  3601. //
  3602. // Not smart card
  3603. //
  3604. goto Cleanup;
  3605. }
  3606. }
  3607. }
  3608. //
  3609. // If we didn't already build a ticket, do so now
  3610. //
  3611. if (!LocalLogon)
  3612. {
  3613. Status = KerbCreateTokenFromLogonTicket(
  3614. WorkstationTicket,
  3615. &LogonId,
  3616. LogonInfo,
  3617. (LogonType == Interactive || LogonType == Service || LogonType == Batch || LogonType == RemoteInteractive ) ? TRUE : FALSE, // cache interactive logons only
  3618. RealmlessWkstaLogon,
  3619. &CredentialKey,
  3620. &ForwardedTgt,
  3621. &MappedClientName,
  3622. S4UClientName,
  3623. &S4UClientRealm,
  3624. LogonSession,
  3625. TokenInformationType,
  3626. TokenInformation,
  3627. ProfileBufferLength,
  3628. ProfileBuffer,
  3629. PrimaryCredentials,
  3630. CachedCredentials,
  3631. &ValidationInfo
  3632. );
  3633. if (!NT_SUCCESS(Status))
  3634. {
  3635. DebugLog((DEB_WARN,"LogonUser: Failed to create token from ticket: 0x%x\n",Status));
  3636. goto Cleanup;
  3637. }
  3638. //
  3639. // Cache the sid, if we are caching - meaning this is an
  3640. // interactive logon to an NT domain and we have a sid
  3641. //
  3642. if (( (LogonType == Interactive) || (LogonType == RemoteInteractive )) &&
  3643. (PrimaryCredentials != NULL) &&
  3644. (PrimaryCredentials->UserSid != NULL) &&
  3645. (MitRealm == NULL) && !RealmlessWkstaLogon
  3646. )
  3647. {
  3648. //
  3649. // Store the pertinent info in the cache.
  3650. //
  3651. KerbReadLockLogonSessions(LogonSession);
  3652. KerbCacheLogonSid(
  3653. &TempName,
  3654. &TempAuthority,
  3655. &LogonSession->PrimaryCredentials.DomainName,
  3656. PrimaryCredentials->UserSid
  3657. );
  3658. KerbUnlockLogonSessions(LogonSession);
  3659. }
  3660. //
  3661. // Add the password to the primary credentials.
  3662. //
  3663. if (LogonInfo->MessageType == KerbInteractiveLogon)
  3664. {
  3665. Status = KerbDuplicateString(
  3666. &PrimaryCredentials->Password,
  3667. &LogonInfo->Password
  3668. );
  3669. if (!NT_SUCCESS(Status))
  3670. {
  3671. goto Cleanup;
  3672. }
  3673. PrimaryCredentials->Flags |= PRIMARY_CRED_CLEAR_PASSWORD;
  3674. }
  3675. }
  3676. //
  3677. // Get the final doamin name and user name out of the logon session,
  3678. // if it is different than the one used for logon.
  3679. //
  3680. KerbReadLockLogonSessions(LogonSession);
  3681. if (!RtlEqualUnicodeString(
  3682. &TempName,
  3683. &PrimaryCredentials->DownlevelName,
  3684. TRUE))
  3685. {
  3686. KerbFreeString(&TempName);
  3687. Status = KerbDuplicateString(
  3688. &TempName,
  3689. &PrimaryCredentials->DownlevelName
  3690. );
  3691. if (!NT_SUCCESS(Status))
  3692. {
  3693. KerbUnlockLogonSessions(LogonSession);
  3694. goto Cleanup;
  3695. }
  3696. }
  3697. if (!RtlEqualDomainName(
  3698. &TempAuthority,
  3699. &PrimaryCredentials->DomainName
  3700. ))
  3701. {
  3702. KerbFreeString(&TempAuthority);
  3703. Status = KerbDuplicateString(
  3704. &TempAuthority,
  3705. &PrimaryCredentials->DomainName
  3706. );
  3707. if (!NT_SUCCESS(Status))
  3708. {
  3709. KerbUnlockLogonSessions(LogonSession);
  3710. goto Cleanup;
  3711. }
  3712. }
  3713. KerbUnlockLogonSessions(LogonSession);
  3714. //
  3715. // Finally create the logon session in the LSA
  3716. //
  3717. Status = LsaFunctions->CreateLogonSession( &LogonId );
  3718. if (!NT_SUCCESS(Status))
  3719. {
  3720. D_DebugLog((DEB_ERROR,"Failed to create logon session: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  3721. goto Cleanup;
  3722. }
  3723. //
  3724. // Add additional names to the logon session name map. Ignore failure
  3725. // as that just means GetUserNameEx calls for these name formats later
  3726. // on will be satisfied by hitting the wire.
  3727. //
  3728. if (ValidationInfo || SCValidationInfo)
  3729. {
  3730. PNETLOGON_VALIDATION_SAM_INFO4 Tmp = (ValidationInfo ? ValidationInfo : SCValidationInfo);
  3731. if (Tmp->FullName.Length)
  3732. {
  3733. I_LsaIAddNameToLogonSession(&LogonId, NameDisplay, &Tmp->FullName);
  3734. }
  3735. //
  3736. // Smart cards use a "special" UPN for the logon cache, so don't
  3737. // use it here, as the cached logon will provide us w/ bogus data...
  3738. //
  3739. /*if (ValidationInfo && ValidationInfo->Upn.Length)
  3740. {
  3741. I_LsaIAddNameToLogonSession(&LogonId, NameUserPrincipal, &ValidationInfo->Upn);
  3742. }*/
  3743. if (Tmp->DnsLogonDomainName.Length)
  3744. {
  3745. I_LsaIAddNameToLogonSession(&LogonId, NameDnsDomain, &Tmp->DnsLogonDomainName);
  3746. }
  3747. }
  3748. I_LsaISetLogonGuidInLogonSession(&LogonId, &LogonGuid);
  3749. //
  3750. // If this was an unlock operation, copy the authentication ticket
  3751. // cache into the original logon session.
  3752. //
  3753. if (DoingUnlock)
  3754. {
  3755. KerbUpdateOldLogonSession(
  3756. LogonSession,
  3757. &OldLogonId,
  3758. WorkstationTicket
  3759. );
  3760. }
  3761. *NewLogonId = LogonId;
  3762. Cleanup:
  3763. //
  3764. // This is a "fake" Info4 struct... Free UPN and dnsdomain
  3765. // name manually -- normally a giant struct alloc'd by RPC
  3766. //
  3767. if (ValidationInfo)
  3768. {
  3769. KerbFreeString(&ValidationInfo->DnsLogonDomainName);
  3770. KerbFreeString(&ValidationInfo->Upn);
  3771. KerbFreeString(&ValidationInfo->FullName);
  3772. KerbFree(ValidationInfo);
  3773. }
  3774. if (SCValidationInfo)
  3775. {
  3776. LocalFree(ValidationInfo); // this was alloc'd by cache lookup.
  3777. }
  3778. KerbFreeString( &CredmanUserName );
  3779. KerbFreeString( &CredmanDomainName );
  3780. KerbFreeString( &CredmanPassword );
  3781. if (CertContext != NULL)
  3782. {
  3783. CertFreeCertificateContext(CertContext);
  3784. }
  3785. //
  3786. // Restore the saved password
  3787. //
  3788. if ( ServiceSecretLogon ) {
  3789. RtlCopyMemory( &LogonInfo->Password,
  3790. &SavedPassword,
  3791. sizeof( UNICODE_STRING ) );
  3792. //
  3793. // Free the secret value we read...
  3794. //
  3795. LsaIFree_LSAPR_CR_CIPHER_VALUE( SecretCurrent );
  3796. }
  3797. KerbFreeString( &CorrectRealm );
  3798. KerbFreeKey(&CredentialKey);
  3799. if (*AccountName != NULL)
  3800. {
  3801. **AccountName = TempName;
  3802. TempName.Buffer = NULL;
  3803. }
  3804. if (*AuthenticatingAuthority != NULL)
  3805. {
  3806. **AuthenticatingAuthority = TempAuthority;
  3807. TempAuthority.Buffer = NULL;
  3808. }
  3809. if (!NT_SUCCESS(Status))
  3810. {
  3811. //
  3812. // Unlink the new logon session
  3813. //
  3814. if (LogonSession != NULL)
  3815. {
  3816. KerbReferenceLogonSessionByPointer(LogonSession, TRUE);
  3817. KerbDereferenceLogonSession(LogonSession);
  3818. }
  3819. if (*ProfileBuffer != NULL)
  3820. {
  3821. LsaFunctions->FreeClientBuffer(NULL, *ProfileBuffer);
  3822. *ProfileBuffer = NULL;
  3823. }
  3824. if (*CachedCredentials != NULL)
  3825. {
  3826. KerbFree(*CachedCredentials);
  3827. *CachedCredentials = NULL;
  3828. }
  3829. //
  3830. // Map status codes to prevent specific information from being
  3831. // released about this user.
  3832. //
  3833. switch (Status) {
  3834. case STATUS_WRONG_PASSWORD:
  3835. case STATUS_NO_SUCH_USER:
  3836. case STATUS_PKINIT_FAILURE:
  3837. case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
  3838. case STATUS_SMARTCARD_WRONG_PIN:
  3839. case STATUS_SMARTCARD_CARD_BLOCKED:
  3840. case STATUS_SMARTCARD_NO_CARD:
  3841. case STATUS_SMARTCARD_NO_KEY_CONTAINER:
  3842. case STATUS_SMARTCARD_NO_CERTIFICATE:
  3843. case STATUS_SMARTCARD_NO_KEYSET:
  3844. case STATUS_SMARTCARD_IO_ERROR:
  3845. case STATUS_SMARTCARD_CERT_REVOKED:
  3846. case STATUS_SMARTCARD_CERT_EXPIRED:
  3847. case STATUS_REVOCATION_OFFLINE_C:
  3848. case STATUS_PKINIT_CLIENT_FAILURE:
  3849. //
  3850. // sleep 3 seconds to "discourage" dictionary attacks.
  3851. // Don't worry about interactive logon dictionary attacks.
  3852. // They will be slow anyway.
  3853. //
  3854. // per bug 171041, SField, RichardW, CliffV all decided this
  3855. // delay has almost zero value for Win2000. Offline attacks at
  3856. // sniffed wire traffic are more efficient and viable. Further,
  3857. // opimizations in logon code path make failed interactive logons.
  3858. // very fast.
  3859. //
  3860. //
  3861. // if (LogonType != Interactive) {
  3862. // Sleep( 3000 );
  3863. // }
  3864. //
  3865. // This is for auditing. Make sure to clear it out before
  3866. // passing it out of LSA to the caller.
  3867. //
  3868. *ApiSubStatus = Status;
  3869. Status = STATUS_LOGON_FAILURE;
  3870. break;
  3871. case STATUS_INVALID_LOGON_HOURS:
  3872. case STATUS_INVALID_WORKSTATION:
  3873. case STATUS_PASSWORD_EXPIRED:
  3874. case STATUS_ACCOUNT_DISABLED:
  3875. case STATUS_SMARTCARD_LOGON_REQUIRED:
  3876. *ApiSubStatus = Status;
  3877. Status = STATUS_ACCOUNT_RESTRICTION;
  3878. break;
  3879. //
  3880. // This shouldn't happen, but guard against it anyway.
  3881. //
  3882. case STATUS_ACCOUNT_RESTRICTION:
  3883. *ApiSubStatus = STATUS_ACCOUNT_RESTRICTION;
  3884. break;
  3885. case STATUS_ACCOUNT_EXPIRED: // fix 122291
  3886. *ApiSubStatus = STATUS_ACCOUNT_EXPIRED;
  3887. break;
  3888. default:
  3889. break;
  3890. }
  3891. if (*TokenInformation != NULL)
  3892. {
  3893. KerbFree( *TokenInformation );
  3894. *TokenInformation = NULL;
  3895. }
  3896. KerbFreeString(
  3897. &PrimaryCredentials->DownlevelName
  3898. );
  3899. KerbFreeString(
  3900. &PrimaryCredentials->DomainName
  3901. );
  3902. KerbFreeString(
  3903. &PrimaryCredentials->LogonServer
  3904. );
  3905. KerbFreeString(
  3906. &PrimaryCredentials->Password
  3907. );
  3908. if (PrimaryCredentials->UserSid != NULL)
  3909. {
  3910. KerbFree(PrimaryCredentials->UserSid);
  3911. PrimaryCredentials->UserSid = NULL;
  3912. }
  3913. RtlZeroMemory(
  3914. PrimaryCredentials,
  3915. sizeof(SECPKG_PRIMARY_CRED)
  3916. );
  3917. }
  3918. if (WorkstationTicket != NULL)
  3919. {
  3920. KerbDereferenceTicketCacheEntry( WorkstationTicket );
  3921. }
  3922. if (AsTicket != NULL)
  3923. {
  3924. KerbDereferenceTicketCacheEntry( AsTicket );
  3925. }
  3926. if (LogonSession != NULL)
  3927. {
  3928. KerbDereferenceLogonSession(LogonSession);
  3929. }
  3930. KerbFreeKdcName(&ClientName);
  3931. KerbFreeString(&ClientRealm);
  3932. KerbFreeString(
  3933. &DomainName
  3934. );
  3935. KerbFreeString(
  3936. &MappedClientName
  3937. );
  3938. KerbFreeString(
  3939. &TempName
  3940. );
  3941. KerbFreeString(
  3942. &TempAuthority
  3943. );
  3944. KerbFreeKdcName(
  3945. &MachineServiceName
  3946. );
  3947. KerbFreeString(
  3948. &S4UClientRealm
  3949. );
  3950. // Allocate the machine name here. Lsa will free it after auditing is
  3951. // done
  3952. *MachineName = (PUNICODE_STRING) KerbAllocate( sizeof( UNICODE_STRING ) );
  3953. if ( *MachineName != NULL )
  3954. {
  3955. NTSTATUS TempStatus;
  3956. KerbGlobalReadLock();
  3957. TempStatus = KerbDuplicateString (*MachineName, &KerbGlobalMachineName);
  3958. KerbGlobalReleaseLock();
  3959. if(!NT_SUCCESS(TempStatus))
  3960. {
  3961. D_DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalMachineName\n"));
  3962. ZeroMemory( *MachineName, sizeof(UNICODE_STRING) );
  3963. }
  3964. }
  3965. if( KerbEventTraceFlag ) // Event Trace: KerbLogonUserEnd {Status, (LogonType), (Username), (Domain)}
  3966. {
  3967. INSERT_ULONG_INTO_MOF( *ApiSubStatus, LogonTraceInfo.MofData, 0 );
  3968. LogonTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + sizeof(MOF_FIELD);
  3969. if( LogonInfo != NULL )
  3970. {
  3971. UNICODE_STRING KerbLogonTypeString;
  3972. PCWSTR KerbLogonTypeTable[] = // see enum _KERB_LOGON_SUBMIT_TYPE
  3973. { L"",L"",
  3974. L"KerbInteractiveLogon", // = 2
  3975. L"",L"",L"",
  3976. L"KerbSmartCardLogon", // = 6
  3977. L"KerbWorkstationUnlockLogon",
  3978. L"KerbSmartCardUnlockLogon",
  3979. L"KerbProxyLogon",
  3980. L"KerbTicketLogon",
  3981. L"KerbTicketUnlockLogon"
  3982. };
  3983. RtlInitUnicodeString( &KerbLogonTypeString, KerbLogonTypeTable[LogonInfo->MessageType] );
  3984. INSERT_UNICODE_STRING_INTO_MOF( KerbLogonTypeString, LogonTraceInfo.MofData, 1 );
  3985. INSERT_UNICODE_STRING_INTO_MOF( LogonInfo->UserName, LogonTraceInfo.MofData, 3 );
  3986. INSERT_UNICODE_STRING_INTO_MOF( LogonInfo->LogonDomainName, LogonTraceInfo.MofData, 5 );
  3987. LogonTraceInfo.EventTrace.Size += 6*sizeof(MOF_FIELD);
  3988. }
  3989. // Set trace parameters
  3990. LogonTraceInfo.EventTrace.Guid = KerbLogonGuid;
  3991. LogonTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END;
  3992. LogonTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
  3993. TraceEvent(
  3994. KerbTraceLoggerHandle,
  3995. (PEVENT_TRACE_HEADER)&LogonTraceInfo
  3996. );
  3997. }
  3998. #if _WIN64
  3999. if (fAllocatedSubmitBuffer)
  4000. {
  4001. KerbFree(pTempSubmitBuffer);
  4002. }
  4003. #endif // _WIN64
  4004. return(Status);
  4005. }
  4006. #endif // WIN32_CHICAGO // later
  4007. //+-------------------------------------------------------------------------
  4008. //
  4009. // Function: LsaApLogonTerminated
  4010. //
  4011. // Synopsis: This routine is called whenever a logon session terminates
  4012. // (the last token for it is closed). It dereferences the
  4013. // logon session (if it exists), which may cause any
  4014. // associated credentials or contexts to be run down.
  4015. //
  4016. // Effects:
  4017. //
  4018. // Arguments:
  4019. //
  4020. // Requires:
  4021. //
  4022. // Returns:
  4023. //
  4024. // Notes:
  4025. //
  4026. //
  4027. //--------------------------------------------------------------------------
  4028. VOID
  4029. LsaApLogonTerminated(
  4030. IN PLUID LogonId
  4031. )
  4032. {
  4033. PKERB_LOGON_SESSION LogonSession;
  4034. D_DebugLog((DEB_TRACE_API, "LsaApLogonTerminated called: 0x%x:0x%x\n",
  4035. LogonId->HighPart, LogonId->LowPart ));
  4036. LogonSession = KerbReferenceLogonSession(
  4037. LogonId,
  4038. TRUE // unlink logon session
  4039. );
  4040. if (LogonSession != NULL)
  4041. {
  4042. KerbDereferenceLogonSession(LogonSession);
  4043. }
  4044. return;
  4045. }
  4046. #ifdef WIN32_CHICAGO
  4047. //+-------------------------------------------------------------------------
  4048. //
  4049. // Function: KerbSspiLogonUser
  4050. //
  4051. // Synopsis: Handles service, batch, and interactive logons
  4052. //
  4053. // Effects:
  4054. //
  4055. // Arguments:
  4056. //
  4057. // Requires:
  4058. //
  4059. // Returns:
  4060. //
  4061. // Notes:
  4062. //
  4063. //
  4064. //--------------------------------------------------------------------------
  4065. SECURITY_STATUS
  4066. KerbSspiLogonUser(
  4067. IN LPTSTR PackageName,
  4068. IN LPTSTR UserName,
  4069. IN LPTSTR DomainName,
  4070. IN LPTSTR Password
  4071. )
  4072. {
  4073. NTSTATUS Status = STATUS_SUCCESS;
  4074. PKERB_LOGON_SESSION LogonSession = NULL;
  4075. UNICODE_STRING TempName = NULL_UNICODE_STRING;
  4076. UNICODE_STRING TempAuthority = NULL_UNICODE_STRING;
  4077. UNICODE_STRING TempPassword = NULL_UNICODE_STRING;
  4078. UNICODE_STRING KdcServiceName = NULL_UNICODE_STRING;
  4079. PKERB_INTERNAL_NAME KdcServiceKdcName = NULL;
  4080. LUID LogonId;
  4081. PKERB_MIT_REALM MitRealm = NULL;
  4082. BOOLEAN UsedAlternateName = NULL;
  4083. //
  4084. // First initialize all the output parameters to NULL.
  4085. //
  4086. Status = STATUS_SUCCESS;
  4087. KdcServiceName.Buffer = NULL;
  4088. //
  4089. // Initialize local pointers to NULL
  4090. //
  4091. LogonId.LowPart = 0;
  4092. LogonId.HighPart = 0;
  4093. //
  4094. // Copy out the user name and Authenticating Authority so we can audit them.
  4095. //
  4096. // NOTE - Do we need to enforce username & password restrictions here
  4097. // or will the redir do the job when we setuid to it?
  4098. if ( UserName != NULL ) {
  4099. Status = RtlCreateUnicodeStringFromAsciiz(
  4100. &TempName,
  4101. UserName);
  4102. if (!NT_SUCCESS(Status))
  4103. {
  4104. goto Cleanup;
  4105. }
  4106. }
  4107. if (!NT_SUCCESS(Status))
  4108. {
  4109. goto Cleanup;
  4110. }
  4111. if ( DomainName != NULL ) {
  4112. Status = RtlCreateUnicodeStringFromAsciiz(
  4113. &TempAuthority,
  4114. DomainName);
  4115. if (!NT_SUCCESS(Status))
  4116. {
  4117. goto Cleanup;
  4118. }
  4119. }
  4120. if ( Password != NULL ) {
  4121. Status = RtlCreateUnicodeStringFromAsciiz(
  4122. &TempPassword,
  4123. Password);
  4124. if (!NT_SUCCESS(Status))
  4125. {
  4126. goto Cleanup;
  4127. }
  4128. }
  4129. //
  4130. // Allocate a locally unique ID for this logon session. We will
  4131. // create it in the LSA just before returning.
  4132. //
  4133. Status = NtAllocateLocallyUniqueId( &LogonId );
  4134. if (!NT_SUCCESS(Status))
  4135. {
  4136. D_DebugLog((DEB_ERROR,"Failed to allocate locally unique ID: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
  4137. goto Cleanup;
  4138. }
  4139. //
  4140. // Check to see if the client is from an MIT realm
  4141. //
  4142. (VOID) KerbLookupMitRealm(
  4143. &TempAuthority,
  4144. &MitRealm,
  4145. &UsedAlternateName
  4146. );
  4147. if ((MitRealm != NULL) && UsedAlternateName)
  4148. {
  4149. KerbFreeString(&TempAuthority);
  4150. Status = KerbDuplicateString(
  4151. &TempAuthority,
  4152. &MitRealm->RealmName
  4153. );
  4154. if (!NT_SUCCESS(Status))
  4155. {
  4156. goto Cleanup;
  4157. }
  4158. }
  4159. // For win95, if there is a logon session in our list, remove it.
  4160. // This is generated from the logon session dumped in the registry.
  4161. // But, we are about to do a new logon. Get rid of the old logon.
  4162. // If the new one does not succeed, too bad. But, that's by design.
  4163. LsaApLogonTerminated(&LogonId);
  4164. //
  4165. // Build a logon session to hold all this information
  4166. //
  4167. Status = KerbCreateLogonSession(
  4168. &LogonId,
  4169. &TempName,
  4170. &TempAuthority,
  4171. &TempPassword,
  4172. NULL, // no old password
  4173. PRIMARY_CRED_CLEAR_PASSWORD,
  4174. Network,
  4175. &LogonSession
  4176. );
  4177. if (!NT_SUCCESS(Status))
  4178. {
  4179. goto Cleanup;
  4180. }
  4181. D_DebugLog((DEB_TRACE,"LogonUser: Attempting to logon user %wZ\\%wZ\n",
  4182. &TempAuthority,
  4183. &TempName
  4184. ));
  4185. //
  4186. // Now the real work of logon begins - getting a TGT and then a ticket
  4187. // to this machine.
  4188. //
  4189. if (!KERB_SUCCESS(KerbBuildFullServiceKdcName(
  4190. &TempAuthority,
  4191. &KerbGlobalKdcServiceName,
  4192. KRB_NT_MS_PRINCIPAL,
  4193. &KdcServiceKdcName
  4194. )))
  4195. {
  4196. Status = STATUS_INSUFFICIENT_RESOURCES;
  4197. goto Cleanup;
  4198. }
  4199. Status = KerbGetTicketGrantingTicket(
  4200. LogonSession,
  4201. NULL,
  4202. NULL,
  4203. NULL, // no credential
  4204. NULL, // don't return ticket cache entry
  4205. NULL // don't return credential key
  4206. );
  4207. if (!NT_SUCCESS(Status))
  4208. {
  4209. DebugLog((DEB_WARN, "LogonUser: Failed to get authentication ticket for %wZ\\%wZ to %wZ: 0x%x\n",
  4210. &TempAuthority,
  4211. &TempName,
  4212. &KdcServiceName,
  4213. Status ));
  4214. goto Cleanup;
  4215. }
  4216. KerbWriteLockLogonSessions(LogonSession);
  4217. LogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  4218. KerbUnlockLogonSessions(LogonSession);
  4219. Cleanup:
  4220. if (!NT_SUCCESS(Status))
  4221. {
  4222. //
  4223. // Unlink the new logon session
  4224. //
  4225. if (LogonSession != NULL)
  4226. {
  4227. KerbReferenceLogonSessionByPointer(LogonSession, TRUE);
  4228. KerbDereferenceLogonSession(LogonSession);
  4229. }
  4230. //
  4231. // Map status codes to prevent specific information from being
  4232. // released about this user.
  4233. //
  4234. switch (Status) {
  4235. case STATUS_WRONG_PASSWORD:
  4236. case STATUS_NO_SUCH_USER:
  4237. //
  4238. // This is for auditing. Make sure to clear it out before
  4239. // passing it out of LSA to the caller.
  4240. //
  4241. Status = STATUS_LOGON_FAILURE;
  4242. break;
  4243. case STATUS_INVALID_LOGON_HOURS:
  4244. case STATUS_INVALID_WORKSTATION:
  4245. case STATUS_PASSWORD_EXPIRED:
  4246. case STATUS_ACCOUNT_DISABLED:
  4247. case STATUS_ACCOUNT_EXPIRED:
  4248. Status = STATUS_ACCOUNT_RESTRICTION;
  4249. break;
  4250. //
  4251. // This shouldn't happen, but guard against it anyway.
  4252. //
  4253. case STATUS_ACCOUNT_RESTRICTION:
  4254. Status = STATUS_ACCOUNT_RESTRICTION;
  4255. break;
  4256. default:
  4257. break;
  4258. }
  4259. }
  4260. if (LogonSession != NULL)
  4261. {
  4262. KerbDereferenceLogonSession(LogonSession);
  4263. }
  4264. KerbFreeString(
  4265. &TempName
  4266. );
  4267. KerbFreeString(
  4268. &TempAuthority
  4269. );
  4270. KerbFreeString(
  4271. &KdcServiceName
  4272. );
  4273. KerbFreeKdcName( &KdcServiceKdcName );
  4274. D_DebugLog((DEB_TRACE, "SspiLogonUser: returns 0x%x\n", Status));
  4275. return(Status);
  4276. }
  4277. #endif // WIN32_CHICAGO