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.

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