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.

2203 lines
61 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. access.c
  5. Abstract:
  6. This module contains routines for interfacing to the security
  7. system in NT.
  8. --*/
  9. #include "precomp.h"
  10. #include "access.tmh"
  11. #pragma hdrstop
  12. #include <ntlmsp.h>
  13. #include <hmac.h>
  14. #define BugCheckFileId SRV_FILE_ACCESS
  15. #if DBG
  16. ULONG SrvLogonCount = 0;
  17. ULONG SrvNullLogonCount = 0;
  18. #endif
  19. #define ROUND_UP_COUNT(Count,Pow2) \
  20. ( ((Count)+(Pow2)-1) & (~((Pow2)-1)) )
  21. typedef struct _LOGON_INFO {
  22. PWCH WorkstationName;
  23. ULONG WorkstationNameLength;
  24. PWCH DomainName;
  25. ULONG DomainNameLength;
  26. PWCH UserName;
  27. ULONG UserNameLength;
  28. PCHAR CaseInsensitivePassword;
  29. ULONG CaseInsensitivePasswordLength;
  30. PCHAR CaseSensitivePassword;
  31. ULONG CaseSensitivePasswordLength;
  32. CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH];
  33. LUID LogonId;
  34. CtxtHandle Token;
  35. USHORT Uid;
  36. BOOLEAN HaveHandle;
  37. LARGE_INTEGER KickOffTime;
  38. LARGE_INTEGER LogOffTime;
  39. USHORT Action;
  40. BOOLEAN GuestLogon;
  41. BOOLEAN EncryptedLogon;
  42. BOOLEAN NtSmbs;
  43. BOOLEAN IsNullSession;
  44. BOOLEAN IsAdmin;
  45. CHAR NtUserSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
  46. CHAR LanManSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
  47. } LOGON_INFO, *PLOGON_INFO;
  48. NTSTATUS
  49. DoUserLogon (
  50. IN PLOGON_INFO LogonInfo,
  51. IN BOOLEAN SecuritySignatureDesired,
  52. IN PCONNECTION Connection OPTIONAL,
  53. IN PSESSION Session
  54. );
  55. NTSTATUS
  56. AcquireExtensibleSecurityCredentials (
  57. VOID
  58. );
  59. NTSTATUS
  60. SrvGetLogonId(
  61. PCtxtHandle Handle,
  62. PLUID LogonId
  63. );
  64. ULONG SrvHaveCreds = 0;
  65. //
  66. // 24 hours short of never, in case any utc/local conversions are done
  67. //
  68. #define SRV_NEVER_TIME (0x7FFFFFFFFFFFFFFFI64 - 0xC92A69C000I64)
  69. #define HAVENTLM 1
  70. #define HAVEEXTENDED 2
  71. #ifdef ALLOC_PRAGMA
  72. #pragma alloc_text( PAGE, SrvValidateUser )
  73. #pragma alloc_text( PAGE, DoUserLogon )
  74. #pragma alloc_text( PAGE, SrvIsAdmin )
  75. #pragma alloc_text( PAGE, SrvFreeSecurityContexts )
  76. #pragma alloc_text( PAGE, AcquireLMCredentials )
  77. #pragma alloc_text( PAGE, AcquireExtensibleSecurityCredentials )
  78. #pragma alloc_text( PAGE, SrvValidateSecurityBuffer )
  79. #pragma alloc_text( PAGE, SrvGetUserAndDomainName )
  80. #pragma alloc_text( PAGE, SrvReleaseUserAndDomainName )
  81. #pragma alloc_text( PAGE, SrvGetExtensibleSecurityNegotiateBuffer )
  82. #pragma alloc_text( PAGE, SrvGetLogonId )
  83. #pragma alloc_text( PAGE, SrvInitializeSmbSecuritySignature )
  84. #pragma alloc_text( PAGE, SrvAddSecurityCredentials )
  85. #endif
  86. NTSTATUS
  87. SrvValidateUser (
  88. OUT CtxtHandle *Token,
  89. IN PSESSION Session OPTIONAL,
  90. IN PCONNECTION Connection OPTIONAL,
  91. IN PUNICODE_STRING UserName OPTIONAL,
  92. IN PCHAR CaseInsensitivePassword,
  93. IN CLONG CaseInsensitivePasswordLength,
  94. IN PCHAR CaseSensitivePassword OPTIONAL,
  95. IN CLONG CaseSensitivePasswordLength,
  96. IN BOOLEAN SmbSecuritySignatureIfPossible,
  97. OUT PUSHORT Action OPTIONAL
  98. )
  99. /*++
  100. Routine Description:
  101. Validates a username/password combination by interfacing to the
  102. security subsystem.
  103. Arguments:
  104. Session - A pointer to a session block so that this routine can
  105. insert a user token.
  106. Connection - A pointer to the connection this user is on.
  107. UserName - ASCIIZ string corresponding to the user name to validate.
  108. CaseInsensitivePassword - ASCII (not ASCIIZ) string containing
  109. password for the user.
  110. CaseInsensitivePasswordLength - Length of Password, in bytes.
  111. This includes the null terminator when the password is not
  112. encrypted.
  113. CaseSensitivePassword - a mixed case, Unicode version of the password.
  114. This is only supplied by NT clients; for downlevel clients,
  115. it will be NULL.
  116. CaseSensitivePasswordLength - the length of the case-sensitive password.
  117. Action - This is part of the sessionsetupandx response.
  118. Return Value:
  119. NTSTATUS from the security system.
  120. --*/
  121. {
  122. NTSTATUS status;
  123. LOGON_INFO logonInfo;
  124. PPAGED_CONNECTION pagedConnection;
  125. UNICODE_STRING domainName;
  126. PAGED_CODE( );
  127. INVALIDATE_SECURITY_HANDLE( *Token );
  128. RtlZeroMemory( &logonInfo, sizeof( logonInfo ) );
  129. //
  130. // Load input parameters for DoUserLogon into the LOGON_INFO struct.
  131. //
  132. // If this is the server's initialization attempt at creating a null
  133. // session, then the Connection and Session pointers will be NULL.
  134. //
  135. domainName.Buffer = NULL;
  136. domainName.Length = 0;
  137. if ( ARGUMENT_PRESENT(Connection) ) {
  138. pagedConnection = Connection->PagedConnection;
  139. logonInfo.WorkstationName =
  140. Connection->ClientMachineNameString.Buffer;
  141. logonInfo.WorkstationNameLength =
  142. Connection->ClientMachineNameString.Length;
  143. RtlCopyMemory(
  144. logonInfo.EncryptionKey,
  145. pagedConnection->EncryptionKey,
  146. MSV1_0_CHALLENGE_LENGTH
  147. );
  148. logonInfo.NtSmbs = CLIENT_CAPABLE_OF( NT_SMBS, Connection );
  149. ASSERT( ARGUMENT_PRESENT(Session) );
  150. SrvGetUserAndDomainName( Session, NULL, &domainName );
  151. logonInfo.DomainName = domainName.Buffer;
  152. logonInfo.DomainNameLength = domainName.Length;
  153. } else {
  154. ASSERT( !ARGUMENT_PRESENT(Session) );
  155. logonInfo.WorkstationName = StrNull;
  156. logonInfo.DomainName = StrNull;
  157. }
  158. if ( ARGUMENT_PRESENT(UserName) ) {
  159. logonInfo.UserName = UserName->Buffer;
  160. logonInfo.UserNameLength = UserName->Length;
  161. } else {
  162. logonInfo.UserName = StrNull;
  163. }
  164. logonInfo.CaseSensitivePassword = CaseSensitivePassword;
  165. logonInfo.CaseSensitivePasswordLength = CaseSensitivePasswordLength;
  166. logonInfo.CaseInsensitivePassword = CaseInsensitivePassword;
  167. logonInfo.CaseInsensitivePasswordLength = CaseInsensitivePasswordLength;
  168. INVALIDATE_SECURITY_HANDLE( logonInfo.Token );
  169. if ( ARGUMENT_PRESENT(Action) ) {
  170. logonInfo.Action = *Action;
  171. }
  172. if( ARGUMENT_PRESENT(Session) ) {
  173. logonInfo.Uid = Session->Uid;
  174. }
  175. //
  176. // Attempt the logon.
  177. //
  178. status = DoUserLogon( &logonInfo, SmbSecuritySignatureIfPossible, Connection, Session );
  179. if( logonInfo.HaveHandle ) {
  180. *Token = logonInfo.Token;
  181. }
  182. if( domainName.Buffer ) {
  183. SrvReleaseUserAndDomainName( Session, NULL, &domainName );
  184. }
  185. if ( NT_SUCCESS(status) ) {
  186. //
  187. // The logon succeeded. Save output data.
  188. //
  189. if ( ARGUMENT_PRESENT(Session) ) {
  190. Session->LogonId = logonInfo.LogonId;
  191. Session->KickOffTime = logonInfo.KickOffTime;
  192. Session->LogOffTime = logonInfo.LogOffTime;
  193. Session->GuestLogon = logonInfo.GuestLogon;
  194. Session->EncryptedLogon = logonInfo.EncryptedLogon;
  195. Session->IsNullSession = logonInfo.IsNullSession;
  196. Session->IsAdmin = logonInfo.IsAdmin;
  197. RtlCopyMemory(
  198. Session->NtUserSessionKey,
  199. logonInfo.NtUserSessionKey,
  200. MSV1_0_USER_SESSION_KEY_LENGTH
  201. );
  202. RtlCopyMemory(
  203. Session->LanManSessionKey,
  204. logonInfo.LanManSessionKey,
  205. MSV1_0_LANMAN_SESSION_KEY_LENGTH
  206. );
  207. SET_BLOCK_STATE( Session, BlockStateActive );
  208. }
  209. if ( ARGUMENT_PRESENT(Action) ) {
  210. *Action = logonInfo.Action;
  211. if( logonInfo.GuestLogon ) {
  212. *Action |= SMB_SETUP_GUEST;
  213. }
  214. }
  215. }
  216. return status;
  217. } // SrvValidateUser
  218. NTSTATUS
  219. DoUserLogon (
  220. IN PLOGON_INFO LogonInfo,
  221. IN BOOLEAN SecuritySignatureDesired,
  222. IN PCONNECTION Connection OPTIONAL,
  223. IN OPTIONAL PSESSION Session
  224. )
  225. /*++
  226. Routine Description:
  227. Validates a username/password combination by interfacing to the
  228. security subsystem.
  229. Arguments:
  230. LogonInfo - Pointer to a block containing in/out information about
  231. the logon.
  232. Return Value:
  233. NTSTATUS from the security system.
  234. --*/
  235. {
  236. NTSTATUS status, subStatus;
  237. ULONG actualUserInfoBufferLength;
  238. ULONG oldSessionCount;
  239. LUID LogonId;
  240. ULONG Catts = 0;
  241. LARGE_INTEGER Expiry;
  242. ULONG BufferOffset;
  243. SecBufferDesc InputToken;
  244. SecBuffer InputBuffers[3];
  245. SecBufferDesc OutputToken;
  246. SecBuffer OutputBuffer;
  247. PNTLM_AUTHENTICATE_MESSAGE NtlmInToken = NULL;
  248. PAUTHENTICATE_MESSAGE InToken = NULL;
  249. PNTLM_ACCEPT_RESPONSE OutToken = NULL;
  250. ULONG NtlmInTokenSize;
  251. ULONG InTokenSize;
  252. ULONG OutTokenSize;
  253. ULONG_PTR AllocateSize;
  254. ULONG profileBufferLength;
  255. PAGED_CODE( );
  256. LogonInfo->IsNullSession = FALSE;
  257. LogonInfo->IsAdmin = FALSE;
  258. #if DBG
  259. SrvLogonCount++;
  260. #endif
  261. //
  262. // If this is a null session request, use the cached null session
  263. // token, which was created during server startup ( if we got one! )
  264. //
  265. if ( (LogonInfo->UserNameLength == 0) &&
  266. (LogonInfo->CaseSensitivePasswordLength == 0) &&
  267. ( (LogonInfo->CaseInsensitivePasswordLength == 0) ||
  268. ( (LogonInfo->CaseInsensitivePasswordLength == 1) &&
  269. (*LogonInfo->CaseInsensitivePassword == '\0') ) ) ) {
  270. if( CONTEXT_NULL( SrvNullSessionToken ) ) {
  271. if( SrvFspActive ) {
  272. return STATUS_ACCESS_DENIED;
  273. }
  274. } else {
  275. LogonInfo->IsNullSession = TRUE;
  276. #if DBG
  277. SrvNullLogonCount++;
  278. #endif
  279. LogonInfo->HaveHandle = TRUE;
  280. LogonInfo->Token = SrvNullSessionToken;
  281. LogonInfo->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
  282. LogonInfo->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
  283. LogonInfo->GuestLogon = FALSE;
  284. LogonInfo->EncryptedLogon = FALSE;
  285. return STATUS_SUCCESS;
  286. }
  287. }
  288. //
  289. // First make sure we have a credential handle
  290. //
  291. if ((SrvHaveCreds & HAVENTLM) == 0) {
  292. status = AcquireLMCredentials();
  293. if (!NT_SUCCESS(status)) {
  294. goto error_exit;
  295. }
  296. }
  297. //
  298. // Figure out how big a buffer we need. We put all the messages
  299. // in one buffer for efficiency's sake.
  300. //
  301. NtlmInTokenSize = sizeof(NTLM_AUTHENTICATE_MESSAGE);
  302. NtlmInTokenSize = (NtlmInTokenSize + 3) & 0xfffffffc;
  303. InTokenSize = sizeof(AUTHENTICATE_MESSAGE) +
  304. LogonInfo->UserNameLength +
  305. LogonInfo->WorkstationNameLength +
  306. LogonInfo->DomainNameLength +
  307. LogonInfo->CaseInsensitivePasswordLength +
  308. ROUND_UP_COUNT(LogonInfo->CaseSensitivePasswordLength, sizeof(USHORT));
  309. InTokenSize = (InTokenSize + 3) & 0xfffffffc;
  310. OutTokenSize = sizeof(NTLM_ACCEPT_RESPONSE);
  311. OutTokenSize = (OutTokenSize + 3) & 0xfffffffc;
  312. //
  313. // Round this up to 8 byte boundary because the out token needs to be
  314. // quad word aligned for the LARGE_INTEGER.
  315. //
  316. AllocateSize = ((NtlmInTokenSize + InTokenSize + 7) & 0xfffffff8) + OutTokenSize;
  317. status = STATUS_SUCCESS ;
  318. InToken = ExAllocatePool( PagedPool, AllocateSize );
  319. if ( InToken == NULL )
  320. {
  321. status = STATUS_NO_MEMORY ;
  322. }
  323. if ( !NT_SUCCESS(status) ) {
  324. actualUserInfoBufferLength = (ULONG)AllocateSize;
  325. INTERNAL_ERROR(
  326. ERROR_LEVEL_EXPECTED,
  327. "SrvValidateUser: ExAllocatePool failed: %X\n.",
  328. status,
  329. NULL
  330. );
  331. SrvLogError(
  332. SrvDeviceObject,
  333. EVENT_SRV_NO_VIRTUAL_MEMORY,
  334. status,
  335. &actualUserInfoBufferLength,
  336. sizeof(ULONG),
  337. NULL,
  338. 0
  339. );
  340. status = STATUS_INSUFF_SERVER_RESOURCES;
  341. goto error_exit;
  342. }
  343. //
  344. // Zero the input tokens
  345. //
  346. RtlZeroMemory(
  347. InToken,
  348. InTokenSize + NtlmInTokenSize
  349. );
  350. NtlmInToken = (PNTLM_AUTHENTICATE_MESSAGE) ((PUCHAR) InToken + InTokenSize);
  351. OutToken = (PNTLM_ACCEPT_RESPONSE) ((PUCHAR) (((ULONG_PTR) NtlmInToken + NtlmInTokenSize + 7) & ~7));
  352. //
  353. // First set up the NtlmInToken, since it is the easiest.
  354. //
  355. RtlCopyMemory(
  356. NtlmInToken->ChallengeToClient,
  357. LogonInfo->EncryptionKey,
  358. MSV1_0_CHALLENGE_LENGTH
  359. );
  360. NtlmInToken->ParameterControl = 0;
  361. //
  362. // Okay, now for the tought part - marshalling the AUTHENTICATE_MESSAGE
  363. //
  364. RtlCopyMemory( InToken->Signature,
  365. NTLMSSP_SIGNATURE,
  366. sizeof(NTLMSSP_SIGNATURE));
  367. InToken->MessageType = NtLmAuthenticate;
  368. BufferOffset = sizeof(AUTHENTICATE_MESSAGE);
  369. //
  370. // LM password - case insensitive
  371. //
  372. InToken->LmChallengeResponse.Buffer = BufferOffset;
  373. InToken->LmChallengeResponse.Length =
  374. InToken->LmChallengeResponse.MaximumLength =
  375. (USHORT) LogonInfo->CaseInsensitivePasswordLength;
  376. RtlCopyMemory( BufferOffset + (PCHAR) InToken,
  377. LogonInfo->CaseInsensitivePassword,
  378. LogonInfo->CaseInsensitivePasswordLength);
  379. BufferOffset += ROUND_UP_COUNT(LogonInfo->CaseInsensitivePasswordLength, sizeof(USHORT));
  380. //
  381. // NT password - case sensitive
  382. //
  383. InToken->NtChallengeResponse.Buffer = BufferOffset;
  384. InToken->NtChallengeResponse.Length =
  385. InToken->NtChallengeResponse.MaximumLength =
  386. (USHORT) LogonInfo->CaseSensitivePasswordLength;
  387. RtlCopyMemory( BufferOffset + (PCHAR) InToken,
  388. LogonInfo->CaseSensitivePassword,
  389. LogonInfo->CaseSensitivePasswordLength);
  390. BufferOffset += LogonInfo->CaseSensitivePasswordLength;
  391. //
  392. // Domain Name
  393. //
  394. InToken->DomainName.Buffer = BufferOffset;
  395. InToken->DomainName.Length =
  396. InToken->DomainName.MaximumLength =
  397. (USHORT) LogonInfo->DomainNameLength;
  398. RtlCopyMemory( BufferOffset + (PCHAR) InToken,
  399. LogonInfo->DomainName,
  400. LogonInfo->DomainNameLength);
  401. BufferOffset += LogonInfo->DomainNameLength;
  402. //
  403. // Workstation Name
  404. //
  405. InToken->Workstation.Buffer = BufferOffset;
  406. InToken->Workstation.Length =
  407. InToken->Workstation.MaximumLength =
  408. (USHORT) LogonInfo->WorkstationNameLength;
  409. RtlCopyMemory( BufferOffset + (PCHAR) InToken,
  410. LogonInfo->WorkstationName,
  411. LogonInfo->WorkstationNameLength);
  412. BufferOffset += LogonInfo->WorkstationNameLength;
  413. //
  414. // User Name
  415. //
  416. InToken->UserName.Buffer = BufferOffset;
  417. InToken->UserName.Length =
  418. InToken->UserName.MaximumLength =
  419. (USHORT) LogonInfo->UserNameLength;
  420. RtlCopyMemory( BufferOffset + (PCHAR) InToken,
  421. LogonInfo->UserName,
  422. LogonInfo->UserNameLength);
  423. BufferOffset += LogonInfo->UserNameLength;
  424. //
  425. // Setup all the buffers properly
  426. //
  427. InputToken.pBuffers = InputBuffers;
  428. if (Connection && (Connection->SockAddr[0] != 0))
  429. {
  430. InputToken.cBuffers = 3;
  431. InputBuffers[2].pvBuffer = Connection->SockAddr;
  432. InputBuffers[2].cbBuffer = SRV_CONNECTION_SOCKADDR_SIZE;
  433. InputBuffers[2].BufferType = SECBUFFER_IPADDRESS;
  434. }
  435. else
  436. {
  437. InputToken.cBuffers = 2;
  438. }
  439. InputToken.ulVersion = 0;
  440. InputBuffers[0].pvBuffer = InToken;
  441. InputBuffers[0].cbBuffer = InTokenSize;
  442. InputBuffers[0].BufferType = SECBUFFER_TOKEN;
  443. InputBuffers[1].pvBuffer = NtlmInToken;
  444. InputBuffers[1].cbBuffer = NtlmInTokenSize;
  445. InputBuffers[1].BufferType = SECBUFFER_TOKEN;
  446. OutputToken.pBuffers = &OutputBuffer;
  447. OutputToken.cBuffers = 1;
  448. OutputToken.ulVersion = 0;
  449. OutputBuffer.pvBuffer = OutToken;
  450. OutputBuffer.cbBuffer = OutTokenSize;
  451. OutputBuffer.BufferType = SECBUFFER_TOKEN;
  452. SrvStatistics.SessionLogonAttempts++;
  453. status = AcceptSecurityContext(
  454. &SrvLmLsaHandle,
  455. NULL,
  456. &InputToken,
  457. ASC_REQ_ALLOW_NON_USER_LOGONS | ASC_REQ_ALLOW_NULL_SESSION,
  458. SECURITY_NATIVE_DREP,
  459. &LogonInfo->Token,
  460. &OutputToken,
  461. &Catts,
  462. (PTimeStamp) &Expiry
  463. );
  464. status = MapSecurityError( status );
  465. if ( !NT_SUCCESS(status) ) {
  466. INVALIDATE_SECURITY_HANDLE( LogonInfo->Token );
  467. INTERNAL_ERROR(
  468. ERROR_LEVEL_EXPECTED,
  469. "SrvValidateUser: LsaLogonUser failed: %X",
  470. status,
  471. NULL
  472. );
  473. ExFreePool( InToken );
  474. goto error_exit;
  475. }
  476. LogonInfo->KickOffTime = OutToken->KickoffTime;
  477. // Sspi will return time in LocalTime, convert to SystemTime
  478. ExLocalTimeToSystemTime( &Expiry, &LogonInfo->LogOffTime );
  479. //LogonInfo->LogOffTime = Expiry;
  480. LogonInfo->GuestLogon = (BOOLEAN)(OutToken->UserFlags & LOGON_GUEST);
  481. LogonInfo->EncryptedLogon = (BOOLEAN)!(OutToken->UserFlags & LOGON_NOENCRYPTION);
  482. LogonInfo->LogonId = OutToken->LogonId;
  483. LogonInfo->HaveHandle = TRUE;
  484. if ( (OutToken->UserFlags & LOGON_USED_LM_PASSWORD) &&
  485. LogonInfo->NtSmbs ) {
  486. ASSERT( MSV1_0_USER_SESSION_KEY_LENGTH >=
  487. MSV1_0_LANMAN_SESSION_KEY_LENGTH );
  488. RtlZeroMemory(
  489. LogonInfo->NtUserSessionKey,
  490. MSV1_0_USER_SESSION_KEY_LENGTH
  491. );
  492. RtlCopyMemory(
  493. LogonInfo->NtUserSessionKey,
  494. OutToken->LanmanSessionKey,
  495. MSV1_0_LANMAN_SESSION_KEY_LENGTH
  496. );
  497. //
  498. // Turn on bit 1 to tell the client that we are using
  499. // the lm session key instead of the user session key.
  500. //
  501. LogonInfo->Action |= SMB_SETUP_USE_LANMAN_KEY;
  502. } else {
  503. RtlCopyMemory(
  504. LogonInfo->NtUserSessionKey,
  505. OutToken->UserSessionKey,
  506. MSV1_0_USER_SESSION_KEY_LENGTH
  507. );
  508. }
  509. //
  510. // If we have a session and we didn't do a guest logon, start up
  511. // security signatures if requested
  512. //
  513. if ( ARGUMENT_PRESENT( Connection ) &&
  514. SecuritySignatureDesired &&
  515. LogonInfo->GuestLogon == FALSE &&
  516. ( SrvSmbSecuritySignaturesRequired ||
  517. SrvEnableW9xSecuritySignatures ||
  518. CLIENT_CAPABLE_OF(NT_STATUS, Connection) )
  519. )
  520. {
  521. if( ARGUMENT_PRESENT( Session ) )
  522. {
  523. if( SrvRequireExtendedSignatures ||
  524. (SrvEnableExtendedSignatures &&
  525. IS_NT_DIALECT(Connection->SmbDialect) &&
  526. CLIENT_CAPABLE_OF( EXTENDED_SECURITY, Connection ) ) )
  527. {
  528. // This session is going to be used as the key for signatures, mark it as unavailible until
  529. // the client tries to upgrade to extended signatures
  530. Session->SessionKeyState = SrvSessionKeyAuthenticating;
  531. }
  532. else
  533. {
  534. Session->SessionKeyState = SrvSessionKeyAvailible;
  535. }
  536. }
  537. SrvInitializeSmbSecuritySignature(
  538. Connection,
  539. LogonInfo->NtUserSessionKey,
  540. ((OutToken->UserFlags & LOGON_USED_LM_PASSWORD) != 0) ?
  541. LogonInfo->CaseInsensitivePassword :
  542. LogonInfo->CaseSensitivePassword,
  543. ((OutToken->UserFlags & LOGON_USED_LM_PASSWORD) != 0) ?
  544. LogonInfo->CaseInsensitivePasswordLength :
  545. LogonInfo->CaseSensitivePasswordLength
  546. );
  547. }
  548. else
  549. {
  550. if( ARGUMENT_PRESENT(Session) )
  551. {
  552. // This key is not used for signing, so no work is necessary
  553. Session->SessionKeyState = SrvSessionKeyAvailible;
  554. }
  555. }
  556. RtlCopyMemory(
  557. LogonInfo->LanManSessionKey,
  558. OutToken->LanmanSessionKey,
  559. MSV1_0_LANMAN_SESSION_KEY_LENGTH
  560. );
  561. ExFreePool( InToken );
  562. //
  563. // Note whether or not this user is an administrator
  564. //
  565. LogonInfo->IsAdmin = SrvIsAdmin( LogonInfo->Token );
  566. //
  567. // One last check: Is our session count being exceeded?
  568. // We will let the session be exceeded by 1 iff the client
  569. // is an administrator.
  570. //
  571. if( LogonInfo->IsNullSession == FALSE ) {
  572. oldSessionCount = ExInterlockedAddUlong(
  573. &SrvStatistics.CurrentNumberOfSessions,
  574. 1,
  575. &GLOBAL_SPIN_LOCK(Statistics)
  576. );
  577. SrvInhibitIdlePowerDown();
  578. if ( ARGUMENT_PRESENT(Session) && (!Session->IsSessionExpired && oldSessionCount >= SrvMaxUsers) ) {
  579. if( oldSessionCount != SrvMaxUsers || !LogonInfo->IsAdmin ) {
  580. ExInterlockedAddUlong(
  581. &SrvStatistics.CurrentNumberOfSessions,
  582. (ULONG)-1,
  583. &GLOBAL_SPIN_LOCK(Statistics)
  584. );
  585. DeleteSecurityContext( &LogonInfo->Token );
  586. INVALIDATE_SECURITY_HANDLE( LogonInfo->Token );
  587. status = STATUS_REQUEST_NOT_ACCEPTED;
  588. SrvAllowIdlePowerDown();
  589. goto error_exit;
  590. }
  591. }
  592. }
  593. return STATUS_SUCCESS;
  594. error_exit:
  595. return status;
  596. } // DoUserLogon
  597. BOOLEAN
  598. SrvIsAdmin(
  599. CtxtHandle Handle
  600. )
  601. /*++
  602. Routine Description:
  603. Returns TRUE if the user represented by Handle is an
  604. administrator
  605. Arguments:
  606. Handle - Represents the user we're interested in
  607. Return Value:
  608. TRUE if the user is an administrator. FALSE otherwise.
  609. --*/
  610. {
  611. NTSTATUS status;
  612. SECURITY_SUBJECT_CONTEXT SubjectContext;
  613. ACCESS_MASK GrantedAccess;
  614. GENERIC_MAPPING Mapping = { FILE_GENERIC_READ,
  615. FILE_GENERIC_WRITE,
  616. FILE_GENERIC_EXECUTE,
  617. FILE_ALL_ACCESS
  618. };
  619. HANDLE NullHandle = NULL;
  620. BOOLEAN retval = FALSE;
  621. PAGED_CODE();
  622. //
  623. // Impersonate the client
  624. //
  625. status = ImpersonateSecurityContext( &Handle );
  626. if( !NT_SUCCESS( status ) )
  627. return FALSE;
  628. SeCaptureSubjectContext( &SubjectContext );
  629. retval = SeAccessCheck( &SrvAdminSecurityDescriptor,
  630. &SubjectContext,
  631. FALSE,
  632. FILE_GENERIC_READ,
  633. 0,
  634. NULL,
  635. &Mapping,
  636. UserMode,
  637. &GrantedAccess,
  638. &status );
  639. SeReleaseSubjectContext( &SubjectContext );
  640. //
  641. // Revert back to our original identity
  642. //
  643. REVERT( );
  644. return retval;
  645. }
  646. BOOLEAN
  647. SrvIsNullSession(
  648. CtxtHandle Handle
  649. )
  650. /*++
  651. Routine Description:
  652. Returns TRUE if the user represented by Handle is an
  653. anonymous logon
  654. Arguments:
  655. Handle - Represents the user we're interested in
  656. Return Value:
  657. TRUE if the user is an anonymous logon. FALSE otherwise.
  658. --*/
  659. {
  660. NTSTATUS status;
  661. SECURITY_SUBJECT_CONTEXT SubjectContext;
  662. ACCESS_MASK GrantedAccess;
  663. GENERIC_MAPPING Mapping = { FILE_GENERIC_READ,
  664. FILE_GENERIC_WRITE,
  665. FILE_GENERIC_EXECUTE,
  666. FILE_ALL_ACCESS
  667. };
  668. HANDLE NullHandle = NULL;
  669. BOOLEAN retval = FALSE;
  670. PAGED_CODE();
  671. //
  672. // Impersonate the client
  673. //
  674. status = ImpersonateSecurityContext( &Handle );
  675. if( !NT_SUCCESS( status ) )
  676. return FALSE;
  677. SeCaptureSubjectContext( &SubjectContext );
  678. retval = SeAccessCheck( &SrvNullSessionSecurityDescriptor,
  679. &SubjectContext,
  680. FALSE,
  681. FILE_GENERIC_READ,
  682. 0,
  683. NULL,
  684. &Mapping,
  685. UserMode,
  686. &GrantedAccess,
  687. &status );
  688. SeReleaseSubjectContext( &SubjectContext );
  689. //
  690. // Revert back to our original identity
  691. //
  692. REVERT( );
  693. return retval;
  694. }
  695. NTSTATUS
  696. SrvGetLogonId(
  697. PCtxtHandle Handle,
  698. PLUID LogonId
  699. )
  700. /*++
  701. Routine Description:
  702. Returns the Logon Id for the requested context.
  703. Arguments:
  704. Handle - Represents the user we're interested in
  705. Return Value:
  706. Error codes from ImpersonateSecurityContext and SeQueryAuthenticationId.
  707. --*/
  708. {
  709. NTSTATUS Status;
  710. SECURITY_SUBJECT_CONTEXT SubjectContext;
  711. PAGED_CODE();
  712. //
  713. // Impersonate the client
  714. //
  715. Status = ImpersonateSecurityContext( Handle );
  716. if( !NT_SUCCESS( Status ) )
  717. return MapSecurityError(Status);
  718. SeCaptureSubjectContext( &SubjectContext );
  719. SeLockSubjectContext( &SubjectContext );
  720. Status = SeQueryAuthenticationIdToken(
  721. SubjectContext.ClientToken,
  722. LogonId
  723. );
  724. SeUnlockSubjectContext( &SubjectContext );
  725. SeReleaseSubjectContext( &SubjectContext );
  726. REVERT( );
  727. return(Status);
  728. }
  729. NTSTATUS
  730. SrvValidateSecurityBuffer(
  731. IN PCONNECTION Connection,
  732. IN OUT PCtxtHandle Handle,
  733. IN PSESSION Session,
  734. IN PCHAR Buffer,
  735. IN ULONG BufferLength,
  736. IN BOOLEAN SecuritySignaturesRequired,
  737. OUT PCHAR ReturnBuffer,
  738. IN OUT PULONG ReturnBufferLength,
  739. OUT PLARGE_INTEGER Expiry,
  740. OUT PCHAR NtUserSessionKey,
  741. OUT PLUID LogonId,
  742. OUT PBOOLEAN IsGuest
  743. )
  744. /*++
  745. Routine Description:
  746. Validates a Security Buffer sent from the client
  747. Arguments:
  748. Handle - On successful return, contains the security context handle
  749. associated with the user login.
  750. Session - Points to the session structure for this user
  751. Buffer - The Buffer to validate
  752. BufferLength - The length in bytes of Buffer
  753. SecuritySignaturesRequired - Are we required to generate a security
  754. signature for the SMBs?
  755. ReturnBuffer - On return, contains a security buffer to return to the
  756. client.
  757. ReturnBufferLength - On return, size in bytes of ReturnBuffer. On entry,
  758. the largest buffer we can return.
  759. Expiry - The time after which this security buffer is no longer valid.
  760. NtUserSessionKey - If STATUS_SUCCESS, the session key is returned here. This
  761. must point to a buffer at least MSV1_0_USER_SESSION_KEY_LENGTH big.
  762. LogonId - If successful, receives the logon id for this context.
  763. IsGuest - If successful, TRUE if the client has been validated as a guest
  764. Return Value:
  765. NTSTATUS from the security system. If STATUS_SUCCESS is returned, the user
  766. has been completely authenticated.
  767. Notes:
  768. BUGBUG
  769. AcceptSecurityContext() needs to return the KickOffTime (ie, the logon
  770. hours restriction) so that the server can enfore it. The contact person
  771. is MikeSw for this.
  772. --*/
  773. {
  774. NTSTATUS Status;
  775. ULONG Catts;
  776. PUCHAR AllocateMemory = NULL;
  777. ULONG maxReturnBuffer = *ReturnBufferLength;
  778. ULONG_PTR AllocateLength = MAX(BufferLength, maxReturnBuffer );
  779. BOOLEAN virtualMemoryAllocated = FALSE;
  780. SecBufferDesc InputToken;
  781. SecBuffer InputBuffer[2];
  782. SecBufferDesc OutputToken;
  783. SecBuffer OutputBuffer;
  784. SecPkgContext_NamesW SecNames;
  785. SecPkgContext_SessionKey SecKeys;
  786. ULONG oldSessionCount;
  787. TimeStamp LocalExpiry = {0};
  788. *ReturnBufferLength = 0;
  789. *IsGuest = FALSE;
  790. if ( (SrvHaveCreds & HAVEEXTENDED) == 0 ) {
  791. return STATUS_ACCESS_DENIED;
  792. }
  793. RtlZeroMemory( &SecKeys, sizeof( SecKeys ) );
  794. RtlZeroMemory( &SecNames, sizeof( SecNames ) );
  795. InputToken.pBuffers = InputBuffer;
  796. if( Connection->SockAddr[0] != 0 )
  797. {
  798. InputToken.cBuffers = 2;
  799. InputBuffer[1].pvBuffer = Connection->SockAddr;
  800. InputBuffer[1].cbBuffer = SRV_CONNECTION_SOCKADDR_SIZE;
  801. InputBuffer[1].BufferType = SECBUFFER_IPADDRESS;
  802. }
  803. else
  804. {
  805. InputToken.cBuffers = 1;
  806. }
  807. InputToken.ulVersion = 0;
  808. InputBuffer[0].pvBuffer = Buffer;
  809. InputBuffer[0].cbBuffer = BufferLength;
  810. InputBuffer[0].BufferType = SECBUFFER_TOKEN;
  811. OutputToken.pBuffers = &OutputBuffer;
  812. OutputToken.cBuffers = 1;
  813. OutputToken.ulVersion = 0;
  814. OutputBuffer.pvBuffer = ReturnBuffer ;
  815. OutputBuffer.cbBuffer = maxReturnBuffer ;
  816. OutputBuffer.BufferType = SECBUFFER_TOKEN;
  817. SrvStatistics.SessionLogonAttempts++;
  818. Catts = 0;
  819. Status = AcceptSecurityContext(
  820. &SrvExtensibleSecurityHandle,
  821. IS_VALID_SECURITY_HANDLE( *Handle ) ? Handle : NULL,
  822. &InputToken,
  823. ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOW_NULL_SESSION |
  824. ASC_REQ_DELEGATE | ASC_REQ_FRAGMENT_TO_FIT,
  825. SECURITY_NATIVE_DREP,
  826. Handle,
  827. &OutputToken,
  828. &Catts,
  829. &LocalExpiry);
  830. Status = MapSecurityError( Status );
  831. //
  832. // If there is a return buffer to be sent back, copy it into the caller's
  833. // buffers now.
  834. //
  835. if ( NT_SUCCESS(Status) || (Catts & ASC_RET_EXTENDED_ERROR) ) {
  836. if( Status == STATUS_SUCCESS ) {
  837. NTSTATUS qcaStatus;
  838. SecPkgContext_UserFlags userFlags;
  839. // Sspi will return time in LocalTime, convert to UTC
  840. // Enable dynamic reauthentication if possible or required
  841. if( SrvEnforceLogoffTimes || CLIENT_CAPABLE_OF( DYNAMIC_REAUTH, Connection ) )
  842. {
  843. ExLocalTimeToSystemTime (&LocalExpiry, Expiry);
  844. }
  845. else
  846. {
  847. Expiry->QuadPart = SRV_NEVER_TIME ;
  848. }
  849. //
  850. // The user has been completely authenticated. See if the session
  851. // count is being exceeded. We'll allow it only if the new client
  852. // is an administrator.
  853. //
  854. oldSessionCount = ExInterlockedAddUlong(
  855. &SrvStatistics.CurrentNumberOfSessions,
  856. 1,
  857. &GLOBAL_SPIN_LOCK(Statistics)
  858. );
  859. SrvInhibitIdlePowerDown();
  860. if ( !Session->IsSessionExpired && oldSessionCount >= SrvMaxUsers ) {
  861. if( oldSessionCount != SrvMaxUsers ||
  862. !SrvIsAdmin( *Handle ) ) {
  863. ExInterlockedAddUlong(
  864. &SrvStatistics.CurrentNumberOfSessions,
  865. (ULONG)-1,
  866. &GLOBAL_SPIN_LOCK(Statistics)
  867. );
  868. DeleteSecurityContext( Handle );
  869. INVALIDATE_SECURITY_HANDLE( *Handle );
  870. Status = STATUS_REQUEST_NOT_ACCEPTED;
  871. SrvAllowIdlePowerDown();
  872. goto exit;
  873. }
  874. }
  875. //
  876. // Figure out if we validated the client as GUEST
  877. //
  878. qcaStatus = QueryContextAttributes(
  879. Handle,
  880. SECPKG_ATTR_USER_FLAGS,
  881. &userFlags);
  882. if( NT_SUCCESS( MapSecurityError( qcaStatus ) ) ) {
  883. if( userFlags.UserFlags & LOGON_GUEST ) {
  884. *IsGuest = TRUE;
  885. }
  886. } else {
  887. SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, qcaStatus );
  888. }
  889. //
  890. // Get the Logon Id for this context
  891. //
  892. Status = SrvGetLogonId( Handle, LogonId );
  893. //
  894. // Capture the session key for this context
  895. //
  896. RtlZeroMemory( (PVOID) NtUserSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH );
  897. qcaStatus = QueryContextAttributes(
  898. Handle,
  899. SECPKG_ATTR_SESSION_KEY,
  900. &SecKeys);
  901. if( NT_SUCCESS( MapSecurityError( qcaStatus ) ) ) {
  902. RtlCopyMemory(
  903. (PVOID) NtUserSessionKey,
  904. SecKeys.SessionKey,
  905. MIN(MSV1_0_USER_SESSION_KEY_LENGTH, SecKeys.SessionKeyLength)
  906. );
  907. //
  908. // Start the security signatures, if required. We do not do security signatures
  909. // if we have a null session or a guest logon.
  910. //
  911. if( NT_SUCCESS( Status ) &&
  912. SecuritySignaturesRequired &&
  913. *IsGuest == FALSE &&
  914. Connection->SmbSecuritySignatureActive == FALSE &&
  915. !SrvIsNullSession( *Handle ) ) {
  916. if( SrvRequireExtendedSignatures ||
  917. (SrvEnableExtendedSignatures &&
  918. IS_NT_DIALECT(Connection->SmbDialect) &&
  919. CLIENT_CAPABLE_OF( EXTENDED_SECURITY, Connection ) ) )
  920. {
  921. // This session is going to be used as the key for signatures, mark it as unavailible until
  922. // the client tries to upgrade to extended signatures
  923. Session->SessionKeyState = SrvSessionKeyAuthenticating;
  924. }
  925. else
  926. {
  927. Session->SessionKeyState = SrvSessionKeyAvailible;
  928. }
  929. //
  930. // Start the sequence number generation
  931. //
  932. SrvInitializeSmbSecuritySignature(
  933. Connection,
  934. NULL,
  935. SecKeys.SessionKey,
  936. SecKeys.SessionKeyLength
  937. );
  938. }
  939. else
  940. {
  941. // This key is not used for signing, so no work is necessary
  942. Session->SessionKeyState = SrvSessionKeyAvailible;
  943. }
  944. FreeContextBuffer( SecKeys.SessionKey );
  945. } else {
  946. SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, qcaStatus );
  947. }
  948. if( !NT_SUCCESS( Status ) ) {
  949. DeleteSecurityContext( Handle );
  950. INVALIDATE_SECURITY_HANDLE( *Handle );
  951. }
  952. }
  953. ASSERT( OutputBuffer.cbBuffer <= maxReturnBuffer );
  954. //
  955. // If it fits, and a buffer was returned, send it to the client. If it doesn't fit,
  956. // then log the problem.
  957. //
  958. if( OutputBuffer.cbBuffer <= maxReturnBuffer ) {
  959. if( OutputBuffer.cbBuffer != 0 ) {
  960. *ReturnBufferLength = OutputBuffer.cbBuffer;
  961. }
  962. } else {
  963. SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, OutputBuffer.cbBuffer );
  964. }
  965. }
  966. exit:
  967. #if DBG
  968. //
  969. // RDR or SRV is sending in a corrupt security blob to LSA -- need to
  970. // find out what the source is.
  971. //
  972. if( NT_SUCCESS(Status) )
  973. {
  974. if( (OutputBuffer.pvBuffer != NULL) &&
  975. (OutputBuffer.cbBuffer >= sizeof(DWORD))
  976. )
  977. {
  978. PUCHAR pValidate = (PUCHAR) OutputBuffer.pvBuffer ;
  979. ASSERT( ( pValidate[0] != 0 ) ||
  980. ( pValidate[1] != 0 ) ||
  981. ( pValidate[2] != 0 ) ||
  982. ( pValidate[3] != 0 ) );
  983. }
  984. }
  985. #endif
  986. if( NT_SUCCESS( Status ) && Status != STATUS_SUCCESS ) {
  987. Status = STATUS_MORE_PROCESSING_REQUIRED;
  988. }
  989. return Status;
  990. } // SrvValidateSecurityBuffer
  991. NTSTATUS
  992. SrvGetUserAndDomainName (
  993. IN PSESSION Session,
  994. OUT PUNICODE_STRING UserName OPTIONAL,
  995. OUT PUNICODE_STRING DomainName OPTIONAL
  996. )
  997. /*++
  998. Routine Description
  999. Return the user and domain names associated with the Session
  1000. Arguments:
  1001. IN PSESSION Session : The session
  1002. Return Value:
  1003. IN OUT PUNICODE_STRING UserName
  1004. IN OUT PUNICODE_STRING DomainName
  1005. Note:
  1006. The caller must call SrvReleaseUserAndDomainName() when finished
  1007. --*/
  1008. {
  1009. SecPkgContext_NamesW SecNames;
  1010. NTSTATUS status;
  1011. UNICODE_STRING fullName, tmpUserName, tmpDomainName;
  1012. USHORT i, fullNameLength;
  1013. BOOLEAN LockConn = FALSE;
  1014. PAGED_CODE();
  1015. if( Session->Connection != NULL )
  1016. {
  1017. ACQUIRE_LOCK( &Session->Connection->Lock );
  1018. LockConn = TRUE;
  1019. }
  1020. if( Session->SecurityContext == NULL ||
  1021. !IS_VALID_SECURITY_HANDLE( Session->SecurityContext->UserHandle ) ) {
  1022. if( ARGUMENT_PRESENT( UserName ) ) {
  1023. *UserName = Session->NtUserName;
  1024. }
  1025. if( ARGUMENT_PRESENT( DomainName ) ) {
  1026. *DomainName = Session->NtUserDomain;
  1027. }
  1028. status = STATUS_SUCCESS;
  1029. goto Cleanup;
  1030. }
  1031. if( ARGUMENT_PRESENT( UserName ) ) {
  1032. UserName->Buffer = NULL;
  1033. UserName->Length = 0;
  1034. }
  1035. if( ARGUMENT_PRESENT( DomainName ) ) {
  1036. DomainName->Buffer = NULL;
  1037. DomainName->Length = 0;
  1038. }
  1039. //
  1040. // If it's the NULL session, then there are no names to be returned!
  1041. //
  1042. if( Session->IsNullSession == TRUE ) {
  1043. status = STATUS_SUCCESS;
  1044. goto Cleanup;
  1045. }
  1046. SecNames.sUserName = NULL;
  1047. status = QueryContextAttributesW(
  1048. &Session->SecurityContext->UserHandle,
  1049. SECPKG_ATTR_NAMES,
  1050. &SecNames
  1051. );
  1052. status = MapSecurityError( status );
  1053. if (!NT_SUCCESS(status)) {
  1054. if( Session->LogonSequenceInProgress == FALSE ) {
  1055. //
  1056. // If the client is in the middle of an extended logon sequence,
  1057. // then failures of this type are expected and we don't want
  1058. // to clutter the event log with them
  1059. //
  1060. SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
  1061. }
  1062. status = STATUS_SUCCESS;
  1063. goto Cleanup;
  1064. }
  1065. //
  1066. // See if we have a NULL user names. This shouldn't happen, but
  1067. // might if a security package is incomplete or something
  1068. //
  1069. if( SecNames.sUserName == NULL || *SecNames.sUserName == L'\0' ) {
  1070. if( SecNames.sUserName != NULL ) {
  1071. FreeContextBuffer( SecNames.sUserName );
  1072. }
  1073. status = STATUS_SUCCESS;
  1074. goto Cleanup;
  1075. }
  1076. //
  1077. // The return SecNames.sUserName should be in domainname\username format.
  1078. // We need to split it apart.
  1079. //
  1080. RtlInitUnicodeString( &fullName, SecNames.sUserName );
  1081. fullNameLength = fullName.Length / sizeof(WCHAR);
  1082. tmpDomainName.Buffer = fullName.Buffer;
  1083. for (i = 0; i < fullNameLength && tmpDomainName.Buffer[i] != L'\\'; i++) {
  1084. NOTHING;
  1085. }
  1086. if( tmpDomainName.Buffer[i] != L'\\' ) {
  1087. FreeContextBuffer( SecNames.sUserName );
  1088. status = STATUS_INVALID_ACCOUNT_NAME;
  1089. goto Cleanup;
  1090. }
  1091. tmpDomainName.Length = i * sizeof(WCHAR);
  1092. tmpDomainName.MaximumLength = tmpDomainName.Length;
  1093. tmpUserName.Buffer = &tmpDomainName.Buffer[i + 1];
  1094. tmpUserName.Length = fullName.Length - tmpDomainName.Length - sizeof(WCHAR);
  1095. tmpUserName.MaximumLength = tmpUserName.Length;
  1096. if( ARGUMENT_PRESENT( UserName ) ) {
  1097. status = RtlUpcaseUnicodeString( UserName, &tmpUserName, TRUE);
  1098. if( !NT_SUCCESS( status ) ) {
  1099. SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
  1100. FreeContextBuffer( SecNames.sUserName );
  1101. goto Cleanup;
  1102. }
  1103. }
  1104. if( ARGUMENT_PRESENT( DomainName ) ) {
  1105. status = RtlUpcaseUnicodeString( DomainName, &tmpDomainName, TRUE );
  1106. if( !NT_SUCCESS( status ) ) {
  1107. SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
  1108. FreeContextBuffer( SecNames.sUserName );
  1109. if( UserName != NULL ) {
  1110. RtlFreeUnicodeString( UserName );
  1111. }
  1112. goto Cleanup;
  1113. }
  1114. }
  1115. FreeContextBuffer( SecNames.sUserName );
  1116. Cleanup:
  1117. if( LockConn ) RELEASE_LOCK( &Session->Connection->Lock );
  1118. return status;
  1119. }
  1120. VOID
  1121. SrvReleaseUserAndDomainName(
  1122. IN PSESSION Session,
  1123. IN OUT PUNICODE_STRING UserName OPTIONAL,
  1124. IN OUT PUNICODE_STRING DomainName OPTIONAL
  1125. )
  1126. /*++
  1127. Routine Description
  1128. This is the complement of SrvGetUserAndDomainName. It frees the memory
  1129. if necessary.
  1130. --*/
  1131. {
  1132. PAGED_CODE();
  1133. if( ARGUMENT_PRESENT( UserName ) &&
  1134. UserName->Buffer != NULL &&
  1135. UserName->Buffer != Session->NtUserName.Buffer ) {
  1136. RtlFreeUnicodeString( UserName );
  1137. }
  1138. if( ARGUMENT_PRESENT( DomainName ) &&
  1139. DomainName->Buffer != NULL &&
  1140. DomainName->Buffer != Session->NtUserDomain.Buffer ) {
  1141. RtlFreeUnicodeString( DomainName );
  1142. }
  1143. }
  1144. NTSTATUS
  1145. SrvFreeSecurityContexts (
  1146. IN PSESSION Session
  1147. )
  1148. /*++
  1149. Routine Description:
  1150. Releases any context obtained for security purposes
  1151. Arguments:
  1152. IN PSESSION Session : The session
  1153. Return Value:
  1154. NTSTATUS
  1155. --*/
  1156. {
  1157. if( Session->SecurityContext != NULL ) {
  1158. if ( !CONTEXT_EQUAL( Session->SecurityContext->UserHandle, SrvNullSessionToken ) ) {
  1159. if( !Session->LogonSequenceInProgress ) {
  1160. ExInterlockedAddUlong(
  1161. &SrvStatistics.CurrentNumberOfSessions,
  1162. (ULONG)-1,
  1163. &GLOBAL_SPIN_LOCK(Statistics)
  1164. );
  1165. SrvAllowIdlePowerDown();
  1166. }
  1167. }
  1168. SrvDereferenceSecurityContext( Session->SecurityContext );
  1169. Session->SecurityContext = NULL;
  1170. }
  1171. return STATUS_SUCCESS;
  1172. } // SrvFreeSecurityContexts
  1173. NTSTATUS
  1174. AcquireLMCredentials (
  1175. VOID
  1176. )
  1177. {
  1178. UNICODE_STRING Ntlm;
  1179. NTSTATUS status;
  1180. TimeStamp Expiry;
  1181. RtlInitUnicodeString( &Ntlm, L"NTLM" );
  1182. //
  1183. // We pass in 1 for the GetKeyArg to indicate that this is
  1184. // downlevel NTLM, to distinguish it from NT5 NTLM.
  1185. //
  1186. status = AcquireCredentialsHandle(
  1187. NULL, // Default principal
  1188. (PSECURITY_STRING) &Ntlm,
  1189. SECPKG_CRED_INBOUND, // Need to define this
  1190. NULL, // No LUID
  1191. NULL, // No AuthData
  1192. NULL, // No GetKeyFn
  1193. NTLMSP_NTLM_CREDENTIAL, // GetKeyArg
  1194. &SrvLmLsaHandle,
  1195. &Expiry
  1196. );
  1197. if ( !NT_SUCCESS(status) ) {
  1198. status = MapSecurityError(status);
  1199. return status;
  1200. }
  1201. SrvHaveCreds |= HAVENTLM;
  1202. return status;
  1203. } // AcquireLMCredentials
  1204. #ifndef EXTENSIBLESSP_NAME
  1205. #define EXTENSIBLESSP_NAME NEGOSSP_NAME_W
  1206. #endif
  1207. NTSTATUS
  1208. AcquireExtensibleSecurityCredentials (
  1209. VOID
  1210. )
  1211. /*++
  1212. Routine Description:
  1213. Acquires the handle to the security negotiate package.
  1214. Arguments:
  1215. none.
  1216. Return Value:
  1217. NTSTATUS
  1218. --*/
  1219. {
  1220. UNICODE_STRING NegotiateName;
  1221. TimeStamp Expiry;
  1222. NTSTATUS status ;
  1223. RtlInitUnicodeString( &NegotiateName, EXTENSIBLESSP_NAME );
  1224. status = AcquireCredentialsHandle(
  1225. NULL, // Default principal
  1226. (PSECURITY_STRING) &NegotiateName,
  1227. SECPKG_CRED_INBOUND, // Need to define this
  1228. NULL, // No LUID
  1229. NULL, // No AuthData
  1230. NULL, // No GetKeyFn
  1231. NULL, // No GetKeyArg
  1232. &SrvExtensibleSecurityHandle,
  1233. &Expiry
  1234. );
  1235. if ( !NT_SUCCESS(status) ) {
  1236. status = MapSecurityError(status);
  1237. return status;
  1238. }
  1239. SrvHaveCreds |= HAVEEXTENDED;
  1240. return status;
  1241. } // AcquireExtensibleSecurityCredentials
  1242. VOID
  1243. SrvAddSecurityCredentials(
  1244. IN PANSI_STRING ComputerNameA,
  1245. IN PUNICODE_STRING DomainName,
  1246. IN DWORD PasswordLength,
  1247. IN PBYTE Password
  1248. )
  1249. /*++
  1250. Routine Description:
  1251. In order for mutual authentication to work, the security subsystem needs to know
  1252. all the names the server is using, as well as any passwords needed to decrypt
  1253. the security information associated with the server name. This routine informs
  1254. the security subsystem.
  1255. Arguments:
  1256. ComputerName, DomainName - these are the names the clients will be using to access this system
  1257. PasswordLength, Password - this is the secret the security system needs to know to decode the
  1258. passed security information
  1259. --*/
  1260. {
  1261. NTSTATUS status;
  1262. UNICODE_STRING ComputerName;
  1263. PUSHORT p;
  1264. PVOID VirtualMem ;
  1265. SIZE_T Size ;
  1266. PSEC_WINNT_AUTH_IDENTITY Auth ;
  1267. PUCHAR Where ;
  1268. UNICODE_STRING NegotiateName;
  1269. TimeStamp Expiry;
  1270. PAGED_CODE();
  1271. status = RtlAnsiStringToUnicodeString( &ComputerName, ComputerNameA, TRUE );
  1272. if( !NT_SUCCESS( status ) ) {
  1273. IF_DEBUG( ERRORS ) {
  1274. KdPrint(( "SRV: SrvAddSecurityCredentials, status %X at %d\n", status, __LINE__ ));
  1275. }
  1276. return;
  1277. }
  1278. if ((SrvHaveCreds & HAVEEXTENDED) == 0) {
  1279. if (status = AcquireExtensibleSecurityCredentials()) {
  1280. return ;
  1281. }
  1282. }
  1283. //
  1284. // Trim off any trailing blanks
  1285. //
  1286. for( p = &ComputerName.Buffer[ (ComputerName.Length / sizeof( WCHAR )) - 1 ];
  1287. p > ComputerName.Buffer;
  1288. p-- ) {
  1289. if( *p != L' ' )
  1290. break;
  1291. }
  1292. ComputerName.Length = (USHORT)((p - ComputerName.Buffer + 1) * sizeof( WCHAR ));
  1293. if( ComputerName.Length ) {
  1294. //
  1295. // Tell the security subsystem about this name.
  1296. //
  1297. RtlInitUnicodeString( &NegotiateName, EXTENSIBLESSP_NAME );
  1298. Size = ComputerName.Length + sizeof( WCHAR ) +
  1299. DomainName->Length + sizeof( WCHAR ) +
  1300. PasswordLength +
  1301. sizeof( SEC_WINNT_AUTH_IDENTITY ) ;
  1302. VirtualMem = NULL ;
  1303. status = NtAllocateVirtualMemory(
  1304. NtCurrentProcess(),
  1305. &VirtualMem,
  1306. 0,
  1307. &Size,
  1308. MEM_COMMIT,
  1309. PAGE_READWRITE );
  1310. if ( NT_SUCCESS( status ) )
  1311. {
  1312. Auth = (PSEC_WINNT_AUTH_IDENTITY) VirtualMem ;
  1313. Where = (PUCHAR) (Auth + 1);
  1314. Auth->User = (PWSTR) Where ;
  1315. Auth->UserLength = ComputerName.Length / sizeof( WCHAR );
  1316. RtlCopyMemory(
  1317. Where,
  1318. ComputerName.Buffer,
  1319. ComputerName.Length );
  1320. Where += ComputerName.Length ;
  1321. Auth->Domain = (PWSTR) Where ;
  1322. Auth->DomainLength = DomainName->Length / sizeof( WCHAR );
  1323. RtlCopyMemory(
  1324. Where,
  1325. DomainName->Buffer,
  1326. DomainName->Length );
  1327. Where += DomainName->Length ;
  1328. Auth->Password = (PWSTR) Where ;
  1329. Auth->PasswordLength = PasswordLength / sizeof( WCHAR );
  1330. RtlCopyMemory(
  1331. Where,
  1332. Password,
  1333. PasswordLength );
  1334. Auth->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE ;
  1335. status = AddCredentials(
  1336. &SrvExtensibleSecurityHandle, // Default principal
  1337. NULL,
  1338. (PSECURITY_STRING) &NegotiateName,
  1339. SECPKG_CRED_INBOUND, // Need to define this
  1340. Auth, // Auth data
  1341. NULL, // No GetKeyFn
  1342. NULL, // No GetKeyArg
  1343. &Expiry );
  1344. NtFreeVirtualMemory(
  1345. NtCurrentProcess(),
  1346. &VirtualMem,
  1347. &Size,
  1348. MEM_RELEASE );
  1349. }
  1350. }
  1351. //
  1352. // Free up our memory
  1353. //
  1354. RtlFreeUnicodeString( &ComputerName );
  1355. }
  1356. NTSTATUS
  1357. SrvGetExtensibleSecurityNegotiateBuffer(
  1358. OUT PCtxtHandle Token,
  1359. OUT PCHAR Buffer,
  1360. IN OUT ULONG *BufferLength
  1361. )
  1362. {
  1363. NTSTATUS Status;
  1364. ULONG Attributes;
  1365. TimeStamp Expiry;
  1366. SecBufferDesc OutputToken;
  1367. SecBuffer OutputBuffer;
  1368. ULONG MaxBufferSize = *BufferLength;
  1369. if ((SrvHaveCreds & HAVEEXTENDED) == 0) {
  1370. if (Status = AcquireExtensibleSecurityCredentials()) {
  1371. *BufferLength = 0;
  1372. return(Status);
  1373. }
  1374. }
  1375. OutputToken.pBuffers = &OutputBuffer;
  1376. OutputToken.cBuffers = 1;
  1377. OutputToken.ulVersion = 0;
  1378. OutputBuffer.pvBuffer = 0;
  1379. OutputBuffer.cbBuffer = 0;
  1380. OutputBuffer.BufferType = SECBUFFER_TOKEN;
  1381. Status = AcceptSecurityContext (
  1382. &SrvExtensibleSecurityHandle,
  1383. NULL,
  1384. NULL,
  1385. ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY |
  1386. ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_ALLOW_NULL_SESSION |
  1387. ASC_REQ_DELEGATE,
  1388. SECURITY_NATIVE_DREP,
  1389. Token,
  1390. &OutputToken,
  1391. &Attributes,
  1392. &Expiry);
  1393. if (!NT_SUCCESS(Status)) {
  1394. *BufferLength = 0;
  1395. return(Status);
  1396. }
  1397. if (OutputBuffer.cbBuffer >=
  1398. MaxBufferSize) {
  1399. Status = STATUS_INVALID_BUFFER_SIZE;
  1400. SrvLogServiceFailure( SRV_SVC_LSA_CALL_AUTH_PACKAGE, Status);
  1401. *BufferLength = 0;
  1402. } else {
  1403. RtlCopyMemory(Buffer, OutputBuffer.pvBuffer, OutputBuffer.cbBuffer);
  1404. *BufferLength = (USHORT) OutputBuffer.cbBuffer;
  1405. }
  1406. #if DBG
  1407. //
  1408. // RDR or SRV is sending in a corrupt security blob to LSA -- need to
  1409. // find out what the source is.
  1410. //
  1411. if( NT_SUCCESS(Status) )
  1412. {
  1413. if( (OutputBuffer.pvBuffer != NULL) &&
  1414. (OutputBuffer.cbBuffer >= sizeof(DWORD))
  1415. )
  1416. {
  1417. PDWORD pdwValidate = (DWORD*)OutputBuffer.pvBuffer;
  1418. ASSERT( *pdwValidate != 0 );
  1419. }
  1420. }
  1421. #endif
  1422. FreeContextBuffer(OutputBuffer.pvBuffer);
  1423. return( Status );
  1424. }
  1425. VOID SRVFASTCALL
  1426. SrvInitializeSmbSecuritySignature(
  1427. IN OUT PCONNECTION Connection,
  1428. IN PUCHAR SessionKey OPTIONAL,
  1429. IN PUCHAR ChallengeResponse,
  1430. IN ULONG ChallengeResponseLength
  1431. )
  1432. /*++
  1433. Routine Description:
  1434. Initializes the security signature generator for a session by calling MD5Update
  1435. on the session key, challenge response
  1436. Arguments:
  1437. SessionKey - Either the LM or NT session key, depending on which
  1438. password was used for authentication, must be at least 16 bytes
  1439. ChallengeResponse - The challenge response used for authentication, must
  1440. be at least 24 bytes
  1441. --*/
  1442. {
  1443. RtlZeroMemory( &Connection->Md5Context, sizeof( Connection->Md5Context ) );
  1444. MD5Init( &Connection->Md5Context );
  1445. if( ARGUMENT_PRESENT( SessionKey ) ) {
  1446. MD5Update( &Connection->Md5Context, SessionKey, USER_SESSION_KEY_LENGTH );
  1447. }
  1448. MD5Update( &Connection->Md5Context, ChallengeResponse, ChallengeResponseLength );
  1449. Connection->SmbSecuritySignatureIndex = 0;
  1450. Connection->SmbSecuritySignatureActive = TRUE;
  1451. //
  1452. // We don't know how to do RAW and security signatures
  1453. //
  1454. Connection->EnableRawIo = FALSE;
  1455. IF_DEBUG( SECSIG ) {
  1456. KdPrint(( "SRV: SMB sigs enabled for %wZ, conn %p, build %d\n",
  1457. &Connection->ClientMachineNameString,
  1458. Connection, Connection->PagedConnection->ClientBuildNumber ));
  1459. }
  1460. }
  1461. VOID SRVFASTCALL
  1462. SrvAddSmbSecuritySignature(
  1463. IN OUT PWORK_CONTEXT WorkContext,
  1464. IN PMDL Mdl,
  1465. IN ULONG SendLength
  1466. )
  1467. /*++
  1468. Routine Description:
  1469. Generates the next security signature
  1470. Arguments:
  1471. WorkContext - the context to sign
  1472. Return Value:
  1473. none.
  1474. --*/
  1475. {
  1476. MD5_CTX Context;
  1477. PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );
  1478. IF_DEBUG( SECSIG ) {
  1479. KdPrint(( "SRV: resp sig: cmd %x, index %u, len %d\n",
  1480. Smb->Command, WorkContext->ResponseSmbSecuritySignatureIndex, SendLength ));
  1481. }
  1482. #if DBG
  1483. //
  1484. // Put the index number right after the signature. This allows us to figure out on the client
  1485. // side if we have a signature mismatch.
  1486. //
  1487. SmbPutUshort( &Smb->SecuritySignature[SMB_SECURITY_SIGNATURE_LENGTH],
  1488. (USHORT)WorkContext->ResponseSmbSecuritySignatureIndex );
  1489. #endif
  1490. //
  1491. // Put the next index number into the SMB
  1492. //
  1493. SmbPutUlong( Smb->SecuritySignature, WorkContext->ResponseSmbSecuritySignatureIndex );
  1494. RtlZeroMemory( Smb->SecuritySignature + sizeof(ULONG),
  1495. SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG)
  1496. );
  1497. //
  1498. // Start out with our initial context
  1499. //
  1500. RtlCopyMemory( &Context, &WorkContext->Connection->Md5Context, sizeof( Context ) );
  1501. //
  1502. // Compute the signature for the SMB we're about to send
  1503. //
  1504. do {
  1505. PCHAR SystemAddressForBuffer;
  1506. ULONG len = MIN( SendLength, MmGetMdlByteCount( Mdl ) );
  1507. SystemAddressForBuffer = MmGetSystemAddressForMdlSafe(Mdl,NormalPoolPriority);
  1508. if (SystemAddressForBuffer == NULL) {
  1509. // return without updating the security signature field. This will
  1510. // in turn cause the client to reject the packet and tear down the
  1511. // connection
  1512. return;
  1513. }
  1514. MD5Update( &Context, SystemAddressForBuffer, len );
  1515. SendLength -= len;
  1516. } while( SendLength && (Mdl = Mdl->Next) != NULL );
  1517. MD5Final( &Context );
  1518. //
  1519. // Put the signature into the SMB
  1520. //
  1521. RtlCopyMemory(
  1522. Smb->SecuritySignature,
  1523. Context.digest,
  1524. SMB_SECURITY_SIGNATURE_LENGTH
  1525. );
  1526. }
  1527. //
  1528. // Print the mismatched signature information to the debugger
  1529. //
  1530. VOID
  1531. SrvDumpSignatureError(
  1532. IN PWORK_CONTEXT WorkContext,
  1533. IN PUCHAR ExpectedSignature,
  1534. IN PUCHAR ActualSignature,
  1535. IN ULONG Length,
  1536. IN ULONG ExpectedIndexNumber
  1537. )
  1538. {
  1539. #if DBG
  1540. DWORD i;
  1541. PMDL Mdl = WorkContext->RequestBuffer->Mdl;
  1542. ULONG requestLength = MIN( WorkContext->RequestBuffer->DataLength, 64 );
  1543. PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );
  1544. if( Smb->Command == SMB_COM_ECHO ) {
  1545. return;
  1546. }
  1547. //
  1548. // Security Signature Mismatch!
  1549. //
  1550. IF_DEBUG( ERRORS ) {
  1551. KdPrint(( "SRV: Invalid security signature in request smb (cmd %X)", Smb->Command ));
  1552. if( WorkContext->Connection && WorkContext->Connection->PagedConnection ) {
  1553. KdPrint(( " from %wZ" ,
  1554. &WorkContext->Connection->ClientMachineNameString ));
  1555. }
  1556. }
  1557. IF_DEBUG( SECSIG ) {
  1558. KdPrint(( "\n\tExpected: " ));
  1559. for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
  1560. KdPrint(( "%X ", ExpectedSignature[i] & 0xff ));
  1561. }
  1562. KdPrint(( "\n\tReceived: " ));
  1563. for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
  1564. KdPrint(( "%X ", ActualSignature[i] & 0xff ));
  1565. }
  1566. KdPrint(( "\n\tLength %u, Expected Index Number %u\n", Length, ExpectedIndexNumber ));
  1567. //
  1568. // Dump out some of the errant SMB
  1569. //
  1570. i = 1;
  1571. do {
  1572. ULONG len = MIN( requestLength, Mdl->ByteCount );
  1573. PBYTE p = MmGetSystemAddressForMdl( Mdl );
  1574. PBYTE ep = (PBYTE)MmGetSystemAddressForMdl( Mdl ) + len;
  1575. for( ; p < ep; p++, i++ ) {
  1576. KdPrint(("%2.2x ", (*p) & 0xff ));
  1577. if( !(i%32) ) {
  1578. KdPrint(( "\n" ));
  1579. }
  1580. }
  1581. requestLength -= len;
  1582. } while( requestLength != 0 && (Mdl = Mdl->Next) != NULL );
  1583. KdPrint(( "\n" ));
  1584. }
  1585. IF_DEBUG( SECSIG ) {
  1586. DbgPrint( "WorkContext: %p\n", WorkContext );
  1587. DbgBreakPoint();
  1588. }
  1589. #endif
  1590. }
  1591. BOOLEAN SRVFASTCALL
  1592. SrvCheckSmbSecuritySignature(
  1593. IN OUT PWORK_CONTEXT WorkContext
  1594. )
  1595. {
  1596. MD5_CTX Context;
  1597. PMDL Mdl = WorkContext->RequestBuffer->Mdl;
  1598. ULONG requestLength = WorkContext->RequestBuffer->DataLength;
  1599. CHAR SavedSignature[ SMB_SECURITY_SIGNATURE_LENGTH ];
  1600. PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );
  1601. ULONG len;
  1602. //
  1603. // Initialize the Context
  1604. //
  1605. RtlCopyMemory( &Context, &WorkContext->Connection->Md5Context, sizeof( Context ) );
  1606. //
  1607. // Save the signature that's presently in the SMB
  1608. //
  1609. RtlCopyMemory( SavedSignature, Smb->SecuritySignature, sizeof( SavedSignature ));
  1610. //
  1611. // Put the correct (expected) signature index into the buffer
  1612. //
  1613. SmbPutUlong( Smb->SecuritySignature, WorkContext->SmbSecuritySignatureIndex );
  1614. RtlZeroMemory( Smb->SecuritySignature + sizeof(ULONG),
  1615. SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG)
  1616. );
  1617. //
  1618. // Compute what the signature should be
  1619. //
  1620. do {
  1621. len = MIN( requestLength, Mdl->ByteCount );
  1622. MD5Update( &Context, MmGetSystemAddressForMdl( Mdl ), len );
  1623. requestLength -= len;
  1624. } while( requestLength != 0 && (Mdl = Mdl->Next) != NULL );
  1625. MD5Final( &Context );
  1626. //
  1627. // Put the signature back
  1628. //
  1629. RtlCopyMemory( Smb->SecuritySignature, SavedSignature, sizeof( Smb->SecuritySignature ));
  1630. //
  1631. // Now compare them!
  1632. //
  1633. if( RtlCompareMemory( Context.digest, SavedSignature, sizeof( SavedSignature ) ) !=
  1634. sizeof( SavedSignature ) ) {
  1635. SrvDumpSignatureError( WorkContext,
  1636. Context.digest,
  1637. SavedSignature,
  1638. WorkContext->RequestBuffer->DataLength,
  1639. WorkContext->SmbSecuritySignatureIndex
  1640. );
  1641. return FALSE;
  1642. }
  1643. return TRUE;
  1644. }
  1645. VOID
  1646. SrvHashUserSessionKey(
  1647. PCHAR SessionKey
  1648. )
  1649. {
  1650. ULONG i;
  1651. HMACMD5_CTX Ctx;
  1652. BYTE SSKeyHash[256] = {
  1653. 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
  1654. 0x72, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x07,
  1655. 0x6e, 0x28, 0x2e, 0x69, 0x88, 0x10, 0xb3, 0xdb, 0x01, 0x55, 0x72, 0xfb, 0x74, 0x14, 0xfb, 0xc4,
  1656. 0xc5, 0xaf, 0x3b, 0x41, 0x65, 0x32, 0x17, 0xba, 0xa3, 0x29, 0x08, 0xc1, 0xde, 0x16, 0x61, 0x7e,
  1657. 0x66, 0x98, 0xa4, 0x0b, 0xfe, 0x06, 0x83, 0x53, 0x4d, 0x05, 0xdf, 0x6d, 0xa7, 0x51, 0x10, 0x73,
  1658. 0xc5, 0x50, 0xdc, 0x5e, 0xf8, 0x21, 0x46, 0xaa, 0x96, 0x14, 0x33, 0xd7, 0x52, 0xeb, 0xaf, 0x1f,
  1659. 0xbf, 0x36, 0x6c, 0xfc, 0xb7, 0x1d, 0x21, 0x19, 0x81, 0xd0, 0x6b, 0xfa, 0x77, 0xad, 0xbe, 0x18,
  1660. 0x78, 0xcf, 0x10, 0xbd, 0xd8, 0x78, 0xf7, 0xd3, 0xc6, 0xdf, 0x43, 0x32, 0x19, 0xd3, 0x9b, 0xa8,
  1661. 0x4d, 0x9e, 0xaa, 0x41, 0xaf, 0xcb, 0xc6, 0xb9, 0x34, 0xe7, 0x48, 0x25, 0xd4, 0x88, 0xc4, 0x51,
  1662. 0x60, 0x38, 0xd9, 0x62, 0xe8, 0x8d, 0x5b, 0x83, 0x92, 0x7f, 0xb5, 0x0e, 0x1c, 0x2d, 0x06, 0x91,
  1663. 0xc3, 0x75, 0xb3, 0xcc, 0xf8, 0xf7, 0x92, 0x91, 0x0b, 0x3d, 0xa1, 0x10, 0x5b, 0xd5, 0x0f, 0xa8,
  1664. 0x3f, 0x5d, 0x13, 0x83, 0x0a, 0x6b, 0x72, 0x93, 0x14, 0x59, 0xd5, 0xab, 0xde, 0x26, 0x15, 0x6d,
  1665. 0x60, 0x67, 0x71, 0x06, 0x6e, 0x3d, 0x0d, 0xa7, 0xcb, 0x70, 0xe9, 0x08, 0x5c, 0x99, 0xfa, 0x0a,
  1666. 0x5f, 0x3d, 0x44, 0xa3, 0x8b, 0xc0, 0x8d, 0xda, 0xe2, 0x68, 0xd0, 0x0d, 0xcd, 0x7f, 0x3d, 0xf8,
  1667. 0x73, 0x7e, 0x35, 0x7f, 0x07, 0x02, 0x0a, 0xb5, 0xe9, 0xb7, 0x87, 0xfb, 0xa1, 0xbf, 0xcb, 0x32,
  1668. 0x31, 0x66, 0x09, 0x48, 0x88, 0xcc, 0x18, 0xa3, 0xb2, 0x1f, 0x1f, 0x1b, 0x90, 0x4e, 0xd7, 0xe1
  1669. };
  1670. ASSERT( MSV1_0_USER_SESSION_KEY_LENGTH == MD5DIGESTLEN );
  1671. HMACMD5Init( &Ctx, SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH );
  1672. HMACMD5Update( &Ctx, SSKeyHash, 256 );
  1673. HMACMD5Final( &Ctx, SessionKey );
  1674. }