Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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