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.

2433 lines
58 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ntlmsspv2.c
  5. Abstract:
  6. NTLM v2 specific modules
  7. Author:
  8. Larry Zhu (LZhu) 29-August-2001
  9. Environment: User Mode
  10. Revision History:
  11. --*/
  12. #ifdef MAC
  13. #ifdef SSP_TARGET_CARBON
  14. #include <Carbon/Carbon.h>
  15. #endif
  16. #include <ntlmsspv2.h>
  17. #include <ntlmsspi.h>
  18. #include <ntlmssp.h>
  19. #include <ntstatus.h>
  20. #include <winerror.h>
  21. #include <crypt.h>
  22. #include "debug.h"
  23. #include "macunicode.h"
  24. #endif //MAC
  25. SECURITY_STATUS
  26. SspNtStatusToSecStatus(
  27. IN NTSTATUS NtStatus,
  28. IN SECURITY_STATUS DefaultStatus
  29. )
  30. /*++
  31. Routine Description:
  32. Convert an NtStatus code to the corresponding Security status code. For
  33. particular errors that are required to be returned as is (for setup code)
  34. don't map the errors.
  35. Arguments:
  36. NtStatus - NT status to convert
  37. DefaultStatus - default security status if NtStatus is not mapped
  38. Return Value:
  39. Returns security status code.
  40. --*/
  41. {
  42. SECURITY_STATUS SecStatus;
  43. //
  44. // Check for security status and let them through
  45. //
  46. if (HRESULT_FACILITY(NtStatus) == FACILITY_SECURITY)
  47. {
  48. return (NtStatus);
  49. }
  50. switch (NtStatus)
  51. {
  52. case STATUS_SUCCESS:
  53. SecStatus = SEC_E_OK;
  54. break;
  55. case STATUS_NO_MEMORY:
  56. case STATUS_INSUFFICIENT_RESOURCES:
  57. SecStatus = SEC_E_INSUFFICIENT_MEMORY;
  58. break;
  59. case STATUS_NETLOGON_NOT_STARTED:
  60. case STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
  61. case STATUS_NO_LOGON_SERVERS:
  62. case STATUS_NO_SUCH_DOMAIN:
  63. case STATUS_BAD_NETWORK_PATH:
  64. case STATUS_TRUST_FAILURE:
  65. case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  66. case STATUS_NETWORK_UNREACHABLE:
  67. SecStatus = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  68. break;
  69. case STATUS_NO_SUCH_LOGON_SESSION:
  70. SecStatus = SEC_E_UNKNOWN_CREDENTIALS;
  71. break;
  72. case STATUS_INVALID_PARAMETER:
  73. case STATUS_PARTIAL_COPY:
  74. SecStatus = SEC_E_INVALID_TOKEN;
  75. break;
  76. case STATUS_PRIVILEGE_NOT_HELD:
  77. SecStatus = SEC_E_NOT_OWNER;
  78. break;
  79. case STATUS_INVALID_HANDLE:
  80. SecStatus = SEC_E_INVALID_HANDLE;
  81. break;
  82. case STATUS_BUFFER_TOO_SMALL:
  83. SecStatus = SEC_E_BUFFER_TOO_SMALL;
  84. break;
  85. case STATUS_NOT_SUPPORTED:
  86. SecStatus = SEC_E_UNSUPPORTED_FUNCTION;
  87. break;
  88. case STATUS_OBJECT_NAME_NOT_FOUND:
  89. case STATUS_NO_TRUST_SAM_ACCOUNT:
  90. SecStatus = SEC_E_TARGET_UNKNOWN;
  91. break;
  92. case STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT:
  93. case STATUS_NOLOGON_SERVER_TRUST_ACCOUNT:
  94. case STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
  95. case STATUS_TRUSTED_DOMAIN_FAILURE:
  96. SecStatus = NtStatus;
  97. break;
  98. case STATUS_LOGON_FAILURE:
  99. case STATUS_NO_SUCH_USER:
  100. case STATUS_ACCOUNT_DISABLED:
  101. case STATUS_ACCOUNT_RESTRICTION:
  102. case STATUS_ACCOUNT_LOCKED_OUT:
  103. case STATUS_WRONG_PASSWORD:
  104. case STATUS_ACCOUNT_EXPIRED:
  105. case STATUS_PASSWORD_EXPIRED:
  106. case STATUS_PASSWORD_MUST_CHANGE:
  107. case STATUS_LOGON_TYPE_NOT_GRANTED:
  108. SecStatus = SEC_E_LOGON_DENIED;
  109. break;
  110. case STATUS_NAME_TOO_LONG:
  111. case STATUS_ILL_FORMED_PASSWORD:
  112. SecStatus = SEC_E_INVALID_TOKEN;
  113. break;
  114. case STATUS_TIME_DIFFERENCE_AT_DC:
  115. SecStatus = SEC_E_TIME_SKEW;
  116. break;
  117. case STATUS_SHUTDOWN_IN_PROGRESS:
  118. SecStatus = SEC_E_SHUTDOWN_IN_PROGRESS;
  119. break;
  120. case STATUS_INTERNAL_ERROR:
  121. SecStatus = SEC_E_INTERNAL_ERROR;
  122. ASSERT(FALSE);
  123. break;
  124. default:
  125. SecStatus = DefaultStatus;
  126. break;
  127. }
  128. return (SecStatus);
  129. }
  130. NTSTATUS
  131. SspInitUnicodeStringNoAlloc(
  132. IN PCSTR pszSource,
  133. IN OUT UNICODE_STRING* pDestination
  134. )
  135. /*++
  136. Routine Description:
  137. Initialize unicode string. This routine does not allocate memory.
  138. Arguments:
  139. pszSource - source string
  140. pDestination - unicode string
  141. Return Value:
  142. NTSTATUS
  143. --*/
  144. {
  145. #ifndef __MACSSP__
  146. STRING OemString;
  147. RtlInitString(&OemString, pszSource);
  148. return SspOemStringToUnicodeString(pDestination, &OemString, FALSE);
  149. #else
  150. UniCharArrayPtr unicodeString = NULL;
  151. OSStatus Status = noErr;
  152. Status = MacSspCStringToUnicode(pszSource, &pDestination->Length, &unicodeString);
  153. if (NT_SUCCESS(Status))
  154. {
  155. _fmemcpy(pDestination->Buffer, unicodeString, pDestination->Length);
  156. }
  157. return(Status);
  158. #endif
  159. }
  160. VOID
  161. SspFreeStringEx(
  162. IN OUT STRING* pString
  163. )
  164. /*++
  165. Routine Description:
  166. Free string.
  167. Arguments:
  168. pString - string to free
  169. Return Value:
  170. none
  171. --*/
  172. {
  173. if (pString->MaximumLength && pString->Buffer)
  174. {
  175. SspFree(pString->Buffer);
  176. pString->MaximumLength = pString->Length = 0;
  177. pString->Buffer = NULL;
  178. }
  179. }
  180. VOID
  181. SspFreeUnicodeString(
  182. IN OUT UNICODE_STRING* pUnicodeString
  183. )
  184. /*++
  185. Routine Description:
  186. Free unicode string.
  187. Arguments:
  188. pUnicodeString - unicode string to free
  189. Return Value:
  190. none
  191. --*/
  192. {
  193. SspFreeStringEx((STRING *) pUnicodeString);
  194. }
  195. #ifdef MAC
  196. VOID
  197. SspSwapUnicodeString(
  198. IN OUT UNICODE_STRING* pString
  199. )
  200. /*++
  201. Routine Description:
  202. Reverse the alignment of each word in the unicode string. Needed
  203. for Macintosh only.
  204. Arguments:
  205. pString - The string to modify.
  206. Return Value:
  207. None
  208. --*/
  209. {
  210. if (pString->Length)
  211. {
  212. //
  213. //For mac's we need to reverse each word in the unicode string.
  214. //
  215. USHORT i;
  216. for (i = 0; i < (pString->Length/sizeof(USHORT)); i++)
  217. {
  218. swapshort(pString->Buffer[i]);
  219. }
  220. }
  221. }
  222. #endif
  223. NTSTATUS
  224. SsprHandleNtlmv2ChallengeMessage(
  225. IN SSP_CREDENTIAL* pCredential,
  226. IN ULONG cbChallengeMessage,
  227. IN CHALLENGE_MESSAGE* pChallengeMessage,
  228. IN OUT ULONG* pNegotiateFlags,
  229. IN OUT ULONG* pcbAuthenticateMessage,
  230. OUT AUTHENTICATE_MESSAGE* pAuthenticateMessage,
  231. OUT USER_SESSION_KEY* pContextSessionKey
  232. )
  233. /*++
  234. Routine Description:
  235. Handle challenge message and generate authentication message and context session key
  236. Arguments:
  237. pCredential - client credentials
  238. cbChallengeMessage - challenge message size
  239. pChallengeMessage - challenge message
  240. pNegotiateFlags - negotiate flags
  241. pcbAuthenticateMessage - size of authentication message
  242. pAuthenticateMessage - authentication message
  243. pContextSessionKey - context session key
  244. Return Value:
  245. NTSTATUS
  246. --*/
  247. {
  248. NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
  249. ULONG cbAuthenticateMessage = 0;
  250. UCHAR* pWhere = NULL;
  251. BOOLEAN DoUnicode = TRUE;
  252. //
  253. // use a scratch buffer to avoid memory allocation in bootssp
  254. //
  255. CHAR ScrtachBuff[sizeof(MSV1_0_NTLMV2_RESPONSE) + sizeof(DWORD) + NTLMV2_RESPONSE_LENGTH] = {0};
  256. STRING LmChallengeResponse = {0};
  257. STRING NtChallengeResponse = {0};
  258. STRING DatagramSessionKey = {0};
  259. USHORT Ntlmv2ResponseSize = 0;
  260. MSV1_0_NTLMV2_RESPONSE* pNtlmv2Response = NULL;
  261. LM_SESSION_KEY LanmanSessionKey;
  262. UNICODE_STRING TargetInfo = {0};
  263. UCHAR DatagramKey[sizeof(USER_SESSION_KEY)] ={0};
  264. USER_SESSION_KEY NtUserSessionKey;
  265. //
  266. // use pre-allocated buffers to avoid memory allocation in bootssp
  267. //
  268. // to be consistent with LSA/SSPI, allow DNS names in szDomainName and
  269. // szWorkstation
  270. //
  271. CHAR szUserName[(UNLEN + 4) * sizeof(WCHAR)] = {0};
  272. CHAR szDomainName[(DNSLEN + 4) * sizeof(WCHAR)] = {0};
  273. CHAR szWorkstation[(DNSLEN + 4) * sizeof(WCHAR)] = {0};
  274. //
  275. // responses to return to the caller
  276. //
  277. LM_RESPONSE LmResponse;
  278. NT_RESPONSE NtResponse;
  279. USER_SESSION_KEY ContextSessionKey;
  280. ULONG NegotiateFlags = 0;
  281. #ifdef MAC
  282. STRING UserName;
  283. STRING DomainName;
  284. STRING Workstation;
  285. UserName.Length = 0;
  286. UserName.MaximumLength = sizeof(szUserName);
  287. UserName.Buffer = szUserName;
  288. DomainName.Length = 0;
  289. DomainName.MaximumLength = sizeof(szDomainName);
  290. DomainName.Buffer = szDomainName;
  291. Workstation.Length = 0;
  292. Workstation.MaximumLength = sizeof(szWorkstation);
  293. Workstation.Buffer = szWorkstation;
  294. #else
  295. STRING UserName = {0, sizeof(szUserName), szUserName};
  296. STRING DomainName = {0, sizeof(szDomainName), szDomainName};
  297. STRING Workstation = {0, sizeof(szWorkstation), szWorkstation};
  298. #endif
  299. _fmemset(&LmResponse, 0, sizeof(LmResponse));
  300. _fmemset(&NtResponse, 0, sizeof(NtResponse));
  301. _fmemset(&LanmanSessionKey, 0, sizeof(LanmanSessionKey));
  302. _fmemset(&NtUserSessionKey, 0, sizeof(NtUserSessionKey));
  303. _fmemset(&ContextSessionKey, 0, sizeof(ContextSessionKey));
  304. if (!pCredential || !pChallengeMessage || !pNegotiateFlags || !pcbAuthenticateMessage || !pContextSessionKey)
  305. {
  306. return STATUS_INVALID_PARAMETER;
  307. }
  308. SspPrint((SSP_NTLMV2, "Entering SsprHandleNtlmv2ChallengeMessage\n"));
  309. NegotiateFlags = *pNegotiateFlags;
  310. NtStatus = SspInitUnicodeStringNoAlloc(pCredential->Username, (UNICODE_STRING *) &UserName);
  311. if (NT_SUCCESS(NtStatus))
  312. {
  313. NtStatus = SspInitUnicodeStringNoAlloc(pCredential->Domain, (UNICODE_STRING *) &DomainName);
  314. }
  315. if (NT_SUCCESS(NtStatus))
  316. {
  317. NtStatus = SspInitUnicodeStringNoAlloc(pCredential->Workstation, (UNICODE_STRING *) &Workstation);
  318. }
  319. if (NT_SUCCESS(NtStatus))
  320. {
  321. NtStatus = !_fstrcmp(NTLMSSP_SIGNATURE, (char *) pChallengeMessage->Signature) && pChallengeMessage->MessageType == NtLmChallenge ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER;
  322. }
  323. if (NT_SUCCESS(NtStatus))
  324. {
  325. if (pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE)
  326. {
  327. NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
  328. NegotiateFlags &= ~NTLMSSP_NEGOTIATE_OEM;
  329. DoUnicode = TRUE;
  330. }
  331. else if (pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_OEM)
  332. {
  333. NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
  334. NegotiateFlags &= ~NTLMSSP_NEGOTIATE_UNICODE;
  335. DoUnicode = FALSE;
  336. }
  337. else
  338. {
  339. NtStatus = STATUS_INVALID_PARAMETER;
  340. }
  341. }
  342. if (NT_SUCCESS(NtStatus))
  343. {
  344. if (!DoUnicode)
  345. {
  346. //
  347. // username will be upcased in SspCalculateNtlmv2Owf
  348. //
  349. SspUpcaseUnicodeString((UNICODE_STRING *) &DomainName);
  350. SspUpcaseUnicodeString((UNICODE_STRING *) &Workstation);
  351. }
  352. if (pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
  353. {
  354. NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
  355. }
  356. else
  357. {
  358. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_TARGET_INFO);
  359. }
  360. if (pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
  361. {
  362. NegotiateFlags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
  363. }
  364. else // (!(pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2))
  365. {
  366. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_NTLM2);
  367. }
  368. if (!(pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM))
  369. {
  370. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_NTLM);
  371. }
  372. if (!(pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH))
  373. {
  374. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_KEY_EXCH);
  375. }
  376. if (!(pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY))
  377. {
  378. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_LM_KEY);
  379. }
  380. if ((NegotiateFlags & NTLMSSP_NEGOTIATE_DATAGRAM) &&
  381. (NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN |NTLMSSP_NEGOTIATE_SEAL)))
  382. {
  383. NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
  384. }
  385. if (!(pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_56))
  386. {
  387. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_56);
  388. }
  389. if ((pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_128) == 0)
  390. {
  391. NegotiateFlags &= ~(NTLMSSP_NEGOTIATE_128);
  392. }
  393. if (pChallengeMessage->NegotiateFlags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
  394. {
  395. NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
  396. }
  397. else
  398. {
  399. NegotiateFlags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
  400. }
  401. NtStatus = SspConvertRelativeToAbsolute(
  402. pChallengeMessage,
  403. cbChallengeMessage,
  404. &pChallengeMessage->TargetInfo,
  405. DoUnicode,
  406. TRUE, // NULL target info OK
  407. (STRING *) &TargetInfo
  408. );
  409. }
  410. if (NT_SUCCESS(NtStatus))
  411. {
  412. Ntlmv2ResponseSize = sizeof(MSV1_0_NTLMV2_RESPONSE) + TargetInfo.Length;
  413. NtStatus = Ntlmv2ResponseSize <= sizeof(ScrtachBuff) ? STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES;
  414. }
  415. if (NT_SUCCESS(NtStatus))
  416. {
  417. // C_ASSERT(sizeof(MSV1_0_NTLMV2_RESPONSE) == sizeof(LM_RESPONSE));
  418. pNtlmv2Response = (MSV1_0_NTLMV2_RESPONSE *) ScrtachBuff;
  419. NtStatus = SspLm20GetNtlmv2ChallengeResponse(
  420. pCredential->NtPassword,
  421. (UNICODE_STRING *) &UserName,
  422. (UNICODE_STRING *) &DomainName,
  423. &TargetInfo,
  424. pChallengeMessage->Challenge,
  425. pNtlmv2Response,
  426. (MSV1_0_LMV2_RESPONSE *) &LmResponse,
  427. &NtUserSessionKey,
  428. &LanmanSessionKey
  429. );
  430. }
  431. if (NT_SUCCESS(NtStatus))
  432. {
  433. NtChallengeResponse.Buffer = (CHAR *) pNtlmv2Response;
  434. NtChallengeResponse.Length = Ntlmv2ResponseSize;
  435. LmChallengeResponse.Buffer = (CHAR *) &LmResponse;
  436. LmChallengeResponse.Length = sizeof(LmResponse);
  437. //
  438. // prepare to send encrypted randomly generated session key
  439. //
  440. DatagramSessionKey.Buffer = (CHAR *) DatagramKey;
  441. DatagramSessionKey.Length = DatagramSessionKey.MaximumLength = 0;
  442. //
  443. // Generate the session key, or encrypt the previosly generated random
  444. // one, from various bits of info. Fill in session key if needed.
  445. //
  446. NtStatus = SspMakeSessionKeys(
  447. NegotiateFlags,
  448. &LmChallengeResponse,
  449. &NtUserSessionKey,
  450. &LanmanSessionKey,
  451. &DatagramSessionKey,
  452. &ContextSessionKey
  453. );
  454. }
  455. if (NT_SUCCESS(NtStatus) && !DoUnicode)
  456. {
  457. NtStatus = SspUpcaseUnicodeStringToOemString((UNICODE_STRING *) &DomainName, &DomainName);
  458. if (NT_SUCCESS(NtStatus))
  459. {
  460. NtStatus = SspUpcaseUnicodeStringToOemString((UNICODE_STRING *) &UserName, &UserName);
  461. }
  462. if (NT_SUCCESS(NtStatus))
  463. {
  464. NtStatus = SspUpcaseUnicodeStringToOemString((UNICODE_STRING *) &Workstation, &Workstation);
  465. }
  466. }
  467. if (NT_SUCCESS(NtStatus))
  468. {
  469. cbAuthenticateMessage =
  470. sizeof(*pAuthenticateMessage) +
  471. LmChallengeResponse.Length +
  472. NtChallengeResponse.Length +
  473. DomainName.Length +
  474. UserName.Length +
  475. Workstation.Length +
  476. DatagramSessionKey.Length;
  477. NtStatus = cbAuthenticateMessage <= *pcbAuthenticateMessage ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
  478. if (NtStatus == STATUS_BUFFER_TOO_SMALL)
  479. {
  480. *pcbAuthenticateMessage = cbAuthenticateMessage;
  481. }
  482. }
  483. if (NT_SUCCESS(NtStatus))
  484. {
  485. _fmemset(pAuthenticateMessage, 0, cbAuthenticateMessage);
  486. //
  487. // Build the authenticate message
  488. //
  489. StringCbCopy(
  490. (char *)pAuthenticateMessage->Signature,
  491. sizeof(pAuthenticateMessage->Signature),
  492. NTLMSSP_SIGNATURE
  493. );
  494. pAuthenticateMessage->MessageType = NtLmAuthenticate;
  495. pWhere = (UCHAR *) (pAuthenticateMessage + 1);
  496. //
  497. // Copy the strings needing 2 byte alignment.
  498. //
  499. SspCopyStringAsString32(
  500. pAuthenticateMessage,
  501. &DomainName,
  502. &pWhere,
  503. &pAuthenticateMessage->DomainName
  504. );
  505. SspCopyStringAsString32(
  506. pAuthenticateMessage,
  507. &UserName,
  508. &pWhere,
  509. &pAuthenticateMessage->UserName
  510. );
  511. SspCopyStringAsString32(
  512. pAuthenticateMessage,
  513. &Workstation,
  514. &pWhere,
  515. &pAuthenticateMessage->Workstation
  516. );
  517. //
  518. // Copy the strings not needing special alignment.
  519. //
  520. SspCopyStringAsString32(
  521. pAuthenticateMessage,
  522. (STRING *) &LmChallengeResponse,
  523. &pWhere,
  524. &pAuthenticateMessage->LmChallengeResponse
  525. );
  526. SspCopyStringAsString32(
  527. pAuthenticateMessage,
  528. (STRING *) &NtChallengeResponse,
  529. &pWhere,
  530. &pAuthenticateMessage->NtChallengeResponse
  531. );
  532. SspCopyStringAsString32(
  533. pAuthenticateMessage,
  534. (STRING *) &DatagramSessionKey,
  535. &pWhere,
  536. &pAuthenticateMessage->SessionKey
  537. );
  538. pAuthenticateMessage->NegotiateFlags = NegotiateFlags;
  539. *pcbAuthenticateMessage = cbAuthenticateMessage;
  540. *pContextSessionKey = ContextSessionKey;
  541. *pNegotiateFlags = NegotiateFlags;
  542. }
  543. SspPrint((SSP_NTLMV2, "Leaving SsprHandleNtlmv2ChallengeMessage %#x\n", NtStatus));
  544. return NtStatus;
  545. }
  546. NTSTATUS
  547. SspGenerateChallenge(
  548. UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH]
  549. )
  550. /*++
  551. Routine Description:
  552. Generate a challenge.
  553. Arguments:
  554. ChallengeFromClient - challenge from client
  555. Return Value:
  556. NTSTATUS
  557. --*/
  558. {
  559. NTSTATUS NtStatus;
  560. MD5_CTX Md5Context;
  561. FILETIME CurTime;
  562. ULONG ulRandom;
  563. SspPrint((SSP_NTLMV2, "SspGenerateChallenge\n"));
  564. #ifdef USE_CONSTANT_CHALLENGE
  565. _fmemset(ChallengeFromClient, 0, MSV1_0_CHALLENGE_LENGTH);
  566. return STATUS_SUCCESS;
  567. #endif
  568. ulRandom = rand();
  569. _fmemcpy(ChallengeFromClient, &ulRandom, sizeof(ULONG));
  570. ulRandom = rand();
  571. _fmemcpy(ChallengeFromClient + sizeof(ULONG), &ulRandom, sizeof(ULONG));
  572. NtStatus = SspGetSystemTimeAsFileTime(&CurTime);
  573. if (!NT_SUCCESS(NtStatus))
  574. {
  575. return NtStatus;
  576. }
  577. MD5Init(&Md5Context);
  578. MD5Update(&Md5Context, ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
  579. MD5Update(&Md5Context, (UCHAR*)&CurTime, sizeof(CurTime));
  580. MD5Final(&Md5Context);
  581. //
  582. // only take the first half of the MD5 hash
  583. //
  584. _fmemcpy(ChallengeFromClient, Md5Context.digest, MSV1_0_CHALLENGE_LENGTH);
  585. return NtStatus;
  586. }
  587. NTSTATUS
  588. SspConvertRelativeToAbsolute(
  589. IN VOID* pMessageBase,
  590. IN ULONG cbMessageSize,
  591. IN STRING32* pStringToRelocate,
  592. IN BOOLEAN AlignToWchar,
  593. IN BOOLEAN AllowNullString,
  594. OUT STRING* pOutputString
  595. )
  596. /*++
  597. Routine Description:
  598. Convert relative string to absolute string
  599. Arguments:
  600. pMessageBase - message base
  601. cbMessageSize - mssage size
  602. pStringToRelocate - relative string
  603. AlignToWchar - align to wide char
  604. AllowNullString - allow null string
  605. pOutputString - output string
  606. Return Value:
  607. NTSTATUS
  608. --*/
  609. {
  610. ULONG Offset;
  611. //
  612. // If the buffer is allowed to be null,
  613. // check that special case.
  614. //
  615. if (AllowNullString && (pStringToRelocate->Length == 0))
  616. {
  617. pOutputString->MaximumLength = pOutputString->Length = pStringToRelocate->Length;
  618. pOutputString->Buffer = NULL;
  619. return STATUS_SUCCESS;
  620. }
  621. //
  622. // Ensure the string in entirely within the message.
  623. //
  624. Offset = (ULONG)pStringToRelocate->Buffer;
  625. if (Offset >= cbMessageSize || Offset + pStringToRelocate->Length > cbMessageSize)
  626. {
  627. return STATUS_INVALID_PARAMETER;
  628. }
  629. //
  630. // Ensure the buffer is properly aligned.
  631. //
  632. if (AlignToWchar && (!COUNT_IS_ALIGNED(Offset, ALIGN_WCHAR) ||
  633. !COUNT_IS_ALIGNED(pStringToRelocate->Length, ALIGN_WCHAR)))
  634. {
  635. return STATUS_INVALID_PARAMETER;
  636. }
  637. //
  638. // Finally make the pointer absolute.
  639. //
  640. pOutputString->Buffer = (CHAR*)(pMessageBase) + Offset;
  641. pOutputString->MaximumLength = pOutputString->Length = pStringToRelocate->Length ;
  642. return STATUS_SUCCESS;
  643. }
  644. NTSTATUS
  645. SspUpcaseUnicodeStringToOemString(
  646. IN UNICODE_STRING* pUnicodeString,
  647. OUT STRING* pOemString
  648. )
  649. /*++
  650. Routine Description:
  651. Upcase unicode string and convert it to oem string.
  652. Arguments:
  653. pUnicodeString - uncide string
  654. pOemString - OEM string
  655. Return Value:
  656. NTSTATUS
  657. --*/
  658. {
  659. ULONG i;
  660. //
  661. // use a scratch buffer: the strings we encounter are among
  662. // username/domainname/workstationname, hence the length are
  663. // UNLEN maximum
  664. //
  665. CHAR Buffer[2 * (UNLEN + 4)] = {0};
  666. #ifndef MAC
  667. STRING OemString = {0, sizeof(Buffer), Buffer};
  668. #else
  669. STRING OemString;
  670. OemString.Length = 0;
  671. OemString.MaximumLength = sizeof(Buffer);
  672. OemString.Buffer = Buffer;
  673. #endif
  674. if (OemString.MaximumLength < pUnicodeString->Length)
  675. {
  676. return STATUS_INSUFFICIENT_RESOURCES;
  677. }
  678. //
  679. // upcase the unicode string and put it into OemString
  680. //
  681. OemString.Length = pUnicodeString->Length;
  682. for (i = 0; i < pUnicodeString->Length / sizeof(WCHAR); i++)
  683. {
  684. ((UNICODE_STRING*)(&OemString))->Buffer[i] = RtlUpcaseUnicodeChar(pUnicodeString->Buffer[i]);
  685. }
  686. return SspUnicodeStringToOemString((STRING*)(pUnicodeString), (UNICODE_STRING*)(&OemString), FALSE);
  687. }
  688. VOID
  689. SspUpcaseUnicodeString(
  690. IN OUT UNICODE_STRING* pUnicodeString
  691. )
  692. /*++
  693. Routine Description:
  694. Upcase unicode string, modifying string in place.
  695. Arguments:
  696. pUnicodeString - string
  697. Return Value:
  698. none
  699. --*/
  700. {
  701. ULONG i;
  702. for (i = 0; i < pUnicodeString->Length / sizeof(WCHAR); i++)
  703. {
  704. pUnicodeString->Buffer[i] = RtlUpcaseUnicodeChar(pUnicodeString->Buffer[i]);
  705. }
  706. }
  707. NTSTATUS
  708. SspGetSystemTimeAsFileTime(
  709. OUT FILETIME* pSystemTimeAsFileTime
  710. )
  711. /*++
  712. Routine Description:
  713. Get system time as FILETIME
  714. Arguments:
  715. pSystemTimeAsFileTime system time as FILETIME
  716. Return Value:
  717. NTSTATUS
  718. --*/
  719. {
  720. #if !defined(USE_CONSTANT_CHALLENGE) && defined(MAC)
  721. DWORD dwTime;
  722. ULONGLONG time64 = 0;
  723. MACFILETIME MacFileTime;
  724. #endif
  725. SspPrint((SSP_NTLMV2, "SspGetSystemTimeAsFileTime\n"));
  726. #ifdef USE_CONSTANT_CHALLENGE
  727. _fmemset(pSystemTimeAsFileTime, 0, sizeof(*pSystemTimeAsFileTime));
  728. return STATUS_SUCCESS;
  729. #else
  730. #ifndef MAC
  731. return BlGetSystemTimeAsFileTime(pSystemTimeAsFileTime);
  732. #else
  733. //
  734. //The following is from the MacOffice folks. I know is just works...
  735. //
  736. GetDateTime(&dwTime);
  737. time64 = dwTime;
  738. time64 -= SspMacSecondsFromGMT();
  739. time64 += WINDOWS_MAC_TIME_DIFFERENCE_IN_SECONDS;
  740. time64 *= NUM_100ns_PER_SECOND;
  741. //
  742. //Get time in FILETIME format.
  743. //
  744. _fmemcpy(&MacFileTime, &time64, sizeof(time64));
  745. pSystemTimeAsFileTime->dwLowDateTime = MacFileTime.dwLowDateTime;
  746. pSystemTimeAsFileTime->dwHighDateTime = MacFileTime.dwHighDateTime;
  747. swaplong(pSystemTimeAsFileTime->dwLowDateTime);
  748. swaplong(pSystemTimeAsFileTime->dwHighDateTime);
  749. return STATUS_SUCCESS;
  750. #endif
  751. #endif
  752. }
  753. NTSTATUS
  754. SspLm20GetNtlmv2ChallengeResponse(
  755. IN NT_OWF_PASSWORD* pNtOwfPassword,
  756. IN UNICODE_STRING* pUserName,
  757. IN UNICODE_STRING* pLogonDomainName,
  758. IN UNICODE_STRING* pTargetInfo,
  759. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  760. OUT MSV1_0_NTLMV2_RESPONSE* pNtlmv2Response,
  761. OUT MSV1_0_LMV2_RESPONSE* pLmv2Response,
  762. OUT USER_SESSION_KEY* pNtUserSessionKey,
  763. OUT LM_SESSION_KEY* pLmSessionKey
  764. )
  765. /*++
  766. Routine Description:
  767. Get NTLMv2 response and session keys. This route fills in time stamps and
  768. challenge from client.
  769. Arguments:
  770. pNtOwfPassword - NT OWF
  771. pUserName - user name
  772. pLogonDomainName - logon domain name
  773. pTargetInfo - target info
  774. ChallengeToClient - challenge to client
  775. pNtlmv2Response - NTLM v2 response
  776. pLmv2Response - LM v2 response
  777. pNtUserSessionKey - NT user session key
  778. pLmSessionKey - LM session key
  779. Return Value:
  780. NTSTATUS
  781. --*/
  782. {
  783. NTSTATUS NtStatus;
  784. SspPrint((SSP_API, "Entering SspLm20GetNtlmv2ChallengeResponse\n"));
  785. //
  786. // fill in version numbers, timestamp, and client's challenge
  787. //
  788. pNtlmv2Response->RespType = 1;
  789. pNtlmv2Response->HiRespType = 1;
  790. pNtlmv2Response->Flags = 0;
  791. pNtlmv2Response->MsgWord = 0;
  792. NtStatus = SspGetSystemTimeAsFileTime((FILETIME*)(&pNtlmv2Response->TimeStamp));
  793. if (NT_SUCCESS(NtStatus))
  794. {
  795. NtStatus = SspGenerateChallenge(pNtlmv2Response->ChallengeFromClient);
  796. }
  797. if (NT_SUCCESS(NtStatus))
  798. {
  799. _fmemcpy(pNtlmv2Response->Buffer, pTargetInfo->Buffer, pTargetInfo->Length);
  800. //
  801. // Calculate Ntlmv2 response, filling in response field
  802. //
  803. SspGetNtlmv2Response(
  804. pNtOwfPassword,
  805. pUserName,
  806. pLogonDomainName,
  807. pTargetInfo->Length,
  808. ChallengeToClient,
  809. pNtlmv2Response,
  810. pNtUserSessionKey,
  811. pLmSessionKey
  812. );
  813. //
  814. // Use same challenge to compute the LMV2 response
  815. //
  816. _fmemcpy(pLmv2Response->ChallengeFromClient, pNtlmv2Response->ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
  817. //
  818. // Calculate LMV2 response
  819. //
  820. SspGetLmv2Response(
  821. pNtOwfPassword,
  822. pUserName,
  823. pLogonDomainName,
  824. ChallengeToClient,
  825. pLmv2Response->ChallengeFromClient,
  826. pLmv2Response->Response
  827. );
  828. }
  829. SspPrint((SSP_API, "Leaving SspLm20GetNtlmv2ChallengeResponse %#x\n", NtStatus));
  830. return NtStatus;
  831. }
  832. VOID
  833. SspGetNtlmv2Response(
  834. IN NT_OWF_PASSWORD* pNtOwfPassword,
  835. IN UNICODE_STRING* pUserName,
  836. IN UNICODE_STRING* pLogonDomainName,
  837. IN ULONG TargetInfoLength,
  838. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  839. IN OUT MSV1_0_NTLMV2_RESPONSE* pNtlmv2Response,
  840. OUT USER_SESSION_KEY* pNtUserSessionKey,
  841. OUT LM_SESSION_KEY* pLmSessionKey
  842. )
  843. /*++
  844. Routine Description:
  845. Get NTLM v2 response.
  846. Arguments:
  847. pNtOwfPassword - NT OWF
  848. pUserName - user name
  849. pLogonDomainName - logon domain name
  850. TargetInfoLength - target info length
  851. ChallengeToClient - challenge to client
  852. pNtlmv2Response - NTLM v2 response
  853. response - response
  854. pNtUserSessionKey - NT user session key
  855. pLmSessionKey - LM session key
  856. Return Value:
  857. none
  858. --*/
  859. {
  860. HMACMD5_CTX HMACMD5Context;
  861. UCHAR Ntlmv2Owf[MSV1_0_NTLMV2_OWF_LENGTH];
  862. SspPrint((SSP_NTLMV2, "SspGetLmv2Response\n"));
  863. //
  864. // get Ntlmv2 OWF
  865. //
  866. SspCalculateNtlmv2Owf(
  867. pNtOwfPassword,
  868. pUserName,
  869. pLogonDomainName,
  870. Ntlmv2Owf
  871. );
  872. //
  873. // Calculate Ntlmv2 Response
  874. // HMAC(Ntlmv2Owf, (NS, V, HV, T, NC, S))
  875. //
  876. HMACMD5Init(
  877. &HMACMD5Context,
  878. Ntlmv2Owf,
  879. MSV1_0_NTLMV2_OWF_LENGTH
  880. );
  881. HMACMD5Update(
  882. &HMACMD5Context,
  883. ChallengeToClient,
  884. MSV1_0_CHALLENGE_LENGTH
  885. );
  886. HMACMD5Update(
  887. &HMACMD5Context,
  888. &pNtlmv2Response->RespType,
  889. (MSV1_0_NTLMV2_INPUT_LENGTH + TargetInfoLength)
  890. );
  891. HMACMD5Final(
  892. &HMACMD5Context,
  893. pNtlmv2Response->Response
  894. );
  895. //
  896. // now compute the session keys
  897. // HMAC(Kr, R)
  898. //
  899. HMACMD5Init(
  900. &HMACMD5Context,
  901. Ntlmv2Owf,
  902. MSV1_0_NTLMV2_OWF_LENGTH
  903. );
  904. HMACMD5Update(
  905. &HMACMD5Context,
  906. pNtlmv2Response->Response,
  907. MSV1_0_NTLMV2_RESPONSE_LENGTH
  908. );
  909. HMACMD5Final(
  910. &HMACMD5Context,
  911. (UCHAR*)(pNtUserSessionKey)
  912. );
  913. _fmemcpy(pLmSessionKey, pNtUserSessionKey, sizeof(LM_SESSION_KEY));
  914. }
  915. VOID
  916. SspCopyStringAsString32(
  917. IN VOID* pMessageBuffer,
  918. IN STRING* pInString,
  919. IN OUT UCHAR** ppWhere,
  920. OUT STRING32* pOutString32
  921. )
  922. /*++
  923. Routine Description:
  924. Copy string as STRING32
  925. Arguments:
  926. pMessageBuffer - STRING32 base
  927. pInString - input STRING
  928. ppWhere - next empty spot in pMessageBuffer
  929. pOutString32 - output STRING32
  930. Return Value:
  931. none
  932. --*/
  933. {
  934. //
  935. // Copy the data to the Buffer
  936. //
  937. if (pInString->Buffer != NULL)
  938. {
  939. _fmemcpy(*ppWhere, pInString->Buffer, pInString->Length);
  940. }
  941. //
  942. // Build a descriptor to the newly copied data
  943. //
  944. pOutString32->Length = pOutString32->MaximumLength = pInString->Length;
  945. pOutString32->Buffer = (ULONG)(*ppWhere - (UCHAR*)(pMessageBuffer));
  946. //
  947. // Update Where to point past the copied data
  948. //
  949. *ppWhere += pInString->Length;
  950. }
  951. VOID
  952. SspCalculateNtlmv2Owf(
  953. IN NT_OWF_PASSWORD* pNtOwfPassword,
  954. IN UNICODE_STRING* pUserName,
  955. IN UNICODE_STRING* pLogonDomainName,
  956. OUT UCHAR Ntlmv2Owf[MSV1_0_NTLMV2_OWF_LENGTH]
  957. )
  958. /*++
  959. Routine Description:
  960. Calculate Ntlm v2 OWF, salted with username and logon domain name
  961. Arguments:
  962. pNtOwfPassword - NT OWF
  963. pUserName - user name
  964. pLogonDomainName - logon domain name
  965. Ntlmv2Owf - NTLM v2 OWF
  966. Return Value:
  967. none
  968. --*/
  969. {
  970. HMACMD5_CTX HMACMD5Context;
  971. SspPrint((SSP_NTLMV2, "SspCalculateNtlmv2Owf\n"));
  972. SspUpcaseUnicodeString(pUserName);
  973. //
  974. //For Macintosh computers, we need to change the alignment
  975. //of the unicode strings so it matches windows alignment.
  976. //
  977. #ifdef MAC
  978. SspSwapUnicodeString(pUserName);
  979. SspSwapUnicodeString(pLogonDomainName);
  980. #endif
  981. //
  982. // Calculate Ntlmv2 OWF -- HMAC(MD4(P), (UserName, LogonDomainName))
  983. //
  984. HMACMD5Init(
  985. &HMACMD5Context,
  986. (UCHAR *) pNtOwfPassword,
  987. sizeof(*pNtOwfPassword)
  988. );
  989. HMACMD5Update(
  990. &HMACMD5Context,
  991. (UCHAR *) pUserName->Buffer,
  992. pUserName->Length
  993. );
  994. HMACMD5Update(
  995. &HMACMD5Context,
  996. (UCHAR *) pLogonDomainName->Buffer,
  997. pLogonDomainName->Length
  998. );
  999. HMACMD5Final(
  1000. &HMACMD5Context,
  1001. Ntlmv2Owf
  1002. );
  1003. //
  1004. //For Macintosh, we need to set the alignment back to the
  1005. //host alignment in case the strings need to be manipulated
  1006. //in the future.
  1007. //
  1008. #ifdef MAC
  1009. SspSwapUnicodeString(pUserName);
  1010. SspSwapUnicodeString(pLogonDomainName);
  1011. #endif
  1012. }
  1013. VOID
  1014. SspGetLmv2Response(
  1015. IN NT_OWF_PASSWORD* pNtOwfPassword,
  1016. IN UNICODE_STRING* pUserName,
  1017. IN UNICODE_STRING* pLogonDomainName,
  1018. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  1019. IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
  1020. OUT UCHAR Response[MSV1_0_NTLMV2_RESPONSE_LENGTH]
  1021. )
  1022. /*++
  1023. Routine Description:
  1024. Get LMv2 response
  1025. Arguments:
  1026. pNtOwfPassword - NT OWF
  1027. pUserName - user name
  1028. pLogonDomainName - logon domain name
  1029. ChallengeToClient - challenge to client
  1030. pLmv2Response - Lm v2 response
  1031. Routine - response
  1032. Return Value:
  1033. NTSTATUS
  1034. --*/
  1035. {
  1036. HMACMD5_CTX HMACMD5Context;
  1037. UCHAR Ntlmv2Owf[MSV1_0_NTLMV2_OWF_LENGTH];
  1038. C_ASSERT(MD5DIGESTLEN == MSV1_0_NTLMV2_RESPONSE_LENGTH);
  1039. SspPrint((SSP_NTLMV2, "SspGetLmv2Response\n"));
  1040. //
  1041. // get Ntlmv2 OWF
  1042. //
  1043. SspCalculateNtlmv2Owf(
  1044. pNtOwfPassword,
  1045. pUserName,
  1046. pLogonDomainName,
  1047. Ntlmv2Owf
  1048. );
  1049. //
  1050. // Calculate Ntlmv2 Response
  1051. // HMAC(Ntlmv2Owf, (NS, V, HV, T, NC, S))
  1052. //
  1053. HMACMD5Init(
  1054. &HMACMD5Context,
  1055. Ntlmv2Owf,
  1056. MSV1_0_NTLMV2_OWF_LENGTH
  1057. );
  1058. HMACMD5Update(
  1059. &HMACMD5Context,
  1060. ChallengeToClient,
  1061. MSV1_0_CHALLENGE_LENGTH
  1062. );
  1063. HMACMD5Update(
  1064. &HMACMD5Context,
  1065. ChallengeFromClient,
  1066. MSV1_0_CHALLENGE_LENGTH
  1067. );
  1068. HMACMD5Final(
  1069. &HMACMD5Context,
  1070. Response
  1071. );
  1072. return;
  1073. }
  1074. NTSTATUS
  1075. SspMakeSessionKeys(
  1076. IN ULONG NegotiateFlags,
  1077. IN STRING* pLmChallengeResponse,
  1078. IN USER_SESSION_KEY* pNtUserSessionKey, // from the DC or GetChalResp
  1079. IN LM_SESSION_KEY* pLanmanSessionKey, // from the DC of GetChalResp
  1080. OUT STRING* pDatagramSessionKey, // this is the session key sent over wire
  1081. OUT USER_SESSION_KEY* pContextSessionKey // session key in context
  1082. )
  1083. /*++
  1084. Routine Description:
  1085. Make NTLMv2 context session key and DatagramSessionKey.
  1086. Arguments:
  1087. NegotiateFlags - negotiate flags
  1088. pLmChallengeResponse - LM challenge response
  1089. pNtUserSessionKey - NtUserSessionKey
  1090. pLanmanSessionKey - LanmanSessionKey
  1091. pDatagramSessionKey - DatagramSessionKey
  1092. pContextSessionKey - NTLMv2 conext session key
  1093. Return Value:
  1094. NTSTATUS
  1095. --*/
  1096. {
  1097. NTSTATUS NtStatus = STATUS_SUCCESS;
  1098. UCHAR pLocalSessionKey[sizeof(USER_SESSION_KEY)] = {0};
  1099. SspPrint((SSP_NTLMV2, "Entering SspMakeSessionKeys\n"));
  1100. if (!(NegotiateFlags & (NTLMSSP_NEGOTIATE_SIGN| NTLMSSP_NEGOTIATE_SEAL)))
  1101. {
  1102. _fmemcpy(pContextSessionKey, pNtUserSessionKey, sizeof(pLocalSessionKey));
  1103. return STATUS_SUCCESS;
  1104. }
  1105. if (NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM2)
  1106. {
  1107. _fmemcpy(pLocalSessionKey, pNtUserSessionKey, sizeof(pLocalSessionKey));
  1108. }
  1109. else if(NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
  1110. {
  1111. LM_OWF_PASSWORD LmKey;
  1112. LM_RESPONSE LmResponseKey;
  1113. BYTE pTemporaryResponse[LM_RESPONSE_LENGTH] = {0};
  1114. if (pLmChallengeResponse->Length > LM_RESPONSE_LENGTH)
  1115. {
  1116. return STATUS_NOT_SUPPORTED;
  1117. }
  1118. _fmemcpy(pTemporaryResponse, pLmChallengeResponse->Buffer, pLmChallengeResponse->Length);
  1119. _fmemcpy(&LmKey, pLanmanSessionKey, sizeof(LM_SESSION_KEY));
  1120. _fmemset((UCHAR*)(&LmKey) + sizeof(LM_SESSION_KEY),
  1121. NTLMSSP_KEY_SALT,
  1122. LM_OWF_PASSWORD_LENGTH - sizeof(LM_SESSION_KEY)
  1123. );
  1124. NtStatus = CalculateLmResponse(
  1125. (LM_CHALLENGE *) pTemporaryResponse,
  1126. &LmKey,
  1127. &LmResponseKey
  1128. );
  1129. if (!NT_SUCCESS(NtStatus))
  1130. {
  1131. return NtStatus;
  1132. }
  1133. _fmemcpy(pLocalSessionKey, &LmResponseKey, sizeof(USER_SESSION_KEY));
  1134. }
  1135. else
  1136. {
  1137. _fmemcpy(pLocalSessionKey, pNtUserSessionKey, sizeof(USER_SESSION_KEY));
  1138. }
  1139. if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
  1140. {
  1141. struct RC4_KEYSTRUCT Rc4Key;
  1142. rc4_key(
  1143. &Rc4Key,
  1144. sizeof(USER_SESSION_KEY),
  1145. pLocalSessionKey
  1146. );
  1147. if (pDatagramSessionKey == NULL)
  1148. {
  1149. rc4(
  1150. &Rc4Key,
  1151. sizeof(USER_SESSION_KEY),
  1152. (UCHAR*) pContextSessionKey
  1153. );
  1154. }
  1155. else
  1156. {
  1157. pDatagramSessionKey->Length =
  1158. pDatagramSessionKey->MaximumLength =
  1159. sizeof(USER_SESSION_KEY);
  1160. _fmemcpy(pDatagramSessionKey->Buffer, pContextSessionKey, sizeof(USER_SESSION_KEY));
  1161. rc4(
  1162. &Rc4Key,
  1163. sizeof(USER_SESSION_KEY),
  1164. (UCHAR*)(pDatagramSessionKey->Buffer)
  1165. );
  1166. }
  1167. }
  1168. else
  1169. {
  1170. _fmemcpy(pContextSessionKey, pLocalSessionKey, sizeof(USER_SESSION_KEY));
  1171. }
  1172. SspPrint((SSP_NTLMV2, "Leaving SspMakeSessionKeys %#x\n", NtStatus));
  1173. return NtStatus;
  1174. }
  1175. VOID
  1176. SspMakeNtlmv2SKeys(
  1177. IN USER_SESSION_KEY* pUserSessionKey,
  1178. IN ULONG NegotiateFlags,
  1179. IN ULONG SendNonce,
  1180. IN ULONG RecvNonce,
  1181. OUT NTLMV2_DERIVED_SKEYS* pNtlmv2Keys
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. Derive all NTLMv2 session keys
  1186. Arguments:
  1187. pUserSessionKey - NTLMv2 user session key
  1188. NegotiateFlags - negotiate flags
  1189. SendNonce - send message sequence number
  1190. RecvNonce - receive message sequence number
  1191. pNtlmv2Keys - derived NTLMv2 session keys
  1192. Return Value:
  1193. none
  1194. --*/
  1195. {
  1196. MD5_CTX Md5Context;
  1197. C_ASSERT(MD5DIGESTLEN == sizeof(USER_SESSION_KEY));
  1198. SspPrint((SSP_NTLMV2, "SspMakeSessionKeys\n"));
  1199. if (NegotiateFlags & NTLMSSP_NEGOTIATE_128)
  1200. {
  1201. pNtlmv2Keys->KeyLen = 16;
  1202. }
  1203. else if (NegotiateFlags & NTLMSSP_NEGOTIATE_56)
  1204. {
  1205. pNtlmv2Keys->KeyLen = 7;
  1206. }
  1207. else
  1208. {
  1209. pNtlmv2Keys->KeyLen = 5;
  1210. }
  1211. //
  1212. // make client to server encryption key
  1213. //
  1214. MD5Init(&Md5Context);
  1215. MD5Update(&Md5Context, (UCHAR*)(pUserSessionKey), pNtlmv2Keys->KeyLen);
  1216. MD5Update(&Md5Context, (UCHAR*)(CSSEALMAGIC), sizeof(CSSEALMAGIC));
  1217. MD5Final(&Md5Context);
  1218. _fmemcpy(&pNtlmv2Keys->SealSessionKey, Md5Context.digest, sizeof(USER_SESSION_KEY));
  1219. //
  1220. // make server to client encryption key
  1221. //
  1222. MD5Init(&Md5Context);
  1223. MD5Update(&Md5Context, (UCHAR*)(pUserSessionKey), pNtlmv2Keys->KeyLen);
  1224. MD5Update(&Md5Context, (UCHAR*)(SCSEALMAGIC), sizeof(SCSEALMAGIC));
  1225. MD5Final(&Md5Context);
  1226. _fmemcpy(&pNtlmv2Keys->UnsealSessionKey, Md5Context.digest, sizeof(USER_SESSION_KEY));
  1227. //
  1228. // make client to server signing key -- always 128 bits!
  1229. //
  1230. MD5Init(&Md5Context);
  1231. MD5Update(&Md5Context, (UCHAR*)(pUserSessionKey), sizeof(USER_SESSION_KEY));
  1232. MD5Update(&Md5Context, (UCHAR*)(CSSIGNMAGIC), sizeof(CSSIGNMAGIC));
  1233. MD5Final(&Md5Context);
  1234. _fmemcpy(&pNtlmv2Keys->SignSessionKey, Md5Context.digest, sizeof(USER_SESSION_KEY));
  1235. //
  1236. // make server to client signing key
  1237. //
  1238. MD5Init(&Md5Context);
  1239. MD5Update(&Md5Context, (UCHAR*)(pUserSessionKey), sizeof(USER_SESSION_KEY));
  1240. MD5Update(&Md5Context, (UCHAR*)(SCSIGNMAGIC), sizeof(SCSIGNMAGIC));
  1241. MD5Final(&Md5Context);
  1242. _fmemcpy(&pNtlmv2Keys->VerifySessionKey, Md5Context.digest, sizeof(USER_SESSION_KEY));
  1243. //
  1244. // set pointers to different key schedule and nonce for each direction
  1245. // key schedule will be filled in later...
  1246. //
  1247. pNtlmv2Keys->pSealRc4Sched = &pNtlmv2Keys->SealRc4Sched;
  1248. pNtlmv2Keys->pUnsealRc4Sched = &pNtlmv2Keys->UnsealRc4Sched;
  1249. pNtlmv2Keys->pSendNonce = &pNtlmv2Keys->SendNonce;
  1250. pNtlmv2Keys->pRecvNonce = &pNtlmv2Keys->RecvNonce;
  1251. pNtlmv2Keys->SendNonce = SendNonce;
  1252. pNtlmv2Keys->RecvNonce = RecvNonce;
  1253. rc4_key(&pNtlmv2Keys->SealRc4Sched, sizeof(USER_SESSION_KEY), (UCHAR*)(&pNtlmv2Keys->SealSessionKey));
  1254. rc4_key(&pNtlmv2Keys->UnsealRc4Sched, sizeof(USER_SESSION_KEY), (UCHAR*)(&pNtlmv2Keys->UnsealSessionKey));
  1255. }
  1256. NTSTATUS
  1257. SspSignSealHelper(
  1258. IN NTLMV2_DERIVED_SKEYS* pNtlmv2Keys,
  1259. IN ULONG NegotiateFlags,
  1260. IN eSignSealOp Op,
  1261. IN ULONG MessageSeqNo,
  1262. IN OUT SecBufferDesc* pMessage,
  1263. OUT NTLMSSP_MESSAGE_SIGNATURE* pSig,
  1264. OUT NTLMSSP_MESSAGE_SIGNATURE** ppSig
  1265. )
  1266. /*++
  1267. Routine Description:
  1268. Helper function for signing/sealing/unsealing/verifying.
  1269. Arguments:
  1270. pNtlmv2Keys - key materials
  1271. NegotiateFlags - negotiate Flags
  1272. Op - which operation to performance
  1273. MessageSeqNo - message sequence number
  1274. pMessage - message buffer descriptor
  1275. pSig - result signature
  1276. ppSig - address of the signature token in message
  1277. buffer descriptor pMessage
  1278. Return Value:
  1279. SECURITY_STATUS
  1280. --*/
  1281. {
  1282. NTSTATUS NtStatus = STATUS_SUCCESS;
  1283. HMACMD5_CTX HMACMD5Context;
  1284. UCHAR TempSig[MD5DIGESTLEN];
  1285. NTLMSSP_MESSAGE_SIGNATURE Sig;
  1286. int Signature;
  1287. ULONG i;
  1288. PUCHAR pKey = NULL; // ptr to key to use for encryption
  1289. PUCHAR pSignKey = NULL; // ptr to key to use for signing
  1290. PULONG pNonce = NULL; // ptr to nonce to use
  1291. struct RC4_KEYSTRUCT* pRc4Sched = NULL; // ptr to key schedule to use
  1292. NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // aligned copy of input sig data
  1293. SspPrint((SSP_NTLMV2, "Entering SspSignSealHelper NegotiateFlags %#x, eSignSealOp %d\n", NegotiateFlags, Op));
  1294. Signature = -1;
  1295. for (i = 0; i < pMessage->cBuffers; i++)
  1296. {
  1297. if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_TOKEN)
  1298. {
  1299. Signature = i;
  1300. break;
  1301. }
  1302. }
  1303. if (Signature == -1)
  1304. {
  1305. NtStatus = STATUS_INVALID_PARAMETER;
  1306. }
  1307. if (NT_SUCCESS(NtStatus))
  1308. {
  1309. if (pMessage->pBuffers[Signature].cbBuffer < sizeof(NTLMSSP_MESSAGE_SIGNATURE))
  1310. {
  1311. NtStatus = STATUS_INVALID_PARAMETER;
  1312. }
  1313. }
  1314. if (NT_SUCCESS(NtStatus))
  1315. {
  1316. *ppSig = (NTLMSSP_MESSAGE_SIGNATURE*)(pMessage->pBuffers[Signature].pvBuffer);
  1317. _fmemcpy(&AlignedSig, *ppSig, sizeof(AlignedSig));
  1318. //
  1319. // If sequence detect wasn't requested, put on an empty security token.
  1320. // Don't do the check if Seal/Unseal is called
  1321. //
  1322. if (!(NegotiateFlags & NTLMSSP_NEGOTIATE_SIGN) &&
  1323. (Op == eSign || Op == eVerify))
  1324. {
  1325. _fmemset(pSig, 0, sizeof(NTLMSSP_MESSAGE_SIGNATURE));
  1326. pSig->Version = NTLM_SIGN_VERSION;
  1327. NtStatus = STATUS_SUCCESS;
  1328. }
  1329. }
  1330. if (NT_SUCCESS(NtStatus))
  1331. {
  1332. switch (Op)
  1333. {
  1334. case eSeal:
  1335. pSignKey = pNtlmv2Keys->SignSessionKey; // if NTLM2
  1336. pKey = pNtlmv2Keys->SealSessionKey;
  1337. pRc4Sched = pNtlmv2Keys->pSealRc4Sched;
  1338. pNonce = pNtlmv2Keys->pSendNonce;
  1339. break;
  1340. case eUnseal:
  1341. pSignKey = pNtlmv2Keys->VerifySessionKey; // if NTLM2
  1342. pKey = pNtlmv2Keys->UnsealSessionKey;
  1343. pRc4Sched = pNtlmv2Keys->pUnsealRc4Sched;
  1344. pNonce = pNtlmv2Keys->pRecvNonce;
  1345. break;
  1346. case eSign:
  1347. pSignKey = pNtlmv2Keys->SignSessionKey; // if NTLM2
  1348. pKey = pNtlmv2Keys->SealSessionKey; // might be used to encrypt the signature
  1349. pRc4Sched = pNtlmv2Keys->pSealRc4Sched;
  1350. pNonce = pNtlmv2Keys->pSendNonce;
  1351. break;
  1352. case eVerify:
  1353. pSignKey = pNtlmv2Keys->VerifySessionKey; // if NTLM2
  1354. pKey = pNtlmv2Keys->UnsealSessionKey; // might be used to decrypt the signature
  1355. pRc4Sched = pNtlmv2Keys->pUnsealRc4Sched;
  1356. pNonce = pNtlmv2Keys->pRecvNonce;
  1357. break;
  1358. default:
  1359. NtStatus = (STATUS_INVALID_LEVEL);
  1360. break;
  1361. }
  1362. }
  1363. //
  1364. // Either we can supply the sequence number, or the application can supply
  1365. // the message sequence number.
  1366. //
  1367. if (NT_SUCCESS(NtStatus))
  1368. {
  1369. Sig.Version = NTLM_SIGN_VERSION;
  1370. if ((NegotiateFlags & NTLMSSP_APP_SEQ) == 0)
  1371. {
  1372. Sig.Nonce = *pNonce; // use our sequence number
  1373. (*pNonce) += 1;
  1374. }
  1375. else
  1376. {
  1377. if (Op == eSeal || Op == eSign || MessageSeqNo != 0)
  1378. {
  1379. Sig.Nonce = MessageSeqNo;
  1380. }
  1381. else
  1382. {
  1383. Sig.Nonce = AlignedSig.Nonce;
  1384. }
  1385. //
  1386. // if using RC4, must rekey for each packet RC4 is used for seal,
  1387. // unseal; and for encrypting the HMAC hash if key exchange was
  1388. // negotiated (we use just HMAC if no key exchange, so that a good
  1389. // signing option exists with no RC4 encryption needed)
  1390. //
  1391. if (Op == eSeal || Op == eUnseal || NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
  1392. {
  1393. MD5_CTX Md5ContextReKey;
  1394. C_ASSERT(MD5DIGESTLEN == sizeof(USER_SESSION_KEY));
  1395. MD5Init(&Md5ContextReKey);
  1396. MD5Update(&Md5ContextReKey, pKey, sizeof(USER_SESSION_KEY));
  1397. MD5Update(&Md5ContextReKey, (unsigned char*)&Sig.Nonce, sizeof(Sig.Nonce));
  1398. MD5Final(&Md5ContextReKey);
  1399. rc4_key(pRc4Sched, sizeof(USER_SESSION_KEY), Md5ContextReKey.digest);
  1400. }
  1401. }
  1402. //
  1403. // using HMAC hash, init it with the key
  1404. //
  1405. HMACMD5Init(&HMACMD5Context, pSignKey, sizeof(USER_SESSION_KEY));
  1406. //
  1407. // include the message sequence number
  1408. //
  1409. HMACMD5Update(&HMACMD5Context, (unsigned char*)&Sig.Nonce, sizeof(Sig.Nonce));
  1410. for (i = 0; i < pMessage->cBuffers; i++)
  1411. {
  1412. if (((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_DATA) &&
  1413. (pMessage->pBuffers[i].cbBuffer != 0))
  1414. {
  1415. //
  1416. // decrypt (before checksum...) if it's not READ_ONLY
  1417. //
  1418. if ((Op == eUnseal)
  1419. && !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
  1420. {
  1421. rc4(
  1422. pRc4Sched,
  1423. pMessage->pBuffers[i].cbBuffer,
  1424. (UCHAR*)(pMessage->pBuffers[i].pvBuffer)
  1425. );
  1426. }
  1427. HMACMD5Update(
  1428. &HMACMD5Context,
  1429. (UCHAR*)(pMessage->pBuffers[i].pvBuffer),
  1430. pMessage->pBuffers[i].cbBuffer
  1431. );
  1432. //
  1433. // Encrypt if its not READ_ONLY
  1434. //
  1435. if ((Op == eSeal)
  1436. && !(pMessage->pBuffers[i].BufferType & SECBUFFER_READONLY))
  1437. {
  1438. rc4(
  1439. pRc4Sched,
  1440. pMessage->pBuffers[i].cbBuffer,
  1441. (UCHAR*)(pMessage->pBuffers[i].pvBuffer)
  1442. );
  1443. }
  1444. }
  1445. }
  1446. HMACMD5Final(&HMACMD5Context, TempSig);
  1447. //
  1448. // use RandomPad and Checksum fields for 8 bytes of MD5 hash
  1449. //
  1450. _fmemcpy(&Sig.RandomPad, TempSig, 8);
  1451. //
  1452. // if we're using crypto for KEY_EXCH, may as well use it for signing too
  1453. //
  1454. if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
  1455. {
  1456. rc4(
  1457. pRc4Sched,
  1458. 8,
  1459. (UCHAR*)(&Sig.RandomPad)
  1460. );
  1461. }
  1462. _fmemcpy(pSig, &Sig, sizeof(NTLMSSP_MESSAGE_SIGNATURE));
  1463. }
  1464. SspPrint((SSP_NTLMV2, "Leaving SspSignSealHelper %#x\n", NtStatus));
  1465. return STATUS_SUCCESS;
  1466. }
  1467. SECURITY_STATUS
  1468. SspNtlmv2MakeSignature(
  1469. IN NTLMV2_DERIVED_SKEYS* pNtlmv2Keys,
  1470. IN ULONG NegotiateFlags,
  1471. IN ULONG fQOP,
  1472. IN ULONG MessageSeqNo,
  1473. IN OUT SecBufferDesc* pMessage
  1474. )
  1475. /*++
  1476. Routine Description:
  1477. Make signature of a message
  1478. Arguments:
  1479. pNtlmv2Keys - key materials
  1480. NegotiateFlags - negotiate Flags
  1481. fQOP - quality of protection
  1482. MessageSeqNo - message Sequence Number
  1483. pMessage - message buffer descriptor
  1484. Return Value:
  1485. SECURITY_STATUS
  1486. --*/
  1487. {
  1488. NTSTATUS Status = STATUS_SUCCESS;
  1489. NTLMSSP_MESSAGE_SIGNATURE Sig;
  1490. NTLMSSP_MESSAGE_SIGNATURE *pSig;
  1491. Status = SspSignSealHelper(
  1492. pNtlmv2Keys,
  1493. NegotiateFlags,
  1494. eSign,
  1495. MessageSeqNo,
  1496. pMessage,
  1497. &Sig,
  1498. &pSig
  1499. );
  1500. if (NT_SUCCESS(Status))
  1501. {
  1502. _fmemcpy(pSig, &Sig, sizeof(NTLMSSP_MESSAGE_SIGNATURE));
  1503. }
  1504. return SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR);
  1505. }
  1506. SECURITY_STATUS
  1507. SspNtlmv2VerifySignature(
  1508. IN NTLMV2_DERIVED_SKEYS* pNtlmv2Keys,
  1509. IN ULONG NegotiateFlags,
  1510. IN ULONG MessageSeqNo,
  1511. IN OUT SecBufferDesc* pMessage,
  1512. OUT ULONG* pfQOP
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. Verify signature of a message
  1517. Arguments:
  1518. pNtlmv2Keys - key materials
  1519. NegotiateFlags - negotiate Flags
  1520. MessageSeqNo - message Sequence Number
  1521. pMessage - message buffer descriptor
  1522. pfQOP - quality of protection
  1523. Return Value:
  1524. SECURITY_STATUS
  1525. --*/
  1526. {
  1527. NTSTATUS Status = STATUS_SUCCESS;
  1528. NTLMSSP_MESSAGE_SIGNATURE Sig;
  1529. NTLMSSP_MESSAGE_SIGNATURE* pSig; // pointer to buffer with sig in it
  1530. NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // Aligned sig buffer.
  1531. Status = SspSignSealHelper(
  1532. pNtlmv2Keys,
  1533. NegotiateFlags,
  1534. eVerify,
  1535. MessageSeqNo,
  1536. pMessage,
  1537. &Sig,
  1538. &pSig
  1539. );
  1540. if (NT_SUCCESS(Status))
  1541. {
  1542. _fmemcpy(&AlignedSig, pSig, sizeof(AlignedSig));
  1543. if (AlignedSig.Version != NTLM_SIGN_VERSION)
  1544. {
  1545. return SEC_E_INVALID_TOKEN;
  1546. }
  1547. //
  1548. // validate the signature...
  1549. //
  1550. if (AlignedSig.CheckSum != Sig.CheckSum)
  1551. {
  1552. return SEC_E_MESSAGE_ALTERED;
  1553. }
  1554. //
  1555. // with MD5 sig, this now matters!
  1556. //
  1557. if (AlignedSig.RandomPad != Sig.RandomPad)
  1558. {
  1559. return SEC_E_MESSAGE_ALTERED;
  1560. }
  1561. if (AlignedSig.Nonce != Sig.Nonce)
  1562. {
  1563. return SEC_E_OUT_OF_SEQUENCE;
  1564. }
  1565. }
  1566. return SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR);
  1567. }
  1568. SECURITY_STATUS
  1569. SspNtlmv2SealMessage(
  1570. IN NTLMV2_DERIVED_SKEYS* pNtlmv2Keys,
  1571. IN ULONG NegotiateFlags,
  1572. IN ULONG fQOP,
  1573. IN ULONG MessageSeqNo,
  1574. IN OUT SecBufferDesc* pMessage
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. Seal a message
  1579. Arguments:
  1580. pNtlmv2Keys - key materials
  1581. NegotiateFlags - negotiate Flags
  1582. fQOP - quality of protection
  1583. MessageSeqNo - message Sequence Number
  1584. pMessage - message buffer descriptor
  1585. Return Value:
  1586. SECURITY_STATUS
  1587. --*/
  1588. {
  1589. NTSTATUS Status = STATUS_SUCCESS;
  1590. NTLMSSP_MESSAGE_SIGNATURE Sig;
  1591. NTLMSSP_MESSAGE_SIGNATURE* pSig; // pointer to buffer where sig goes
  1592. ULONG i;
  1593. Status = SspSignSealHelper(
  1594. pNtlmv2Keys,
  1595. NegotiateFlags,
  1596. eSeal,
  1597. MessageSeqNo,
  1598. pMessage,
  1599. &Sig,
  1600. &pSig
  1601. );
  1602. if (NT_SUCCESS(Status))
  1603. {
  1604. _fmemcpy(pSig, &Sig, sizeof(NTLMSSP_MESSAGE_SIGNATURE));
  1605. //
  1606. // for gss style sign/seal, strip the padding as RC4 requires none.
  1607. // (in fact, we rely on this to simplify the size computation in
  1608. // DecryptMessage). if we support some other block cipher, need to rev
  1609. // the NTLM_ token version to make blocksize
  1610. //
  1611. for (i = 0; i < pMessage->cBuffers; i++)
  1612. {
  1613. if ((pMessage->pBuffers[i].BufferType & 0xFF) == SECBUFFER_PADDING)
  1614. {
  1615. //
  1616. // no padding required!
  1617. //
  1618. pMessage->pBuffers[i].cbBuffer = 0;
  1619. break;
  1620. }
  1621. }
  1622. }
  1623. return SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR);
  1624. }
  1625. SECURITY_STATUS
  1626. SspNtlmv2UnsealMessage(
  1627. IN NTLMV2_DERIVED_SKEYS* pNtlmv2Keys,
  1628. IN ULONG NegotiateFlags,
  1629. IN ULONG MessageSeqNo,
  1630. IN OUT SecBufferDesc* pMessage,
  1631. OUT ULONG* pfQOP
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. Unseal a message
  1636. Arguments:
  1637. pNtlmv2Keys - key materials
  1638. NegotiateFlags - negotiate Flags
  1639. MessageSeqNo - message Sequence Number
  1640. pMessage - message buffer descriptor
  1641. pfQOP - quality of protection
  1642. Return Value:
  1643. SECURITY_STATUS
  1644. --*/
  1645. {
  1646. NTSTATUS Status = STATUS_SUCCESS;
  1647. NTLMSSP_MESSAGE_SIGNATURE Sig;
  1648. NTLMSSP_MESSAGE_SIGNATURE* pSig; // pointer to buffer where sig goes
  1649. NTLMSSP_MESSAGE_SIGNATURE AlignedSig; // aligned buffer.
  1650. SecBufferDesc* pMessageBuffers = pMessage;
  1651. ULONG Index;
  1652. SecBuffer* pSignatureBuffer = NULL;
  1653. SecBuffer* pStreamBuffer = NULL;
  1654. SecBuffer* pDataBuffer = NULL;
  1655. SecBufferDesc ProcessBuffers;
  1656. SecBuffer wrap_bufs[2];
  1657. //
  1658. // Find the body and signature SecBuffers from pMessage
  1659. //
  1660. for (Index = 0; Index < pMessageBuffers->cBuffers; Index++)
  1661. {
  1662. if ((pMessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_TOKEN)
  1663. {
  1664. pSignatureBuffer = &pMessageBuffers->pBuffers[Index];
  1665. }
  1666. else if ((pMessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_STREAM)
  1667. {
  1668. pStreamBuffer = &pMessageBuffers->pBuffers[Index];
  1669. }
  1670. else if ((pMessageBuffers->pBuffers[Index].BufferType & ~SECBUFFER_ATTRMASK) == SECBUFFER_DATA)
  1671. {
  1672. pDataBuffer = &pMessageBuffers->pBuffers[Index];
  1673. }
  1674. }
  1675. if (pStreamBuffer != NULL)
  1676. {
  1677. if (pSignatureBuffer != NULL)
  1678. {
  1679. return SEC_E_INVALID_TOKEN;
  1680. }
  1681. //
  1682. // for version 1 NTLM blobs, padding is never present, since RC4 is
  1683. // stream cipher
  1684. //
  1685. wrap_bufs[0].cbBuffer = sizeof(NTLMSSP_MESSAGE_SIGNATURE);
  1686. wrap_bufs[1].cbBuffer = pStreamBuffer->cbBuffer - sizeof(NTLMSSP_MESSAGE_SIGNATURE);
  1687. if (pStreamBuffer->cbBuffer < wrap_bufs[0].cbBuffer)
  1688. {
  1689. return SEC_E_INVALID_TOKEN;
  1690. }
  1691. wrap_bufs[0].BufferType = SECBUFFER_TOKEN;
  1692. wrap_bufs[0].pvBuffer = pStreamBuffer->pvBuffer;
  1693. wrap_bufs[1].BufferType = SECBUFFER_DATA;
  1694. wrap_bufs[1].pvBuffer = (PBYTE)wrap_bufs[0].pvBuffer + wrap_bufs[0].cbBuffer;
  1695. if (pDataBuffer == NULL)
  1696. {
  1697. return SEC_E_INVALID_TOKEN;
  1698. }
  1699. pDataBuffer->cbBuffer = wrap_bufs[1].cbBuffer;
  1700. pDataBuffer->pvBuffer = wrap_bufs[1].pvBuffer;
  1701. ProcessBuffers.cBuffers = 2;
  1702. ProcessBuffers.pBuffers = wrap_bufs;
  1703. ProcessBuffers.ulVersion = SECBUFFER_VERSION;
  1704. }
  1705. else
  1706. {
  1707. ProcessBuffers = *pMessageBuffers;
  1708. }
  1709. Status = SspSignSealHelper(
  1710. pNtlmv2Keys,
  1711. NegotiateFlags,
  1712. eUnseal,
  1713. MessageSeqNo,
  1714. &ProcessBuffers,
  1715. &Sig,
  1716. &pSig
  1717. );
  1718. if (NT_SUCCESS(Status))
  1719. {
  1720. _fmemcpy(&AlignedSig, pSig, sizeof(AlignedSig));
  1721. if (AlignedSig.Version != NTLM_SIGN_VERSION)
  1722. {
  1723. return SEC_E_INVALID_TOKEN;
  1724. }
  1725. //
  1726. // validate the signature...
  1727. //
  1728. if (AlignedSig.CheckSum != Sig.CheckSum)
  1729. {
  1730. return SEC_E_MESSAGE_ALTERED;
  1731. }
  1732. if (AlignedSig.RandomPad != Sig.RandomPad)
  1733. {
  1734. return SEC_E_MESSAGE_ALTERED;
  1735. }
  1736. if (AlignedSig.Nonce != Sig.Nonce)
  1737. {
  1738. return SEC_E_OUT_OF_SEQUENCE;
  1739. }
  1740. }
  1741. return SspNtStatusToSecStatus(Status, SEC_E_INTERNAL_ERROR);
  1742. }
  1743. #ifdef MAC
  1744. VOID
  1745. SspSwapString32Bytes(
  1746. IN STRING32* pString
  1747. )
  1748. /*++
  1749. Routine Description:
  1750. Take a STRING32 struct and swap it's bytes (big and little endian).
  1751. Arguments:
  1752. pString - the STRING32 to swap
  1753. Return Value:
  1754. none
  1755. --*/
  1756. {
  1757. swapshort(pString->Length);
  1758. swapshort(pString->MaximumLength);
  1759. swaplong(pString->Buffer);
  1760. }
  1761. VOID
  1762. SspSwapChallengeMessageBytes(
  1763. IN CHALLENGE_MESSAGE* pChallengeMessage
  1764. )
  1765. /*++
  1766. Routine Description:
  1767. Make sure all the fields are in big endian format before parsing
  1768. the fields. This routine is only usefull when this code is running
  1769. on machines that align bigendian (ie. Macintosh computers).
  1770. Arguments:
  1771. pChallengeMessage - the challenge message to swap
  1772. Return Value:
  1773. none
  1774. --*/
  1775. {
  1776. if (pChallengeMessage)
  1777. {
  1778. swaplong(pChallengeMessage->NegotiateFlags);
  1779. SspSwapString32Bytes(&pChallengeMessage->TargetName);
  1780. SspSwapString32Bytes(&pChallengeMessage->TargetInfo);
  1781. }
  1782. }
  1783. VOID
  1784. SspSwapAuthenticateMessageBytes(
  1785. IN AUTHENTICATE_MESSAGE* pAuthenticateMessage
  1786. )
  1787. /*++
  1788. Routine Description:
  1789. Make sure all the fields are in little endian format before sending to a Windows
  1790. server. This routine is only usefull when this code is running on machines that
  1791. align bigendian (ie. Macintosh computers).
  1792. Arguments:
  1793. pAuthenticateMessage - the authentication message to swap
  1794. Return Value:
  1795. none
  1796. --*/
  1797. {
  1798. if (pAuthenticateMessage)
  1799. {
  1800. SspSwapString32Bytes(&pAuthenticateMessage->LmChallengeResponse);
  1801. SspSwapString32Bytes(&pAuthenticateMessage->NtChallengeResponse);
  1802. SspSwapString32Bytes(&pAuthenticateMessage->DomainName);
  1803. SspSwapString32Bytes(&pAuthenticateMessage->UserName);
  1804. SspSwapString32Bytes(&pAuthenticateMessage->Workstation);
  1805. SspSwapString32Bytes(&pAuthenticateMessage->SessionKey);
  1806. swaplong(pAuthenticateMessage->NegotiateFlags);
  1807. }
  1808. }
  1809. LONG
  1810. SspMacSecondsFromGMT(void)
  1811. /*++
  1812. Routine Description:
  1813. Get the numbers of seconds from GMT on Macintosh computers.
  1814. Arguments:
  1815. None.
  1816. Return Value:
  1817. LONG - seconds from GMT (in mac time format)
  1818. --*/
  1819. {
  1820. MachineLocation location;
  1821. DWORD secondsFromGMT;
  1822. ReadLocation(&location);
  1823. secondsFromGMT = location.u.gmtDelta & 0x00FFFFFF;
  1824. if (secondsFromGMT & 0x00800000)
  1825. secondsFromGMT |= 0xFF000000;
  1826. return secondsFromGMT;
  1827. }
  1828. #endif