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

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