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.

7677 lines
208 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. smbsupp.c
  5. Abstract:
  6. This module contains various support routines for processing SMBs.
  7. Author:
  8. Chuck Lenzmeier (chuckl) 9-Nov-1989
  9. David Treadwell (davidtr)
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include "smbsupp.tmh"
  14. #pragma hdrstop
  15. #define BugCheckFileId SRV_FILE_SMBSUPP
  16. #define CHAR_SP ' '
  17. //
  18. // Mapping is defined in inc\srvfsctl.h
  19. //
  20. STATIC GENERIC_MAPPING SrvFileAccessMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING;
  21. //
  22. // Forward references
  23. //
  24. #ifdef ALLOC_PRAGMA
  25. #pragma alloc_text( PAGE, Srv8dot3ToUnicodeString )
  26. #pragma alloc_text( PAGE, SrvAllocateAndBuildPathName )
  27. #pragma alloc_text( PAGE, SrvCanonicalizePathName )
  28. #pragma alloc_text( PAGE, SrvCanonicalizePathNameWithReparse )
  29. #pragma alloc_text( PAGE, SrvCheckSearchAttributesForHandle )
  30. #pragma alloc_text( PAGE, SrvCheckSearchAttributes )
  31. #pragma alloc_text( PAGE, SrvGetAlertServiceName )
  32. #pragma alloc_text( PAGE, SrvGetBaseFileName )
  33. #pragma alloc_text( PAGE, SrvGetMultiSZList )
  34. #pragma alloc_text( PAGE, SrvGetOsVersionString )
  35. #pragma alloc_text( PAGE, SrvGetString )
  36. #pragma alloc_text( PAGE, SrvGetStringLength )
  37. #pragma alloc_text( PAGE, SrvGetSubdirectoryLength )
  38. #pragma alloc_text( PAGE, SrvIsLegalFatName )
  39. #pragma alloc_text( PAGE, SrvMakeUnicodeString )
  40. //#pragma alloc_text( PAGE, SrvReleaseContext )
  41. #pragma alloc_text( PAGE, SrvSetFileWritethroughMode )
  42. #pragma alloc_text( PAGE, SrvOemStringTo8dot3 )
  43. #pragma alloc_text( PAGE, SrvUnicodeStringTo8dot3 )
  44. #pragma alloc_text( PAGE, SrvVerifySid )
  45. #pragma alloc_text( PAGE, SrvVerifyTid )
  46. #pragma alloc_text( PAGE, SrvVerifyUid )
  47. #pragma alloc_text( PAGE, SrvVerifyUidAndTid )
  48. #pragma alloc_text( PAGE, SrvIoCreateFile )
  49. #pragma alloc_text( PAGE, SrvNtClose )
  50. #pragma alloc_text( PAGE, SrvVerifyDeviceStackSize )
  51. #pragma alloc_text( PAGE, SrvImpersonate )
  52. #pragma alloc_text( PAGE, SrvRevert )
  53. #pragma alloc_text( PAGE, SrvSetLastWriteTime )
  54. #pragma alloc_text( PAGE, SrvCheckShareFileAccess )
  55. #pragma alloc_text( PAGE, SrvReleaseShareRootHandle )
  56. #pragma alloc_text( PAGE, SrvUpdateVcQualityOfService )
  57. #pragma alloc_text( PAGE, SrvIsAllowedOnAdminShare )
  58. #pragma alloc_text( PAGE, SrvRetrieveMaximalAccessRightsForUser )
  59. #pragma alloc_text( PAGE, SrvRetrieveMaximalAccessRights )
  60. #pragma alloc_text( PAGE, SrvRetrieveMaximalShareAccessRights )
  61. #pragma alloc_text( PAGE, SrvUpdateMaximalAccessRightsInResponse )
  62. #pragma alloc_text( PAGE, SrvUpdateMaximalShareAccessRightsInResponse )
  63. //#pragma alloc_text( PAGE, SrvValidateSmb )
  64. #pragma alloc_text( PAGE, SrvWildcardRename )
  65. #pragma alloc_text( PAGE8FIL, SrvCheckForSavedError )
  66. #pragma alloc_text( PAGE, SrvIsDottedQuadAddress )
  67. #endif
  68. #if 0
  69. NOT PAGEABLE -- SrvUpdateStatistics2
  70. NOT PAGEABLE -- SrvVerifyFid2
  71. NOT PAGEABLE -- SrvVerifyFidForRawWrite
  72. NOT PAGEABLE -- SrvReceiveBufferShortage
  73. #endif
  74. VOID
  75. Srv8dot3ToUnicodeString (
  76. IN PSZ Input8dot3,
  77. OUT PUNICODE_STRING OutputString
  78. )
  79. /*++
  80. Routine Description:
  81. Convert FAT 8.3 format into a string.
  82. Arguments:
  83. Input8dot3 - Supplies the input 8.3 name to convert
  84. OutputString - Receives the converted name. The memory must be
  85. supplied by the caller.
  86. Return Value:
  87. None
  88. --*/
  89. {
  90. LONG i;
  91. CLONG lastOutputChar;
  92. UCHAR tempBuffer[8+1+3];
  93. OEM_STRING tempString;
  94. PAGED_CODE( );
  95. //
  96. // If we get "." or "..", just return them. They do not follow
  97. // the usual rules for FAT names.
  98. //
  99. lastOutputChar = 0;
  100. if ( Input8dot3[0] == '.' && Input8dot3[1] == '\0' ) {
  101. tempBuffer[0] = '.';
  102. lastOutputChar = 0;
  103. } else if ( Input8dot3[0] == '.' && Input8dot3[1] == '.' &&
  104. Input8dot3[2] == '\0' ) {
  105. tempBuffer[0] = '.';
  106. tempBuffer[1] = '.';
  107. lastOutputChar = 1;
  108. } else {
  109. //
  110. // Copy over the 8 part of the 8.3 name into the output buffer,
  111. // then back up the index to the first non-space character,
  112. // searching backwards.
  113. //
  114. RtlCopyMemory( tempBuffer, Input8dot3, 8 );
  115. for ( i = 7;
  116. (i >= 0) && (tempBuffer[i] == CHAR_SP);
  117. i -- ) {
  118. ;
  119. }
  120. //
  121. // Add a dot.
  122. //
  123. i++;
  124. tempBuffer[i] = '.';
  125. //
  126. // Copy over the 3 part of the 8.3 name into the output buffer,
  127. // then back up the index to the first non-space character,
  128. // searching backwards.
  129. //
  130. lastOutputChar = i;
  131. for ( i = 8; i < 11; i++ ) {
  132. //
  133. // Copy the byte.
  134. //
  135. // *** This code used to mask off the top bit. This was a
  136. // legacy of very ancient times when the bit may have
  137. // been used as a resume key sequence bit.
  138. //
  139. tempBuffer[++lastOutputChar] = (UCHAR)Input8dot3[i];
  140. }
  141. while ( tempBuffer[lastOutputChar] == CHAR_SP ) {
  142. lastOutputChar--;
  143. }
  144. //
  145. // If the last character is a '.', then we don't have an
  146. // extension, so back up before the dot.
  147. //
  148. if ( tempBuffer[lastOutputChar] == '.') {
  149. lastOutputChar--;
  150. }
  151. }
  152. //
  153. // Convert to Unicode.
  154. //
  155. tempString.Length = (SHORT)(lastOutputChar + 1);
  156. tempString.Buffer = tempBuffer;
  157. OutputString->MaximumLength =
  158. (SHORT)((lastOutputChar + 2) * sizeof(WCHAR));
  159. RtlOemStringToUnicodeString( OutputString, &tempString, FALSE );
  160. return;
  161. } // Srv8dot3ToUnicodeString
  162. VOID
  163. SrvAllocateAndBuildPathName(
  164. IN PUNICODE_STRING Path1,
  165. IN PUNICODE_STRING Path2 OPTIONAL,
  166. IN PUNICODE_STRING Path3 OPTIONAL,
  167. OUT PUNICODE_STRING BuiltPath
  168. )
  169. /*++
  170. Routine Description:
  171. Allocates space and concatenates the paths in the parameter strings.
  172. ALLOCATE_HEAP is used to allocate the memory for the full
  173. pathname. Directory separator characters ('\') are added as
  174. necessary so that a legitimate path is built. If the third
  175. parameter is NULL, then only the first two strings are concatenated,
  176. and if both the second and third parameters are NULL then the first
  177. string is simply copied over to a new location.
  178. Arguments:
  179. Input8dot3 - Supplies the input 8.3 name to convert
  180. OutputString - Receives the converted name, the memory must be supplied
  181. by the caller.
  182. Return Value:
  183. None
  184. --*/
  185. {
  186. UNICODE_STRING path2;
  187. UNICODE_STRING path3;
  188. PWCH nextLocation;
  189. PWSTR pathBuffer;
  190. ULONG allocationLength;
  191. WCHAR nullString = 0;
  192. PAGED_CODE( );
  193. //
  194. // Set up the strings for optional parameters Path2 and Path3. Doing
  195. // this allows later code to be ignorant of whether the strings were
  196. // actually passed.
  197. //
  198. if ( ARGUMENT_PRESENT(Path2) ) {
  199. path2.Buffer = Path2->Buffer;
  200. path2.Length = Path2->Length;
  201. } else {
  202. path2.Buffer = &nullString;
  203. path2.Length = 0;
  204. }
  205. if ( ARGUMENT_PRESENT(Path3) ) {
  206. path3.Buffer = Path3->Buffer;
  207. path3.Length = Path3->Length;
  208. } else {
  209. path3.Buffer = &nullString;
  210. path3.Length = 0;
  211. }
  212. //
  213. // Allocate space in which to put the path name we are building.
  214. // The +3 if to account for as many as two directory separator
  215. // characters being added and the zero terminator at the end. This
  216. // has a small cost in terms of memory usage, but it simplifies this
  217. // code.
  218. //
  219. // The calling routine must be careful to deallocate this space
  220. // when it is done with the path name.
  221. //
  222. allocationLength = Path1->Length + path2.Length + path3.Length +
  223. 3 * sizeof(WCHAR);
  224. pathBuffer = ALLOCATE_HEAP_COLD(
  225. allocationLength,
  226. BlockTypeDataBuffer
  227. );
  228. if ( pathBuffer == NULL ) {
  229. INTERNAL_ERROR(
  230. ERROR_LEVEL_EXPECTED,
  231. "SrvAllocateAndBuildPathName: Unable to allocate %d bytes "
  232. "from heap.",
  233. allocationLength,
  234. NULL
  235. );
  236. BuiltPath->Buffer = NULL;
  237. return;
  238. }
  239. BuiltPath->Buffer = pathBuffer;
  240. BuiltPath->MaximumLength = (USHORT)allocationLength;
  241. ASSERT ( ( allocationLength & 0xffff0000 ) == 0 );
  242. RtlZeroMemory( pathBuffer, allocationLength );
  243. //
  244. // Copy the first path name to the space we have allocated.
  245. //
  246. RtlCopyMemory( pathBuffer, Path1->Buffer, Path1->Length );
  247. nextLocation = (PWCH)((PCHAR)pathBuffer + Path1->Length);
  248. //
  249. // If there was no separator character at the end of the first path
  250. // or at the beginning of the next path, put one in. We don't
  251. // want to put in leading slashes, however, so don't put one in
  252. // if it would be the first character. Also, we don't want to insert
  253. // a slash if a relative stream is being opened (i.e. name begins with ':')
  254. //
  255. if ( nextLocation > pathBuffer &&
  256. *(nextLocation - 1) != DIRECTORY_SEPARATOR_CHAR &&
  257. *path2.Buffer != DIRECTORY_SEPARATOR_CHAR &&
  258. *path2.Buffer != RELATIVE_STREAM_INITIAL_CHAR ) {
  259. *nextLocation++ = DIRECTORY_SEPARATOR_CHAR;
  260. }
  261. //
  262. // Concatenate the second path name with the first.
  263. //
  264. RtlCopyMemory( nextLocation, path2.Buffer, path2.Length );
  265. nextLocation = (PWCH)((PCHAR)nextLocation + path2.Length);
  266. //
  267. // If there was no separator character at the end of the first path
  268. // or at the beginning of the next path, put one in. Again, don't
  269. // put in leading slashes, and watch out for relative stream opens.
  270. //
  271. if ( nextLocation > pathBuffer &&
  272. *(nextLocation - 1) != DIRECTORY_SEPARATOR_CHAR &&
  273. *path3.Buffer != DIRECTORY_SEPARATOR_CHAR &&
  274. *path3.Buffer != RELATIVE_STREAM_INITIAL_CHAR ) {
  275. *nextLocation++ = DIRECTORY_SEPARATOR_CHAR;
  276. }
  277. //
  278. // Concatenate the third path name.
  279. //
  280. RtlCopyMemory( nextLocation, path3.Buffer, path3.Length );
  281. nextLocation = (PWCH)((PCHAR)nextLocation + path3.Length);
  282. //
  283. // The path cannot end in a '\', so if there was one at the end get
  284. // rid of it.
  285. //
  286. if ( nextLocation > pathBuffer &&
  287. *(nextLocation - 1) == DIRECTORY_SEPARATOR_CHAR ) {
  288. *(--nextLocation) = '\0';
  289. }
  290. //
  291. // Find the length of the path we built.
  292. //
  293. BuiltPath->Length = (SHORT)((PCHAR)nextLocation - (PCHAR)pathBuffer);
  294. return;
  295. } // SrvAllocateAndBuildPathName
  296. NTSTATUS
  297. SrvCanonicalizePathName(
  298. IN PWORK_CONTEXT WorkContext,
  299. IN PSHARE Share OPTIONAL,
  300. IN PUNICODE_STRING RelatedPath OPTIONAL,
  301. IN OUT PVOID Name,
  302. IN PCHAR LastValidLocation,
  303. IN BOOLEAN RemoveTrailingDots,
  304. IN BOOLEAN SourceIsUnicode,
  305. OUT PUNICODE_STRING String
  306. )
  307. /*++
  308. Routine Description:
  309. This routine canonicalizes a filename. All ".\" are removed, and
  310. "..\" are evaluated to go up a directory level. A check is also
  311. made to ensure that the pathname does not go to a directory above
  312. the share root directory (i.e., no leading "..\"). Trailing blanks
  313. are always removed, as are trailing dots if RemoveTrailingDots is
  314. TRUE.
  315. If the input string is not Unicode, a Unicode representation of the
  316. input is obtained. This requires that additional space be
  317. allocated, and it is the caller's responsibility to free this space.
  318. If the input string IS Unicode, this routine will align the input
  319. pointer (Name) to the next two-byte boundary before performing the
  320. canonicalization. All Unicode strings in SMBs must be aligned
  321. properly.
  322. This routine operates "in place," meaning that it puts the
  323. canonicalized pathname in the same storage as the uncanonicalized
  324. pathname. This is useful for operating on the Buffer fields of the
  325. request SMBs--simply call this routine and it will fix the pathname.
  326. However, the calling routine must be careful if there are two
  327. pathnames stored in the buffer field--the second won't necessarily
  328. start in the space just after the first '\0'.
  329. The LastValidLocation parameter is used to determine the maximum
  330. possible length of the name. This prevents an access violation if
  331. the client fails to include a zero terminator, or for strings (such
  332. as the file name in NT Create And X) that are not required to be
  333. zero terminated.
  334. If the SMB described by WorkContext is marked as containing Dfs names,
  335. this routine will additionally call the Dfs driver to translate the
  336. Dfs name to a path relative to the Share. Since this call to the Dfs
  337. driver is NOT idempotent, the SMB flag indicating that it contains a
  338. Dfs name is CLEARED after a call to this routine. This posses a problem
  339. for the few SMBs that contain multiple names. The handlers for those
  340. SMBs must make sure that they conditionally call the SMB_MARK_AS_DFS_NAME
  341. macro before calling this routine.
  342. Arguments:
  343. WorkContext - contains information about the negotiated dialect. This
  344. is used for deciding whether to strip trailing spaces and dots.
  345. Share - a pointer to the share entry
  346. Name - a pointer to the filename to canonicalize.
  347. LastValidLocation - a pointer to the last valid location in the
  348. buffer pointed to by Name.
  349. RemoveTrailingDots - if TRUE, trailing dots are removed. Otherwise,
  350. they are left in (this supports special behavior needed by
  351. directory search logic).
  352. SourceIsUnicode - if TRUE, the input is canonicalized in place.
  353. If FALSE, the input is first converted to Unicode, then
  354. canonicalized.
  355. String - a pointer to string descriptor.
  356. Return Value:
  357. BOOLEAN - FALSE if the name was invalid or if storage for the
  358. Unicode string could not be obtained.
  359. --*/
  360. {
  361. PWCH source, destination, lastComponent, name;
  362. BOOLEAN notNtClient;
  363. NTSTATUS status = STATUS_SUCCESS;
  364. DWORD numberOfPathElements = 0;
  365. PAGED_CODE( );
  366. #if DBG
  367. return SrvCanonicalizePathNameWithReparse( WorkContext, Share, RelatedPath, Name, LastValidLocation, RemoveTrailingDots, SourceIsUnicode, String );
  368. #else
  369. if( SMB_IS_UNICODE( WorkContext ) &&
  370. (
  371. FlagOn( WorkContext->RequestHeader->Flags2, SMB_FLAGS2_REPARSE_PATH ) ||
  372. (Share && (Share->SnapShotEpic != -1) && !SrvDisableDownlevelTimewarp)
  373. )
  374. )
  375. {
  376. return SrvCanonicalizePathNameWithReparse( WorkContext, Share, RelatedPath, Name, LastValidLocation, RemoveTrailingDots, SourceIsUnicode, String );
  377. }
  378. #endif
  379. notNtClient = !IS_NT_DIALECT( WorkContext->Connection->SmbDialect );
  380. if ( SourceIsUnicode ) {
  381. //
  382. // The source string is already Unicode. Align the pointer.
  383. // Save the character at the last location in the buffer, then
  384. // set that location to zero. This prevents any loops from
  385. // going past the end of the buffer.
  386. //
  387. name = ALIGN_SMB_WSTR(Name);
  388. String->Buffer = name;
  389. } else {
  390. OEM_STRING oemString;
  391. PCHAR p;
  392. ULONG length;
  393. //
  394. // The source string is not Unicode. Determine the length of
  395. // the string by finding the zero terminator or the end of the
  396. // input buffer. We need the length in order to convert the
  397. // string to Unicode, and we can't just call RtlInitString, in
  398. // case the string isn't terminated.
  399. //
  400. for ( p = Name, length = 0;
  401. p <= LastValidLocation && *p != 0;
  402. p++, length++ ) {
  403. ;
  404. }
  405. //
  406. // Convert the source string to Unicode.
  407. //
  408. oemString.Buffer = Name;
  409. oemString.Length = (USHORT)length;
  410. oemString.MaximumLength = (USHORT)length;
  411. status = RtlOemStringToUnicodeString(
  412. String,
  413. &oemString,
  414. TRUE
  415. );
  416. if( !NT_SUCCESS( status ) ) {
  417. return status;
  418. }
  419. name = (PWCH)String->Buffer;
  420. LastValidLocation = (PCHAR)String->Buffer + String->Length;
  421. }
  422. //
  423. // Though everything is done in place, separate source and
  424. // destination pointers are maintained. It is necessary that source
  425. // >= destination at all times to avoid writing into space we
  426. // haven't looked at yet. The three main operations performed by
  427. // this routine ( ".\", "..\", and getting rid of trailing "." and "
  428. // ") do not interfere with this goal.
  429. //
  430. destination = name;
  431. source = name;
  432. //
  433. // The lastComponent variable is used as a placeholder when
  434. // backtracking over trailing blanks and dots. It points to the
  435. // first character after the last directory separator or the
  436. // beginning of the pathname.
  437. //
  438. lastComponent = destination;
  439. //
  440. // Get rid of leading directory separators.
  441. //
  442. while ( source <= (PWCH)LastValidLocation &&
  443. (*source == UNICODE_DIR_SEPARATOR_CHAR) && (*source != L'\0') ) {
  444. source++;
  445. }
  446. //
  447. // Walk through the pathname until we reach the zero terminator. At
  448. // the start of this loop, source points to the first charaecter
  449. // after a directory separator or the first character of the
  450. // pathname.
  451. //
  452. while ( (source <= (PWCH)LastValidLocation) && (*source != L'\0') ) {
  453. if ( *source == L'.' ) {
  454. //
  455. // If we see a dot, look at the next character.
  456. //
  457. if ( notNtClient &&
  458. ((source+1) <= (PWCH)LastValidLocation) &&
  459. (*(source+1) == UNICODE_DIR_SEPARATOR_CHAR) ) {
  460. //
  461. // If the next character is a directory separator,
  462. // advance the source pointer to the directory
  463. // separator.
  464. //
  465. source += 1;
  466. } else if ( ((source+1) <= (PWCH)LastValidLocation) &&
  467. (*(source+1) == L'.') &&
  468. ((source+1) == (PWCH)LastValidLocation ||
  469. IS_UNICODE_PATH_SEPARATOR( *(source+2) ))) {
  470. //
  471. // If the following characters are ".\", we have a "..\".
  472. // Advance the source pointer to the "\".
  473. //
  474. source += 2;
  475. //
  476. // Move the destination pointer to the charecter before the
  477. // last directory separator in order to prepare for backing
  478. // up. This may move the pointer before the beginning of
  479. // the name pointer.
  480. //
  481. destination -= 2;
  482. //
  483. // If destination points before the beginning of the name
  484. // pointer, fail because the user is attempting to go
  485. // to a higher directory than the share root. This is
  486. // the equivalent of a leading "..\", but may result from
  487. // a case like "dir\..\..\file".
  488. //
  489. if ( destination <= name ) {
  490. if ( !SourceIsUnicode ) {
  491. RtlFreeUnicodeString( String );
  492. String->Buffer = NULL;
  493. }
  494. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  495. }
  496. //
  497. // Back up the destination pointer to after the last
  498. // directory separator or to the beginning of the pathname.
  499. // Backup to the beginning of the pathname will occur
  500. // in a case like "dir\..\file".
  501. //
  502. while ( destination >= name &&
  503. *destination != UNICODE_DIR_SEPARATOR_CHAR ) {
  504. destination--;
  505. }
  506. //
  507. // destination points to \ or character before name; we
  508. // want it to point to character after last \.
  509. //
  510. destination++;
  511. } else {
  512. //
  513. // The characters after the dot are not "\" or ".\", so
  514. // so just copy source to destination until we reach a
  515. // directory separator character. This will occur in
  516. // a case like ".file" (filename starts with a dot).
  517. //
  518. do {
  519. *destination++ = *source++;
  520. } while ( (source <= (PWCH)LastValidLocation) &&
  521. !IS_UNICODE_PATH_SEPARATOR( *source ) );
  522. numberOfPathElements++;
  523. }
  524. } else { // if ( *source == L'.' )
  525. //
  526. // source does not point to a dot, so copy source to
  527. // destination until we get to a directory separator.
  528. //
  529. while ( (source <= (PWCH)LastValidLocation) &&
  530. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  531. *destination++ = *source++;
  532. }
  533. numberOfPathElements++;
  534. }
  535. //
  536. // Truncate trailing dots and blanks. destination should point
  537. // to the last character before the directory separator, so back
  538. // up over blanks and dots.
  539. //
  540. if ( notNtClient ) {
  541. while ( ( destination > lastComponent ) &&
  542. ( (RemoveTrailingDots && *(destination-1) == '.')
  543. || *(destination-1) == ' ' ) ) {
  544. destination--;
  545. }
  546. }
  547. //
  548. // At this point, source points to a directory separator or to
  549. // a zero terminator. If it is a directory separator, put one
  550. // in the destination.
  551. //
  552. if ( (source <= (PWCH)LastValidLocation) &&
  553. (*source == UNICODE_DIR_SEPARATOR_CHAR) ) {
  554. //
  555. // If we haven't put the directory separator in the path name,
  556. // put it in.
  557. //
  558. if ( destination != name &&
  559. *(destination-1) != UNICODE_DIR_SEPARATOR_CHAR ) {
  560. *destination++ = UNICODE_DIR_SEPARATOR_CHAR;
  561. }
  562. //
  563. // It is legal to have multiple directory separators, so get
  564. // rid of them here. Example: "dir\\\\\\\\file".
  565. //
  566. do {
  567. source++;
  568. } while ( (source <= (PWCH)LastValidLocation) &&
  569. (*source == UNICODE_DIR_SEPARATOR_CHAR) );
  570. //
  571. // Make lastComponent point to the character after the directory
  572. // separator.
  573. //
  574. lastComponent = destination;
  575. }
  576. }
  577. //
  578. // We're just about done. If there was a trailing .. (example:
  579. // "file\.."), trailing . ("file\."), or multiple trailing
  580. // separators ("file\\\\"), then back up one since separators are
  581. // illegal at the end of a pathname.
  582. //
  583. if ( destination > name &&
  584. *(destination-1) == UNICODE_DIR_SEPARATOR_CHAR ) {
  585. destination--;
  586. }
  587. *destination = L'\0';
  588. //
  589. // The length of the destination string is the difference between the
  590. // destination pointer (points to zero terminator at this point)
  591. // and the name pointer (points to the beginning of the destination
  592. // string).
  593. //
  594. String->Length = (SHORT)((PCHAR)destination - (PCHAR)name);
  595. String->MaximumLength = String->Length;
  596. //
  597. // One final thing: Is this SMB referring to a DFS name? If so, ask
  598. // the DFS driver to turn it into a local name.
  599. //
  600. if( ARGUMENT_PRESENT( Share ) &&
  601. Share->IsDfs &&
  602. SMB_CONTAINS_DFS_NAME( WorkContext )) {
  603. BOOLEAN stripLastComponent = FALSE;
  604. //
  605. // We have to special case some SMBs (like TRANS2_FIND_FIRST2)
  606. // because they contain path Dfs path names that could refer to a
  607. // junction point. The SMB handlers for these SMBs are not interested
  608. // in a STATUS_PATH_NOT_COVERED error; instead they want the name
  609. // to be resolved to the the junction point.
  610. //
  611. if (WorkContext->NextCommand == SMB_COM_TRANSACTION2 ) {
  612. PTRANSACTION transaction;
  613. USHORT command;
  614. transaction = WorkContext->Parameters.Transaction;
  615. command = SmbGetUshort( &transaction->InSetup[0] );
  616. if (command == TRANS2_FIND_FIRST2 && numberOfPathElements > 2 )
  617. stripLastComponent = TRUE;
  618. }
  619. status =
  620. DfsNormalizeName(Share, RelatedPath, stripLastComponent, String);
  621. SMB_MARK_AS_DFS_TRANSLATED( WorkContext );
  622. if( !NT_SUCCESS( status ) ) {
  623. if ( !SourceIsUnicode ) {
  624. RtlFreeUnicodeString( String );
  625. String->Buffer = NULL;
  626. }
  627. }
  628. }
  629. return status;
  630. } // SrvCanonicalizePathName
  631. NTSTATUS
  632. SrvCanonicalizePathNameWithReparse(
  633. IN PWORK_CONTEXT WorkContext,
  634. IN PSHARE Share OPTIONAL,
  635. IN PUNICODE_STRING RelatedPath OPTIONAL,
  636. IN OUT PVOID Name,
  637. IN PCHAR LastValidLocation,
  638. IN BOOLEAN RemoveTrailingDots,
  639. IN BOOLEAN SourceIsUnicode,
  640. OUT PUNICODE_STRING String
  641. )
  642. /*++
  643. Routine Description:
  644. This routine is identical to the one above with the exception that it
  645. checks the path for reparse-able names (such as snapshot references) and
  646. handles them accordingly. This allows us to present new features within
  647. the Win32 namespace so old applications can use them.
  648. Arguments:
  649. WorkContext - contains information about the negotiated dialect. This
  650. is used for deciding whether to strip trailing spaces and dots.
  651. Share - a pointer to the share entry
  652. Name - a pointer to the filename to canonicalize.
  653. LastValidLocation - a pointer to the last valid location in the
  654. buffer pointed to by Name.
  655. RemoveTrailingDots - if TRUE, trailing dots are removed. Otherwise,
  656. they are left in (this supports special behavior needed by
  657. directory search logic).
  658. SourceIsUnicode - if TRUE, the input is canonicalized in place.
  659. If FALSE, the input is first converted to Unicode, then
  660. canonicalized.
  661. String - a pointer to string descriptor.
  662. Return Value:
  663. BOOLEAN - FALSE if the name was invalid or if storage for the
  664. Unicode string could not be obtained.
  665. --*/
  666. {
  667. PWCH source, destination, lastComponent, name;
  668. BOOLEAN notNtClient;
  669. NTSTATUS status = STATUS_SUCCESS;
  670. DWORD numberOfPathElements = 0;
  671. PAGED_CODE( );
  672. notNtClient = !IS_NT_DIALECT( WorkContext->Connection->SmbDialect );
  673. if ( SourceIsUnicode ) {
  674. //
  675. // The source string is already Unicode. Align the pointer.
  676. // Save the character at the last location in the buffer, then
  677. // set that location to zero. This prevents any loops from
  678. // going past the end of the buffer.
  679. //
  680. name = ALIGN_SMB_WSTR(Name);
  681. String->Buffer = name;
  682. } else {
  683. OEM_STRING oemString;
  684. PCHAR p;
  685. ULONG length;
  686. //
  687. // The source string is not Unicode. Determine the length of
  688. // the string by finding the zero terminator or the end of the
  689. // input buffer. We need the length in order to convert the
  690. // string to Unicode, and we can't just call RtlInitString, in
  691. // case the string isn't terminated.
  692. //
  693. for ( p = Name, length = 0;
  694. p <= LastValidLocation && *p != 0;
  695. p++, length++ ) {
  696. ;
  697. }
  698. //
  699. // Convert the source string to Unicode.
  700. //
  701. oemString.Buffer = Name;
  702. oemString.Length = (USHORT)length;
  703. oemString.MaximumLength = (USHORT)length;
  704. status = RtlOemStringToUnicodeString(
  705. String,
  706. &oemString,
  707. TRUE
  708. );
  709. if( !NT_SUCCESS( status ) ) {
  710. return status;
  711. }
  712. name = (PWCH)String->Buffer;
  713. LastValidLocation = (PCHAR)String->Buffer + String->Length;
  714. }
  715. //
  716. // Though everything is done in place, separate source and
  717. // destination pointers are maintained. It is necessary that source
  718. // >= destination at all times to avoid writing into space we
  719. // haven't looked at yet. The three main operations performed by
  720. // this routine ( ".\", "..\", and getting rid of trailing "." and "
  721. // ") do not interfere with this goal.
  722. //
  723. destination = name;
  724. source = name;
  725. //
  726. // The lastComponent variable is used as a placeholder when
  727. // backtracking over trailing blanks and dots. It points to the
  728. // first character after the last directory separator or the
  729. // beginning of the pathname.
  730. //
  731. lastComponent = destination;
  732. //
  733. // Get rid of leading directory separators.
  734. //
  735. while ( source <= (PWCH)LastValidLocation &&
  736. (*source == UNICODE_DIR_SEPARATOR_CHAR) && (*source != L'\0') ) {
  737. source++;
  738. }
  739. //
  740. // Walk through the pathname until we reach the zero terminator. At
  741. // the start of this loop, source points to the first charaecter
  742. // after a directory separator or the first character of the
  743. // pathname.
  744. //
  745. while ( (source <= (PWCH)LastValidLocation) && (*source != L'\0') ) {
  746. if ( *source == L'.' ) {
  747. //
  748. // If we see a dot, look at the next character.
  749. //
  750. if ( notNtClient &&
  751. ((source+1) <= (PWCH)LastValidLocation) &&
  752. (*(source+1) == UNICODE_DIR_SEPARATOR_CHAR) ) {
  753. //
  754. // If the next character is a directory separator,
  755. // advance the source pointer to the directory
  756. // separator.
  757. //
  758. source += 1;
  759. } else if ( ((source+1) <= (PWCH)LastValidLocation) &&
  760. (*(source+1) == L'.') &&
  761. ((source+1) == (PWCH)LastValidLocation ||
  762. IS_UNICODE_PATH_SEPARATOR( *(source+2) ))) {
  763. //
  764. // If the following characters are ".\", we have a "..\".
  765. // Advance the source pointer to the "\".
  766. //
  767. source += 2;
  768. //
  769. // Move the destination pointer to the charecter before the
  770. // last directory separator in order to prepare for backing
  771. // up. This may move the pointer before the beginning of
  772. // the name pointer.
  773. //
  774. destination -= 2;
  775. //
  776. // If destination points before the beginning of the name
  777. // pointer, fail because the user is attempting to go
  778. // to a higher directory than the share root. This is
  779. // the equivalent of a leading "..\", but may result from
  780. // a case like "dir\..\..\file".
  781. //
  782. if ( destination <= name ) {
  783. if ( !SourceIsUnicode ) {
  784. RtlFreeUnicodeString( String );
  785. String->Buffer = NULL;
  786. }
  787. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  788. }
  789. //
  790. // Back up the destination pointer to after the last
  791. // directory separator or to the beginning of the pathname.
  792. // Backup to the beginning of the pathname will occur
  793. // in a case like "dir\..\file".
  794. //
  795. while ( destination >= name &&
  796. *destination != UNICODE_DIR_SEPARATOR_CHAR ) {
  797. destination--;
  798. }
  799. //
  800. // destination points to \ or character before name; we
  801. // want it to point to character after last \.
  802. //
  803. destination++;
  804. } else {
  805. //
  806. // The characters after the dot are not "\" or ".\", so
  807. // so just copy source to destination until we reach a
  808. // directory separator character. This will occur in
  809. // a case like ".file" (filename starts with a dot).
  810. //
  811. do {
  812. *destination++ = *source++;
  813. } while ( (source <= (PWCH)LastValidLocation) &&
  814. !IS_UNICODE_PATH_SEPARATOR( *source ) );
  815. numberOfPathElements++;
  816. }
  817. } else { // if ( *source == L'.' )
  818. LARGE_INTEGER SnapShotTime;
  819. // Try to parse out a snap token
  820. // Length = LastValidLocation - Source + sizeof(WCHAR) (since LastValidLocation is potentially a valid character)
  821. if( SrvSnapParseToken( source, ((ULONG)((PCHAR)LastValidLocation - (PCHAR)source + sizeof(WCHAR))), &SnapShotTime ) )
  822. {
  823. if( (WorkContext->SnapShotTime.QuadPart != 0) || (SnapShotTime.QuadPart == 0) )
  824. {
  825. // Don't allow 2 timestamps in the same path, or 0-time timestamps. These
  826. // can cause a variety of confusing or Denail-of-Service situations.
  827. return STATUS_INVALID_PARAMETER;
  828. }
  829. WorkContext->SnapShotTime = SnapShotTime;
  830. while ( (source <= (PWCH)LastValidLocation) &&
  831. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  832. source++;
  833. }
  834. #if 0 //DBG
  835. if( !(WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_REPARSE_PATH) )
  836. {
  837. DbgPrint( "Found token but REPARSE not set!\n" );
  838. DbgBreakPoint();
  839. }
  840. #endif
  841. }
  842. else
  843. {
  844. while ( (source <= (PWCH)LastValidLocation) &&
  845. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  846. *destination++ = *source++;
  847. }
  848. }
  849. numberOfPathElements++;
  850. }
  851. //
  852. // Truncate trailing dots and blanks. destination should point
  853. // to the last character before the directory separator, so back
  854. // up over blanks and dots.
  855. //
  856. if ( notNtClient ) {
  857. while ( ( destination > lastComponent ) &&
  858. ( (RemoveTrailingDots && *(destination-1) == '.')
  859. || *(destination-1) == ' ' ) ) {
  860. destination--;
  861. }
  862. }
  863. //
  864. // At this point, source points to a directory separator or to
  865. // a zero terminator. If it is a directory separator, put one
  866. // in the destination.
  867. //
  868. if ( (source <= (PWCH)LastValidLocation) &&
  869. (*source == UNICODE_DIR_SEPARATOR_CHAR) ) {
  870. //
  871. // If we haven't put the directory separator in the path name,
  872. // put it in.
  873. //
  874. if ( destination != name &&
  875. *(destination-1) != UNICODE_DIR_SEPARATOR_CHAR ) {
  876. *destination++ = UNICODE_DIR_SEPARATOR_CHAR;
  877. }
  878. //
  879. // It is legal to have multiple directory separators, so get
  880. // rid of them here. Example: "dir\\\\\\\\file".
  881. //
  882. do {
  883. source++;
  884. } while ( (source <= (PWCH)LastValidLocation) &&
  885. (*source == UNICODE_DIR_SEPARATOR_CHAR) );
  886. //
  887. // Make lastComponent point to the character after the directory
  888. // separator.
  889. //
  890. lastComponent = destination;
  891. }
  892. }
  893. //
  894. // We're just about done. If there was a trailing .. (example:
  895. // "file\.."), trailing . ("file\."), or multiple trailing
  896. // separators ("file\\\\"), then back up one since separators are
  897. // illegal at the end of a pathname.
  898. //
  899. if ( destination > name &&
  900. *(destination-1) == UNICODE_DIR_SEPARATOR_CHAR ) {
  901. destination--;
  902. }
  903. *destination = L'\0';
  904. //
  905. // The length of the destination string is the difference between the
  906. // destination pointer (points to zero terminator at this point)
  907. // and the name pointer (points to the beginning of the destination
  908. // string).
  909. //
  910. String->Length = (SHORT)((PCHAR)destination - (PCHAR)name);
  911. String->MaximumLength = String->Length;
  912. //
  913. // One final thing: Is this SMB referring to a DFS name? If so, ask
  914. // the DFS driver to turn it into a local name.
  915. //
  916. if( ARGUMENT_PRESENT( Share ) &&
  917. Share->IsDfs &&
  918. SMB_CONTAINS_DFS_NAME( WorkContext )) {
  919. BOOLEAN stripLastComponent = FALSE;
  920. //
  921. // We have to special case some SMBs (like TRANS2_FIND_FIRST2)
  922. // because they contain path Dfs path names that could refer to a
  923. // junction point. The SMB handlers for these SMBs are not interested
  924. // in a STATUS_PATH_NOT_COVERED error; instead they want the name
  925. // to be resolved to the the junction point.
  926. //
  927. if (WorkContext->NextCommand == SMB_COM_TRANSACTION2 ) {
  928. PTRANSACTION transaction;
  929. USHORT command;
  930. transaction = WorkContext->Parameters.Transaction;
  931. command = SmbGetUshort( &transaction->InSetup[0] );
  932. if (command == TRANS2_FIND_FIRST2 && numberOfPathElements > 2 )
  933. stripLastComponent = TRUE;
  934. }
  935. status =
  936. DfsNormalizeName(Share, RelatedPath, stripLastComponent, String);
  937. SMB_MARK_AS_DFS_TRANSLATED( WorkContext );
  938. if( !NT_SUCCESS( status ) ) {
  939. if ( !SourceIsUnicode ) {
  940. RtlFreeUnicodeString( String );
  941. String->Buffer = NULL;
  942. }
  943. }
  944. }
  945. #if 0 //DBG
  946. if( (WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_REPARSE_PATH) &&
  947. (WorkContext->SnapShotTime.QuadPart == 0) )
  948. {
  949. DbgPrint( "Token not found but REPARSE set!\n" );
  950. DbgBreakPoint();
  951. }
  952. #endif
  953. return status;
  954. } // SrvCanonicalizePathNameWithReparse
  955. NTSTATUS
  956. SrvCheckForSavedError(
  957. IN PWORK_CONTEXT WorkContext,
  958. IN PRFCB Rfcb
  959. )
  960. /*++
  961. Routine Description:
  962. This routine checks to see if there was a saved error.
  963. Arguments:
  964. WorkContext - Pointer to the workcontext block which will be
  965. marked with the error.
  966. Rfcb - Pointer to the rfcb which contains the saved error status.
  967. Return Value:
  968. status of SavedErrorCode.
  969. --*/
  970. {
  971. NTSTATUS savedErrorStatus;
  972. KIRQL oldIrql;
  973. UNLOCKABLE_CODE( 8FIL );
  974. //
  975. // Acquire the spin lock and see if the saved error
  976. // is still there.
  977. //
  978. ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
  979. savedErrorStatus = Rfcb->SavedError;
  980. if ( !NT_SUCCESS( savedErrorStatus ) ) {
  981. //
  982. // There was a write behind error. Fail this operation
  983. // with the write error.
  984. //
  985. Rfcb->SavedError = STATUS_SUCCESS;
  986. RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, oldIrql );
  987. IF_DEBUG(ERRORS) {
  988. KdPrint(( "SrvCheckForSavedError: Returning write"
  989. "behind error %X\n", savedErrorStatus ));
  990. }
  991. SrvSetSmbError( WorkContext, savedErrorStatus );
  992. } else {
  993. RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, oldIrql );
  994. }
  995. return savedErrorStatus;
  996. } // SrvCheckForSavedError
  997. NTSTATUS SRVFASTCALL
  998. SrvCheckSearchAttributes(
  999. IN USHORT FileAttributes,
  1000. IN USHORT SmbSearchAttributes
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. Determines whether the FileAttributes has
  1005. attributes not specified in SmbSearchAttributes. Only the system
  1006. and hidden bits are examined.
  1007. Arguments:
  1008. FileAttributes - The attributes in question
  1009. SmbSearchAttributes - the search attributes passed in an SMB.
  1010. Return Value:
  1011. STATUS_NO_SUCH_FILE if the attributes do not jive, or STATUS_SUCCESS if
  1012. the search attributes encompass the attributes on the file.
  1013. --*/
  1014. {
  1015. PAGED_CODE( );
  1016. //
  1017. // If the search attributes has both the system and hidden bits set,
  1018. // then the file must be OK.
  1019. //
  1020. if ( (SmbSearchAttributes & FILE_ATTRIBUTE_SYSTEM) != 0 &&
  1021. (SmbSearchAttributes & FILE_ATTRIBUTE_HIDDEN) != 0 ) {
  1022. return STATUS_SUCCESS;
  1023. }
  1024. //
  1025. // Mask out everything but the system and hidden bits--they're all
  1026. // we care about.
  1027. //
  1028. FileAttributes &= (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
  1029. //
  1030. // If a bit is set in fileAttributes that was not set in the search
  1031. // attributes, then their bitwise OR will have a bit set that is
  1032. // not set in search attributes.
  1033. //
  1034. if ( (SmbSearchAttributes | FileAttributes) != SmbSearchAttributes ) {
  1035. return STATUS_NO_SUCH_FILE;
  1036. }
  1037. return STATUS_SUCCESS;
  1038. } // SrvCheckSearchAttributes
  1039. NTSTATUS
  1040. SrvCheckSearchAttributesForHandle(
  1041. IN HANDLE FileHandle,
  1042. IN USHORT SmbSearchAttributes
  1043. )
  1044. /*++
  1045. Routine Description:
  1046. Determines whether the file corresponding to FileHandle has
  1047. attributes not specified in SmbSearchAttributes. Only the system
  1048. and hidden bits are examined.
  1049. Arguments:
  1050. FileHandle - handle to the file; must have FILE_READ_ATTRIBUTES access.
  1051. SmbSearchAttributes - the search attributes passed in an SMB.
  1052. Return Value:
  1053. STATUS_NO_SUCH_FILE if the attributes do not jive, some other status
  1054. code if the NtQueryInformationFile fails, or STATUS_SUCCESS if
  1055. the search attributes encompass the attributes on the file.
  1056. --*/
  1057. {
  1058. NTSTATUS status;
  1059. FILE_BASIC_INFORMATION fileBasicInformation;
  1060. PAGED_CODE( );
  1061. //
  1062. // If the search attributes has both the system and hidden bits set,
  1063. // then the file must be OK.
  1064. //
  1065. if ( (SmbSearchAttributes & FILE_ATTRIBUTE_SYSTEM) != 0 &&
  1066. (SmbSearchAttributes & FILE_ATTRIBUTE_HIDDEN) != 0 ) {
  1067. return STATUS_SUCCESS;
  1068. }
  1069. //
  1070. // Get the attributes on the file.
  1071. //
  1072. status = SrvQueryBasicAndStandardInformation(
  1073. FileHandle,
  1074. NULL,
  1075. &fileBasicInformation,
  1076. NULL
  1077. );
  1078. if ( !NT_SUCCESS(status) ) {
  1079. INTERNAL_ERROR(
  1080. ERROR_LEVEL_UNEXPECTED,
  1081. "SrvCheckSearchAttributesForHandle: NtQueryInformationFile (basic "
  1082. "information) returned %X",
  1083. status,
  1084. NULL
  1085. );
  1086. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  1087. return status;
  1088. }
  1089. return SrvCheckSearchAttributes( (USHORT)fileBasicInformation.FileAttributes,
  1090. SmbSearchAttributes );
  1091. } // SrvCheckSearchAttributesForHandle
  1092. VOID
  1093. SrvGetAlertServiceName(
  1094. VOID
  1095. )
  1096. /*++
  1097. Routine Description:
  1098. This routine gets the server display string from the registry.
  1099. Arguments:
  1100. None.
  1101. Return Value:
  1102. none.
  1103. --*/
  1104. {
  1105. UNICODE_STRING unicodeKeyName;
  1106. UNICODE_STRING unicodeRegPath;
  1107. OBJECT_ATTRIBUTES objAttributes;
  1108. HANDLE keyHandle;
  1109. ULONG lengthNeeded;
  1110. NTSTATUS status;
  1111. PWCHAR displayString;
  1112. PWCHAR newString;
  1113. PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
  1114. PAGED_CODE( );
  1115. RtlInitUnicodeString( &unicodeRegPath, StrRegServerPath );
  1116. RtlInitUnicodeString( &unicodeKeyName, StrRegSrvDisplayName );
  1117. InitializeObjectAttributes(
  1118. &objAttributes,
  1119. &unicodeRegPath,
  1120. OBJ_CASE_INSENSITIVE,
  1121. NULL,
  1122. NULL
  1123. );
  1124. status = ZwOpenKey(
  1125. &keyHandle,
  1126. KEY_QUERY_VALUE,
  1127. &objAttributes
  1128. );
  1129. if ( !NT_SUCCESS(status) ) {
  1130. goto use_default;
  1131. }
  1132. status = ZwQueryValueKey(
  1133. keyHandle,
  1134. &unicodeKeyName,
  1135. KeyValueFullInformation,
  1136. NULL,
  1137. 0,
  1138. &lengthNeeded
  1139. );
  1140. if ( status != STATUS_BUFFER_TOO_SMALL ) {
  1141. NtClose( keyHandle );
  1142. goto use_default;
  1143. }
  1144. infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1145. if ( infoBuffer == NULL ) {
  1146. NtClose( keyHandle );
  1147. goto use_default;
  1148. }
  1149. status = ZwQueryValueKey(
  1150. keyHandle,
  1151. &unicodeKeyName,
  1152. KeyValueFullInformation,
  1153. infoBuffer,
  1154. lengthNeeded,
  1155. &lengthNeeded
  1156. );
  1157. NtClose( keyHandle );
  1158. if ( !NT_SUCCESS(status) ) {
  1159. goto use_default;
  1160. }
  1161. //
  1162. // If it's empty, use the default.
  1163. //
  1164. lengthNeeded = infoBuffer->DataLength;
  1165. if ( lengthNeeded <= sizeof(WCHAR) ) {
  1166. goto use_default;
  1167. }
  1168. //
  1169. // Get the display string. If this is the same as the default,
  1170. // exit.
  1171. //
  1172. displayString = (PWCHAR)((PCHAR)infoBuffer + infoBuffer->DataOffset);
  1173. if ( wcscmp( displayString, StrDefaultSrvDisplayName ) == 0 ) {
  1174. goto use_default;
  1175. }
  1176. //
  1177. // allocate memory for the new display string
  1178. //
  1179. newString = (PWCHAR)ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1180. if ( newString == NULL ) {
  1181. goto use_default;
  1182. }
  1183. RtlCopyMemory(
  1184. newString,
  1185. displayString,
  1186. lengthNeeded
  1187. );
  1188. SrvAlertServiceName = newString;
  1189. FREE_HEAP( infoBuffer );
  1190. return;
  1191. use_default:
  1192. if ( infoBuffer != NULL ) {
  1193. FREE_HEAP( infoBuffer );
  1194. }
  1195. SrvAlertServiceName = StrDefaultSrvDisplayName;
  1196. return;
  1197. } // SrvGetAlertServiceName
  1198. VOID
  1199. SrvGetBaseFileName (
  1200. IN PUNICODE_STRING InputName,
  1201. OUT PUNICODE_STRING OutputName
  1202. )
  1203. /*++
  1204. Routine Description:
  1205. This routine finds the part of a path name that is only a file
  1206. name. For example, with a\b\c\filename, it sets the buffer
  1207. field of OutputName to point to "filename" and the length to 8.
  1208. *** This routine should be used AFTER SrvCanonicalizePathName has
  1209. been used on the path name to ensure that the name is good and
  1210. '..' have been removed.
  1211. Arguments:
  1212. InputName - Supplies a pointer to the pathname string.
  1213. OutputName - a pointer to where the base name information should
  1214. be written.
  1215. Return Value:
  1216. None.
  1217. --*/
  1218. {
  1219. PWCH ep = &InputName->Buffer[ InputName->Length / sizeof(WCHAR) ];
  1220. PWCH baseFileName = ep - 1;
  1221. PAGED_CODE( );
  1222. for( ; baseFileName > InputName->Buffer; --baseFileName ) {
  1223. if( *baseFileName == DIRECTORY_SEPARATOR_CHAR ) {
  1224. OutputName->Buffer = baseFileName + 1;
  1225. OutputName->Length = PTR_DIFF_SHORT(ep, OutputName->Buffer);
  1226. OutputName->MaximumLength = OutputName->Length;
  1227. return;
  1228. }
  1229. }
  1230. *OutputName = *InputName;
  1231. return;
  1232. } // SrvGetBaseFileName
  1233. VOID
  1234. SrvGetMultiSZList(
  1235. PWSTR **ListPointer,
  1236. PWSTR BaseKeyName,
  1237. PWSTR ParameterKeyName,
  1238. PWSTR *DefaultList
  1239. )
  1240. /*++
  1241. Routine Description:
  1242. This routine queries a registry value key for its MULTI_SZ values.
  1243. Arguments:
  1244. ListPointer - Pointer to receive the pointer to the null session pipes.
  1245. ParameterKeyValue - Name of the value parameter to query.
  1246. DefaultList - Value to assign to the list pointer in case
  1247. something goes wrong.
  1248. Return Value:
  1249. none.
  1250. --*/
  1251. {
  1252. UNICODE_STRING unicodeKeyName;
  1253. UNICODE_STRING unicodeParamPath;
  1254. OBJECT_ATTRIBUTES objAttributes;
  1255. HANDLE keyHandle;
  1256. ULONG lengthNeeded;
  1257. ULONG i;
  1258. ULONG numberOfEntries;
  1259. ULONG numberOfDefaultEntries = 0;
  1260. NTSTATUS status;
  1261. PWCHAR regEntry;
  1262. PWCHAR dataEntry;
  1263. PWSTR *ptrEntry;
  1264. PCHAR newBuffer;
  1265. PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
  1266. PAGED_CODE( );
  1267. RtlInitUnicodeString( &unicodeParamPath, BaseKeyName );
  1268. RtlInitUnicodeString( &unicodeKeyName, ParameterKeyName );
  1269. InitializeObjectAttributes(
  1270. &objAttributes,
  1271. &unicodeParamPath,
  1272. OBJ_CASE_INSENSITIVE,
  1273. NULL,
  1274. NULL
  1275. );
  1276. status = ZwOpenKey(
  1277. &keyHandle,
  1278. KEY_QUERY_VALUE,
  1279. &objAttributes
  1280. );
  1281. if ( !NT_SUCCESS(status) ) {
  1282. goto use_default;
  1283. }
  1284. status = ZwQueryValueKey(
  1285. keyHandle,
  1286. &unicodeKeyName,
  1287. KeyValueFullInformation,
  1288. NULL,
  1289. 0,
  1290. &lengthNeeded
  1291. );
  1292. if ( status != STATUS_BUFFER_TOO_SMALL ) {
  1293. NtClose( keyHandle );
  1294. goto use_default;
  1295. }
  1296. infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1297. if ( infoBuffer == NULL ) {
  1298. NtClose( keyHandle );
  1299. goto use_default;
  1300. }
  1301. status = ZwQueryValueKey(
  1302. keyHandle,
  1303. &unicodeKeyName,
  1304. KeyValueFullInformation,
  1305. infoBuffer,
  1306. lengthNeeded,
  1307. &lengthNeeded
  1308. );
  1309. NtClose( keyHandle );
  1310. if ( !NT_SUCCESS(status) ) {
  1311. goto use_default;
  1312. }
  1313. //
  1314. // Figure out how many entries there are.
  1315. //
  1316. // numberOfEntries should be total number of entries + 1. The extra
  1317. // one is for the NULL sentinel entry.
  1318. //
  1319. lengthNeeded = infoBuffer->DataLength;
  1320. if ( lengthNeeded <= sizeof(WCHAR) ) {
  1321. //
  1322. // No entries on the list. Use default.
  1323. //
  1324. goto use_default;
  1325. }
  1326. dataEntry = (PWCHAR)((PCHAR)infoBuffer + infoBuffer->DataOffset);
  1327. for ( i = 0, regEntry = dataEntry, numberOfEntries = 0;
  1328. i < lengthNeeded;
  1329. i += sizeof(WCHAR) ) {
  1330. if ( *regEntry++ == L'\0' ) {
  1331. numberOfEntries++;
  1332. }
  1333. }
  1334. //
  1335. // Add the number of entries in the default list.
  1336. //
  1337. if ( DefaultList != NULL ) {
  1338. for ( i = 0; DefaultList[i] != NULL ; i++ ) {
  1339. numberOfDefaultEntries++;
  1340. }
  1341. }
  1342. //
  1343. // Allocate space needed for the array of pointers. This is in addition
  1344. // to the ones in the default list.
  1345. //
  1346. newBuffer = ALLOCATE_HEAP_COLD(
  1347. lengthNeeded +
  1348. (numberOfDefaultEntries + numberOfEntries + 1) *
  1349. sizeof( PWSTR ),
  1350. BlockTypeDataBuffer
  1351. );
  1352. if ( newBuffer == NULL ) {
  1353. goto use_default;
  1354. }
  1355. //
  1356. // Copy the names
  1357. //
  1358. regEntry = (PWCHAR)(newBuffer +
  1359. (numberOfDefaultEntries + numberOfEntries + 1) * sizeof(PWSTR));
  1360. RtlCopyMemory(
  1361. regEntry,
  1362. dataEntry,
  1363. lengthNeeded
  1364. );
  1365. //
  1366. // Free the info buffer
  1367. //
  1368. FREE_HEAP( infoBuffer );
  1369. //
  1370. // Copy the pointers in the default list.
  1371. //
  1372. ptrEntry = (PWSTR *) newBuffer;
  1373. for ( i = 0; i < numberOfDefaultEntries ; i++ ) {
  1374. *ptrEntry++ = DefaultList[i];
  1375. }
  1376. //
  1377. // Build the array of pointers. If numberOfEntries is 1, then
  1378. // it means that the list is empty.
  1379. //
  1380. if ( numberOfEntries > 1 ) {
  1381. *ptrEntry++ = regEntry++;
  1382. //
  1383. // Skip the first WCHAR and the last 2 NULL terminators.
  1384. //
  1385. for ( i = 3*sizeof(WCHAR) ; i < lengthNeeded ; i += sizeof(WCHAR) ) {
  1386. if ( *regEntry++ == L'\0' ) {
  1387. *ptrEntry++ = regEntry;
  1388. }
  1389. }
  1390. }
  1391. *ptrEntry = NULL;
  1392. *ListPointer = (PWSTR *)newBuffer;
  1393. return;
  1394. use_default:
  1395. if ( infoBuffer != NULL ) {
  1396. FREE_HEAP( infoBuffer );
  1397. }
  1398. *ListPointer = DefaultList;
  1399. return;
  1400. } // SrvGetMultiSZList
  1401. NTSTATUS
  1402. SrvGetDWord(
  1403. PWSTR BaseKeyName,
  1404. PWSTR ParameterKeyName,
  1405. LPDWORD Value
  1406. )
  1407. /*++
  1408. Routine Description:
  1409. This routine queries a dword registry value key
  1410. Arguments:
  1411. BaseKeyName - value of the base key
  1412. ParameterKeyValue - Name of the value parameter to query.
  1413. Value - pointer to where to fill in the value if successful
  1414. Return Value:
  1415. none.
  1416. --*/
  1417. {
  1418. UNICODE_STRING unicodeKeyName;
  1419. UNICODE_STRING unicodeParamPath;
  1420. OBJECT_ATTRIBUTES objAttributes;
  1421. HANDLE keyHandle;
  1422. ULONG lengthNeeded;
  1423. NTSTATUS status;
  1424. BYTE bData[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+2*(sizeof(DWORD))];
  1425. PKEY_VALUE_PARTIAL_INFORMATION infoBuffer = (PKEY_VALUE_PARTIAL_INFORMATION)bData;
  1426. PAGED_CODE( );
  1427. *Value = 0;
  1428. RtlInitUnicodeString( &unicodeParamPath, BaseKeyName );
  1429. RtlInitUnicodeString( &unicodeKeyName, ParameterKeyName );
  1430. InitializeObjectAttributes(
  1431. &objAttributes,
  1432. &unicodeParamPath,
  1433. OBJ_CASE_INSENSITIVE,
  1434. NULL,
  1435. NULL
  1436. );
  1437. status = ZwOpenKey(
  1438. &keyHandle,
  1439. KEY_QUERY_VALUE,
  1440. &objAttributes
  1441. );
  1442. if ( !NT_SUCCESS(status) ) {
  1443. return status;
  1444. }
  1445. status = ZwQueryValueKey(
  1446. keyHandle,
  1447. &unicodeKeyName,
  1448. KeyValuePartialInformation,
  1449. infoBuffer,
  1450. sizeof(KEY_VALUE_PARTIAL_INFORMATION)+2*(sizeof(DWORD)),
  1451. &lengthNeeded
  1452. );
  1453. NtClose( keyHandle );
  1454. if ( !NT_SUCCESS(status) ) {
  1455. return status;
  1456. }
  1457. if( infoBuffer->Type != REG_DWORD ||
  1458. infoBuffer->DataLength < sizeof(DWORD) )
  1459. {
  1460. return STATUS_INVALID_PARAMETER;
  1461. }
  1462. *Value = *( (LPDWORD)(infoBuffer->Data) );
  1463. return STATUS_SUCCESS;
  1464. } // SrvGetDWord
  1465. //
  1466. // NOTE: Any changes made here should also be made to the accompanying routines in the MRXSMB
  1467. // RDR such that NativeLanMan and NativeOS match between the two.
  1468. //
  1469. VOID
  1470. SrvGetOsVersionString(
  1471. VOID
  1472. )
  1473. {
  1474. ULONG Storage[256], Storage2[256], Storage3[256];
  1475. UNICODE_STRING UnicodeString;
  1476. HANDLE hRegistryKey;
  1477. NTSTATUS Status, Status2;
  1478. ULONG BytesRead;
  1479. OBJECT_ATTRIBUTES ObjectAttributes;
  1480. PKEY_VALUE_FULL_INFORMATION Value = (PKEY_VALUE_FULL_INFORMATION)Storage;
  1481. PKEY_VALUE_FULL_INFORMATION Value2 = (PKEY_VALUE_FULL_INFORMATION)Storage2;
  1482. PKEY_VALUE_FULL_INFORMATION Value3 = (PKEY_VALUE_FULL_INFORMATION)Storage3;
  1483. KEY_VALUE_PARTIAL_INFORMATION InitialPartialInformationValue;
  1484. ULONG AllocationLength;
  1485. PAGED_CODE();
  1486. RtlInitUnicodeString(&UnicodeString, StrRegOsVersionPath);
  1487. InitializeObjectAttributes(
  1488. &ObjectAttributes,
  1489. &UnicodeString, // name
  1490. OBJ_CASE_INSENSITIVE, // attributes
  1491. NULL, // root
  1492. NULL); // security descriptor
  1493. Status = ZwOpenKey (&hRegistryKey, KEY_READ, &ObjectAttributes);
  1494. if( NT_SUCCESS(Status) )
  1495. {
  1496. RtlInitUnicodeString(&UnicodeString, StrRegVersionBuildNumberKeyName);
  1497. Status = ZwQueryValueKey(
  1498. hRegistryKey,
  1499. &UnicodeString,
  1500. KeyValueFullInformation,
  1501. Value,
  1502. sizeof(Storage),
  1503. &BytesRead);
  1504. if (NT_SUCCESS(Status)) {
  1505. RtlInitUnicodeString(&UnicodeString, StrRegVersionProductKeyName );
  1506. Status = ZwQueryValueKey(
  1507. hRegistryKey,
  1508. &UnicodeString,
  1509. KeyValueFullInformation,
  1510. Value3,
  1511. sizeof(Storage3),
  1512. &BytesRead);
  1513. if( NT_SUCCESS(Status) )
  1514. {
  1515. // Change the data pointers
  1516. PWSTR pProduct = (PWSTR)((PCHAR)Value3 + Value3->DataOffset);
  1517. if( (Value3->DataLength > 20) &&
  1518. (_wcsnicmp( pProduct, L"Microsoft ", 10 ) == 0) )
  1519. {
  1520. Value3->DataLength -= 20;
  1521. Value3->DataOffset += 20;
  1522. }
  1523. // check for existance of Service Pack String
  1524. RtlInitUnicodeString(&UnicodeString, StrRegVersionSPKeyName);
  1525. Status2 = ZwQueryValueKey(
  1526. hRegistryKey,
  1527. &UnicodeString,
  1528. KeyValueFullInformation,
  1529. Value2,
  1530. sizeof(Storage2),
  1531. &BytesRead);
  1532. SrvNativeOS.MaximumLength = (USHORT)Value->DataLength + (USHORT)Value3->DataLength + sizeof(WCHAR);
  1533. if(NT_SUCCESS(Status2)) {
  1534. SrvNativeOS.MaximumLength += (USHORT)Value2->DataLength;
  1535. }
  1536. SrvNativeOS.Length = 0;
  1537. SrvNativeOS.Buffer = ALLOCATE_HEAP_COLD(SrvNativeOS.MaximumLength, BlockTypeDataBuffer);
  1538. if (SrvNativeOS.Buffer != NULL) {
  1539. RtlZeroMemory( SrvNativeOS.Buffer, SrvNativeOS.MaximumLength );
  1540. // Copy string
  1541. RtlCopyMemory(SrvNativeOS.Buffer,
  1542. (PCHAR)Value3+Value3->DataOffset,
  1543. Value3->DataLength);
  1544. // Replace NULL with SPACE
  1545. RtlCopyMemory((SrvNativeOS.Buffer +
  1546. (Value3->DataLength/sizeof(WCHAR)) - 1),
  1547. L" ",
  1548. sizeof(WCHAR));
  1549. SrvNativeOS.Length += (USHORT)Value3->DataLength;
  1550. // Copy next string
  1551. RtlCopyMemory((SrvNativeOS.Buffer +
  1552. (Value3->DataLength/sizeof(WCHAR))),
  1553. (PCHAR)Value+Value->DataOffset,
  1554. Value->DataLength);
  1555. SrvNativeOS.Length += (USHORT)Value->DataLength;
  1556. if(NT_SUCCESS(Status2)) {
  1557. // replace NULL with SPACE
  1558. RtlCopyMemory(SrvNativeOS.Buffer +
  1559. (Value3->DataLength + Value->DataLength)/sizeof(WCHAR) - 1,
  1560. L" ",
  1561. sizeof(WCHAR));
  1562. // Copy last string (including NULL)
  1563. RtlCopyMemory(SrvNativeOS.Buffer +
  1564. (Value3->DataLength + Value->DataLength)/sizeof(WCHAR),
  1565. (PCHAR)Value2+Value2->DataOffset,
  1566. Value2->DataLength);
  1567. SrvNativeOS.Length += (USHORT)Value2->DataLength;
  1568. }
  1569. Status = RtlUnicodeStringToOemString(
  1570. &SrvOemNativeOS,
  1571. &SrvNativeOS,
  1572. TRUE
  1573. );
  1574. if ( !NT_SUCCESS(Status) ) {
  1575. FREE_HEAP( SrvNativeOS.Buffer );
  1576. SrvNativeOS.Buffer = NULL;
  1577. }
  1578. } else {
  1579. Status = STATUS_INSUFFICIENT_RESOURCES;
  1580. }
  1581. }
  1582. }
  1583. if (NT_SUCCESS(Status)) {
  1584. RtlInitUnicodeString(&UnicodeString, StrRegVersionKeyName);
  1585. Status = ZwQueryValueKey(
  1586. hRegistryKey,
  1587. &UnicodeString,
  1588. KeyValueFullInformation,
  1589. Value,
  1590. sizeof(Storage),
  1591. &BytesRead);
  1592. if (NT_SUCCESS(Status)) {
  1593. SrvNativeLanMan.MaximumLength =
  1594. SrvNativeLanMan.Length = (USHORT)Value->DataLength +
  1595. (USHORT)Value3->DataLength + sizeof(WCHAR);
  1596. SrvNativeLanMan.Buffer = ALLOCATE_HEAP_COLD(SrvNativeLanMan.Length, BlockTypeDataBuffer );
  1597. if (SrvNativeLanMan.Buffer != NULL) {
  1598. RtlZeroMemory( SrvNativeLanMan.Buffer, SrvNativeLanMan.MaximumLength );
  1599. SrvNativeLanMan.Length = 0;
  1600. RtlCopyMemory(
  1601. SrvNativeLanMan.Buffer,
  1602. (PCHAR)Value3 + Value3->DataOffset,
  1603. Value3->DataLength);
  1604. RtlCopyMemory(
  1605. (SrvNativeLanMan.Buffer +
  1606. (Value3->DataLength/sizeof(WCHAR)) - 1),
  1607. L" ",
  1608. sizeof(WCHAR));
  1609. SrvNativeLanMan.Length += (USHORT)Value3->DataLength;
  1610. RtlCopyMemory(
  1611. (SrvNativeLanMan.Buffer +
  1612. (Value3->DataLength/sizeof(WCHAR))),
  1613. (PCHAR)Value+Value->DataOffset,
  1614. Value->DataLength);
  1615. SrvNativeLanMan.Length += (USHORT)Value->DataLength;
  1616. Status = RtlUnicodeStringToOemString(
  1617. &SrvOemNativeLanMan,
  1618. &SrvNativeLanMan,
  1619. TRUE
  1620. );
  1621. if ( !NT_SUCCESS(Status) ) {
  1622. FREE_HEAP( SrvNativeOS.Buffer );
  1623. SrvNativeOS.Buffer = NULL;
  1624. RtlFreeOemString( &SrvOemNativeOS );
  1625. SrvOemNativeOS.Buffer = NULL;
  1626. FREE_HEAP( SrvNativeLanMan.Buffer );
  1627. SrvNativeLanMan.Buffer = NULL;
  1628. }
  1629. } else {
  1630. Status = STATUS_INSUFFICIENT_RESOURCES;
  1631. }
  1632. }
  1633. }
  1634. ZwClose(hRegistryKey);
  1635. }
  1636. if( !NT_SUCCESS(Status) )
  1637. {
  1638. RtlInitUnicodeString( &SrvNativeOS, StrDefaultNativeOs );
  1639. RtlInitAnsiString( (PANSI_STRING)&SrvOemNativeOS, StrDefaultNativeOsOem );
  1640. RtlInitUnicodeString( &SrvNativeLanMan, StrNativeLanman );
  1641. RtlInitAnsiString( (PANSI_STRING)&SrvOemNativeLanMan, StrNativeLanmanOem );
  1642. }
  1643. return;
  1644. }
  1645. USHORT
  1646. SrvGetString (
  1647. IN OUT PUNICODE_STRING Destination,
  1648. IN PVOID Source,
  1649. IN PVOID EndOfSourceBuffer,
  1650. IN BOOLEAN SourceIsUnicode
  1651. )
  1652. /*++
  1653. Routine Description:
  1654. Reads a string out of an SMB buffer, and converts it to Unicode,
  1655. if necessary. This function is similar to SrvMakeUnicodeString,
  1656. except
  1657. (1) It always copies data from source to destination
  1658. (2) It assumes storage for destination has been preallocated. Its length
  1659. is Destination->MaximumLength
  1660. Arguments:
  1661. Destination - the resultant Unicode string.
  1662. Source - a zero-terminated input.
  1663. EndOfSourceBuffer - A pointer to the end of the SMB buffer. Used to
  1664. protect the server from accessing beyond the end of the SMB buffer,
  1665. if the format is invalid.
  1666. SourceIsUnicode - TRUE if the source is already Unicode.
  1667. Return Value:
  1668. Length - Length of input buffer (included the NUL terminator).
  1669. -1 if EndOfSourceBuffer is reached before the NUL terminator.
  1670. --*/
  1671. {
  1672. USHORT length;
  1673. PAGED_CODE( );
  1674. if ( SourceIsUnicode ) {
  1675. PWCH currentChar = Source;
  1676. ASSERT( ((ULONG_PTR)Source & 1) == 0 );
  1677. length = 0;
  1678. while ( currentChar < (PWCH)EndOfSourceBuffer &&
  1679. *currentChar != UNICODE_NULL ) {
  1680. currentChar++;
  1681. length += sizeof( WCHAR );
  1682. }
  1683. //
  1684. // If we hit the end of the SMB buffer without finding a NUL, this
  1685. // is a bad string. Return an error.
  1686. //
  1687. if ( currentChar >= (PWCH)EndOfSourceBuffer ) {
  1688. return (USHORT)-1;
  1689. }
  1690. //
  1691. // If we overran our storage buffer, this is a bad string. Return an error
  1692. //
  1693. if( length + sizeof( UNICODE_NULL ) > Destination->MaximumLength ) {
  1694. return (USHORT)-1;
  1695. }
  1696. //
  1697. // Copy the unicode data to the destination, including the NULL. Set length
  1698. // of Destination to the non-null string length.
  1699. //
  1700. Destination->Length = length;
  1701. //
  1702. // We didn't change this to RtlCopyMemory because it is possible that
  1703. // the source and destination can overlap. Copy the NULL as well
  1704. //
  1705. RtlMoveMemory( Destination->Buffer, Source, length );
  1706. } else {
  1707. PCHAR currentChar = Source;
  1708. OEM_STRING sourceString;
  1709. length = 0;
  1710. while ( currentChar <= (PCHAR)EndOfSourceBuffer &&
  1711. *currentChar != '\0' ) {
  1712. currentChar++;
  1713. length++;
  1714. }
  1715. //
  1716. // If we hit the end of the SMB buffer without finding a NUL, this
  1717. // is a bad string. Return an error.
  1718. //
  1719. if ( currentChar > (PCHAR)EndOfSourceBuffer ) {
  1720. return (USHORT)-1;
  1721. }
  1722. //
  1723. // If we overran our storage buffer, this is a bad string. Return an error
  1724. //
  1725. if( (USHORT)(length + 1)*sizeof(WCHAR) > Destination->MaximumLength ) {
  1726. return (USHORT)-1;
  1727. }
  1728. sourceString.Buffer = Source;
  1729. sourceString.Length = length;
  1730. //
  1731. // Convert the data to unicode.
  1732. //
  1733. Destination->Length = 0;
  1734. RtlOemStringToUnicodeString( Destination, &sourceString, FALSE );
  1735. //
  1736. // Increment 'length', to indicate that the NUL has been copied.
  1737. //
  1738. length++;
  1739. }
  1740. //
  1741. // Return the number of bytes copied from the source buffer.
  1742. //
  1743. return length;
  1744. } // SrvGetString
  1745. USHORT
  1746. SrvGetStringLength (
  1747. IN PVOID Source,
  1748. IN PVOID EndOfSourceBuffer,
  1749. IN BOOLEAN SourceIsUnicode,
  1750. IN BOOLEAN IncludeNullTerminator
  1751. )
  1752. /*++
  1753. Routine Description:
  1754. This routine returns the length of a string in an SMB buffer in bytes.
  1755. If the end of the buffer is encountered before the NUL terminator,
  1756. the function returns -1 as the length.
  1757. Arguments:
  1758. Source - a NUL-terminated input.
  1759. EndOfSourceBuffer - A pointer to the end of the SMB buffer. Used to
  1760. protect the server from accessing beyond the end of the SMB buffer,
  1761. if the format is invalid.
  1762. SourceIsUnicode - TRUE if the source is already Unicode.
  1763. IncludeNullTerminator - TRUE if the Length to be returned includes the
  1764. null terminator.
  1765. Return Value:
  1766. Length - Length of input buffer. -1 if EndOfSourceBuffer is reached
  1767. before the NUL terminator.
  1768. --*/
  1769. {
  1770. USHORT length;
  1771. PAGED_CODE( );
  1772. if ( IncludeNullTerminator) {
  1773. length = 1;
  1774. } else {
  1775. length = 0;
  1776. }
  1777. if ( SourceIsUnicode ) {
  1778. PWCH currentChar = (PWCH)Source;
  1779. ASSERT( ((ULONG_PTR)currentChar & 1) == 0 );
  1780. while ( currentChar < (PWCH)EndOfSourceBuffer &&
  1781. *currentChar != UNICODE_NULL ) {
  1782. currentChar++;
  1783. length++;
  1784. }
  1785. //
  1786. // If we hit the end of the SMB buffer without finding a NUL, this
  1787. // is a bad string. Return an error.
  1788. //
  1789. if ( currentChar >= (PWCH)EndOfSourceBuffer ) {
  1790. length = (USHORT)-1;
  1791. } else {
  1792. length = (USHORT)(length * sizeof(WCHAR));
  1793. }
  1794. } else {
  1795. PCHAR currentChar = Source;
  1796. while ( currentChar <= (PCHAR)EndOfSourceBuffer &&
  1797. *currentChar != '\0' ) {
  1798. currentChar++;
  1799. length++;
  1800. }
  1801. //
  1802. // If we hit the end of the SMB buffer without finding a NUL, this
  1803. // is a bad string. Return an error.
  1804. //
  1805. if ( currentChar > (PCHAR)EndOfSourceBuffer ) {
  1806. length = (USHORT)-1;
  1807. }
  1808. }
  1809. //
  1810. // Return the length of the string.
  1811. //
  1812. return length;
  1813. } // SrvGetStringLength
  1814. USHORT
  1815. SrvGetSubdirectoryLength (
  1816. IN PUNICODE_STRING InputName
  1817. )
  1818. /*++
  1819. Routine Description:
  1820. This routine finds the length of "subdirectory" information in a
  1821. path name, that is, the parts of the path name that are not the
  1822. actual name of the file. For example, for a\b\c\filename, it
  1823. returns 5. This allows the calling routine to open the directory
  1824. containing the file or get a full pathname to a file after a search.
  1825. *** This routine should be used AFTER SrvCanonicalizePathName has
  1826. been used on the path name to ensure that the name is good and
  1827. '..' have been removed.
  1828. Arguments:
  1829. InputName - Supplies a pointer to the pathname string.
  1830. Return Value:
  1831. The number of bytes that contain directory information. If the
  1832. input is just a filename, then 0 is returned.
  1833. --*/
  1834. {
  1835. ULONG i;
  1836. PWCH baseFileName = InputName->Buffer;
  1837. PAGED_CODE( );
  1838. for ( i = 0; i < InputName->Length / sizeof(WCHAR); i++ ) {
  1839. //
  1840. // If s points to a directory separator, set fileBaseName to
  1841. // the character after the separator.
  1842. //
  1843. if ( InputName->Buffer[i] == DIRECTORY_SEPARATOR_CHAR ) {
  1844. baseFileName = &InputName->Buffer[i];
  1845. }
  1846. }
  1847. return (USHORT)((baseFileName - InputName->Buffer) * sizeof(WCHAR));
  1848. } // SrvGetSubdirectoryLength
  1849. BOOLEAN SRVFASTCALL
  1850. SrvIsLegalFatName (
  1851. IN PWSTR InputName,
  1852. IN CLONG InputNameLength
  1853. )
  1854. /*++
  1855. Routine Description:
  1856. Determines whether a file name would be legal for FAT. This
  1857. is needed for SrvQueryDirectoryFile because it must filter names
  1858. for clients that do not know about long or non-FAT filenames.
  1859. Arguments:
  1860. InputName - Supplies the string to test
  1861. InputNameLength - Length of the string (excluding the NULL termination)
  1862. to test.
  1863. Return Value:
  1864. TRUE if the name is a legal FAT name, FALSE if the name would be
  1865. rejected by FAT.
  1866. --*/
  1867. {
  1868. UNICODE_STRING original_name;
  1869. UNICODE_STRING upcase_name;
  1870. WCHAR upcase_buffer[ 13 ];
  1871. STRING oem_string;
  1872. CHAR oem_buffer[ 13 ];
  1873. UNICODE_STRING converted_name;
  1874. WCHAR converted_name_buffer[ 13 ];
  1875. BOOLEAN spacesInName, nameValid8Dot3;
  1876. PAGED_CODE();
  1877. //
  1878. // Special case . and .. -- they are legal FAT names
  1879. //
  1880. if( InputName[0] == L'.' ) {
  1881. if( InputNameLength == sizeof(WCHAR) ||
  1882. ((InputNameLength == 2*sizeof(WCHAR)) && InputName[1] == L'.')) {
  1883. return TRUE;
  1884. }
  1885. return FALSE;
  1886. }
  1887. original_name.Buffer = InputName;
  1888. original_name.Length = original_name.MaximumLength = (USHORT)InputNameLength;
  1889. nameValid8Dot3 = RtlIsNameLegalDOS8Dot3( &original_name, NULL, &spacesInName );
  1890. if( !nameValid8Dot3 || spacesInName ) {
  1891. return FALSE;
  1892. }
  1893. if( SrvFilterExtendedCharsInPath == FALSE ) {
  1894. //
  1895. // One final test -- we must be able to convert this name to OEM and back again
  1896. // without any loss of information.
  1897. //
  1898. oem_string.Buffer = oem_buffer;
  1899. upcase_name.Buffer = upcase_buffer;
  1900. converted_name.Buffer = converted_name_buffer;
  1901. oem_string.MaximumLength = sizeof( oem_buffer );
  1902. upcase_name.MaximumLength = sizeof( upcase_buffer );
  1903. converted_name.MaximumLength = sizeof( converted_name_buffer );
  1904. oem_string.Length = 0;
  1905. upcase_name.Length = 0;
  1906. converted_name.Length = 0;
  1907. nameValid8Dot3 = NT_SUCCESS( RtlUpcaseUnicodeString( &upcase_name, &original_name, FALSE )) &&
  1908. NT_SUCCESS( RtlUnicodeStringToOemString( &oem_string, &upcase_name, FALSE )) &&
  1909. FsRtlIsFatDbcsLegal( oem_string, FALSE, FALSE, FALSE ) &&
  1910. NT_SUCCESS( RtlOemStringToUnicodeString( &converted_name, &oem_string, FALSE )) &&
  1911. RtlEqualUnicodeString( &upcase_name, &converted_name, FALSE );
  1912. }
  1913. return nameValid8Dot3;
  1914. } // SrvIsLegalFatName
  1915. NTSTATUS
  1916. SrvMakeUnicodeString (
  1917. IN BOOLEAN SourceIsUnicode,
  1918. OUT PUNICODE_STRING Destination,
  1919. IN PVOID Source,
  1920. IN PUSHORT SourceLength OPTIONAL
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. Makes a unicode string from a zero-terminated input that is either
  1925. ANSI or Unicode.
  1926. Arguments:
  1927. SourceIsUnicode - TRUE if the source is already Unicode. If FALSE,
  1928. RtlOemStringToUnicodeString will allocate space to hold the
  1929. Unicode string; it is the responsibility of the caller to
  1930. free this space.
  1931. Destination - the resultant Unicode string.
  1932. Source - a zero-terminated input.
  1933. Return Value:
  1934. NTSTATUS - result of operation.
  1935. --*/
  1936. {
  1937. OEM_STRING oemString;
  1938. PAGED_CODE( );
  1939. if ( SourceIsUnicode ) {
  1940. ASSERT( ((ULONG_PTR)Source & 1) == 0 );
  1941. if ( ARGUMENT_PRESENT( SourceLength ) ) {
  1942. ASSERT( (*SourceLength) != (USHORT) -1 );
  1943. Destination->Buffer = Source;
  1944. Destination->Length = *SourceLength;
  1945. Destination->MaximumLength = *SourceLength;
  1946. } else {
  1947. RtlInitUnicodeString( Destination, Source );
  1948. }
  1949. return STATUS_SUCCESS;
  1950. }
  1951. if ( ARGUMENT_PRESENT( SourceLength ) ) {
  1952. oemString.Buffer = Source;
  1953. oemString.Length = *SourceLength;
  1954. oemString.MaximumLength = *SourceLength;
  1955. } else {
  1956. RtlInitString( &oemString, Source );
  1957. }
  1958. return RtlOemStringToUnicodeString(
  1959. Destination,
  1960. &oemString,
  1961. TRUE
  1962. );
  1963. } // SrvMakeUnicodeString
  1964. VOID
  1965. SrvReleaseContext (
  1966. IN PWORK_CONTEXT WorkContext
  1967. )
  1968. /*++
  1969. Routine Description:
  1970. This function releases (dereferences) control blocks referenced by a
  1971. Work Context block. It is called when processing of an incoming SMB
  1972. is complete, just before the response SMB (if any) is set.
  1973. The following control blocks are dereferenced: Share, Session,
  1974. TreeConnect, and File. If any of these fields is nonzero in
  1975. WorkContext, the block is dereferenced and the fields is zeroed.
  1976. Note that the Connection block and the Endpoint block are NOT
  1977. dereferenced. This is based on the assumption that a response is
  1978. about to be sent, so the connection must stay referenced. The
  1979. Connection block is dereferenced after the send of the response (if
  1980. any) when SrvRequeueReceiveIrp is called. That function also
  1981. releases the response buffer, if it is different from the request
  1982. buffer.
  1983. Arguments:
  1984. WorkContext - Supplies a pointer to a work context block.
  1985. Return Value:
  1986. None.
  1987. --*/
  1988. {
  1989. PAGED_CODE( );
  1990. //
  1991. // !!! If you change the way this routine works (e.g., you add
  1992. // another block that needs to be dereferenced), make sure you
  1993. // check fsd.c\SrvFsdRestartSmbComplete to see if it needs to be
  1994. // changed too.
  1995. //
  1996. //
  1997. // Dereference the Share block, if any.
  1998. //
  1999. if ( WorkContext->Share != NULL ) {
  2000. SrvDereferenceShare( WorkContext->Share );
  2001. WorkContext->Share = NULL;
  2002. }
  2003. //
  2004. // Dereference the Security Context, if any.
  2005. //
  2006. if ( WorkContext->SecurityContext != NULL ) {
  2007. SrvDereferenceSecurityContext( WorkContext->SecurityContext );
  2008. WorkContext->SecurityContext = NULL;
  2009. }
  2010. //
  2011. // Dereference the Session block, if any.
  2012. //
  2013. if ( WorkContext->Session != NULL ) {
  2014. SrvDereferenceSession( WorkContext->Session );
  2015. WorkContext->Session= NULL;
  2016. }
  2017. //
  2018. // Dereference the Tree Connect block, if any.
  2019. //
  2020. if ( WorkContext->TreeConnect != NULL ) {
  2021. SrvDereferenceTreeConnect( WorkContext->TreeConnect );
  2022. WorkContext->TreeConnect = NULL;
  2023. }
  2024. //
  2025. // Dereference the RFCB, if any.
  2026. //
  2027. if ( WorkContext->Rfcb != NULL ) {
  2028. SrvDereferenceRfcb( WorkContext->Rfcb );
  2029. WorkContext->OplockOpen = FALSE;
  2030. WorkContext->Rfcb = NULL;
  2031. }
  2032. //
  2033. // Dereference the wait for oplock break, if any
  2034. //
  2035. if ( WorkContext->WaitForOplockBreak != NULL ) {
  2036. SrvDereferenceWaitForOplockBreak( WorkContext->WaitForOplockBreak );
  2037. WorkContext->WaitForOplockBreak = NULL;
  2038. }
  2039. //
  2040. // If this was a blocking operation, update the blocking i/o count.
  2041. //
  2042. if ( WorkContext->BlockingOperation ) {
  2043. InterlockedDecrement( &SrvBlockingOpsInProgress );
  2044. WorkContext->BlockingOperation = FALSE;
  2045. }
  2046. return;
  2047. } // SrvReleaseContext
  2048. BOOLEAN
  2049. SrvSetFileWritethroughMode (
  2050. IN PLFCB Lfcb,
  2051. IN BOOLEAN Writethrough
  2052. )
  2053. /*++
  2054. Routine Description:
  2055. Sets the writethrough mode of a file as specified. Returns the
  2056. original mode of the file.
  2057. Arguments:
  2058. Lfcb - A pointer to the LFCB representing the open file.
  2059. Writethrough - A boolean indicating whether the file is to be placed
  2060. into writethrough mode (TRUE) or writebehind mode (FALSE).
  2061. Return Value:
  2062. BOOLEAN - Returns the original mode of the file.
  2063. --*/
  2064. {
  2065. FILE_MODE_INFORMATION modeInformation;
  2066. IO_STATUS_BLOCK iosb;
  2067. PAGED_CODE( );
  2068. //
  2069. // If the file is already in the correct mode, simply return.
  2070. // Otherwise, set the file to the correct mode.
  2071. //
  2072. if ( Writethrough ) {
  2073. if ( (Lfcb->FileMode & FILE_WRITE_THROUGH) != 0 ) {
  2074. return TRUE;
  2075. } else {
  2076. Lfcb->FileMode |= FILE_WRITE_THROUGH;
  2077. }
  2078. } else {
  2079. if ( (Lfcb->FileMode & FILE_WRITE_THROUGH) == 0 ) {
  2080. return FALSE;
  2081. } else {
  2082. Lfcb->FileMode &= ~FILE_WRITE_THROUGH;
  2083. }
  2084. }
  2085. //
  2086. // Change the file mode.
  2087. //
  2088. // !!! Don't do this by file handle -- build and issue an IRP using
  2089. // the file object pointer directly.
  2090. //
  2091. modeInformation.Mode = Lfcb->FileMode;
  2092. (VOID)NtSetInformationFile(
  2093. Lfcb->FileHandle,
  2094. &iosb,
  2095. &modeInformation,
  2096. sizeof( modeInformation ),
  2097. FileModeInformation
  2098. );
  2099. //
  2100. // Return the original mode of the file, which was the opposite of
  2101. // what was requested.
  2102. //
  2103. return (BOOLEAN)!Writethrough;
  2104. } // SrvSetFileWritethroughMode
  2105. VOID
  2106. SrvOemStringTo8dot3 (
  2107. IN POEM_STRING InputString,
  2108. OUT PSZ Output8dot3
  2109. )
  2110. /*++
  2111. Routine Description:
  2112. Convert a string into FAT 8.3 format. This derived from GaryKi's
  2113. routine FatStringTo8dot3 in fastfat\namesup.c.
  2114. Arguments:
  2115. InputString - Supplies the input string to convert
  2116. Output8dot3 - Receives the converted string. The memory must be
  2117. supplied by the caller.
  2118. Return Value:
  2119. None.
  2120. --*/
  2121. {
  2122. CLONG i, j;
  2123. PCHAR inBuffer = InputString->Buffer;
  2124. ULONG inLength = InputString->Length;
  2125. PAGED_CODE( );
  2126. ASSERT( inLength <= 12 );
  2127. //
  2128. // First make the output name all blanks.
  2129. //
  2130. RtlFillMemory( Output8dot3, 11, CHAR_SP );
  2131. //
  2132. // If we get "." or "..", just return them. They do not follow
  2133. // the usual rules for FAT names.
  2134. //
  2135. if( inBuffer[0] == '.' ) {
  2136. if( inLength == 1 ) {
  2137. Output8dot3[0] = '.';
  2138. return;
  2139. }
  2140. if( inLength == 2 && inBuffer[1] == '.' ) {
  2141. Output8dot3[0] = '.';
  2142. Output8dot3[1] = '.';
  2143. return;
  2144. }
  2145. }
  2146. //
  2147. // Copy over the first part of the file name. Stop when we get to
  2148. // the end of the input string or a dot.
  2149. //
  2150. if (NLS_MB_CODE_PAGE_TAG) {
  2151. for ( i = 0;
  2152. (i < inLength) && (inBuffer[i] != '.') && (inBuffer[i] != '\\');
  2153. i++ ) {
  2154. if (FsRtlIsLeadDbcsCharacter(inBuffer[i])) {
  2155. if (i+1 < inLength) {
  2156. Output8dot3[i] = inBuffer[i];
  2157. i++;
  2158. Output8dot3[i] = inBuffer[i];
  2159. } else {
  2160. break;
  2161. }
  2162. } else {
  2163. Output8dot3[i] = inBuffer[i];
  2164. }
  2165. }
  2166. } else {
  2167. for ( i = 0;
  2168. (i < inLength) && (inBuffer[i] != '.') && (inBuffer[i] != '\\');
  2169. i++ ) {
  2170. Output8dot3[i] = inBuffer[i];
  2171. }
  2172. }
  2173. //
  2174. // See if we need to add an extension.
  2175. //
  2176. if ( i < inLength ) {
  2177. //
  2178. // Skip over the dot.
  2179. //
  2180. ASSERT( (inLength - i) <= 4 );
  2181. ASSERT( inBuffer[i] == '.' );
  2182. i++;
  2183. //
  2184. // Add the extension to the output name
  2185. //
  2186. if (NLS_MB_CODE_PAGE_TAG) {
  2187. for ( j = 8;
  2188. (i < inLength) && (inBuffer[i] != '\\');
  2189. i++, j++ ) {
  2190. if (FsRtlIsLeadDbcsCharacter(inBuffer[i])) {
  2191. if (i+1 < inLength) {
  2192. Output8dot3[j] = inBuffer[i];
  2193. i++; j++;
  2194. Output8dot3[j] = inBuffer[i];
  2195. } else {
  2196. break;
  2197. }
  2198. } else {
  2199. Output8dot3[j] = inBuffer[i];
  2200. }
  2201. }
  2202. } else {
  2203. for ( j = 8;
  2204. (i < inLength) && (inBuffer[i] != '\\');
  2205. i++, j++ ) {
  2206. Output8dot3[j] = inBuffer[i];
  2207. }
  2208. }
  2209. }
  2210. //
  2211. // We're all done with the conversion.
  2212. //
  2213. return;
  2214. } // SrvOemStringTo8dot3
  2215. VOID
  2216. SrvUnicodeStringTo8dot3 (
  2217. IN PUNICODE_STRING InputString,
  2218. OUT PSZ Output8dot3,
  2219. IN BOOLEAN Upcase
  2220. )
  2221. /*++
  2222. Routine Description:
  2223. Convert a string into fat 8.3 format. This derived from GaryKi's
  2224. routine FatStringTo8dot3 in fat\fatname.c.
  2225. Arguments:
  2226. InputString - Supplies the input string to convert
  2227. Output8dot3 - Receives the converted string, the memory must be supplied
  2228. by the caller.
  2229. Upcase - Whether the string is to be uppercased.
  2230. Return Value:
  2231. None.
  2232. --*/
  2233. {
  2234. ULONG oemSize;
  2235. OEM_STRING oemString;
  2236. ULONG index = 0;
  2237. UCHAR aSmallBuffer[ 50 ];
  2238. NTSTATUS status;
  2239. PAGED_CODE( );
  2240. oemSize = RtlUnicodeStringToOemSize( InputString );
  2241. ASSERT( oemSize < MAXUSHORT );
  2242. if( oemSize <= sizeof( aSmallBuffer ) ) {
  2243. oemString.Buffer = aSmallBuffer;
  2244. } else {
  2245. oemString.Buffer = ALLOCATE_HEAP( oemSize, BlockTypeBuffer );
  2246. if( oemString.Buffer == NULL ) {
  2247. *Output8dot3 = '\0';
  2248. return;
  2249. }
  2250. }
  2251. oemString.MaximumLength = (USHORT)oemSize;
  2252. oemString.Length = (USHORT)oemSize - 1;
  2253. if ( Upcase ) {
  2254. status = RtlUpcaseUnicodeToOemN(
  2255. oemString.Buffer,
  2256. oemString.Length,
  2257. &index,
  2258. InputString->Buffer,
  2259. InputString->Length
  2260. );
  2261. ASSERT( NT_SUCCESS( status ) );
  2262. } else {
  2263. status = RtlUnicodeToOemN(
  2264. oemString.Buffer,
  2265. oemString.Length,
  2266. &index,
  2267. InputString->Buffer,
  2268. InputString->Length
  2269. );
  2270. ASSERT( NT_SUCCESS( status ) );
  2271. }
  2272. if( NT_SUCCESS( status ) ) {
  2273. oemString.Buffer[ index ] = '\0';
  2274. SrvOemStringTo8dot3(
  2275. &oemString,
  2276. Output8dot3
  2277. );
  2278. } else {
  2279. *Output8dot3 = '\0';
  2280. }
  2281. if( oemSize > sizeof( aSmallBuffer ) ) {
  2282. FREE_HEAP( oemString.Buffer );
  2283. }
  2284. } // SrvUnicodeStringTo8dot3
  2285. #if SRVDBG_STATS
  2286. VOID SRVFASTCALL
  2287. SrvUpdateStatistics2 (
  2288. PWORK_CONTEXT WorkContext,
  2289. UCHAR SmbCommand
  2290. )
  2291. /*++
  2292. Routine Description:
  2293. Update the server statistics database to reflect the work item
  2294. that is being completed.
  2295. Arguments:
  2296. WorkContext - pointer to the work item containing the statistics
  2297. for this request.
  2298. SmbCommand - The SMB command code of the current operation.
  2299. Return Value:
  2300. None.
  2301. --*/
  2302. {
  2303. if ( WorkContext->StartTime != 0 ) {
  2304. LARGE_INTEGER td;
  2305. td.QuadPart = WorkContext->CurrentWorkQueue->stats.SystemTime - WorkContext->StartTime;
  2306. //
  2307. // Update the SMB-specific statistics fields.
  2308. //
  2309. // !!! doesn't work for original transact smb--SmbCommand is too
  2310. // large.
  2311. //ASSERT( SmbCommand <= MAX_STATISTICS_SMB );
  2312. if ( SmbCommand <= MAX_STATISTICS_SMB ) {
  2313. SrvDbgStatistics.Smb[SmbCommand].SmbCount++;
  2314. SrvDbgStatistics.Smb[SmbCommand].TotalTurnaroundTime.QuadPart +=
  2315. td.QuadPart;
  2316. }
  2317. #if 0 // this code is no longer valid!
  2318. //
  2319. // Update the size-dependent IO fields if necessary. The arrays
  2320. // in SrvStatistics correspond to powers of two sizes for the IO.
  2321. // The correspondence between array location and IO size is:
  2322. //
  2323. // Location IO Size (min)
  2324. // 0 0
  2325. // 1 1
  2326. // 2 2
  2327. // 3 4
  2328. // 4 8
  2329. // 5 16
  2330. // 6 32
  2331. // 7 64
  2332. // 8 128
  2333. // 9 256
  2334. // 10 512
  2335. // 11 1024
  2336. // 12 2048
  2337. // 13 4096
  2338. // 14 8192
  2339. // 15 16384
  2340. // 16 32768
  2341. //
  2342. if ( WorkContext->BytesRead != 0 ) {
  2343. CLONG i;
  2344. for ( i = 0;
  2345. i < 17 && WorkContext->BytesRead != 0;
  2346. i++, WorkContext->BytesRead >>= 1 );
  2347. SrvDbgStatistics.ReadSize[i].SmbCount++;
  2348. SrvDbgStatistics.ReadSize[i].TotalTurnaroundTime.QuadPart +=
  2349. td.QuadPart;
  2350. }
  2351. if ( WorkContext->BytesWritten != 0 ) {
  2352. CLONG i;
  2353. for ( i = 0;
  2354. i < 17 && WorkContext->BytesWritten != 0;
  2355. i++, WorkContext->BytesWritten >>= 1 );
  2356. SrvDbgStatistics.WriteSize[i].SmbCount++;
  2357. SrvDbgStatistics.WriteSize[i].TotalTurnaroundTime.QuadPart +=
  2358. td.QuadPart;
  2359. }
  2360. #endif
  2361. }
  2362. return;
  2363. } // SrvUpdateStatistics2
  2364. #endif // SRVDBG_STATS
  2365. PRFCB
  2366. SrvVerifyFid2 (
  2367. IN PWORK_CONTEXT WorkContext,
  2368. IN USHORT Fid,
  2369. IN BOOLEAN FailOnSavedError,
  2370. IN PRESTART_ROUTINE SerializeWithRawRestartRoutine OPTIONAL,
  2371. OUT PNTSTATUS NtStatus
  2372. )
  2373. /*++
  2374. Routine Description:
  2375. Verifies the FID, TID, and UID in an incoming SMB. If they are
  2376. valid, the address of the RFCB corresponding to the FID is returned,
  2377. and the block is referenced.
  2378. Arguments:
  2379. WorkContext - Supplies a pointer to the work context block for the
  2380. current SMB. In particular, the Connection block pointer is
  2381. used to find the appropriate file table. If the FID is valid,
  2382. the RFCB address is stored in WorkContext->Rfcb.
  2383. Fid - Supplies the FID sent in the request SMB
  2384. FailOnSavedError - If TRUE, return NULL to the caller if there is
  2385. an outstanding write behind error. If FALSE, always attempt
  2386. to return a pointer to the RFCB.
  2387. SerializeWithRawRestartRoutine - If not NULL, is the address of an
  2388. FSP restart routine, and specifies that this operation should be
  2389. queued if a raw write is currently in progress on the file. If
  2390. this is the case, this routine queues the work context block to
  2391. a queue in the RFCB. When the raw write completes, all work
  2392. items on the queue are restarted.
  2393. NtStatus - This field is filled in only when this function returns
  2394. NULL. If there was a write behind error, NtStatus returns the
  2395. write behind error status, otherwise it returns
  2396. STATUS_INVALID_HANDLE.
  2397. Return Value:
  2398. PRFCB - Address of the RFCB, or SRV_INVALID_RFCB_POINTER if the Fid
  2399. was invalid, or if there is a raw write in progress and
  2400. serialization was requested (in which case *NtStatus is set
  2401. to STATUS_SUCCESS).
  2402. --*/
  2403. {
  2404. PCONNECTION connection;
  2405. PTABLE_HEADER tableHeader;
  2406. PRFCB rfcb;
  2407. USHORT index;
  2408. USHORT sequence;
  2409. KIRQL oldIrql;
  2410. #if 0
  2411. // THIS IS NOW DONE IN THE SrvVerifyFid MACRO.
  2412. //
  2413. // If the FID has already been verified, return the RFCB pointer.
  2414. //
  2415. // *** Note that we don't do the saved error checking or the raw
  2416. // write serialization in this case, on the assumption that
  2417. // since we already passed the checks once (in order to get the
  2418. // RFCB pointer in the first place), we don't need to do them
  2419. // again.
  2420. //
  2421. if ( WorkContext->Rfcb != NULL ) {
  2422. return WorkContext->Rfcb;
  2423. }
  2424. #endif
  2425. //
  2426. // Initialize local variables: obtain the connection block address
  2427. // and crack the FID into its components.
  2428. //
  2429. connection = WorkContext->Connection;
  2430. //
  2431. // Acquire the spin lock that guards the connection's file table.
  2432. //
  2433. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  2434. //
  2435. // See if this is the cached rfcb
  2436. //
  2437. if ( connection->CachedFid == (ULONG)Fid ) {
  2438. rfcb = connection->CachedRfcb;
  2439. } else {
  2440. //
  2441. // Verify that the FID is in range, is in use, and has the correct
  2442. // sequence number.
  2443. index = FID_INDEX( Fid );
  2444. sequence = FID_SEQUENCE( Fid );
  2445. tableHeader = &connection->FileTable;
  2446. if ( (index >= tableHeader->TableSize) ||
  2447. (tableHeader->Table[index].Owner == NULL) ||
  2448. (tableHeader->Table[index].SequenceNumber != sequence) ) {
  2449. *NtStatus = STATUS_INVALID_HANDLE;
  2450. goto error_exit;
  2451. }
  2452. rfcb = tableHeader->Table[index].Owner;
  2453. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  2454. *NtStatus = STATUS_INVALID_HANDLE;
  2455. goto error_exit;
  2456. }
  2457. //
  2458. // If the caller wants to fail when there is a write behind
  2459. // error and the error exists, fill in NtStatus and do not
  2460. // return the RFCB pointer.
  2461. //
  2462. if ( !NT_SUCCESS(rfcb->SavedError) && FailOnSavedError ) {
  2463. if ( !NT_SUCCESS(rfcb->SavedError) ) {
  2464. *NtStatus = rfcb->SavedError;
  2465. rfcb->SavedError = STATUS_SUCCESS;
  2466. goto error_exit;
  2467. }
  2468. }
  2469. //
  2470. // Cache the fid.
  2471. //
  2472. connection->CachedRfcb = rfcb;
  2473. connection->CachedFid = (ULONG)Fid;
  2474. }
  2475. //
  2476. // The FID is valid within the context of this connection. Verify
  2477. // that the owning tree connect's TID is correct.
  2478. //
  2479. // Do not verify the UID for clients that do not understand it.
  2480. //
  2481. if ( (rfcb->Tid !=
  2482. SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )) ||
  2483. ((rfcb->Uid !=
  2484. SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )) &&
  2485. DIALECT_HONORS_UID(connection->SmbDialect)) ) {
  2486. *NtStatus = STATUS_INVALID_HANDLE;
  2487. goto error_exit;
  2488. }
  2489. //
  2490. // If raw write serialization was requested, and a raw write
  2491. // is active, queue this work item in the RFCB pending
  2492. // completion of the raw write.
  2493. //
  2494. if ( (rfcb->RawWriteCount != 0) &&
  2495. ARGUMENT_PRESENT(SerializeWithRawRestartRoutine) ) {
  2496. InsertTailList(
  2497. &rfcb->RawWriteSerializationList,
  2498. &WorkContext->ListEntry
  2499. );
  2500. WorkContext->FspRestartRoutine = SerializeWithRawRestartRoutine;
  2501. *NtStatus = STATUS_SUCCESS;
  2502. goto error_exit;
  2503. }
  2504. //
  2505. // The file is active and the TID is valid. Reference the
  2506. // RFCB. Release the spin lock (we don't need it anymore).
  2507. //
  2508. rfcb->BlockHeader.ReferenceCount++;
  2509. UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
  2510. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2511. //
  2512. // Save the RFCB address in the work context block and
  2513. // return the file address.
  2514. //
  2515. WorkContext->Rfcb = rfcb;
  2516. //
  2517. // Mark the rfcb as active
  2518. //
  2519. rfcb->IsActive = TRUE;
  2520. ASSERT( GET_BLOCK_TYPE( rfcb->Mfcb ) == BlockTypeMfcb );
  2521. return rfcb;
  2522. error_exit:
  2523. //
  2524. // Either the FID is invalid for this connection, the file is
  2525. // closing, or the TID doesn't match. Release the lock, clear the
  2526. // file address in the work context block, and return a file address
  2527. // of NULL.
  2528. //
  2529. WorkContext->Rfcb = NULL; // connection spinlock must be held
  2530. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2531. return SRV_INVALID_RFCB_POINTER;
  2532. } // SrvVerifyFid2
  2533. PRFCB
  2534. SrvVerifyFidForRawWrite (
  2535. IN PWORK_CONTEXT WorkContext,
  2536. IN USHORT Fid,
  2537. OUT PNTSTATUS NtStatus
  2538. )
  2539. /*++
  2540. Routine Description:
  2541. Verifies the FID, TID, and UID in an incoming SMB. If they are
  2542. valid, the address of the RFCB corresponding to the FID is returned,
  2543. and the block is referenced. In addition, the RawWriteCount in the
  2544. RFCB is incremented.
  2545. Arguments:
  2546. WorkContext - Supplies a pointer to the work context block for the
  2547. current SMB. In particular, the Connection block pointer is
  2548. used to find the appropriate file table. If the FID is valid,
  2549. the RFCB address is stored in WorkContext->Rfcb.
  2550. Fid - Supplies the FID sent in the request SMB
  2551. NtStatus - This field is filled in only when this function returns
  2552. NULL. If there was a write behind error, NtStatus returns the
  2553. write behind error status, otherwise it returns
  2554. STATUS_INVALID_HANDLE.
  2555. Return Value:
  2556. PRFCB - Address of the RFCB, or SRV_INVALID_RFCB_POINTER if the Fid
  2557. was invalid, or if there is a raw write in progress and
  2558. serialization was requested (in which case *NtStatus is set
  2559. to STATUS_SUCCESS).
  2560. --*/
  2561. {
  2562. PCONNECTION connection;
  2563. PTABLE_HEADER tableHeader;
  2564. PRFCB rfcb;
  2565. USHORT index;
  2566. USHORT sequence;
  2567. KIRQL oldIrql;
  2568. ASSERT( WorkContext->Rfcb == NULL );
  2569. //
  2570. // Initialize local variables: obtain the connection block address
  2571. // and crack the FID into its components.
  2572. //
  2573. connection = WorkContext->Connection;
  2574. //
  2575. // Acquire the spin lock that guards the connection's file table.
  2576. //
  2577. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  2578. //
  2579. // See if this is the cached rfcb
  2580. //
  2581. if ( connection->CachedFid == Fid ) {
  2582. rfcb = connection->CachedRfcb;
  2583. } else {
  2584. //
  2585. // Verify that the FID is in range, is in use, and has the correct
  2586. // sequence number.
  2587. index = FID_INDEX( Fid );
  2588. sequence = FID_SEQUENCE( Fid );
  2589. tableHeader = &connection->FileTable;
  2590. if ( (index >= tableHeader->TableSize) ||
  2591. (tableHeader->Table[index].Owner == NULL) ||
  2592. (tableHeader->Table[index].SequenceNumber != sequence) ) {
  2593. *NtStatus = STATUS_INVALID_HANDLE;
  2594. goto error_exit;
  2595. }
  2596. rfcb = tableHeader->Table[index].Owner;
  2597. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  2598. *NtStatus = STATUS_INVALID_HANDLE;
  2599. goto error_exit;
  2600. }
  2601. //
  2602. // If there is a write behind error, fill in NtStatus and do
  2603. // not return the RFCB pointer.
  2604. //
  2605. if ( !NT_SUCCESS( rfcb->SavedError ) ) {
  2606. if ( !NT_SUCCESS( rfcb->SavedError ) ) {
  2607. *NtStatus = rfcb->SavedError;
  2608. rfcb->SavedError = STATUS_SUCCESS;
  2609. goto error_exit;
  2610. }
  2611. }
  2612. connection->CachedRfcb = rfcb;
  2613. connection->CachedFid = (ULONG)Fid;
  2614. //
  2615. // The FID is valid within the context of this connection. Verify
  2616. // that the owning tree connect's TID is correct.
  2617. //
  2618. // Do not verify the UID for clients that do not understand it.
  2619. //
  2620. if ( (rfcb->Tid !=
  2621. SmbGetAlignedUshort(&WorkContext->RequestHeader->Tid)) ||
  2622. ( (rfcb->Uid !=
  2623. SmbGetAlignedUshort(&WorkContext->RequestHeader->Uid)) &&
  2624. DIALECT_HONORS_UID(connection->SmbDialect) ) ) {
  2625. *NtStatus = STATUS_INVALID_HANDLE;
  2626. goto error_exit;
  2627. }
  2628. }
  2629. //
  2630. // If a raw write is already active, queue this work item in
  2631. // the RFCB pending completion of the raw write.
  2632. //
  2633. if ( rfcb->RawWriteCount != 0 ) {
  2634. InsertTailList(
  2635. &rfcb->RawWriteSerializationList,
  2636. &WorkContext->ListEntry
  2637. );
  2638. WorkContext->FspRestartRoutine = SrvRestartSmbReceived;
  2639. *NtStatus = STATUS_SUCCESS;
  2640. goto error_exit;
  2641. }
  2642. //
  2643. // The file is active and the TID is valid. Reference the
  2644. // RFCB and increment the raw write count. Release the spin
  2645. // lock (we don't need it anymore).
  2646. //
  2647. rfcb->BlockHeader.ReferenceCount++;
  2648. UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
  2649. rfcb->RawWriteCount++;
  2650. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2651. //
  2652. // Save the RFCB address in the work context block and
  2653. // return the file address.
  2654. //
  2655. WorkContext->Rfcb = rfcb;
  2656. ASSERT( GET_BLOCK_TYPE( rfcb->Mfcb ) == BlockTypeMfcb );
  2657. //
  2658. // Mark the rfcb as active
  2659. //
  2660. rfcb->IsActive = TRUE;
  2661. return rfcb;
  2662. error_exit:
  2663. //
  2664. // Either the FID is invalid for this connection, the file is
  2665. // closing, or the TID and UID don't match. Clear the file address
  2666. // in the work context block, and return a file address of NULL.
  2667. //
  2668. WorkContext->Rfcb = NULL; // connection spinlock must be held
  2669. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2670. return SRV_INVALID_RFCB_POINTER;
  2671. } // SrvVerifyFidForRawWrite
  2672. PSEARCH
  2673. SrvVerifySid (
  2674. IN PWORK_CONTEXT WorkContext,
  2675. IN USHORT Index,
  2676. IN USHORT Sequence,
  2677. IN PSRV_DIRECTORY_INFORMATION DirectoryInformation,
  2678. IN CLONG BufferSize
  2679. )
  2680. /*++
  2681. Routine Description:
  2682. Verifies the SID in the resume key of a Search or Find SMB. If the
  2683. SID is valid, the address of the search block corresponding to the
  2684. SID is returned. The appropiate fields in the DirectoryInformation
  2685. structure are filled in so that SrvQueryDirectoryFile may be called.
  2686. Arguments:
  2687. WorkContext - Supplies a pointer to the work context block for the
  2688. current SMB. In particular, the Connection block pointer is
  2689. used to find the appropriate search table.
  2690. ResumeKey - a pointer the the resume key to evaluate.
  2691. Return Value:
  2692. PSEARCH - address of the Search block, or NULL.
  2693. --*/
  2694. {
  2695. PCONNECTION connection;
  2696. PTABLE_HEADER tableHeader;
  2697. PSEARCH search;
  2698. PAGED_CODE( );
  2699. connection = WorkContext->Connection;
  2700. //
  2701. // Acquire the connection's lock.
  2702. //
  2703. ACQUIRE_LOCK( &connection->Lock );
  2704. //
  2705. // Verify that the index is in range, that the search block is in use,
  2706. // and that the resume key has the correct sequence number.
  2707. //
  2708. tableHeader = &connection->PagedConnection->SearchTable;
  2709. if ( (Index < tableHeader->TableSize) &&
  2710. (tableHeader->Table[Index].Owner != NULL) &&
  2711. (tableHeader->Table[Index].SequenceNumber == Sequence) ) {
  2712. search = tableHeader->Table[Index].Owner;
  2713. //
  2714. // The SID is valid. Verify that the search block is still
  2715. // active.
  2716. //
  2717. // !!! Does this really apply for search blocks?
  2718. //
  2719. if ( GET_BLOCK_STATE(search) != BlockStateActive || search->InUse ) {
  2720. //
  2721. // The search block is no longer active or somebody is
  2722. // already using the search block.
  2723. //
  2724. search = NULL;
  2725. } else {
  2726. //
  2727. // We found a legitimate search block, so reference it.
  2728. //
  2729. SrvReferenceSearch( search );
  2730. //
  2731. // Fill in fields of DirectoryInformation.
  2732. //
  2733. DirectoryInformation->DirectoryHandle = search->DirectoryHandle;
  2734. DirectoryInformation->CurrentEntry = NULL;
  2735. DirectoryInformation->BufferLength = BufferSize -
  2736. sizeof(SRV_DIRECTORY_INFORMATION);
  2737. DirectoryInformation->Wildcards = search->Wildcards;
  2738. DirectoryInformation->ErrorOnFileOpen = FALSE;
  2739. DirectoryInformation->DownlevelTimewarp = search->DownlevelTimewarp;
  2740. //
  2741. // Indicate that the search is being used.
  2742. //
  2743. search->InUse = TRUE;
  2744. }
  2745. } else {
  2746. //
  2747. // The SID is invalid.
  2748. //
  2749. search = NULL;
  2750. }
  2751. //
  2752. // Release the lock and return the search block address (or NULL).
  2753. //
  2754. RELEASE_LOCK( &connection->Lock );
  2755. return search;
  2756. } // SrvVerifySid
  2757. PTREE_CONNECT
  2758. SrvVerifyTid (
  2759. IN PWORK_CONTEXT WorkContext,
  2760. IN USHORT Tid
  2761. )
  2762. /*++
  2763. Routine Description:
  2764. Verifies the TID in an incoming SMB. If the TID is valid, the
  2765. address of the tree connect block corresponding to the TID is
  2766. returned, and the block is referenced.
  2767. Arguments:
  2768. WorkContext - Supplies a pointer to the work context block for the
  2769. current SMB. In particular, the Connection block pointer is
  2770. used to find the appropriate tree table. Also, the tree connect
  2771. block address, if the TID is valid, is stored in
  2772. WorkContext->TreeConnect.
  2773. Tid - Supplies the TID sent in the request SMB
  2774. Return Value:
  2775. PTREE_CONNECT - Address of the tree connect block, or NULL
  2776. --*/
  2777. {
  2778. PCONNECTION connection;
  2779. PTREE_CONNECT treeConnect;
  2780. PTABLE_HEADER tableHeader;
  2781. USHORT index;
  2782. USHORT sequence;
  2783. PAGED_CODE( );
  2784. //
  2785. // If the TID has already been verified, return the tree connect
  2786. // pointer.
  2787. //
  2788. if ( WorkContext->TreeConnect != NULL ) {
  2789. return WorkContext->TreeConnect;
  2790. }
  2791. //
  2792. // Initialize local variables: obtain the connection block address
  2793. // and crack the TID into its components.
  2794. //
  2795. connection = WorkContext->Connection;
  2796. index = TID_INDEX( Tid );
  2797. sequence = TID_SEQUENCE( Tid );
  2798. //
  2799. // Acquire the connection's tree connect lock.
  2800. //
  2801. ACQUIRE_LOCK( &connection->Lock );
  2802. //
  2803. // Verify that the TID is in range, is in use, and has the correct
  2804. // sequence number.
  2805. tableHeader = &connection->PagedConnection->TreeConnectTable;
  2806. if ( (index < tableHeader->TableSize) &&
  2807. (tableHeader->Table[index].Owner != NULL) &&
  2808. (tableHeader->Table[index].SequenceNumber == sequence) ) {
  2809. treeConnect = tableHeader->Table[index].Owner;
  2810. //
  2811. // The TID is valid within the context of this connection.
  2812. // Verify that the tree connect is still active.
  2813. //
  2814. if ( GET_BLOCK_STATE(treeConnect) == BlockStateActive ) {
  2815. //
  2816. // The tree connect is active. Reference it.
  2817. //
  2818. SrvReferenceTreeConnect( treeConnect );
  2819. } else {
  2820. //
  2821. // The tree connect is closing.
  2822. //
  2823. treeConnect = NULL;
  2824. }
  2825. } else {
  2826. //
  2827. // The TID is invalid for this connection.
  2828. //
  2829. treeConnect = NULL;
  2830. }
  2831. //
  2832. // Release the lock, save the tree connect address in the work context
  2833. // block, and return the tree connect address (or NULL).
  2834. //
  2835. RELEASE_LOCK( &connection->Lock );
  2836. WorkContext->TreeConnect = treeConnect;
  2837. return treeConnect;
  2838. } // SrvVerifyTid
  2839. PSESSION
  2840. SrvVerifyUid (
  2841. IN PWORK_CONTEXT WorkContext,
  2842. IN USHORT Uid
  2843. )
  2844. /*++
  2845. Routine Description:
  2846. Verifies the UID in an incoming SMB. If the UID is valid, the
  2847. address of the session block corresponding to the UID is returned,
  2848. and the block is referenced.
  2849. Arguments:
  2850. WorkContext - Supplies a pointer to the work context block for the
  2851. current SMB. In particular, the Connection block pointer is
  2852. used to find the appropriate user table. Also, the session block
  2853. address, if the UID is valid, is stored in WorkContext->Session.
  2854. Uid - Supplies the UID sent in the request SMB
  2855. Return Value:
  2856. PSESSION - Address of the session block, or NULL
  2857. --*/
  2858. {
  2859. PCONNECTION connection;
  2860. PTABLE_HEADER tableHeader;
  2861. PSESSION session;
  2862. PSECURITY_CONTEXT securityContext;
  2863. USHORT index;
  2864. USHORT sequence;
  2865. PAGED_CODE( );
  2866. //
  2867. // If the UID has already been verified, return the session pointer.
  2868. //
  2869. if ( WorkContext->Session != NULL ) {
  2870. return WorkContext->Session;
  2871. }
  2872. //
  2873. // Initialize local variables: obtain the connection block address
  2874. // and crack the UID into its components.
  2875. //
  2876. connection = WorkContext->Connection;
  2877. index = UID_INDEX( Uid );
  2878. sequence = UID_SEQUENCE( Uid );
  2879. //
  2880. // Acquire the connection's session lock.
  2881. //
  2882. ACQUIRE_LOCK( &connection->Lock );
  2883. //
  2884. // If this is a down-level (LAN Man 1.0 or earlier) client, than
  2885. // we will not receive a UID, and there will only be one session
  2886. // per connection. Reference that session.
  2887. //
  2888. tableHeader = &connection->PagedConnection->SessionTable;
  2889. if (!DIALECT_HONORS_UID(connection->SmbDialect) ) {
  2890. session = tableHeader->Table[0].Owner;
  2891. } else if ( (index < tableHeader->TableSize) &&
  2892. (tableHeader->Table[index].Owner != NULL) &&
  2893. (tableHeader->Table[index].SequenceNumber == sequence) ) {
  2894. //
  2895. // The UID is in range, is in use, and has the correct sequence
  2896. // number.
  2897. //
  2898. session = tableHeader->Table[index].Owner;
  2899. } else {
  2900. //
  2901. // The UID is invalid for this connection.
  2902. //
  2903. IF_DEBUG( ERRORS ) {
  2904. KdPrint(( "SrvVerifyUid: index %d, size %d\n", index, tableHeader->TableSize ));
  2905. if( index < tableHeader->TableSize ) {
  2906. KdPrint((" Owner %p, Table.SequenceNumber %d, seq %d\n",
  2907. tableHeader->Table[index].Owner,
  2908. tableHeader->Table[index].SequenceNumber,
  2909. sequence
  2910. ));
  2911. }
  2912. }
  2913. session = NULL;
  2914. }
  2915. if ( session != NULL ) {
  2916. //
  2917. // The UID is valid within the context of this connection.
  2918. // Verify that the session is still active.
  2919. //
  2920. if ( GET_BLOCK_STATE(session) == BlockStateActive ) {
  2921. LARGE_INTEGER liNow;
  2922. KeQuerySystemTime( &liNow);
  2923. if( session->LogonSequenceInProgress == FALSE &&
  2924. liNow.QuadPart >= session->LogOffTime.QuadPart )
  2925. {
  2926. IF_DEBUG( ERRORS ) {
  2927. KdPrint(( "SrvVerifyUid: LogOffTime has passed %x %x\n",
  2928. session->LogOffTime.HighPart,
  2929. session->LogOffTime.LowPart
  2930. ));
  2931. }
  2932. // Mark the session as expired
  2933. session->IsSessionExpired = TRUE;
  2934. KdPrint(( "Marking session as expired.\n" ));
  2935. }
  2936. //
  2937. // The session is active. Reference it.
  2938. //
  2939. SrvReferenceSession( session );
  2940. securityContext = session->SecurityContext;
  2941. if( securityContext != NULL ) SrvReferenceSecurityContext( securityContext );
  2942. //
  2943. // Update the last use time for autologoff.
  2944. //
  2945. session->LastUseTime = liNow;
  2946. } else {
  2947. //
  2948. // The session is closing.
  2949. //
  2950. IF_DEBUG( ERRORS ) {
  2951. KdPrint(( "SrvVerifyUid: Session state %x\n", GET_BLOCK_STATE( session ) ));
  2952. }
  2953. session = NULL;
  2954. securityContext = NULL;
  2955. }
  2956. }
  2957. else
  2958. {
  2959. securityContext = NULL;
  2960. }
  2961. //
  2962. // Release the lock, save the session address in the work context
  2963. // block, and return the session address (or NULL).
  2964. //
  2965. RELEASE_LOCK( &connection->Lock );
  2966. WorkContext->Session = session;
  2967. WorkContext->SecurityContext = securityContext;
  2968. return session;
  2969. } // SrvVerifyUid
  2970. NTSTATUS
  2971. SrvVerifyUidAndTid (
  2972. IN PWORK_CONTEXT WorkContext,
  2973. OUT PSESSION *Session,
  2974. OUT PTREE_CONNECT *TreeConnect,
  2975. IN SHARE_TYPE ShareType
  2976. )
  2977. /*++
  2978. Routine Description:
  2979. Verifies the UID and TID in an incoming SMB. If both the UID and
  2980. the TDI are valid, the addresses of the session/tree connect blocks
  2981. corresponding to the UID/TID are returned, and the blocks are
  2982. referenced.
  2983. Arguments:
  2984. WorkContext - Supplies a pointer to the work context block for the
  2985. current SMB. In particular, the Connection block pointer is
  2986. used to find the appropriate user table. If the UID and TID are
  2987. valid, the session/tree connect block addresses are stored in
  2988. WorkContext->Session and WorkContext->TreeConnect.
  2989. Uid - Supplies the UID sent in the request SMB
  2990. Tid - Supplies the TID sent in the request SMB
  2991. Session - Returns a pointer to the session block
  2992. TreeConnect - Returns a pointer to the tree connect block
  2993. ShareType - the type of share it should be
  2994. Return Value:
  2995. NTSTATUS - STATUS_SUCCESS, STATUS_SMB_BAD_UID, or STATUS_SMB_BAD_TID
  2996. --*/
  2997. {
  2998. PCONNECTION connection;
  2999. PSESSION session;
  3000. PTREE_CONNECT treeConnect;
  3001. PPAGED_CONNECTION pagedConnection;
  3002. PTABLE_HEADER tableHeader;
  3003. USHORT index;
  3004. USHORT Uid;
  3005. USHORT Tid;
  3006. USHORT sequence;
  3007. LARGE_INTEGER liNow;
  3008. PAGED_CODE( );
  3009. KeQuerySystemTime(&liNow);
  3010. //
  3011. // If the UID and TID have already been verified, don't do all this
  3012. // work again.
  3013. //
  3014. if ( (WorkContext->Session != NULL) &&
  3015. (WorkContext->TreeConnect != NULL) ) {
  3016. if( ShareType != ShareTypeWild &&
  3017. WorkContext->TreeConnect->Share->ShareType != ShareType ) {
  3018. return STATUS_ACCESS_DENIED;
  3019. }
  3020. *Session = WorkContext->Session;
  3021. *TreeConnect = WorkContext->TreeConnect;
  3022. return STATUS_SUCCESS;
  3023. }
  3024. //
  3025. // Obtain the connection block address and lock the connection.
  3026. //
  3027. connection = WorkContext->Connection;
  3028. pagedConnection = connection->PagedConnection;
  3029. ACQUIRE_LOCK( &connection->Lock );
  3030. //
  3031. // If we haven't negotiated successfully with this client, then we have
  3032. // a failure
  3033. //
  3034. if( connection->SmbDialect == SmbDialectIllegal) {
  3035. RELEASE_LOCK( &connection->Lock );
  3036. return STATUS_INVALID_SMB;
  3037. }
  3038. //
  3039. // If the UID has already been verified, don't verify it again.
  3040. //
  3041. if ( WorkContext->Session != NULL ) {
  3042. session = WorkContext->Session;
  3043. } else {
  3044. //
  3045. // Crack the UID into its components.
  3046. //
  3047. Uid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ),
  3048. index = UID_INDEX( Uid );
  3049. sequence = UID_SEQUENCE( Uid );
  3050. //
  3051. // If this is a down-level (LAN Man 1.0 or earlier) client, than
  3052. // we will not receive a UID, and there will only be one session
  3053. // per connection. Reference that session.
  3054. //
  3055. // For clients that do send UIDs, verify that the UID is in
  3056. // range, is in use, and has the correct sequence number, and
  3057. // that the session is not closing.
  3058. //
  3059. tableHeader = &pagedConnection->SessionTable;
  3060. if (!DIALECT_HONORS_UID(connection->SmbDialect))
  3061. {
  3062. session = tableHeader->Table[0].Owner;
  3063. }
  3064. else if( (index >= tableHeader->TableSize) ||
  3065. ((session = tableHeader->Table[index].Owner) == NULL) ||
  3066. (tableHeader->Table[index].SequenceNumber != sequence) ||
  3067. (GET_BLOCK_STATE(session) != BlockStateActive) )
  3068. {
  3069. //
  3070. // The UID is invalid for this connection, or the session is
  3071. // closing.
  3072. //
  3073. RELEASE_LOCK( &connection->Lock );
  3074. return STATUS_SMB_BAD_UID;
  3075. }
  3076. //
  3077. // it's valid
  3078. //
  3079. if( session == NULL )
  3080. {
  3081. RELEASE_LOCK( &connection->Lock );
  3082. return STATUS_SMB_BAD_UID;
  3083. }
  3084. if( session->LogonSequenceInProgress == FALSE &&
  3085. liNow.QuadPart >= session->LogOffTime.QuadPart )
  3086. {
  3087. // Mark the session as expired
  3088. session->IsSessionExpired = TRUE;
  3089. }
  3090. }
  3091. //
  3092. // The UID is valid. Check the TID. If the TID has already been
  3093. // verified, don't verify it again.
  3094. //
  3095. if ( WorkContext->TreeConnect != NULL ) {
  3096. treeConnect = WorkContext->TreeConnect;
  3097. } else {
  3098. //
  3099. // Crack the TID into its components.
  3100. //
  3101. Tid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ),
  3102. index = TID_INDEX( Tid );
  3103. sequence = TID_SEQUENCE( Tid );
  3104. //
  3105. // Verify that the TID is in range, is in use, and has the
  3106. // correct sequence number, and that the tree connect is not
  3107. // closing.
  3108. //
  3109. tableHeader = &pagedConnection->TreeConnectTable;
  3110. if ( (index >= tableHeader->TableSize) ||
  3111. ((treeConnect = tableHeader->Table[index].Owner) == NULL) ||
  3112. (tableHeader->Table[index].SequenceNumber != sequence) ||
  3113. (GET_BLOCK_STATE(treeConnect) != BlockStateActive) ) {
  3114. //
  3115. // The TID is invalid for this connection, or the tree
  3116. // connect is closing.
  3117. //
  3118. RELEASE_LOCK( &connection->Lock );
  3119. return STATUS_SMB_BAD_TID;
  3120. }
  3121. //
  3122. // Make sure this is not a Null session trying to sneak in
  3123. // through an established tree connect.
  3124. //
  3125. if ( session->IsNullSession &&
  3126. SrvRestrictNullSessionAccess &&
  3127. ( treeConnect->Share->ShareType != ShareTypePipe ) ) {
  3128. BOOLEAN matchFound = FALSE;
  3129. ULONG i;
  3130. ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
  3131. for ( i = 0; SrvNullSessionShares[i] != NULL ; i++ ) {
  3132. if ( _wcsicmp(
  3133. SrvNullSessionShares[i],
  3134. treeConnect->Share->ShareName.Buffer
  3135. ) == 0 ) {
  3136. matchFound = TRUE;
  3137. break;
  3138. }
  3139. }
  3140. RELEASE_LOCK( &SrvConfigurationLock );
  3141. //
  3142. // The null session is not allowed to access this share - reject.
  3143. //
  3144. if ( !matchFound ) {
  3145. RELEASE_LOCK( &connection->Lock );
  3146. return(STATUS_ACCESS_DENIED);
  3147. }
  3148. }
  3149. }
  3150. //
  3151. // Both the UID and the TID are valid. Reference the session and
  3152. // tree connect blocks.
  3153. //
  3154. if ( WorkContext->Session == NULL ) {
  3155. SrvReferenceSession( session );
  3156. WorkContext->Session = session;
  3157. if( session->SecurityContext != NULL )
  3158. {
  3159. SrvReferenceSecurityContext( session->SecurityContext );
  3160. WorkContext->SecurityContext = session->SecurityContext;
  3161. }
  3162. //
  3163. // Update the last use time for autologoff.
  3164. //
  3165. session->LastUseTime = liNow;
  3166. }
  3167. if ( WorkContext->TreeConnect == NULL ) {
  3168. SrvReferenceTreeConnect( treeConnect );
  3169. WorkContext->TreeConnect = treeConnect;
  3170. }
  3171. //
  3172. // Release the connection lock and return success.
  3173. //
  3174. RELEASE_LOCK( &connection->Lock );
  3175. *Session = session;
  3176. *TreeConnect = treeConnect;
  3177. //
  3178. // Make sure this is the correct type of share
  3179. //
  3180. if( ShareType != ShareTypeWild && (*TreeConnect)->Share->ShareType != ShareType ) {
  3181. return STATUS_ACCESS_DENIED;
  3182. }
  3183. return STATUS_SUCCESS;
  3184. } // SrvVerifyUidAndTid
  3185. BOOLEAN
  3186. SrvReceiveBufferShortage (
  3187. VOID
  3188. )
  3189. /*++
  3190. Routine Description:
  3191. This function calculates if the server is running low on receive
  3192. work items that are not involved in blocking operations.
  3193. Arguments:
  3194. None.
  3195. Return Value:
  3196. TRUE - The server is running short on receive work items.
  3197. FALSE - The server is *not* running short on receive work items.
  3198. --*/
  3199. {
  3200. KIRQL oldIrql;
  3201. BOOLEAN bufferShortage;
  3202. PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
  3203. //
  3204. // Even if we have reached our limit, we will allow this blocking
  3205. // operation if we have enough free work items to allocate. This
  3206. // will allow the resource thread to allocate more work items to
  3207. // service blocking requests.
  3208. //
  3209. if ( (queue->FreeWorkItems < queue->MaximumWorkItems) ||
  3210. ((queue->FreeWorkItems - SrvBlockingOpsInProgress)
  3211. > SrvMinFreeWorkItemsBlockingIo) ) {
  3212. //
  3213. // The caller will start a blocking operation. Increment the
  3214. // blocking operation count.
  3215. //
  3216. InterlockedIncrement( &SrvBlockingOpsInProgress );
  3217. bufferShortage = FALSE;
  3218. } else {
  3219. //
  3220. // The server is running short on uncommitted receive work items.
  3221. //
  3222. bufferShortage = TRUE;
  3223. }
  3224. return bufferShortage;
  3225. } // SrvReceiveBufferShortage
  3226. #if SMBDBG
  3227. //
  3228. // The following functions are defined in smbgtpt.h. When debug mode is
  3229. // disabled (!SMBDBG), these functions are instead defined as macros.
  3230. //
  3231. USHORT
  3232. SmbGetUshort (
  3233. IN PSMB_USHORT SrcAddress
  3234. )
  3235. {
  3236. return (USHORT)(
  3237. ( ( (PUCHAR)(SrcAddress) )[0] ) |
  3238. ( ( (PUCHAR)(SrcAddress) )[1] << 8 )
  3239. );
  3240. }
  3241. USHORT
  3242. SmbGetAlignedUshort (
  3243. IN PUSHORT SrcAddress
  3244. )
  3245. {
  3246. return *(SrcAddress);
  3247. }
  3248. VOID
  3249. SmbPutUshort (
  3250. OUT PSMB_USHORT DestAddress,
  3251. IN USHORT Value
  3252. )
  3253. {
  3254. ( (PUCHAR)(DestAddress) )[0] = BYTE_0(Value);
  3255. ( (PUCHAR)(DestAddress) )[1] = BYTE_1(Value);
  3256. return;
  3257. }
  3258. VOID
  3259. SmbPutAlignedUshort (
  3260. OUT PUSHORT DestAddress,
  3261. IN USHORT Value
  3262. )
  3263. {
  3264. *(DestAddress) = (Value);
  3265. return;
  3266. }
  3267. ULONG
  3268. SmbGetUlong (
  3269. IN PSMB_ULONG SrcAddress
  3270. )
  3271. {
  3272. return (ULONG)(
  3273. ( ( (PUCHAR)(SrcAddress) )[0] ) |
  3274. ( ( (PUCHAR)(SrcAddress) )[1] << 8 ) |
  3275. ( ( (PUCHAR)(SrcAddress) )[2] << 16 ) |
  3276. ( ( (PUCHAR)(SrcAddress) )[3] << 24 )
  3277. );
  3278. }
  3279. ULONG
  3280. SmbGetAlignedUlong (
  3281. IN PULONG SrcAddress
  3282. )
  3283. {
  3284. return *(SrcAddress);
  3285. }
  3286. VOID
  3287. SmbPutUlong (
  3288. OUT PSMB_ULONG DestAddress,
  3289. IN ULONG Value
  3290. )
  3291. {
  3292. ( (PUCHAR)(DestAddress) )[0] = BYTE_0(Value);
  3293. ( (PUCHAR)(DestAddress) )[1] = BYTE_1(Value);
  3294. ( (PUCHAR)(DestAddress) )[2] = BYTE_2(Value);
  3295. ( (PUCHAR)(DestAddress) )[3] = BYTE_3(Value);
  3296. return;
  3297. }
  3298. VOID
  3299. SmbPutAlignedUlong (
  3300. OUT PULONG DestAddress,
  3301. IN ULONG Value
  3302. )
  3303. {
  3304. *(DestAddress) = Value;
  3305. return;
  3306. }
  3307. VOID
  3308. SmbPutDate (
  3309. OUT PSMB_DATE DestAddress,
  3310. IN SMB_DATE Value
  3311. )
  3312. {
  3313. ( (PUCHAR)&(DestAddress)->Ushort )[0] = BYTE_0(Value.Ushort);
  3314. ( (PUCHAR)&(DestAddress)->Ushort )[1] = BYTE_1(Value.Ushort);
  3315. return;
  3316. }
  3317. VOID
  3318. SmbMoveDate (
  3319. OUT PSMB_DATE DestAddress,
  3320. IN PSMB_DATE SrcAddress
  3321. )
  3322. {
  3323. (DestAddress)->Ushort = (USHORT)(
  3324. ( ( (PUCHAR)&(SrcAddress)->Ushort )[0] ) |
  3325. ( ( (PUCHAR)&(SrcAddress)->Ushort )[1] << 8 ) );
  3326. return;
  3327. }
  3328. VOID
  3329. SmbZeroDate (
  3330. IN PSMB_DATE Date
  3331. )
  3332. {
  3333. (Date)->Ushort = 0;
  3334. }
  3335. BOOLEAN
  3336. SmbIsDateZero (
  3337. IN PSMB_DATE Date
  3338. )
  3339. {
  3340. return (BOOLEAN)( (Date)->Ushort == 0 );
  3341. }
  3342. VOID
  3343. SmbPutTime (
  3344. OUT PSMB_TIME DestAddress,
  3345. IN SMB_TIME Value
  3346. )
  3347. {
  3348. ( (PUCHAR)&(DestAddress)->Ushort )[0] = BYTE_0(Value.Ushort);
  3349. ( (PUCHAR)&(DestAddress)->Ushort )[1] = BYTE_1(Value.Ushort);
  3350. return;
  3351. }
  3352. VOID
  3353. SmbMoveTime (
  3354. OUT PSMB_TIME DestAddress,
  3355. IN PSMB_TIME SrcAddress
  3356. )
  3357. {
  3358. (DestAddress)->Ushort = (USHORT)(
  3359. ( ( (PUCHAR)&(SrcAddress)->Ushort )[0] ) |
  3360. ( ( (PUCHAR)&(SrcAddress)->Ushort )[1] << 8 ) );
  3361. return;
  3362. }
  3363. VOID
  3364. SmbZeroTime (
  3365. IN PSMB_TIME Time
  3366. )
  3367. {
  3368. (Time)->Ushort = 0;
  3369. }
  3370. BOOLEAN
  3371. SmbIsTimeZero (
  3372. IN PSMB_TIME Time
  3373. )
  3374. {
  3375. return (BOOLEAN)( (Time)->Ushort == 0 );
  3376. }
  3377. #endif // SMBDBG
  3378. NTSTATUS
  3379. SrvIoCreateFile (
  3380. IN PWORK_CONTEXT WorkContext,
  3381. OUT PHANDLE FileHandle,
  3382. IN ACCESS_MASK DesiredAccess,
  3383. IN POBJECT_ATTRIBUTES ObjectAttributes,
  3384. OUT PIO_STATUS_BLOCK IoStatusBlock,
  3385. IN PLARGE_INTEGER AllocationSize OPTIONAL,
  3386. IN ULONG FileAttributes,
  3387. IN ULONG ShareAccess,
  3388. IN ULONG Disposition,
  3389. IN ULONG CreateOptions,
  3390. IN PVOID EaBuffer OPTIONAL,
  3391. IN ULONG EaLength,
  3392. IN CREATE_FILE_TYPE CreateFileType,
  3393. IN PVOID ExtraCreateParameters OPTIONAL,
  3394. IN ULONG Options,
  3395. IN PSHARE Share OPTIONAL
  3396. )
  3397. {
  3398. PFILE_OBJECT fileObject;
  3399. PDEVICE_OBJECT deviceObject;
  3400. NTSTATUS status;
  3401. NTSTATUS tempStatus;
  3402. BOOLEAN dispositionModified = FALSE;
  3403. ULONG eventToLog = 0;
  3404. ULONG newUsage;
  3405. ULONG requiredSize;
  3406. SHARE_TYPE shareType = ShareTypeWild;
  3407. UNICODE_STRING fileName, *pName;
  3408. #if SRVDBG_STATS
  3409. LARGE_INTEGER timeStamp, currentTime;
  3410. LARGE_INTEGER timeDifference;
  3411. #endif
  3412. PAGED_CODE( );
  3413. IF_DEBUG( CREATE ) {
  3414. KdPrint(("\nSrvIoCreateFile:\n" ));
  3415. KdPrint((" Obja->ObjectName <%wZ>\n", ObjectAttributes->ObjectName ));
  3416. KdPrint((" Obja->Attributes %X,", ObjectAttributes->Attributes ));
  3417. KdPrint((" RootDirectory %p,", ObjectAttributes->RootDirectory ));
  3418. KdPrint((" SecurityDescriptor %p,", ObjectAttributes->SecurityDescriptor ));
  3419. KdPrint((" SecurityQOS %p\n", ObjectAttributes->SecurityQualityOfService ));
  3420. KdPrint((" DesiredAccess %X, FileAttributes %X, ShareAccess %X\n",
  3421. DesiredAccess, FileAttributes, ShareAccess ));
  3422. KdPrint((" Disposition %X, CreateOptions %X, EaLength %X\n",
  3423. Disposition, CreateOptions, EaLength ));
  3424. KdPrint((" CreateFileType %X, ExtraCreateParameters %p, Options %X\n",
  3425. CreateFileType, ExtraCreateParameters, Options ));
  3426. }
  3427. //
  3428. // See if this operation is allowed on this share
  3429. //
  3430. if( ARGUMENT_PRESENT( Share ) ) {
  3431. status = SrvIsAllowedOnAdminShare( WorkContext, Share );
  3432. if( !NT_SUCCESS( status ) ) {
  3433. IF_DEBUG( CREATE ) {
  3434. KdPrint(("Create disallowed on Admin Share: %X\n", status ));
  3435. }
  3436. return status;
  3437. }
  3438. }
  3439. //
  3440. // We do not allow the remote opening of structured storage files
  3441. //
  3442. if( (CreateOptions & FILE_STRUCTURED_STORAGE) == FILE_STRUCTURED_STORAGE ) {
  3443. IF_DEBUG( CREATE ) {
  3444. KdPrint(("Create FILE_STRUCTURED_STORAGE unsupported\n" ));
  3445. }
  3446. return STATUS_NOT_SUPPORTED;
  3447. }
  3448. //
  3449. // We do not allow opening files by ID. It is too easy to escape the share
  3450. //
  3451. if( CreateOptions & FILE_OPEN_BY_FILE_ID ) {
  3452. IF_DEBUG( CREATE ) {
  3453. KdPrint(("Create FILE_OPEN_BY_FILE_ID unsupported\n" ));
  3454. }
  3455. return STATUS_NOT_SUPPORTED;
  3456. }
  3457. //
  3458. // Make sure the client isn't trying to create a file having the name
  3459. // of a DOS device
  3460. //
  3461. SrvGetBaseFileName( ObjectAttributes->ObjectName, &fileName );
  3462. for( pName = SrvDosDevices; pName->Length; pName++ ) {
  3463. if( pName->Length == fileName.Length &&
  3464. RtlCompareUnicodeString( pName, &fileName, TRUE ) == 0 ) {
  3465. //
  3466. // Whoa! We don't want clients trying to create files having a
  3467. // DOS device name
  3468. //
  3469. IF_DEBUG( CREATE ) {
  3470. KdPrint(("Create open %wZ unsupported\n", &fileName ));
  3471. }
  3472. return STATUS_ACCESS_DENIED;
  3473. }
  3474. }
  3475. //
  3476. // If this is from the NULL session, allow it to open only certain
  3477. // pipes.
  3478. //
  3479. if ( CreateFileType != CreateFileTypeMailslot ) {
  3480. shareType = WorkContext->TreeConnect->Share->ShareType;
  3481. if( shareType == ShareTypePipe ) {
  3482. if ( WorkContext->Session->IsNullSession ) {
  3483. if( SrvRestrictNullSessionAccess ) {
  3484. BOOLEAN matchFound = FALSE;
  3485. ULONG i;
  3486. ACQUIRE_LOCK( &SrvConfigurationLock );
  3487. for ( i = 0; SrvNullSessionPipes[i] != NULL ; i++ ) {
  3488. if ( _wcsicmp(
  3489. SrvNullSessionPipes[i],
  3490. ObjectAttributes->ObjectName->Buffer
  3491. ) == 0 ) {
  3492. matchFound = TRUE;
  3493. break;
  3494. }
  3495. }
  3496. RELEASE_LOCK( &SrvConfigurationLock );
  3497. if ( !matchFound ) {
  3498. IF_DEBUG( CREATE ) {
  3499. KdPrint(( "Create via NULL session denied\n" ));
  3500. }
  3501. return(STATUS_ACCESS_DENIED);
  3502. }
  3503. }
  3504. } else if( WorkContext->Session->IsLSNotified == FALSE ) {
  3505. //
  3506. // We have a pipe open request, not a NULL session, and
  3507. // we haven't gotten clearance from the license server yet.
  3508. // If this pipe requires clearance, get a license.
  3509. //
  3510. ULONG i;
  3511. BOOLEAN matchFound = FALSE;
  3512. ACQUIRE_LOCK( &SrvConfigurationLock );
  3513. for ( i = 0; SrvPipesNeedLicense[i] != NULL ; i++ ) {
  3514. if ( _wcsicmp(
  3515. SrvPipesNeedLicense[i],
  3516. ObjectAttributes->ObjectName->Buffer
  3517. ) == 0 ) {
  3518. matchFound = TRUE;
  3519. break;
  3520. }
  3521. }
  3522. RELEASE_LOCK( &SrvConfigurationLock );
  3523. if( matchFound == TRUE ) {
  3524. status = SrvXsLSOperation( WorkContext->Session,
  3525. XACTSRV_MESSAGE_LSREQUEST );
  3526. if( !NT_SUCCESS( status ) ) {
  3527. IF_DEBUG( CREATE ) {
  3528. KdPrint(( "Create failed due to license server: %X\n", status ));
  3529. }
  3530. return status;
  3531. }
  3532. }
  3533. }
  3534. }
  3535. //
  3536. // !!! a hack to handle a bug in the Object system and path-based
  3537. // operations on print shares. to test a fix, try from OS/2:
  3538. //
  3539. // copy config.sys \\server\printshare
  3540. //
  3541. if ( Share == NULL &&
  3542. ObjectAttributes->ObjectName->Length == 0 &&
  3543. ObjectAttributes->RootDirectory == NULL ) {
  3544. IF_DEBUG( CREATE ) {
  3545. KdPrint(("Create failed: ObjectName Len == 0, an ! Root directory\n" ));
  3546. }
  3547. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  3548. }
  3549. //
  3550. // Check desired access against share ACL.
  3551. //
  3552. // This gets a little hairy. Basically, we simply want to check the
  3553. // desired access against the ACL. But this doesn't correctly
  3554. // handle the case where the client only has read access to the
  3555. // share, and wants to (perhaps optionally) create (or overwrite) a
  3556. // file or directory, but only asks for read access to the file. We
  3557. // deal with this problem by, in effect, adding write access to the
  3558. // access requested by the client if the client specifies a
  3559. // dispostion mode that will or may create or overwrite the file.
  3560. //
  3561. // If the client specifies a dispostion that WILL create or
  3562. // overwrite (CREATE, SUPERSEDE, OVERWRITE, or OVERWRITE_IF), we
  3563. // turn on the FILE_WRITE_DATA (aka FILE_ADD_FILE) and
  3564. // FILE_APPEND_DATA (aka FILE_ADD_SUBDIRECTORY) accesses. If the
  3565. // access check fails, we return STATUS_ACCESS_DENIED.
  3566. //
  3567. // If the client specifies optional creation, then we have to be
  3568. // even more tricky. We don't know if the file actually exists, so
  3569. // we can't just reject the request out-of-hand, because if the file
  3570. // does exist, and the client really does have read access to the
  3571. // file, it will look weird if we deny the open. So in this case we
  3572. // turn the OPEN_IF request into an OPEN (fail if doesn't exist)
  3573. // request. If the open fails because the file doesn't exist, we
  3574. // return STATUS_ACCESS_DENIED.
  3575. //
  3576. // Note that this method effectively means that the share ACL cannot
  3577. // distinguish between a user who can write to existing files but
  3578. // who cannot create new files. This is because of the overloading
  3579. // of FILE_WRITE_DATA/FILE_ADD_FILE and
  3580. // FILE_APPEND_DATA/FILE_ADD_SUBDIRECTORY.
  3581. //
  3582. //
  3583. // OK. First, check the access exactly as requested.
  3584. //
  3585. status = SrvCheckShareFileAccess( WorkContext, DesiredAccess, &ShareAccess );
  3586. if ( !NT_SUCCESS( status )) {
  3587. //
  3588. // Some clients want ACCESS_DENIED to be in the server class
  3589. // instead of the DOS class when it's due to share ACL
  3590. // restrictions. So we need to keep track of why we're
  3591. // returning ACCESS_DENIED.
  3592. //
  3593. IF_DEBUG( CREATE ) {
  3594. KdPrint(("Create failed, SrvCheckShareFileAccess returns %X\n", status ));
  3595. }
  3596. WorkContext->ShareAclFailure = TRUE;
  3597. return STATUS_ACCESS_DENIED;
  3598. }
  3599. } else {
  3600. //
  3601. // Set it to CreateFileTypeNone so no extra checking is done.
  3602. //
  3603. CreateFileType = CreateFileTypeNone;
  3604. }
  3605. //
  3606. // That worked. Now, if the Disposition may or will create or
  3607. // overwrite, do more checking.
  3608. //
  3609. if ( Disposition != FILE_OPEN ) {
  3610. status = SrvCheckShareFileAccess(
  3611. WorkContext,
  3612. DesiredAccess | FILE_WRITE_DATA | FILE_APPEND_DATA,
  3613. &ShareAccess
  3614. );
  3615. if ( !NT_SUCCESS( status )) {
  3616. //
  3617. // The client cannot create or overwrite files. Unless
  3618. // they asked for FILE_OPEN_IF, jump out now.
  3619. //
  3620. if ( Disposition != FILE_OPEN_IF ) {
  3621. //
  3622. // Some clients want ACCESS_DENIED to be in the server class
  3623. // instead of the DOS class when it's due to share ACL
  3624. // restrictions. So we need to keep track of why we're
  3625. // returning ACCESS_DENIED.
  3626. //
  3627. IF_DEBUG( CREATE ) {
  3628. KdPrint(("Create failed, SrvCheckShareFileAccess returns ACCESS_DENIED\n"));
  3629. }
  3630. WorkContext->ShareAclFailure = TRUE;
  3631. return STATUS_ACCESS_DENIED;
  3632. }
  3633. //
  3634. // Change OPEN_IF to OPEN, and remember that we did it.
  3635. //
  3636. Disposition = FILE_OPEN;
  3637. dispositionModified = TRUE;
  3638. }
  3639. }
  3640. //
  3641. // If this client is reading from the file, turn off FILE_SEQUENTIAL_ONLY in case
  3642. // caching this file would be beneficial to other clients.
  3643. //
  3644. if( shareType == ShareTypeDisk &&
  3645. !(DesiredAccess & (FILE_WRITE_DATA|FILE_APPEND_DATA)) ) {
  3646. CreateOptions &= ~FILE_SEQUENTIAL_ONLY;
  3647. }
  3648. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  3649. //
  3650. // Make sure that this open will not push the server over its
  3651. // nonpaged and paged quotas.
  3652. //
  3653. newUsage = InterlockedExchangeAdd(
  3654. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  3655. IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  3656. ) + IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE;
  3657. if ( newUsage > SrvMaxNonPagedPoolUsage ) {
  3658. status = STATUS_INSUFF_SERVER_RESOURCES;
  3659. eventToLog = EVENT_SRV_NONPAGED_POOL_LIMIT;
  3660. goto error_exit1;
  3661. }
  3662. if ( SrvStatistics.CurrentNonPagedPoolUsage > SrvStatistics.PeakNonPagedPoolUsage) {
  3663. SrvStatistics.PeakNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
  3664. }
  3665. }
  3666. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  3667. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3668. newUsage = InterlockedExchangeAdd(
  3669. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  3670. IO_FILE_OBJECT_PAGED_POOL_CHARGE
  3671. ) + IO_FILE_OBJECT_PAGED_POOL_CHARGE;
  3672. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3673. if ( newUsage > SrvMaxPagedPoolUsage ) {
  3674. status = STATUS_INSUFF_SERVER_RESOURCES;
  3675. eventToLog = EVENT_SRV_PAGED_POOL_LIMIT;
  3676. goto error_exit;
  3677. }
  3678. if ( SrvStatistics.CurrentPagedPoolUsage > SrvStatistics.PeakPagedPoolUsage) {
  3679. SrvStatistics.PeakPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
  3680. }
  3681. }
  3682. //
  3683. // If Share is specified, we may need to fill up the root share
  3684. // handle of the object attribute.
  3685. //
  3686. if ( ARGUMENT_PRESENT( Share ) && (shareType != ShareTypePrint) ) {
  3687. //
  3688. // Get the Share root handle.
  3689. //
  3690. status = SrvGetShareRootHandle( Share );
  3691. if ( !NT_SUCCESS(status) ) {
  3692. IF_DEBUG(CREATE) {
  3693. KdPrint(( "SrvIoCreateFile: SrvGetShareRootHandle failed: %X\n",
  3694. status ));
  3695. }
  3696. goto error_exit;
  3697. }
  3698. //
  3699. // Fill in the root handle.
  3700. //
  3701. status = SrvSnapGetRootHandle( WorkContext, &ObjectAttributes->RootDirectory );
  3702. if( !NT_SUCCESS( status ) )
  3703. {
  3704. goto error_exit;
  3705. }
  3706. //
  3707. // Make sure no data is being written
  3708. //
  3709. if( (WorkContext->SnapShotTime.QuadPart != 0) &&
  3710. (DesiredAccess & (FILE_WRITE_DATA|FILE_APPEND_DATA)) )
  3711. {
  3712. status = STATUS_ACCESS_DENIED;
  3713. goto error_exit;
  3714. }
  3715. }
  3716. //
  3717. // Impersonate the client. This makes us look like the client for
  3718. // the purpose of checking security. Don't do impersonation if
  3719. // this is a spool file since spool files have admin all access,
  3720. // everybody read access by definition.
  3721. //
  3722. status = STATUS_SUCCESS;
  3723. if ( shareType != ShareTypePrint ) {
  3724. status = IMPERSONATE( WorkContext );
  3725. }
  3726. #if SRVDBG_STATS
  3727. //
  3728. // Get the system time for statistics tracking.
  3729. //
  3730. KeQuerySystemTime( &timeStamp );
  3731. #endif
  3732. //
  3733. // Perform the actual open.
  3734. //
  3735. // *** Do not lose the status returned by IoCreateFile! Even if
  3736. // it's a success code. The caller needs to know if it's
  3737. // STATUS_OPLOCK_BREAK_IN_PROGRESS.
  3738. //
  3739. if( NT_SUCCESS( status ) ) {
  3740. status = IoCreateFile(
  3741. FileHandle,
  3742. DesiredAccess,
  3743. ObjectAttributes,
  3744. IoStatusBlock,
  3745. AllocationSize,
  3746. FileAttributes,
  3747. ShareAccess,
  3748. Disposition,
  3749. CreateOptions,
  3750. EaBuffer,
  3751. EaLength,
  3752. CreateFileType,
  3753. ExtraCreateParameters,
  3754. Options
  3755. );
  3756. //
  3757. // If the volume was dismounted, and we can refresh the share root handle,
  3758. // we should try the operation again.
  3759. //
  3760. if( ARGUMENT_PRESENT( Share ) && SrvRetryDueToDismount( Share, status ) ) {
  3761. status = SrvSnapGetRootHandle( WorkContext, &ObjectAttributes->RootDirectory );
  3762. if( !NT_SUCCESS( status ) )
  3763. {
  3764. goto error_exit;
  3765. }
  3766. status = IoCreateFile(
  3767. FileHandle,
  3768. DesiredAccess,
  3769. ObjectAttributes,
  3770. IoStatusBlock,
  3771. AllocationSize,
  3772. FileAttributes,
  3773. ShareAccess,
  3774. Disposition,
  3775. CreateOptions,
  3776. EaBuffer,
  3777. EaLength,
  3778. CreateFileType,
  3779. ExtraCreateParameters,
  3780. Options
  3781. );
  3782. }
  3783. }
  3784. #if SRVDBG_STATS
  3785. //
  3786. // Grab the time again.
  3787. //
  3788. KeQuerySystemTime( &currentTime );
  3789. #endif
  3790. //
  3791. // Go back to the server's security context.
  3792. //
  3793. if ( shareType != ShareTypePrint ) {
  3794. //
  3795. // Calling REVERT() even if the impersonate failed is harmless
  3796. //
  3797. REVERT( );
  3798. }
  3799. #if SRVDBG_STATS
  3800. //
  3801. // Determine how long the IoCreateFile took.
  3802. //
  3803. timeDifference.QuadPart = currentTime.QuadPart - timeStamp.QuadPart;
  3804. //
  3805. // Update statistics, including server pool quota statistics if the
  3806. // open didn't succeed.
  3807. //
  3808. ExInterlockedAddLargeInteger(
  3809. &SrvDbgStatistics.TotalIoCreateFileTime,
  3810. timeDifference,
  3811. &GLOBAL_SPIN_LOCK(Statistics)
  3812. );
  3813. #endif
  3814. //
  3815. // Release the share root handle
  3816. //
  3817. if ( ARGUMENT_PRESENT( Share ) ) {
  3818. SrvReleaseShareRootHandle( Share );
  3819. }
  3820. if ( NT_SUCCESS(status) ) {
  3821. IF_DEBUG( CREATE ) {
  3822. KdPrint(( " ** %wZ, handle %p\n",
  3823. ObjectAttributes->ObjectName, *FileHandle ));
  3824. }
  3825. tempStatus = SrvVerifyDeviceStackSize(
  3826. *FileHandle,
  3827. TRUE,
  3828. &fileObject,
  3829. &deviceObject,
  3830. NULL
  3831. );
  3832. if ( !NT_SUCCESS( tempStatus )) {
  3833. INTERNAL_ERROR(
  3834. ERROR_LEVEL_EXPECTED,
  3835. "SrvIoCreateFile: Verify Device Stack Size failed: %X\n",
  3836. tempStatus,
  3837. NULL
  3838. );
  3839. SRVDBG_RELEASE_HANDLE( *FileHandle, "FIL", 50, 0 );
  3840. SrvNtClose( *FileHandle, FALSE );
  3841. status = tempStatus;
  3842. } else {
  3843. //
  3844. // Mark the orgin of this file as remote. This should
  3845. // never fail. If it does, it means it is already set.
  3846. // Check for this in a debug build, but ignore errors
  3847. // in the retail build.
  3848. //
  3849. #if DBG
  3850. tempStatus =
  3851. #endif
  3852. IoSetFileOrigin( fileObject,
  3853. TRUE );
  3854. ASSERT( tempStatus == STATUS_SUCCESS );
  3855. //
  3856. // Remove the reference we added in SrvVerifyDeviceStackSize().
  3857. //
  3858. ObDereferenceObject( fileObject );
  3859. }
  3860. }
  3861. if ( !NT_SUCCESS(status) ) {
  3862. goto error_exit;
  3863. }
  3864. IF_DEBUG(HANDLES) {
  3865. if ( NT_SUCCESS(status) ) {
  3866. PVOID caller, callersCaller;
  3867. RtlGetCallersAddress( &caller, &callersCaller );
  3868. KdPrint(( "opened handle %p for %wZ (%p %p)\n",
  3869. *FileHandle, ObjectAttributes->ObjectName,
  3870. caller, callersCaller ));
  3871. }
  3872. }
  3873. IF_DEBUG( CREATE ) {
  3874. KdPrint((" Status %X\n", status ));
  3875. }
  3876. return status;
  3877. error_exit:
  3878. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  3879. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= IO_FILE_OBJECT_PAGED_POOL_CHARGE );
  3880. InterlockedExchangeAdd(
  3881. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  3882. -IO_FILE_OBJECT_PAGED_POOL_CHARGE
  3883. );
  3884. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3885. }
  3886. error_exit1:
  3887. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  3888. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE );
  3889. InterlockedExchangeAdd(
  3890. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  3891. -IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  3892. );
  3893. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  3894. }
  3895. if ( status == STATUS_INSUFF_SERVER_RESOURCES ) {
  3896. requiredSize = ((eventToLog == EVENT_SRV_NONPAGED_POOL_LIMIT) ?
  3897. IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE :
  3898. IO_FILE_OBJECT_PAGED_POOL_CHARGE);
  3899. INTERNAL_ERROR(
  3900. ERROR_LEVEL_EXPECTED,
  3901. "SrvIoCreateFile: nonpaged pool limit reached, current = %ld, max = %ld\n",
  3902. newUsage - requiredSize,
  3903. SrvMaxNonPagedPoolUsage
  3904. );
  3905. SrvLogError(
  3906. SrvDeviceObject,
  3907. eventToLog,
  3908. STATUS_INSUFFICIENT_RESOURCES,
  3909. &requiredSize,
  3910. sizeof(ULONG),
  3911. NULL,
  3912. 0
  3913. );
  3914. } else {
  3915. //
  3916. // Finish up the access checking started above. If the open failed
  3917. // because the file didn't exist, and we turned off the create-if
  3918. // mode because the client doesn't have write access, change the
  3919. // status to STATUS_ACCESS_DENIED.
  3920. //
  3921. if ( dispositionModified && (status == STATUS_OBJECT_NAME_NOT_FOUND) ) {
  3922. status = STATUS_ACCESS_DENIED;
  3923. }
  3924. }
  3925. IF_DEBUG( CREATE ) {
  3926. KdPrint((" Status %X\n", status ));
  3927. }
  3928. return status;
  3929. } // SrvIoCreateFile
  3930. NTSTATUS
  3931. SrvNtClose (
  3932. IN HANDLE Handle,
  3933. IN BOOLEAN QuotaCharged
  3934. )
  3935. /*++
  3936. Routine Description:
  3937. Closes a handle, records statistics for number of handles closed,
  3938. total time spent closing handles.
  3939. Arguments:
  3940. Handle - the handle to close.
  3941. QuotaCharged - indicates whether the server's internal quota for
  3942. paged and nonpaged pool was charged for this open.
  3943. Return Value:
  3944. NTSTATUS - result of operation.
  3945. --*/
  3946. {
  3947. NTSTATUS status;
  3948. #if SRVDBG_STATS
  3949. LARGE_INTEGER timeStamp, currentTime;
  3950. LARGE_INTEGER timeDifference;
  3951. #endif
  3952. PEPROCESS process;
  3953. PAGED_CODE( );
  3954. #if SRVDBG_STATS
  3955. //
  3956. // SnapShot the system time.
  3957. //
  3958. KeQuerySystemTime( &timeStamp );
  3959. #endif
  3960. //
  3961. // Make sure we're in the server FSP.
  3962. //
  3963. process = IoGetCurrentProcess();
  3964. if ( process != SrvServerProcess ) {
  3965. //KdPrint(( "SRV: Closing handle %x in process %x\n", Handle, process ));
  3966. KeAttachProcess( SrvServerProcess );
  3967. }
  3968. IF_DEBUG( CREATE ) {
  3969. KdPrint(( "SrvNtClose handle %p\n", Handle ));
  3970. }
  3971. //
  3972. // Close the handle.
  3973. //
  3974. status = NtClose( Handle );
  3975. //
  3976. // Return to the original process.
  3977. //
  3978. if ( process != SrvServerProcess ) {
  3979. KeDetachProcess();
  3980. }
  3981. IF_DEBUG( ERRORS ) {
  3982. if ( !NT_SUCCESS( status ) ) {
  3983. KdPrint(( "SRV: NtClose failed: %x\n", status ));
  3984. #if DBG
  3985. DbgBreakPoint( );
  3986. #endif
  3987. }
  3988. }
  3989. ASSERT( NT_SUCCESS( status ) );
  3990. #if SRVDBG_STATS
  3991. //
  3992. // Get the time again.
  3993. //
  3994. KeQuerySystemTime( &currentTime );
  3995. //
  3996. // Determine how long the close took.
  3997. //
  3998. timeDifference.QuadPart = currentTime.QuadPart - timeStamp.QuadPart;
  3999. #endif
  4000. //
  4001. // Update the relevant statistics, including server quota statistics.
  4002. //
  4003. #if SRVDBG_STATS
  4004. SrvDbgStatistics.TotalNtCloseTime.QuadPart += timeDifference.QuadPart;
  4005. #endif
  4006. if ( QuotaCharged ) {
  4007. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  4008. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  4009. InterlockedExchangeAdd(
  4010. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  4011. -(LONG)IO_FILE_OBJECT_PAGED_POOL_CHARGE
  4012. );
  4013. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  4014. }
  4015. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  4016. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  4017. InterlockedExchangeAdd(
  4018. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  4019. -(LONG)IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  4020. );
  4021. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  4022. }
  4023. }
  4024. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalHandlesClosed );
  4025. IF_DEBUG(HANDLES) {
  4026. PVOID caller, callersCaller;
  4027. RtlGetCallersAddress( &caller, &callersCaller );
  4028. if ( NT_SUCCESS(status) ) {
  4029. KdPrint(( "closed handle %p (%p %p)\n",
  4030. Handle, caller, callersCaller ));
  4031. } else {
  4032. KdPrint(( "closed handle %p (%p %p) FAILED: %X\n",
  4033. Handle, caller, callersCaller, status ));
  4034. }
  4035. }
  4036. return STATUS_SUCCESS;
  4037. } // SrvNtClose
  4038. NTSTATUS
  4039. SrvVerifyDeviceStackSize (
  4040. IN HANDLE FileHandle,
  4041. IN BOOLEAN ReferenceFileObject,
  4042. OUT PFILE_OBJECT *FileObject,
  4043. OUT PDEVICE_OBJECT *DeviceObject,
  4044. OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
  4045. )
  4046. /*++
  4047. Routine Description:
  4048. Ths routine references the file object associated with the
  4049. file handle and checks whether our work item has sufficient
  4050. irp stack size to handle requests to this device or the device
  4051. associated with this file.
  4052. Arguments:
  4053. FileHandle - The handle to an open device or file
  4054. ReferenceFileObject - if TRUE, the file object is left referenced.
  4055. FileObject - The file object associated with the filehandle.
  4056. DeviceObject - the device object associated with the filehandle.
  4057. HandleInformation - if not NULL, returns information about the file handle.
  4058. Return Value:
  4059. Status of request.
  4060. --*/
  4061. {
  4062. NTSTATUS status;
  4063. PAGED_CODE( );
  4064. //
  4065. // Get a pointer to the file object, so that we can directly
  4066. // get the related device object that should contain a count
  4067. // of the irp stack size needed by that device.
  4068. //
  4069. status = ObReferenceObjectByHandle(
  4070. FileHandle,
  4071. 0,
  4072. NULL,
  4073. KernelMode,
  4074. (PVOID *)FileObject,
  4075. HandleInformation
  4076. );
  4077. if ( !NT_SUCCESS(status) ) {
  4078. SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
  4079. //
  4080. // This internal error bugchecks the system.
  4081. //
  4082. INTERNAL_ERROR(
  4083. ERROR_LEVEL_IMPOSSIBLE,
  4084. "SrvVerifyDeviceStackSize: unable to reference file handle 0x%lx",
  4085. FileHandle,
  4086. NULL
  4087. );
  4088. } else {
  4089. *DeviceObject = IoGetRelatedDeviceObject( *FileObject );
  4090. if ( (*DeviceObject)->StackSize > SrvReceiveIrpStackSize ) {
  4091. INTERNAL_ERROR(
  4092. ERROR_LEVEL_UNEXPECTED,
  4093. "SrvVerifyStackSize: WorkItem Irp StackSize too small. Need %d Allocated %d\n",
  4094. (*DeviceObject)->StackSize+1,
  4095. SrvReceiveIrpStackSize
  4096. );
  4097. SrvLogSimpleEvent( EVENT_SRV_IRP_STACK_SIZE, STATUS_SUCCESS );
  4098. ObDereferenceObject( *FileObject );
  4099. *FileObject = NULL;
  4100. status = STATUS_INSUFF_SERVER_RESOURCES;
  4101. } else if ( !ReferenceFileObject ) {
  4102. ObDereferenceObject( *FileObject );
  4103. *FileObject = NULL;
  4104. }
  4105. }
  4106. return status;
  4107. } // SrvVerifyDeviceStackSize
  4108. NTSTATUS
  4109. SrvImpersonate (
  4110. IN PWORK_CONTEXT WorkContext
  4111. )
  4112. /*++
  4113. Routine Description:
  4114. Impersonates the remote client specified in the Session pointer
  4115. of the work context block.
  4116. Arguments:
  4117. WorkContext - a work context block containing a valid pointer to
  4118. a session block.
  4119. Return Value:
  4120. status code of the attempt
  4121. --*/
  4122. {
  4123. NTSTATUS status;
  4124. PAGED_CODE( );
  4125. ASSERT( WorkContext->Session != NULL );
  4126. if( WorkContext->SecurityContext == NULL ||
  4127. !IS_VALID_SECURITY_HANDLE (WorkContext->SecurityContext->UserHandle) ) {
  4128. return STATUS_ACCESS_DENIED;
  4129. }
  4130. status = ImpersonateSecurityContext(
  4131. &WorkContext->SecurityContext->UserHandle
  4132. );
  4133. if ( !NT_SUCCESS(status) ) {
  4134. INTERNAL_ERROR(
  4135. ERROR_LEVEL_UNEXPECTED,
  4136. "IMPERSONATE: NtSetInformationThread failed: %X",
  4137. status,
  4138. NULL
  4139. );
  4140. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_THREAD, status );
  4141. }
  4142. return status;
  4143. } // SrvImpersonate
  4144. NTSTATUS
  4145. SrvImpersonateSession (
  4146. IN PSESSION Session
  4147. )
  4148. /*++
  4149. Routine Description:
  4150. Impersonates the remote client specified in the Session pointer
  4151. of the work context block.
  4152. Arguments:
  4153. WorkContext - a work context block containing a valid pointer to
  4154. a session block.
  4155. Return Value:
  4156. status code of the attempt
  4157. --*/
  4158. {
  4159. NTSTATUS status;
  4160. PAGED_CODE( );
  4161. ASSERT( Session != NULL );
  4162. ACQUIRE_LOCK( &Session->Connection->Lock );
  4163. if( Session->SecurityContext == NULL ||
  4164. !IS_VALID_SECURITY_HANDLE (Session->SecurityContext->UserHandle) ) {
  4165. status = STATUS_ACCESS_DENIED;
  4166. }
  4167. else
  4168. {
  4169. status = ImpersonateSecurityContext(
  4170. &Session->SecurityContext->UserHandle
  4171. );
  4172. }
  4173. RELEASE_LOCK( &Session->Connection->Lock );
  4174. return status;
  4175. } // SrvImpersonate
  4176. VOID
  4177. SrvRevert (
  4178. VOID
  4179. )
  4180. /*++
  4181. Routine Description:
  4182. Reverts to the server FSP's default thread context.
  4183. Arguments:
  4184. None.
  4185. Return Value:
  4186. None.
  4187. --*/
  4188. {
  4189. NTSTATUS status;
  4190. PAGED_CODE( );
  4191. status = PsAssignImpersonationToken(PsGetCurrentThread(),NULL);
  4192. if ( !NT_SUCCESS(status) ) {
  4193. INTERNAL_ERROR(
  4194. ERROR_LEVEL_UNEXPECTED,
  4195. "REVERT: NtSetInformationThread failed: %X",
  4196. status,
  4197. NULL
  4198. );
  4199. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_THREAD, status );
  4200. }
  4201. return;
  4202. } // SrvRevert
  4203. NTSTATUS
  4204. SrvSetLastWriteTime (
  4205. IN PRFCB Rfcb,
  4206. IN ULONG LastWriteTimeInSeconds,
  4207. IN ACCESS_MASK GrantedAccess
  4208. )
  4209. /*++
  4210. Routine Description:
  4211. Sets the last write time on a file if the specified handle has
  4212. sufficient access. This is used by the Close and Create SMBs to
  4213. ensure that file times on server files are consistent with
  4214. times on clients.
  4215. Arguments:
  4216. Rfcb - pointer to the rfcb block that is associated with the file
  4217. which we need to set the lastwrite time on.
  4218. LastWriteTimeInSeconds - the time, in seconds since 1970, to put
  4219. on the file. If it is 0 or -1, the last write time is not
  4220. changed.
  4221. GrantedAccess - an access mask specifying the access the specified
  4222. handle has. If it has insufficient access, the file time is
  4223. not changed.
  4224. ForceChanges - flag set to true if we want to send down the
  4225. SetFileInfo anyway even if the client specified a zero lastWriteTime.
  4226. Useful to force the filesystem to update the file control block now
  4227. rather than at the close.
  4228. Return Value:
  4229. NTSTATUS - result of operation.
  4230. --*/
  4231. {
  4232. NTSTATUS status;
  4233. FILE_BASIC_INFORMATION fileBasicInfo;
  4234. IO_STATUS_BLOCK ioStatusBlock;
  4235. PAGED_CODE( );
  4236. //
  4237. // If the client doesn't want to set the time, don't set it.
  4238. //
  4239. if ( Rfcb->ShareType != ShareTypeDisk ||
  4240. LastWriteTimeInSeconds == 0 ||
  4241. LastWriteTimeInSeconds == 0xFFFFFFFF ) {
  4242. //
  4243. // If the file was written to, we won't cache the file. This is to
  4244. // ensure the file directory entry gets updated by the file system.
  4245. //
  4246. if ( Rfcb->WrittenTo ) {
  4247. Rfcb->IsCacheable = FALSE;
  4248. }
  4249. return STATUS_SUCCESS;
  4250. }
  4251. //
  4252. // Make sure that we have the correct access on the specified handle.
  4253. //
  4254. CHECK_FILE_INFORMATION_ACCESS(
  4255. GrantedAccess,
  4256. IRP_MJ_SET_INFORMATION,
  4257. FileBasicInformation,
  4258. &status
  4259. );
  4260. if ( !NT_SUCCESS(status) ) {
  4261. return status;
  4262. }
  4263. //
  4264. // Set to 0 the fields we don't want to change.
  4265. //
  4266. fileBasicInfo.CreationTime.QuadPart = 0;
  4267. fileBasicInfo.LastAccessTime.QuadPart = 0;
  4268. fileBasicInfo.ChangeTime.QuadPart = 0;
  4269. fileBasicInfo.FileAttributes = 0;
  4270. //
  4271. // Set up the last write time.
  4272. //
  4273. RtlSecondsSince1970ToTime(
  4274. LastWriteTimeInSeconds,
  4275. &fileBasicInfo.LastWriteTime
  4276. );
  4277. ExLocalTimeToSystemTime(
  4278. &fileBasicInfo.LastWriteTime,
  4279. &fileBasicInfo.LastWriteTime
  4280. );
  4281. //
  4282. // Set the time using the passed-in file handle.
  4283. //
  4284. status = NtSetInformationFile(
  4285. Rfcb->Lfcb->FileHandle,
  4286. &ioStatusBlock,
  4287. &fileBasicInfo,
  4288. sizeof(FILE_BASIC_INFORMATION),
  4289. FileBasicInformation
  4290. );
  4291. if ( !NT_SUCCESS(status) ) {
  4292. INTERNAL_ERROR(
  4293. ERROR_LEVEL_UNEXPECTED,
  4294. "SrvSetLastWriteTime: NtSetInformationFile returned %X",
  4295. status,
  4296. NULL
  4297. );
  4298. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );
  4299. return status;
  4300. }
  4301. return STATUS_SUCCESS;
  4302. } // SrvSetLastWriteTime
  4303. NTSTATUS
  4304. SrvCheckShareFileAccess(
  4305. IN PWORK_CONTEXT WorkContext,
  4306. IN ACCESS_MASK FileDesiredAccess,
  4307. IN OUT PULONG ShareMode
  4308. )
  4309. /*++
  4310. Routine Description:
  4311. This routine checks the desired access against the permissions
  4312. set for this client.
  4313. Arguments:
  4314. WorkContext - pointer to the work context block that contains information
  4315. about the request.
  4316. FileDesiredAccess - the desired access.
  4317. ShareMode - The sharing mode desired. This can be manipulated to prevent Read-Deny Read on
  4318. shares marked as such, to prevent a variety of DoS attacks
  4319. Return Value:
  4320. Status of operation.
  4321. --*/
  4322. {
  4323. NTSTATUS status = STATUS_SUCCESS;
  4324. SECURITY_SUBJECT_CONTEXT subjectContext;
  4325. PSECURITY_DESCRIPTOR securityDescriptor;
  4326. ACCESS_MASK grantedAccess;
  4327. ACCESS_MASK mappedAccess = FileDesiredAccess;
  4328. PPRIVILEGE_SET privileges = NULL;
  4329. PAGED_CODE( );
  4330. //
  4331. // For Restrict Opens behavior, we no longer allow someone to lock the file exclusively if they are opening
  4332. // simple for read. This prevents cases where clients with read-only access can lock a file and prevent anyone
  4333. // else from reading it. It presents interesting DoS situations in policy.
  4334. //
  4335. if( WorkContext->TreeConnect->Share->ShareProperties & SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS )
  4336. {
  4337. if( (!((*ShareMode) & FILE_SHARE_READ) &&
  4338. !(FileDesiredAccess & (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|FILE_APPEND_DATA)) ) )
  4339. {
  4340. *ShareMode |= FILE_SHARE_READ;
  4341. }
  4342. }
  4343. if( WorkContext->TreeConnect->Share->ShareProperties & SHI1005_FLAGS_FORCE_SHARED_DELETE )
  4344. {
  4345. *ShareMode |= FILE_SHARE_DELETE;
  4346. }
  4347. ACQUIRE_LOCK_SHARED( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  4348. securityDescriptor = WorkContext->TreeConnect->Share->FileSecurityDescriptor;
  4349. if (securityDescriptor != NULL) {
  4350. status = IMPERSONATE( WorkContext );
  4351. if( NT_SUCCESS( status ) ) {
  4352. SeCaptureSubjectContext( &subjectContext );
  4353. RtlMapGenericMask( &mappedAccess, &SrvFileAccessMapping );
  4354. //
  4355. // SYNCHRONIZE and ACCESS_SYSTEM_SECURITY do not make any sense for a share ACL
  4356. //
  4357. mappedAccess &= ~(SYNCHRONIZE|ACCESS_SYSTEM_SECURITY);
  4358. if ( !SeAccessCheck(
  4359. securityDescriptor,
  4360. &subjectContext,
  4361. FALSE, // Locked ?
  4362. mappedAccess,
  4363. 0, // PreviousGrantedAccess
  4364. &privileges,
  4365. &SrvFileAccessMapping,
  4366. UserMode,
  4367. &grantedAccess,
  4368. &status
  4369. ) ) {
  4370. IF_DEBUG(ERRORS) {
  4371. KdPrint((
  4372. "SrvCheckShareFileAccess: Status %x, Desired access %x, mappedAccess %x\n",
  4373. status,
  4374. FileDesiredAccess,
  4375. mappedAccess
  4376. ));
  4377. }
  4378. }
  4379. if ( privileges != NULL ) {
  4380. SeFreePrivileges( privileges );
  4381. }
  4382. SeReleaseSubjectContext( &subjectContext );
  4383. REVERT( );
  4384. }
  4385. }
  4386. RELEASE_LOCK( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  4387. return status;
  4388. } // SrvCheckShareFileAccess
  4389. VOID
  4390. SrvReleaseShareRootHandle (
  4391. IN PSHARE Share
  4392. )
  4393. /*++
  4394. Routine Description:
  4395. This routine releases the root handle for a given share if the
  4396. shared device is removable (floopy, or cdrom).
  4397. Arguments:
  4398. Share - The share for which the root directory handle is to be released.
  4399. Return Value:
  4400. None.
  4401. --*/
  4402. {
  4403. PAGED_CODE( );
  4404. if ( Share->Removable ) {
  4405. ASSERT( Share->CurrentRootHandleReferences > 0 );
  4406. ACQUIRE_LOCK( &SrvShareLock );
  4407. if ( --Share->CurrentRootHandleReferences == 0 ) {
  4408. ASSERT( Share->RootDirectoryHandle != NULL );
  4409. SRVDBG_RELEASE_HANDLE( Share->RootDirectoryHandle, "RTD", 51, Share );
  4410. SrvNtClose( Share->RootDirectoryHandle, FALSE );
  4411. Share->RootDirectoryHandle = NULL;
  4412. SrvDereferenceShare( Share );
  4413. }
  4414. RELEASE_LOCK( &SrvShareLock );
  4415. }
  4416. return;
  4417. } // SrvReleaseShareRootHandle
  4418. VOID
  4419. SrvUpdateVcQualityOfService (
  4420. IN PCONNECTION Connection,
  4421. IN PLARGE_INTEGER CurrentTime OPTIONAL
  4422. )
  4423. /*++
  4424. Routine Description:
  4425. Updates the connection quality of service information by
  4426. querying the underlying transport.
  4427. Arguments:
  4428. Connection - pointer to the connection whose qos we want to update.
  4429. CurrentTime - an optional pointer to a large interger containing the
  4430. current time.
  4431. Return Value:
  4432. None.
  4433. --*/
  4434. {
  4435. NTSTATUS status;
  4436. PTDI_CONNECTION_INFO connectionInfo;
  4437. LARGE_INTEGER currentTime;
  4438. LARGE_INTEGER throughput;
  4439. LARGE_INTEGER linkDelay;
  4440. PPAGED_CONNECTION pagedConnection = Connection->PagedConnection;
  4441. PAGED_CODE( );
  4442. //
  4443. // This routine is a no-op on connectionless transports.
  4444. //
  4445. if ( Connection->Endpoint->IsConnectionless ) {
  4446. Connection->EnableOplocks = FALSE;
  4447. Connection->EnableRawIo = FALSE;
  4448. return;
  4449. }
  4450. //
  4451. // Update the connection information
  4452. //
  4453. if ( ARGUMENT_PRESENT( CurrentTime ) ) {
  4454. currentTime = *CurrentTime;
  4455. } else {
  4456. KeQuerySystemTime( &currentTime );
  4457. }
  4458. //
  4459. // Check if connection info is still valid.
  4460. //
  4461. if ( pagedConnection->LinkInfoValidTime.QuadPart > currentTime.QuadPart ) {
  4462. return;
  4463. }
  4464. //
  4465. // We need to update the connection information.
  4466. //
  4467. connectionInfo = ALLOCATE_NONPAGED_POOL(
  4468. sizeof(TDI_CONNECTION_INFO),
  4469. BlockTypeDataBuffer
  4470. );
  4471. if ( connectionInfo == NULL ) {
  4472. goto exitquery;
  4473. }
  4474. //
  4475. // Issue a TdiQueryInformation to get the current connection info
  4476. // from the transport provider for this connection. This is a
  4477. // synchronous operation.
  4478. //
  4479. status = SrvIssueTdiQuery(
  4480. Connection->FileObject,
  4481. &Connection->DeviceObject,
  4482. (PUCHAR)connectionInfo,
  4483. sizeof(TDI_CONNECTION_INFO),
  4484. TDI_QUERY_CONNECTION_INFO
  4485. );
  4486. //
  4487. // If the request failed, log an event.
  4488. //
  4489. // *** We special-case STATUS_INVALID_CONNECTION because NBF completes
  4490. // our Accept IRP before it's actually ready to accept requests.
  4491. //
  4492. if ( !NT_SUCCESS(status) ) {
  4493. if ( status != STATUS_INVALID_CONNECTION &&
  4494. status != STATUS_CONNECTION_INVALID ) {
  4495. INTERNAL_ERROR(
  4496. ERROR_LEVEL_UNEXPECTED,
  4497. "SrvUpdateVcQualityOfService: SrvIssueTdiQuery failed: %X\n",
  4498. status,
  4499. NULL
  4500. );
  4501. SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
  4502. }
  4503. DEALLOCATE_NONPAGED_POOL( connectionInfo );
  4504. goto exitquery;
  4505. }
  4506. //
  4507. // Set the time when this information becomes invalid.
  4508. //
  4509. currentTime.QuadPart += SrvLinkInfoValidTime.QuadPart;
  4510. //
  4511. // Get a positive delay. The TP returns a relative time which
  4512. // is negative.
  4513. //
  4514. linkDelay.QuadPart = -connectionInfo->Delay.QuadPart;
  4515. if ( linkDelay.QuadPart < 0 ) {
  4516. linkDelay.QuadPart = 0;
  4517. }
  4518. //
  4519. // Get the throughput
  4520. //
  4521. throughput = connectionInfo->Throughput;
  4522. //
  4523. // If connection is reliable, check and see if the delay and throughput
  4524. // are within our limits. If not, the vc is unreliable.
  4525. //
  4526. Connection->EnableOplocks =
  4527. (BOOLEAN) ( !connectionInfo->Unreliable &&
  4528. throughput.QuadPart >= SrvMinLinkThroughput.QuadPart );
  4529. DEALLOCATE_NONPAGED_POOL( connectionInfo );
  4530. //
  4531. // We need to check the delay for Raw I/O.
  4532. //
  4533. Connection->EnableRawIo =
  4534. (BOOLEAN) ( Connection->EnableOplocks &&
  4535. linkDelay.QuadPart <= SrvMaxLinkDelay.QuadPart );
  4536. //
  4537. // See if oplocks are always disabled for this connection. We do it
  4538. // here so that Connection->EnableRawIo can be computed correctly.
  4539. //
  4540. if ( Connection->OplocksAlwaysDisabled ) {
  4541. Connection->EnableOplocks = FALSE;
  4542. }
  4543. //
  4544. // Access "large" connection QOS fields using a lock, to
  4545. // ensure consistent values.
  4546. //
  4547. ACQUIRE_LOCK( &Connection->Lock );
  4548. pagedConnection->LinkInfoValidTime = currentTime;
  4549. pagedConnection->Delay = linkDelay;
  4550. pagedConnection->Throughput = throughput;
  4551. RELEASE_LOCK( &Connection->Lock );
  4552. return;
  4553. exitquery:
  4554. Connection->EnableOplocks = TRUE;
  4555. Connection->EnableRawIo = TRUE;
  4556. return;
  4557. } // SrvUpdateVcQualityOfService
  4558. BOOLEAN SRVFASTCALL
  4559. SrvValidateSmb (
  4560. IN PWORK_CONTEXT WorkContext
  4561. )
  4562. /*++
  4563. Routine Description:
  4564. This function validates an SMB header.
  4565. Arguments:
  4566. WorkContext - Pointer to a work context block. The RequestHeader
  4567. and RequestParameter fields must be valid.
  4568. Return Value:
  4569. TRUE - The SMB is valid
  4570. FALSE - The SMB is invalid
  4571. --*/
  4572. {
  4573. PSMB_HEADER smbHeader;
  4574. UCHAR wordCount = 0;
  4575. PSMB_USHORT byteCount = NULL;
  4576. ULONG availableSpaceForSmb = 0;
  4577. PAGED_CODE( );
  4578. smbHeader = WorkContext->RequestHeader;
  4579. //
  4580. // Did we get an entire SMB? We check here for an SMB that at least goes
  4581. // to the WordCount field.
  4582. //
  4583. if( WorkContext->RequestBuffer->DataLength < sizeof( SMB_HEADER ) + sizeof( UCHAR ) ) {
  4584. IF_DEBUG(SMB_ERRORS) {
  4585. KdPrint(( "SMB of %d bytes too short!\n", availableSpaceForSmb ));
  4586. }
  4587. IF_DEBUG( ERRORS ) {
  4588. KdPrint(("Closing connection %p -- msg too small\n", WorkContext->Connection ));
  4589. }
  4590. //
  4591. // This client has really misbehaved. Nuke it!
  4592. //
  4593. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4594. SrvCloseConnection( WorkContext->Connection, FALSE );
  4595. return FALSE;
  4596. }
  4597. //
  4598. // Does it start with 0xFF S M B ?
  4599. //
  4600. if ( SmbGetAlignedUlong( (PULONG)smbHeader->Protocol ) !=
  4601. SMB_HEADER_PROTOCOL ) {
  4602. IF_DEBUG(SMB_ERRORS) {
  4603. KdPrint(( "SMB does not start with SMB_HEADER_PROTOCOL\n" ));
  4604. }
  4605. IF_DEBUG( ERRORS ) {
  4606. KdPrint(("Closing connection %p -- no ffSMB\n", WorkContext->Connection ));
  4607. }
  4608. //
  4609. // This client has really misbehaved. Nuke it!
  4610. //
  4611. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4612. SrvCloseConnection( WorkContext->Connection, FALSE );
  4613. return FALSE;
  4614. }
  4615. #if 0
  4616. if ( smbHeader->Reserved != 0 ) {
  4617. IF_DEBUG(SMB_ERRORS) {
  4618. KdPrint(( "SMB Header->Reserved %x != 0!\n", smbHeader->Reserved ));
  4619. }
  4620. SrvLogInvalidSmb( WorkContext );
  4621. return FALSE;
  4622. }
  4623. //
  4624. // DOS LM2.1 sets SMB_FLAGS_SERVER_TO_REDIR on an oplock break
  4625. // response, so ignore that bit.
  4626. //
  4627. if ( (smbHeader->Flags &
  4628. ~(INCOMING_SMB_FLAGS | SMB_FLAGS_SERVER_TO_REDIR)) != 0 ) {
  4629. IF_DEBUG(SMB_ERRORS) {
  4630. KdPrint(( "SMB Header->Flags (%x) invalid\n", smbHeader->Flags ));
  4631. }
  4632. SrvLogInvalidSmb( WorkContext );
  4633. return FALSE;
  4634. }
  4635. if ( (SmbGetAlignedUshort( &smbHeader->Flags2 ) &
  4636. ~INCOMING_SMB_FLAGS2) != 0 ) {
  4637. KdPrint(( "ValidatesmbHeader: Flags2 = %lx, valid bits = %lx, "
  4638. "invalid bit(s) = %lx\n",
  4639. SmbGetAlignedUshort( &smbHeader->Flags2 ),
  4640. INCOMING_SMB_FLAGS2,
  4641. SmbGetAlignedUshort( &smbHeader->Flags2 ) &
  4642. ~INCOMING_SMB_FLAGS2 ));
  4643. SrvLogInvalidSmb( WorkContext );
  4644. return FALSE;
  4645. }
  4646. #endif
  4647. #if 0
  4648. if( (smbHeader->Command != SMB_COM_LOCKING_ANDX) &&
  4649. (smbHeader->Flags & SMB_FLAGS_SERVER_TO_REDIR) ) {
  4650. //
  4651. // A client has set the bit indicating that this is a server response
  4652. // packet. This could be an attempt by a client to sneak through a
  4653. // firewall -- because the firewall may be configured to allow incoming
  4654. // responses, but no incomming requests (thereby allowing internal clients
  4655. // to access Internet servers, but not allowing external clients to access
  4656. // internal servers). Reject this SMB.
  4657. //
  4658. //
  4659. SrvLogInvalidSmb( WorkContext );
  4660. return FALSE;
  4661. }
  4662. #endif
  4663. if( WorkContext->Connection->SmbDialect == SmbDialectIllegal &&
  4664. smbHeader->Command != SMB_COM_NEGOTIATE ) {
  4665. //
  4666. // Whoa -- the client sent us an SMB, but we haven't negotiated a dialect
  4667. // yet!
  4668. //
  4669. IF_DEBUG(SMB_ERRORS) {
  4670. KdPrint(( "SMB command %x w/o negotiate!\n", smbHeader->Command ));
  4671. }
  4672. IF_DEBUG( ERRORS ) {
  4673. KdPrint(("Closing connection %p -- no Negotiate\n", WorkContext->Connection ));
  4674. }
  4675. //
  4676. // This client has really misbehaved. Nuke it!
  4677. //
  4678. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4679. SrvCloseConnection( WorkContext->Connection, FALSE );
  4680. return FALSE;
  4681. }
  4682. //
  4683. // Get the WordCount and ByteCount values to make sure that there
  4684. // was enough information sent to satisfy the specifications.
  4685. //
  4686. wordCount = *((PUCHAR)WorkContext->RequestParameters);
  4687. byteCount = (PSMB_USHORT)( (PCHAR)WorkContext->RequestParameters +
  4688. sizeof(UCHAR) + (wordCount * sizeof(USHORT)) );
  4689. availableSpaceForSmb = WorkContext->RequestBuffer->DataLength -
  4690. PTR_DIFF( WorkContext->ResponseParameters,
  4691. WorkContext->RequestBuffer->Buffer );
  4692. //
  4693. // Verify all of the fixed valued SMB header fields. Variable
  4694. // valued fields (such as Tid) are verified as needed by the
  4695. // individual SMB handlers.
  4696. //
  4697. //
  4698. // Make sure that the valid word count was sent across. If the
  4699. // value in the table is -1, then the processing routine will
  4700. // verify the word count, and if the word count is -2 then this
  4701. // is an illegal command which will be caught later.
  4702. //
  4703. // We check whether the word count is negative first since
  4704. // critical smbs like read/write (andX/raw) have -1.
  4705. //
  4706. //
  4707. // Make sure that the ByteCount lies within the boundaries of
  4708. // the received SMB. Without this test, it would be possible,
  4709. // when at the end of a long AndX chain and WordCount is large,
  4710. // for the server to take an access violation when looking at
  4711. // ByteCount. The location for ByteCount must be at least two
  4712. // bytes short of the end of the buffer, as ByteCount is a
  4713. // USHORT (two bytes).
  4714. //
  4715. //
  4716. // The WordCount parameter is a byte that indicates the number of
  4717. // word parameters, and ByteCount is a word that indicated the
  4718. // number of following bytes. They do not account for their own
  4719. // sizes, so add sizeof(UCHAR) + sizeof(USHORT) to account for them.
  4720. //
  4721. if ( ((SrvSmbWordCount[WorkContext->NextCommand] < 0)
  4722. ||
  4723. ((CHAR)wordCount == SrvSmbWordCount[WorkContext->NextCommand]))
  4724. &&
  4725. ((PCHAR)byteCount <= (PCHAR)WorkContext->RequestBuffer->Buffer +
  4726. WorkContext->RequestBuffer->DataLength -
  4727. sizeof(USHORT))
  4728. &&
  4729. ((wordCount*sizeof(USHORT) + sizeof(UCHAR) + sizeof(USHORT) +
  4730. SmbGetUshort( byteCount )) <= availableSpaceForSmb) ) {
  4731. return(TRUE);
  4732. }
  4733. //
  4734. // If we have an NT style WriteAndX, we let the client exceed the negotiated
  4735. // buffer size. We do not need to check the WordCount, because we know that
  4736. // the SMB processor itself checks it.
  4737. //
  4738. if( WorkContext->LargeIndication ) {
  4739. if( WorkContext->NextCommand == SMB_COM_WRITE_ANDX ) {
  4740. return(TRUE);
  4741. } else {
  4742. IF_DEBUG(SMB_ERRORS) {
  4743. KdPrint(( "LargeIndication but not WRITE_AND_X (%x) received!\n",
  4744. WorkContext->NextCommand ));
  4745. }
  4746. IF_DEBUG( ERRORS ) {
  4747. KdPrint(("Closing connection %p -- msg too large\n", WorkContext->Connection ));
  4748. }
  4749. //
  4750. // This client has really misbehaved. Nuke it!
  4751. //
  4752. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4753. SrvCloseConnection( WorkContext->Connection, FALSE );
  4754. return( FALSE );
  4755. }
  4756. }
  4757. //
  4758. // Make sure that the valid word count was sent across. If the
  4759. // value in the table is -1, then the processing routine will
  4760. // verify the word count, and if the word count is -2 then this
  4761. // is an illegal command which will be caught later.
  4762. //
  4763. // We check whether the word count is negative first since
  4764. // critical smbs like read/write (andX/raw) have -1.
  4765. //
  4766. if ( (CHAR)wordCount != SrvSmbWordCount[WorkContext->NextCommand] ) {
  4767. //
  4768. // Living with sin. The DOS redir sends a word count of 9
  4769. // (instead of 8) on a Transaction secondary SMB. Pretend it
  4770. // sent the correct number.
  4771. //
  4772. if ( WorkContext->RequestHeader->Command ==
  4773. SMB_COM_TRANSACTION_SECONDARY &&
  4774. IS_DOS_DIALECT( WorkContext->Connection->SmbDialect) &&
  4775. wordCount == 9 ) {
  4776. wordCount = 8;
  4777. *((PUCHAR)WorkContext->RequestParameters) = 8;
  4778. byteCount = (PSMB_USHORT)( (PCHAR)WorkContext->RequestParameters +
  4779. sizeof(UCHAR) + (8 * sizeof(USHORT)) );
  4780. } else {
  4781. //
  4782. // Any other request with an incorrect word count is
  4783. // toast. Reject the request.
  4784. //
  4785. IF_DEBUG(SMB_ERRORS) {
  4786. KdPrint(( "SMB WordCount incorrect. WordCount=%ld, "
  4787. "should be %ld (command = 0x%lx)\n", wordCount,
  4788. SrvSmbWordCount[WorkContext->NextCommand],
  4789. WorkContext->NextCommand ));
  4790. KdPrint(( " SMB received from %z\n",
  4791. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4792. }
  4793. SrvLogInvalidSmb( WorkContext );
  4794. return FALSE;
  4795. }
  4796. }
  4797. //
  4798. // Make sure that the ByteCount lies within the boundaries of
  4799. // the received SMB. Without this test, it would be possible,
  4800. // when at the end of a long AndX chain and WordCount is large,
  4801. // for the server to take an access violation when looking at
  4802. // ByteCount. The location for ByteCount must be at least two
  4803. // bytes short of the end of the buffer, as ByteCount is a
  4804. // USHORT (two bytes).
  4805. //
  4806. if ( (PCHAR)byteCount > (PCHAR)WorkContext->RequestBuffer->Buffer +
  4807. WorkContext->RequestBuffer->DataLength -
  4808. sizeof(USHORT) ) {
  4809. IF_DEBUG(SMB_ERRORS) {
  4810. KdPrint(( "ByteCount address past end of sent SMB. "
  4811. "ByteCount address=0x%p, "
  4812. "End of buffer=0x%lx\n",
  4813. byteCount,
  4814. WorkContext->RequestBuffer->DataLength ));
  4815. KdPrint(( " SMB received from %z\n",
  4816. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4817. }
  4818. SrvLogInvalidSmb( WorkContext );
  4819. IF_DEBUG( ERRORS ) {
  4820. KdPrint(("Closing connection %p -- ByteCount too big\n", WorkContext->Connection ));
  4821. }
  4822. //
  4823. // This client has really misbehaved. Nuke it!
  4824. //
  4825. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4826. SrvCloseConnection( WorkContext->Connection, FALSE );
  4827. } else {
  4828. //
  4829. // if this is an IOCTL smb with category 0x53, set byte count to zero.
  4830. // This is due to a DOS Lm2.0 and Lm2.1 bug which does not zero out
  4831. // the bcc causing the preceding check to fail.
  4832. //
  4833. if ( (WorkContext->RequestHeader->Command == SMB_COM_IOCTL) &&
  4834. (((PREQ_IOCTL) WorkContext->RequestParameters)->Category == 0x53)
  4835. ) {
  4836. SmbPutUshort( byteCount , 0 );
  4837. return(TRUE);
  4838. }
  4839. IF_DEBUG(SMB_ERRORS) {
  4840. KdPrint(( "SMB WordCount and/or ByteCount incorrect. "
  4841. "WordCount=%ld, ByteCount=%ld, Space=%ld\n",
  4842. wordCount, SmbGetUshort( byteCount ),
  4843. availableSpaceForSmb ));
  4844. KdPrint(( " SMB received from %z\n",
  4845. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4846. }
  4847. SrvLogInvalidSmb( WorkContext );
  4848. }
  4849. return FALSE;
  4850. } // SrvValidateSmb
  4851. NTSTATUS
  4852. SrvWildcardRename(
  4853. IN PUNICODE_STRING FileSpec,
  4854. IN PUNICODE_STRING SourceString,
  4855. OUT PUNICODE_STRING TargetString
  4856. )
  4857. /*++
  4858. Routine Description:
  4859. This routine converts a filespec and a source filename into a
  4860. destination file name. This routine is used to support DOS-based
  4861. wildcard renames.
  4862. Arguments:
  4863. FileSpec - The wildcard specification describing the destination file.
  4864. SourceString - Pointer to a string that contains the source file name.
  4865. TargetString - Pointer to a string that will contain the destination name.
  4866. Return Value:
  4867. Status of operation.
  4868. --*/
  4869. {
  4870. PWCHAR currentFileSpec;
  4871. WCHAR delimit;
  4872. PWCHAR buffer;
  4873. PWCHAR source;
  4874. ULONG bufferSize;
  4875. ULONG sourceLeft;
  4876. ULONG i;
  4877. //
  4878. // This will store the number of bytes we have written to the
  4879. // target buffer so far.
  4880. //
  4881. ULONG resultLength = 0;
  4882. PAGED_CODE( );
  4883. //
  4884. // This points to the current character in the filespec.
  4885. //
  4886. currentFileSpec = FileSpec->Buffer;
  4887. //
  4888. // Initialize the pointer and the length of the source buffer.
  4889. //
  4890. source = SourceString->Buffer;
  4891. sourceLeft = SourceString->Length;
  4892. //
  4893. // Initialize the pointer and the length of the target buffer.
  4894. //
  4895. buffer = TargetString->Buffer;
  4896. bufferSize = TargetString->MaximumLength;
  4897. //
  4898. // Go throught each character in the filespec.
  4899. //
  4900. for ( i = 0; i < (ULONG)FileSpec->Length ; i += sizeof(WCHAR) ) {
  4901. if (resultLength < bufferSize) {
  4902. switch ( *currentFileSpec ) {
  4903. case L':':
  4904. case L'\\':
  4905. return STATUS_OBJECT_NAME_INVALID;
  4906. case L'*':
  4907. //
  4908. // Store the next character.
  4909. //
  4910. delimit = *(currentFileSpec+1);
  4911. //
  4912. // While we have not exceeded the buffer and
  4913. // we have not reached the end of the source string
  4914. // and the current source character is not equal to
  4915. // the delimeter, copy the source character to the
  4916. // target string.
  4917. //
  4918. while ( ( resultLength < bufferSize ) &&
  4919. ( sourceLeft > 0 ) &&
  4920. ( *source != delimit ) ) {
  4921. *(buffer++) = *(source++);
  4922. sourceLeft -= sizeof(WCHAR);
  4923. resultLength += sizeof(WCHAR);
  4924. }
  4925. break;
  4926. case L'?': //
  4927. case L'>': // should we even consider >, <, and "
  4928. case L'<': // I'll just put this here to be safe
  4929. //
  4930. // For each ? in the filespec, we copy one character
  4931. // from the source string.
  4932. //
  4933. if ( ( *source != L'.' ) && ( sourceLeft > 0 )) {
  4934. if (resultLength < bufferSize) {
  4935. *(buffer++) = *(source++);
  4936. sourceLeft -= sizeof(WCHAR);
  4937. resultLength += sizeof(WCHAR);
  4938. } else {
  4939. return(STATUS_BUFFER_OVERFLOW);
  4940. }
  4941. }
  4942. break;
  4943. case L'.':
  4944. case L'"':
  4945. //
  4946. // Discard all the characters from the source string up
  4947. // to . or the end of the string.
  4948. //
  4949. while ( (*source != L'.') && (sourceLeft > 0) ) {
  4950. source++;
  4951. sourceLeft -= sizeof(WCHAR);
  4952. }
  4953. *(buffer++) = L'.';
  4954. resultLength += sizeof(WCHAR);
  4955. if ( sourceLeft > 0 ) {
  4956. source++;
  4957. sourceLeft -= sizeof(WCHAR);
  4958. }
  4959. break;
  4960. default:
  4961. //
  4962. // Just copy one to one
  4963. //
  4964. if ( (*source != L'.') && (sourceLeft > 0)) {
  4965. source++;
  4966. sourceLeft -= sizeof(WCHAR);
  4967. }
  4968. if (resultLength < bufferSize) {
  4969. *(buffer++) = *currentFileSpec;
  4970. resultLength += sizeof(WCHAR);
  4971. } else {
  4972. return(STATUS_BUFFER_OVERFLOW);
  4973. }
  4974. break;
  4975. }
  4976. currentFileSpec++;
  4977. } else {
  4978. return(STATUS_BUFFER_OVERFLOW);
  4979. }
  4980. }
  4981. TargetString->Length = (USHORT)resultLength;
  4982. return( STATUS_SUCCESS );
  4983. } // SrvWildcardRename
  4984. VOID
  4985. DispatchToOrphanage(
  4986. IN PQUEUEABLE_BLOCK_HEADER Block
  4987. )
  4988. {
  4989. KIRQL oldIrql;
  4990. ASSERT( Block->BlockHeader.ReferenceCount == 1 );
  4991. ExInterlockedPushEntrySList(
  4992. &SrvBlockOrphanage,
  4993. &Block->SingleListEntry,
  4994. &GLOBAL_SPIN_LOCK(Fsd)
  4995. );
  4996. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  4997. InterlockedIncrement( &SrvResourceOrphanedBlocks );
  4998. SrvFsdQueueExWorkItem(
  4999. &SrvResourceThreadWorkItem,
  5000. &SrvResourceThreadRunning,
  5001. CriticalWorkQueue
  5002. );
  5003. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  5004. return;
  5005. } // DispatchToOrphanage
  5006. NTSTATUS
  5007. SrvIsAllowedOnAdminShare(
  5008. IN PWORK_CONTEXT WorkContext,
  5009. IN PSHARE Share
  5010. )
  5011. /*++
  5012. Routine Description:
  5013. This routine returns STATUS_SUCCESS if the client represented by
  5014. the WorkContext should be allowed to access the Share, if the share
  5015. is an Administrative Disk share.
  5016. Arguments:
  5017. WorkContext - the unit of work
  5018. Share - pointer to a share, possibly an administrative share
  5019. Return Value:
  5020. STATUS_SUCCESS if allowed. Error otherwise.
  5021. --*/
  5022. {
  5023. NTSTATUS status = STATUS_SUCCESS;
  5024. PAGED_CODE();
  5025. if( Share->SpecialShare && Share->ShareType == ShareTypeDisk ) {
  5026. SECURITY_SUBJECT_CONTEXT subjectContext;
  5027. ACCESS_MASK desiredAccess, grantedAccess;
  5028. status = IMPERSONATE( WorkContext );
  5029. if( NT_SUCCESS( status ) ) {
  5030. SeCaptureSubjectContext( &subjectContext );
  5031. if( !SeAccessCheck(
  5032. Share->SecurityDescriptor,
  5033. &subjectContext,
  5034. FALSE,
  5035. SRVSVC_SHARE_CONNECT,
  5036. 0L,
  5037. NULL,
  5038. &SrvShareConnectMapping,
  5039. UserMode,
  5040. &grantedAccess,
  5041. &status
  5042. ) ) {
  5043. //
  5044. // We have a non-administrative user trying to access a file
  5045. // through an administrative share. Can't allow that!
  5046. //
  5047. // Some clients want ACCESS_DENIED to be in the server class
  5048. // instead of the DOS class when it's due to share ACL
  5049. // restrictions. So we need to keep track of why we're
  5050. // returning ACCESS_DENIED.
  5051. //
  5052. WorkContext->ShareAclFailure = TRUE;
  5053. }
  5054. SeReleaseSubjectContext( &subjectContext );
  5055. REVERT();
  5056. }
  5057. }
  5058. return status;
  5059. }
  5060. NTSTATUS
  5061. SrvRetrieveMaximalAccessRightsForUser(
  5062. CtxtHandle *pUserHandle,
  5063. PSECURITY_DESCRIPTOR pSecurityDescriptor,
  5064. PGENERIC_MAPPING pMapping,
  5065. PACCESS_MASK pMaximalAccessRights)
  5066. /*++
  5067. Routine Description:
  5068. This routine retrieves the maximal access rights for this client
  5069. Arguments:
  5070. pUserHandle - the users security handle
  5071. pSecurityDescriptor - the security descriptor
  5072. pMapping - the mapping of access rights
  5073. pMaximalAccessRights - the computed rights
  5074. Return Value:
  5075. Status of operation.
  5076. Notes:
  5077. The srv macros IMPERSONATE is defined in terms of a WORK_CONTEXT. Since
  5078. we desire this routine should be used in all situations even when a
  5079. WORK_CONTEXT is not available the code in SrvImpersonate is duplicated
  5080. over here
  5081. --*/
  5082. {
  5083. NTSTATUS status;
  5084. PPRIVILEGE_SET privileges = NULL;
  5085. SECURITY_SUBJECT_CONTEXT subjectContext;
  5086. if( !IS_VALID_SECURITY_HANDLE (*pUserHandle) ) {
  5087. return STATUS_ACCESS_DENIED;
  5088. }
  5089. status = ImpersonateSecurityContext(
  5090. pUserHandle);
  5091. if( NT_SUCCESS( status ) ) {
  5092. SeCaptureSubjectContext( &subjectContext );
  5093. if (!SeAccessCheck(
  5094. pSecurityDescriptor,
  5095. &subjectContext,
  5096. FALSE, // Locked ?
  5097. MAXIMUM_ALLOWED,
  5098. 0, // PreviousGrantedAccess
  5099. &privileges,
  5100. pMapping,
  5101. UserMode,
  5102. pMaximalAccessRights,
  5103. &status
  5104. ) ) {
  5105. IF_DEBUG(ERRORS) {
  5106. KdPrint((
  5107. "SrvCheckShareFileAccess: Status %x, Desired access %x\n",
  5108. status,
  5109. MAXIMUM_ALLOWED
  5110. ));
  5111. }
  5112. }
  5113. if ( privileges != NULL ) {
  5114. SeFreePrivileges( privileges );
  5115. }
  5116. SeReleaseSubjectContext( &subjectContext );
  5117. REVERT();
  5118. if (status == STATUS_ACCESS_DENIED) {
  5119. *pMaximalAccessRights = 0;
  5120. status = STATUS_SUCCESS;
  5121. }
  5122. }
  5123. return status;
  5124. }
  5125. NTSTATUS
  5126. SrvRetrieveMaximalAccessRights(
  5127. IN OUT PWORK_CONTEXT WorkContext,
  5128. OUT PACCESS_MASK pMaximalAccessRights,
  5129. OUT PACCESS_MASK pGuestMaximalAccessRights)
  5130. /*++
  5131. Routine Description:
  5132. This routine retrieves the maximal access rights for this client as well
  5133. as a guest based upon the ACLs specified for the file
  5134. Arguments:
  5135. WorkContext - pointer to the work context block that contains information
  5136. about the request.
  5137. pMaximalAccessRights - the maximal access rights for this client.
  5138. pGuestMaximalAccessRights - the maximal access rights for a guest
  5139. Return Value:
  5140. Status of operation.
  5141. --*/
  5142. {
  5143. NTSTATUS status;
  5144. BOOLEAN SecurityBufferAllocated = FALSE;
  5145. PRFCB rfcb;
  5146. ULONG lengthNeeded;
  5147. LONG SecurityDescriptorBufferLength;
  5148. PSECURITY_DESCRIPTOR SecurityDescriptorBuffer;
  5149. GENERIC_MAPPING Mapping = {
  5150. FILE_GENERIC_READ,
  5151. FILE_GENERIC_WRITE,
  5152. FILE_GENERIC_EXECUTE,
  5153. FILE_ALL_ACCESS
  5154. };
  5155. rfcb = WorkContext->Rfcb;
  5156. SecurityDescriptorBufferLength = (WorkContext->RequestBuffer->DataLength -
  5157. sizeof(SMB_HEADER) -
  5158. - 4);
  5159. if (SecurityDescriptorBufferLength > 0) {
  5160. SecurityDescriptorBufferLength &= ~3;
  5161. } else {
  5162. SecurityDescriptorBufferLength = 0;
  5163. }
  5164. SecurityDescriptorBuffer = ((PCHAR)WorkContext->RequestBuffer->Buffer +
  5165. WorkContext->RequestBuffer->BufferLength -
  5166. SecurityDescriptorBufferLength);
  5167. status = NtQuerySecurityObject(
  5168. rfcb->Lfcb->FileHandle,
  5169. (DACL_SECURITY_INFORMATION |
  5170. SACL_SECURITY_INFORMATION |
  5171. GROUP_SECURITY_INFORMATION |
  5172. OWNER_SECURITY_INFORMATION),
  5173. SecurityDescriptorBuffer,
  5174. SecurityDescriptorBufferLength,
  5175. &lengthNeeded
  5176. );
  5177. if (status == STATUS_BUFFER_TOO_SMALL) {
  5178. SecurityDescriptorBuffer = ALLOCATE_HEAP(lengthNeeded,PagedPool);
  5179. if (SecurityDescriptorBuffer != NULL) {
  5180. SecurityBufferAllocated = TRUE;
  5181. SecurityDescriptorBufferLength = lengthNeeded;
  5182. status = NtQuerySecurityObject(
  5183. rfcb->Lfcb->FileHandle,
  5184. (DACL_SECURITY_INFORMATION |
  5185. SACL_SECURITY_INFORMATION |
  5186. GROUP_SECURITY_INFORMATION |
  5187. OWNER_SECURITY_INFORMATION),
  5188. SecurityDescriptorBuffer,
  5189. SecurityDescriptorBufferLength,
  5190. &lengthNeeded
  5191. );
  5192. } else {
  5193. status = STATUS_INSUFFICIENT_RESOURCES;
  5194. }
  5195. }
  5196. if (status == STATUS_SUCCESS) {
  5197. status = SrvRetrieveMaximalAccessRightsForUser(
  5198. &WorkContext->SecurityContext->UserHandle,
  5199. SecurityDescriptorBuffer,
  5200. &Mapping,
  5201. pMaximalAccessRights);
  5202. }
  5203. // Extract the GUEST access rights
  5204. if (status == STATUS_SUCCESS) {
  5205. status = SrvRetrieveMaximalAccessRightsForUser(
  5206. &SrvNullSessionToken,
  5207. SecurityDescriptorBuffer,
  5208. &Mapping,
  5209. pGuestMaximalAccessRights);
  5210. }
  5211. if (SecurityBufferAllocated) {
  5212. FREE_HEAP(SecurityDescriptorBuffer);
  5213. }
  5214. return status;
  5215. }
  5216. NTSTATUS
  5217. SrvRetrieveMaximalShareAccessRights(
  5218. IN PWORK_CONTEXT WorkContext,
  5219. OUT PACCESS_MASK pMaximalAccessRights,
  5220. OUT PACCESS_MASK pGuestMaximalAccessRights)
  5221. /*++
  5222. Routine Description:
  5223. This routine retrieves the maximal access rights for this client as well
  5224. as a guest based upon the ACLs specified for the share
  5225. Arguments:
  5226. WorkContext - pointer to the work context block that contains information
  5227. about the request.
  5228. pMaximalAccessRights - the maximal access rights for this client.
  5229. pGuestMaximalAccessRights - the maximal access rights for a guest
  5230. Return Value:
  5231. Status of operation.
  5232. --*/
  5233. {
  5234. NTSTATUS status = STATUS_SUCCESS;
  5235. PSECURITY_DESCRIPTOR securityDescriptor;
  5236. ACCESS_MASK grantedAccess;
  5237. ACCESS_MASK mappedAccess = MAXIMUM_ALLOWED;
  5238. PAGED_CODE( );
  5239. ACQUIRE_LOCK_SHARED( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  5240. securityDescriptor = WorkContext->TreeConnect->Share->FileSecurityDescriptor;
  5241. if (securityDescriptor != NULL) {
  5242. status = SrvRetrieveMaximalAccessRightsForUser(
  5243. &WorkContext->SecurityContext->UserHandle,
  5244. securityDescriptor,
  5245. &SrvFileAccessMapping,
  5246. pMaximalAccessRights);
  5247. if (NT_SUCCESS(status)) {
  5248. // Get the guest rights
  5249. status = SrvRetrieveMaximalAccessRightsForUser(
  5250. &SrvNullSessionToken,
  5251. securityDescriptor,
  5252. &SrvFileAccessMapping,
  5253. pGuestMaximalAccessRights);
  5254. }
  5255. } else {
  5256. // No Share Level ACL, Grant maximum access to both the current client
  5257. // as well as guest
  5258. *pMaximalAccessRights = 0x1ff;
  5259. *pGuestMaximalAccessRights = 0x1ff;
  5260. }
  5261. RELEASE_LOCK( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  5262. return status;
  5263. }
  5264. NTSTATUS
  5265. SrvUpdateMaximalAccessRightsInResponse(
  5266. IN OUT PWORK_CONTEXT WorkContext,
  5267. OUT PSMB_ULONG pMaximalAccessRightsInResponse,
  5268. OUT PSMB_ULONG pGuestMaximalAccessRightsInResponse
  5269. )
  5270. /*++
  5271. Routine Description:
  5272. This routine updates the maximal access rights fields in an extended
  5273. response. This is used to update these fields in various kinds of
  5274. OPEN requests
  5275. Arguments:
  5276. WorkContext - Supplies the address of a Work Context Block
  5277. describing the current request. See smbtypes.h for a more
  5278. complete description of the valid fields.
  5279. pMaximalAccessRightsInResponse - the maximal access rights field in
  5280. the response
  5281. pGuestMaximalAccessRightsInResponse - the guest maximal access rights field
  5282. in the response
  5283. Return Value:
  5284. STATUS_SUCCESS if successful, otherwise appropriate error code
  5285. --*/
  5286. {
  5287. NTSTATUS status;
  5288. ACCESS_MASK OwnerMaximalAccessRights = 0;
  5289. ACCESS_MASK GuestMaximalAccessRights = 0;
  5290. status = SrvRetrieveMaximalAccessRights(
  5291. WorkContext,
  5292. &OwnerMaximalAccessRights,
  5293. &GuestMaximalAccessRights);
  5294. if (status == STATUS_SUCCESS) {
  5295. SmbPutUlong(
  5296. pMaximalAccessRightsInResponse,
  5297. OwnerMaximalAccessRights
  5298. );
  5299. SmbPutUlong(
  5300. pGuestMaximalAccessRightsInResponse,
  5301. GuestMaximalAccessRights
  5302. );
  5303. }
  5304. return status;
  5305. }
  5306. NTSTATUS
  5307. SrvUpdateMaximalShareAccessRightsInResponse(
  5308. IN OUT PWORK_CONTEXT WorkContext,
  5309. OUT PSMB_ULONG pMaximalAccessRightsInResponse,
  5310. OUT PSMB_ULONG pGuestMaximalAccessRightsInResponse
  5311. )
  5312. /*++
  5313. Routine Description:
  5314. This routine updates the maximal access rights fields in an extended
  5315. response. This is used to update these fields in various kinds of
  5316. TREE_CONNECT requests
  5317. Arguments:
  5318. WorkContext - Supplies the address of a Work Context Block
  5319. describing the current request. See smbtypes.h for a more
  5320. complete description of the valid fields.
  5321. pMaximalAccessRightsInResponse - the maximal access rights field in
  5322. the response
  5323. pGuestMaximalAccessRightsInResponse - the guest maximal access rights field
  5324. in the response
  5325. Return Value:
  5326. STATUS_SUCCESS if successful, otherwise appropriate error code
  5327. --*/
  5328. {
  5329. NTSTATUS status;
  5330. ACCESS_MASK OwnerMaximalAccessRights = 0;
  5331. ACCESS_MASK GuestMaximalAccessRights = 0;
  5332. status = SrvRetrieveMaximalShareAccessRights(
  5333. WorkContext,
  5334. &OwnerMaximalAccessRights,
  5335. &GuestMaximalAccessRights);
  5336. if (status == STATUS_SUCCESS) {
  5337. SmbPutUlong(
  5338. pMaximalAccessRightsInResponse,
  5339. OwnerMaximalAccessRights
  5340. );
  5341. SmbPutUlong(
  5342. pGuestMaximalAccessRightsInResponse,
  5343. GuestMaximalAccessRights
  5344. );
  5345. }
  5346. return status;
  5347. }
  5348. VOID SRVFASTCALL
  5349. RestartConsumeSmbData(
  5350. IN OUT PWORK_CONTEXT WorkContext
  5351. )
  5352. /*++
  5353. Routine Description:
  5354. This is the restart routine for 'SrvConsumeSmbData'. We need to see if we
  5355. drained the current message from the transport. If we have, then we send
  5356. the response SMB to the client. If we have not, we keep going.
  5357. --*/
  5358. {
  5359. PIRP irp = WorkContext->Irp;
  5360. PIO_STACK_LOCATION irpSp;
  5361. PTDI_REQUEST_KERNEL_RECEIVE parameters;
  5362. ASSERT( WorkContext->LargeIndication );
  5363. //
  5364. // Check to see if we are done. If so, send the response to the client
  5365. //
  5366. if( irp->Cancel ||
  5367. NT_SUCCESS( irp->IoStatus.Status ) ||
  5368. irp->IoStatus.Status != STATUS_BUFFER_OVERFLOW ) {
  5369. RtlZeroMemory( WorkContext->ResponseHeader + 1, sizeof( SMB_PARAMS ) );
  5370. WorkContext->ResponseBuffer->DataLength = sizeof( SMB_HEADER ) + sizeof( SMB_PARAMS );
  5371. WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
  5372. //
  5373. // Send the data!
  5374. //
  5375. SRV_START_SEND_2(
  5376. WorkContext,
  5377. SrvFsdRestartSmbAtSendCompletion,
  5378. NULL,
  5379. NULL
  5380. );
  5381. return;
  5382. }
  5383. //
  5384. // Not done yet. Consume more data!
  5385. //
  5386. WorkContext->Connection->ReceivePending = FALSE;
  5387. irp->Tail.Overlay.OriginalFileObject = NULL;
  5388. irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
  5389. DEBUG irp->RequestorMode = KernelMode;
  5390. //
  5391. // Get a pointer to the next stack location. This one is used to
  5392. // hold the parameters for the device I/O control request.
  5393. //
  5394. irpSp = IoGetNextIrpStackLocation( irp );
  5395. //
  5396. // Set up the completion routine
  5397. //
  5398. IoSetCompletionRoutine(
  5399. irp,
  5400. SrvFsdIoCompletionRoutine,
  5401. WorkContext,
  5402. TRUE,
  5403. TRUE,
  5404. TRUE
  5405. );
  5406. WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
  5407. WorkContext->FspRestartRoutine = RestartConsumeSmbData;
  5408. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  5409. irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
  5410. irpSp->FileObject = WorkContext->Connection->FileObject;
  5411. irpSp->DeviceObject = WorkContext->Connection->DeviceObject;
  5412. irpSp->Flags = 0;
  5413. parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
  5414. parameters->ReceiveLength = WorkContext->ResponseBuffer->BufferLength - sizeof( SMB_HEADER );
  5415. parameters->ReceiveFlags = 0;
  5416. //
  5417. // Set the buffer's partial mdl to point just after the header for this
  5418. // WriteAndX SMB. We need to preserve the header to make it easier to send
  5419. // back the response.
  5420. //
  5421. IoBuildPartialMdl(
  5422. WorkContext->RequestBuffer->Mdl,
  5423. WorkContext->RequestBuffer->PartialMdl,
  5424. WorkContext->ResponseHeader + 1,
  5425. parameters->ReceiveLength
  5426. );
  5427. irp->MdlAddress = WorkContext->RequestBuffer->PartialMdl;
  5428. irp->AssociatedIrp.SystemBuffer = NULL;
  5429. irp->Flags = (ULONG)IRP_BUFFERED_IO; // ???
  5430. (VOID)IoCallDriver( irpSp->DeviceObject, irp );
  5431. }
  5432. SMB_PROCESSOR_RETURN_TYPE
  5433. SrvConsumeSmbData(
  5434. IN OUT PWORK_CONTEXT WorkContext
  5435. )
  5436. /*++
  5437. Routine Description:
  5438. This routine handles the case where we have received a LargeIndication
  5439. from a client (i.e. received SMB exceeds the negotiated buffer size). Some
  5440. error has occurred prior to consuming the entire message. The SMB header is
  5441. already formatted for the response, but we need to consume the rest of the
  5442. incoming data and then send the response.
  5443. --*/
  5444. {
  5445. if( WorkContext->LargeIndication == FALSE ) {
  5446. return SmbStatusSendResponse;
  5447. }
  5448. IF_DEBUG( ERRORS ) {
  5449. KdPrint(("SRV: SrvConsumeSmbData, BytesAvailable = %u\n",
  5450. WorkContext->BytesAvailable ));
  5451. }
  5452. WorkContext->Irp->Cancel = FALSE;
  5453. WorkContext->Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
  5454. RestartConsumeSmbData( WorkContext );
  5455. return SmbStatusInProgress;
  5456. }
  5457. BOOLEAN
  5458. SrvIsDottedQuadAddress(
  5459. IN PUNICODE_STRING ServerName
  5460. )
  5461. /*++
  5462. Routine Description:
  5463. Return true if the ServerName appears to be a dotted quad address along the lines
  5464. of xxx.yyy.zzz.qqq
  5465. False otherwise
  5466. --*/
  5467. {
  5468. PWCHAR p, ep;
  5469. DWORD numberOfDots = 0;
  5470. DWORD numberOfDigits = 0;
  5471. PAGED_CODE();
  5472. //
  5473. // If the address is all digits and contains 3 dots, then we'll figure that it's
  5474. // a dotted quad address.
  5475. //
  5476. ep = &ServerName->Buffer[ ServerName->Length / sizeof( WCHAR ) ];
  5477. for( p = ServerName->Buffer; p < ep; p++ ) {
  5478. if( *p == L'.' ) {
  5479. if( ++numberOfDots > 3 || numberOfDigits == 0 ) {
  5480. return FALSE;
  5481. }
  5482. numberOfDigits = 0;
  5483. } else if( (*p < L'0' || *p > L'9') || ++numberOfDigits > 3 ) {
  5484. return FALSE;
  5485. }
  5486. }
  5487. return (numberOfDots == 3) && (numberOfDigits <= 3);
  5488. }
  5489. BOOLEAN
  5490. SrvIsLocalHost(
  5491. IN PUNICODE_STRING ServerName
  5492. )
  5493. /*++
  5494. Routine Description:
  5495. Return true if the ServerName represents the loopback connection
  5496. False otherwise
  5497. --*/
  5498. {
  5499. UNICODE_STRING LocalHost = { 18, 18, L"localhost" };
  5500. return !RtlCompareUnicodeString( ServerName, &LocalHost, TRUE );
  5501. }
  5502. PSESSION
  5503. SrvFindSession(
  5504. IN PCONNECTION connection,
  5505. IN USHORT Uid
  5506. )
  5507. {
  5508. PTABLE_HEADER tableHeader;
  5509. PSESSION session;
  5510. USHORT index;
  5511. USHORT sequence;
  5512. PAGED_CODE( );
  5513. //
  5514. // Initialize local variables: obtain the connection block address
  5515. // and crack the UID into its components.
  5516. //
  5517. index = UID_INDEX( Uid );
  5518. sequence = UID_SEQUENCE( Uid );
  5519. //
  5520. // Acquire the connection's session lock.
  5521. //
  5522. ACQUIRE_LOCK( &connection->Lock );
  5523. //
  5524. // If this is a down-level (LAN Man 1.0 or earlier) client, than
  5525. // we will not receive a UID, and there will only be one session
  5526. // per connection. Reference that session.
  5527. //
  5528. tableHeader = &connection->PagedConnection->SessionTable;
  5529. if (!DIALECT_HONORS_UID(connection->SmbDialect) ) {
  5530. session = tableHeader->Table[0].Owner;
  5531. } else if ( (index < tableHeader->TableSize) &&
  5532. (tableHeader->Table[index].Owner != NULL) &&
  5533. (tableHeader->Table[index].SequenceNumber == sequence) ) {
  5534. //
  5535. // The UID is in range, is in use, and has the correct sequence
  5536. // number.
  5537. //
  5538. session = tableHeader->Table[index].Owner;
  5539. } else {
  5540. //
  5541. // The UID is invalid for this connection.
  5542. //
  5543. session = NULL;
  5544. }
  5545. if( session && GET_BLOCK_STATE(session) == BlockStateActive )
  5546. {
  5547. SrvReferenceSession( session );
  5548. }
  5549. else
  5550. {
  5551. session = NULL;
  5552. }
  5553. RELEASE_LOCK( &connection->Lock );
  5554. return session;
  5555. }
  5556. NTSTATUS
  5557. DuplicateSystemHandle(PKPROCESS InitialSystemProcess,
  5558. HANDLE FileHandle,
  5559. HANDLE *NewFileHandle)
  5560. {
  5561. HANDLE SystemProcess;
  5562. NTSTATUS Status;
  5563. if (NewFileHandle == NULL) {
  5564. return STATUS_INVALID_PARAMETER;
  5565. }
  5566. Status = ObOpenObjectByPointer(
  5567. InitialSystemProcess,
  5568. 0,
  5569. NULL,
  5570. 0,
  5571. NULL, //PsProcessType,
  5572. KernelMode,
  5573. &SystemProcess
  5574. );
  5575. if ( !NT_SUCCESS(Status)) {
  5576. return Status;
  5577. }
  5578. Status = ZwDuplicateObject(
  5579. SystemProcess,
  5580. FileHandle,
  5581. NtCurrentProcess(),
  5582. NewFileHandle,
  5583. 0,
  5584. 0,
  5585. DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS
  5586. );
  5587. // close system process
  5588. ZwClose (SystemProcess);
  5589. return Status;
  5590. }
  5591. NTSTATUS
  5592. SrvCreateHandleWithOptions(PUNICODE_STRING FileName,
  5593. HANDLE FileHandle,
  5594. ULONG Options,
  5595. ACCESS_MASK Access,
  5596. ULONG Share,
  5597. BOOLEAN KernelHandle,
  5598. HANDLE *Handle)
  5599. {
  5600. OBJECT_ATTRIBUTES ObjectAttributes;
  5601. IO_STATUS_BLOCK IoStatusBlock;
  5602. NTSTATUS err;
  5603. UNICODE_STRING cwspath;
  5604. WCHAR *name;
  5605. WCHAR *prefix = L"\\??\\";
  5606. ULONG length;
  5607. length = (wcslen(prefix) * sizeof(WCHAR)) + FileName->MaximumLength + sizeof(WCHAR);
  5608. name = ALLOCATE_HEAP( length, BlockTypeMisc );
  5609. if (name == NULL) {
  5610. return STATUS_NO_MEMORY;
  5611. }
  5612. cwspath.Buffer = name;
  5613. cwspath.MaximumLength = (USHORT)length;
  5614. cwspath.Length = 0;
  5615. RtlAppendUnicodeToString(&cwspath, prefix);
  5616. RtlAppendUnicodeStringToString(&cwspath, FileName);
  5617. InitializeObjectAttributes(&ObjectAttributes,
  5618. &cwspath,
  5619. OBJ_CASE_INSENSITIVE |
  5620. ((KernelHandle == TRUE) ? OBJ_KERNEL_HANDLE : 0),
  5621. NULL,
  5622. 0);
  5623. err = IoCreateFileSpecifyDeviceObjectHint (Handle,
  5624. Access,
  5625. &ObjectAttributes,
  5626. &IoStatusBlock,
  5627. NULL,
  5628. 0L,
  5629. Share,
  5630. FILE_OPEN,
  5631. Options,
  5632. NULL,
  5633. 0,
  5634. CreateFileTypeNone,
  5635. NULL,
  5636. IO_IGNORE_SHARE_ACCESS_CHECK |
  5637. IO_NO_PARAMETER_CHECKING,
  5638. NULL);
  5639. FREE_HEAP(name);
  5640. return err;
  5641. }
  5642. NTSTATUS
  5643. SrvProcessHandleDuplicateRequest(
  5644. IN PIRP Irp,
  5645. IN PIO_STACK_LOCATION IrpSp,
  5646. IN PSRV_REQUEST_HANDLE_DUP pDupRequest,
  5647. IN OUT PSRV_RESPONSE_HANDLE_DUP pOutHandleDup
  5648. )
  5649. {
  5650. PRFCB rfcb;
  5651. PSESSION session;
  5652. NTSTATUS status;
  5653. PAGED_CODE( );
  5654. //
  5655. // Try to find a file that matches the file ID. Only an exact
  5656. // match will work.
  5657. //
  5658. rfcb = SrvFindEntryInOrderedList(
  5659. &SrvRfcbList,
  5660. NULL,
  5661. NULL,
  5662. (ULONG)(pDupRequest->Key.ResumeKey),
  5663. TRUE,
  5664. NULL
  5665. );
  5666. if ( (rfcb == NULL) ||
  5667. ( (UINT64)(rfcb->PagedRfcb->OpenTime.QuadPart) != pDupRequest->Key.Timestamp) ) {
  5668. return STATUS_OBJECT_NAME_NOT_FOUND;
  5669. }
  5670. //
  5671. // Don't do LWIO on TIMEWARP files
  5672. //
  5673. if ( rfcb->Mfcb->SnapShotTime.QuadPart != 0 )
  5674. {
  5675. SrvDereferenceRfcb( rfcb );
  5676. return STATUS_NOT_SUPPORTED;
  5677. }
  5678. //
  5679. // Validate that the SID matches that used to open the file
  5680. //
  5681. session = SrvFindSession( rfcb->Connection, rfcb->Uid );
  5682. if( !session )
  5683. {
  5684. status = STATUS_OBJECT_NAME_NOT_FOUND;
  5685. }
  5686. else
  5687. {
  5688. pOutHandleDup->LockKey = rfcb->ShiftedFid | SmbGetUshort(&pDupRequest->Key.Pid);
  5689. status = SrvImpersonateSession( session );
  5690. if( NT_SUCCESS(status) )
  5691. {
  5692. if (IoGetCurrentProcess() != SrvServerProcess) {
  5693. status = DuplicateSystemHandle(SrvServerProcess,
  5694. rfcb->Lfcb->FileHandle,
  5695. &pOutHandleDup->hFile);
  5696. if (pDupRequest->Options != 0 && status == STATUS_SUCCESS) {
  5697. HANDLE tmp;
  5698. if (SrvCreateHandleWithOptions(&rfcb->Lfcb->Mfcb->FileName,
  5699. pOutHandleDup->hFile,
  5700. pDupRequest->Options,
  5701. rfcb->GrantedAccess,
  5702. rfcb->ShareAccess,
  5703. FALSE,
  5704. &tmp) == STATUS_SUCCESS) {
  5705. ZwClose(pOutHandleDup->hFile);
  5706. pOutHandleDup->hFile = tmp;
  5707. }
  5708. }
  5709. } else {
  5710. status = SrvCreateHandleWithOptions(
  5711. &rfcb->Lfcb->Mfcb->FileName,
  5712. rfcb->Lfcb->FileHandle,
  5713. pDupRequest->Options,
  5714. rfcb->GrantedAccess,
  5715. rfcb->ShareAccess,
  5716. TRUE,
  5717. &pOutHandleDup->hFile);
  5718. }
  5719. SrvRevert();
  5720. }
  5721. SrvDereferenceSession( session );
  5722. }
  5723. SrvDereferenceRfcb( rfcb );
  5724. return status;
  5725. }