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.

5864 lines
161 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. userdb.cxx
  7. This module manages the user database for the FTPD Service.
  8. Functions exported by this module:
  9. DisconnectUser()
  10. DisconnectUsersWithNoAccess()
  11. EnumerateUsers()
  12. USER_DATA::USER_DATA()
  13. USER_DATA::Reset()
  14. USER_DATA::~USER_DATA()
  15. USER_DATA::Cleanup()
  16. USER_DATA::ProcessAsyncIoCompletion()
  17. USER_DATA::ReInitializeForNewUser()
  18. USER_DATA::ReadCommand()
  19. USER_DATA::DisconnectUserWithError()
  20. USER_DATA::SendMultilineMessage()
  21. USER_DATA::SendDirectoryAnnotation()
  22. USER_DATA::GetFileSize();
  23. ProcessUserAsyncIoCompletion()
  24. FILE HISTORY:
  25. KeithMo 07-Mar-1993 Created.
  26. MuraliK March-May, 1995
  27. Adding support for Async Io/Transfers
  28. + new USER_DATA class functions defined.
  29. + oob_inline enabled; ReadCommand() issued after
  30. data socket is established.
  31. + added member functions for common operations
  32. + added ProcessAsyncIoCompletion()
  33. + added Establish & Destroy of Data connection
  34. MuraliK 26-July-1995 Added Allocation caching of client conns.
  35. Terryk 18-Sep-1996 Added GetFileSize
  36. AMallet Sep 1998 Added support for AcceptEx() of PASV data connections
  37. */
  38. #include "ftpdp.hxx"
  39. #include <security.h>
  40. # include "tsunami.hxx"
  41. #include <timer.h>
  42. # include "auxctrs.h"
  43. # include <mbstring.h>
  44. #include "acptctxt.hxx"
  45. #define FIRST_TELNET_COMMAND 240
  46. #define TELNET_DM_COMMAND 242
  47. #define TELNET_IP_COMMAND 244
  48. #define TELNET_SB_CODE 250
  49. #define TELNET_SB_CODE_MIN 251
  50. #define TELNET_SB_CODE_MAX 254
  51. #define TELNET_IAC_CODE 255
  52. # define MAX_FILE_SIZE_SPEC ( 32)
  53. //
  54. // Private globals.
  55. //
  56. // This will be expanded into
  57. // static const char PSZ_StringName[] = ActualString;
  58. //
  59. # define ConstantStringsForThisModule() \
  60. CStrM( ANONYMOUS_NAME, "Anonymous") /* must match engine.cxx */ \
  61. CStrM( DEFAULT_SUB_DIRECTORY, "Default" ) \
  62. CStrM( SENT_VERB, "sent" ) \
  63. CStrM( CONNECTION_CLOSED_VERB, "closed" ) \
  64. CStrM( FILE_ERROR, "%s: %s" ) \
  65. CStrM( TRANSFER_COMPLETE, "Transfer complete." ) \
  66. CStrM( TRANSFER_ABORTED, "Connection closed; transfer aborted." ) \
  67. CStrM( TRANSFER_STARTING, "Data connection already open; Transfer starting." ) \
  68. CStrM( INSUFFICIENT_RESOURCES, "Insufficient system resources." ) \
  69. CStrM( TOO_MANY_PASV_USERS, "Too many passive-mode users." ) \
  70. CStrM( OPENING_DATA_CONNECTION, "Opening %s mode data connection for %s%s." ) \
  71. CStrM( CANNOT_OPEN_DATA_CONNECTION, "Can't open data connection." ) \
  72. CStrM( COMMAND_TOO_LONG, "Command was too long" ) \
  73. CStrM( LOCALUSER_DIR, "LocalUser\\" ) \
  74. CStrM( ANONYMOUS_DIR, "Public" ) \
  75. CStrM( DUMMY_END, "DummyMsg. Add string before this one")
  76. //
  77. // Generate the strings ( these are private globals of this module).
  78. //
  79. # define CStrM( StringName, ActualString) \
  80. static const char PSZ_ ## StringName[] = ActualString ;
  81. ConstantStringsForThisModule()
  82. # undef CStrM
  83. static DWORD p_NextUserId = 0; // Next available user id.
  84. //
  85. // Private prototypes.
  86. //
  87. DWORD
  88. UserpGetNextId(
  89. VOID
  90. );
  91. inline VOID
  92. StopControlRead( IN LPUSER_DATA pUserData)
  93. /*++
  94. Stops control read operation, if one is proceeding.
  95. Resets the CONTROL_READ flag as well as decrements ref count in user data.
  96. --*/
  97. {
  98. if ( TEST_UF( pUserData, CONTROL_READ)) {
  99. if ( InterlockedDecrement( &pUserData->m_nControlRead) < 0 ) {
  100. DBGPRINTF(( DBG_CONTEXT,
  101. "StopControLRead: no read active!!!\n"));
  102. DBG_ASSERT( FALSE);
  103. }
  104. DBG_REQUIRE( pUserData->DeReference() > 0);
  105. CLEAR_UF( pUserData, CONTROL_READ);
  106. }
  107. } // StopControlRead()
  108. BOOL
  109. FilterTelnetCommands(IN CHAR * pszLine, IN DWORD cchLine,
  110. IN LPBOOL pfLineEnded,
  111. IN LPDWORD pcchRequestRecvd)
  112. /*++
  113. Filters out the Telnet commands and
  114. terminates the command line with linefeed.
  115. Also this function filters out the out of band data.
  116. This works similar to the Sockutil.cxx::DiscardOutOfBandData().
  117. We scan for the pattern "ABOR\r\n" and
  118. set the OOB_DATA flag if it is present.
  119. Arguments:
  120. pszLine pointer to null terminated string containing the input data.
  121. cchLine count of characters of data received
  122. pfLineEnded pointer to Boolean flag which is set to true if complete
  123. line has been received.
  124. pcchRequestRecvd pointer to DWORD which on return contains the number
  125. of bytes received.
  126. Returns:
  127. TRUE if the filtering is successful without any out of band abort request.
  128. FALSE if there was any abort request in the input.
  129. --*/
  130. {
  131. BOOL fDontAbort = TRUE;
  132. BOOL fStateTelnetCmd = FALSE;
  133. BOOL fStateTelnetSB = FALSE;
  134. BOOL fFoundTelnetIP = FALSE;
  135. CHAR * pszSrc;
  136. CHAR * pszDst;
  137. LPCSTR pszAbort = "ABOR\r\n";
  138. LPCSTR pszNext = pszAbort;
  139. DBG_ASSERT( pszLine != NULL && cchLine > 0 &&
  140. pfLineEnded != NULL && pcchRequestRecvd != NULL);
  141. *pfLineEnded = FALSE;
  142. for( pszSrc = pszDst = pszLine; pszSrc < pszLine + cchLine && *pszSrc;
  143. pszSrc++) {
  144. CHAR ch = *pszSrc;
  145. BYTE uch = (BYTE)ch;
  146. //
  147. // Filter out TELNET commands. these are of the form: IAC <cmd> or
  148. // IAC SB <op> (IAC = 255, SB = 250, op 251..254, cmd > 240)
  149. //
  150. if( fStateTelnetCmd ) {
  151. //
  152. // we are in a Telbent command sequence
  153. //
  154. fStateTelnetCmd = FALSE;
  155. DBG_ASSERT( uch >= FIRST_TELNET_COMMAND );
  156. if( fStateTelnetSB ) {
  157. //
  158. // we are in a Telnet subsequence command
  159. //
  160. fStateTelnetSB = FALSE;
  161. DBG_ASSERT( (uch >= TELNET_SB_CODE_MIN) &&
  162. (uch <= TELNET_SB_CODE_MAX) );
  163. if( uch >= FIRST_TELNET_COMMAND ) {
  164. //
  165. // consider it a valid Telnet command, as long as it's in
  166. // the Telnet range. Filter this char out.
  167. //
  168. continue;
  169. }
  170. //
  171. // this is a TELNET protocol error, we'll ignore it.
  172. //
  173. // fall through with this char
  174. //
  175. } else if( uch == TELNET_SB_CODE ) {
  176. //
  177. // enter Telnet subsequense command state
  178. //
  179. fStateTelnetCmd = fStateTelnetSB = TRUE;
  180. continue;
  181. } else if( uch == TELNET_IAC_CODE ) {
  182. //
  183. // this is an escape sequence for a 255 data byte
  184. //
  185. // let it fall through
  186. //
  187. } else if ( uch == TELNET_IP_COMMAND ) {
  188. //
  189. // remember this, it is the first in a SYNCH sequence
  190. //
  191. fFoundTelnetIP = TRUE;
  192. continue;
  193. } else if ( uch == TELNET_DM_COMMAND ) {
  194. //
  195. // if in a SYNCH sequence, this resets the input stream
  196. //
  197. if( fFoundTelnetIP ) {
  198. pszDst = pszLine;
  199. fFoundTelnetIP = FALSE; // completed the SYNCH sequence
  200. }
  201. continue;
  202. } else {
  203. //
  204. // we expect a Telnet command code here. filter it out
  205. //
  206. DBG_ASSERT( uch >= FIRST_TELNET_COMMAND );
  207. if ( uch >= FIRST_TELNET_COMMAND ) {
  208. continue;
  209. }
  210. //
  211. // this is a TELNET protocol error, we'll ignore it.
  212. //
  213. // fall through with this char
  214. //
  215. }
  216. } else if( uch == TELNET_IAC_CODE ) {
  217. //
  218. // entering Telnet command parsing state
  219. //
  220. fStateTelnetCmd = TRUE;
  221. continue;
  222. } else if( uch == TELNET_DM_COMMAND ) {
  223. //
  224. // FTP.EXE on Win2k is sending an unexpected SYNCH sequence: DM, IAC, IP. See if this is it.
  225. //
  226. if( ( pszSrc == pszLine ) &&
  227. ( cchLine >= 3 ) &&
  228. ( (UINT)*(pszSrc+1) == TELNET_IAC_CODE ) &&
  229. ( (UINT)*(pszSrc+2) == TELNET_DM_COMMAND ) ) {
  230. //
  231. // just filter the sequence out
  232. //
  233. pszSrc += 2;
  234. continue;
  235. } else if( fFoundTelnetIP ) {
  236. //
  237. // or, it could be a single byte URGENT notification in the telnet Sync
  238. //
  239. pszDst = pszLine;
  240. fFoundTelnetIP = FALSE; // completed the SYNCH sequence
  241. continue;
  242. }
  243. }
  244. //
  245. // if we have seen a Telnet IP, then skip everything up to a DM
  246. //
  247. if (fFoundTelnetIP) {
  248. continue;
  249. }
  250. //
  251. // try matching ABOR\r\n
  252. //
  253. if ( *pszNext != ch) {
  254. // the pattern match failed. reset to start at the beginning.
  255. pszNext = pszAbort;
  256. }
  257. if ( *pszNext == ch) {
  258. // pattern match at this character. move forward
  259. pszNext++;
  260. if ( *pszNext == '\0') { // end of string==> all matched.
  261. // only consider this an OOB Abort if at the beginning of
  262. // a (reset) line
  263. if( (pszDst - pszLine + 2) == (pszNext - pszAbort) ) {
  264. fDontAbort = FALSE;
  265. }
  266. pszNext = pszAbort;
  267. }
  268. }
  269. //
  270. // don't copy <CR> and <LF> to the output
  271. //
  272. if ( (ch != '\r') && ( ch != '\n')) {
  273. *pszDst++ = ch;
  274. } else if ( ch == '\n') {
  275. // terminate at the linefeed
  276. *pfLineEnded = TRUE;
  277. break;
  278. }
  279. } // for
  280. *pszDst = '\0';
  281. *pcchRequestRecvd = DIFF(pszDst - pszLine);
  282. DBG_ASSERT( *pcchRequestRecvd <= cchLine);
  283. return (fDontAbort);
  284. } // FilterTelnetCommands()
  285. //
  286. // Public functions.
  287. //
  288. USER_DATA::USER_DATA(
  289. IN FTP_SERVER_INSTANCE *pInstance
  290. )
  291. /*++
  292. This function creates a new UserData object for the information
  293. required to process requests from a new User connection ( FTP).
  294. Arguments:
  295. sControl Socket used for control channel in FTP connection
  296. clientIpAddress strcuture containing the client Ip address
  297. Returns:
  298. a newly constructed USER_DATA object.
  299. Check IsValid() to ensure the object was properly created.
  300. NOTE:
  301. This function is to be used for dummy creation of the object so
  302. allocation cacher can use this object.
  303. Fields are randomly initialized. Reset() will initialize them properly.
  304. However when a new effective USER_DATA object is needed, after allocation
  305. one can call USER_DATA::Reset() to initialize all vars.
  306. --*/
  307. :
  308. m_References ( 0),
  309. m_ActiveRefAdded ( 0),
  310. m_cchRecvBuffer ( 0),
  311. m_licbRecvd ( 0),
  312. m_cchPartialReqRecvd ( 0),
  313. m_pOpenFileInfo ( NULL),
  314. Flags ( 0),
  315. UserToken ( NULL),
  316. m_UserId ( 0),
  317. DataPort ( 0),
  318. UserState ( UserStateEmbryonic),
  319. m_fOnActiveConnectionList ( FALSE),
  320. m_AioControlConnection ( ProcessUserAsyncIoCompletion),
  321. m_AioDataConnection ( ProcessUserAsyncIoCompletion),
  322. m_AioDataFile ( ProcessUserAsyncIoCompletion),
  323. m_AsyncTransferBuff ( NULL),
  324. m_sPassiveDataListen ( INVALID_SOCKET),
  325. CurrentDirHandle ( INVALID_HANDLE_VALUE),
  326. RenameSourceBuffer ( NULL),
  327. m_fCleanedup ( TRUE),
  328. m_pMetaData ( NULL),
  329. m_pInstance ( pInstance ),
  330. m_acCheck ( AC_NOT_CHECKED ),
  331. m_fNeedDnsCheck ( FALSE ),
  332. m_dwLastReplyCode ( 0 ),
  333. m_fHavePASVConn ( FALSE ),
  334. m_fWaitingForPASVConn ( FALSE ),
  335. m_fFakeIOCompletion ( FALSE ),
  336. m_pszCmd ( NULL ),
  337. m_dwRootDirStatus ( ERROR_ACCESS_DENIED ),
  338. m_pAdioReq ( NULL ),
  339. m_hPASVAcceptEvent ( NULL )
  340. #if DBG
  341. ,m_RefTraceLog( NULL )
  342. #endif
  343. {
  344. DWORD dwTimeout = m_pInstance->QueryConnectionTimeout();
  345. INITIALIZE_CRITICAL_SECTION( &m_UserLock );
  346. //
  347. // Setup the structure signature. Until Reset(), it is invalid.
  348. //
  349. KILL_USER_SIG( this );
  350. m_AioControlConnection.SetAioInformation( this, dwTimeout);
  351. m_AioDataConnection.SetAioInformation( this, dwTimeout);
  352. m_AioDataFile.SetAioInformation( this, dwTimeout);
  353. InitializeListHead( &ListEntry);
  354. ZeroMemory( m_recvBuffer, sizeof(m_recvBuffer));
  355. IF_DEBUG( USER_DATABASE ) {
  356. DBGPRINTF(( DBG_CONTEXT,
  357. "user_data object created @ %08lX.\n",
  358. this));
  359. }
  360. m_licbSent = 0;
  361. #if DBG
  362. m_RefTraceLog = CreateRefTraceLog( TRACE_LOG_SIZE, 0 );
  363. #endif
  364. FakeIOTimes = 0;
  365. } // USER_DATA::USER_DATA()
  366. USER_DATA::~USER_DATA(VOID)
  367. {
  368. Cleanup();
  369. if( RenameSourceBuffer != NULL ) {
  370. TCP_FREE( RenameSourceBuffer);
  371. RenameSourceBuffer = NULL;
  372. }
  373. if ( m_pszCmd != NULL )
  374. {
  375. TCP_FREE( m_pszCmd );
  376. m_pszCmd = NULL;
  377. }
  378. if ( m_hPASVAcceptEvent != NULL )
  379. {
  380. RemovePASVAcceptEvent( TRUE );
  381. }
  382. if ( m_pInstance != NULL ) {
  383. m_pInstance->DecrementCurrentConnections();
  384. m_pInstance->Dereference();
  385. m_pInstance = NULL;
  386. }
  387. #if DBG
  388. if( m_RefTraceLog != NULL ) {
  389. DestroyRefTraceLog( m_RefTraceLog );
  390. }
  391. #endif
  392. DeleteCriticalSection( &m_UserLock );
  393. } // USER_DATA::~USER_DATA()
  394. BOOL
  395. USER_DATA::Reset(IN SOCKET sControl,
  396. IN PVOID EndpointObject,
  397. IN IN_ADDR clientIpAddress,
  398. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  399. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  400. IN PVOID pvInitialRequest /* = NULL */ ,
  401. IN DWORD cbWritten /* = 0 */ ,
  402. IN AC_RESULT acCheck
  403. )
  404. {
  405. BOOL fReturn = TRUE;
  406. //
  407. // Setup the structure signature.
  408. //
  409. INIT_USER_SIG( this );
  410. m_References = 1; // set to 1 to prevent immediate deletion.
  411. m_ActiveRefAdded= 1;
  412. m_fCleanedup = FALSE;
  413. Flags = m_pInstance->QueryUserFlags();
  414. UserState = UserStateEmbryonic;
  415. #if DBG
  416. if( m_RefTraceLog != NULL ) {
  417. ResetTraceLog( m_RefTraceLog );
  418. }
  419. #endif
  420. m_pOpenFileInfo = NULL;
  421. UserToken = NULL;
  422. if ( m_pMetaData != NULL )
  423. {
  424. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  425. m_pMetaData = NULL;
  426. }
  427. m_UserId = UserpGetNextId();
  428. m_xferType = XferTypeAscii;
  429. m_xferMode = XferModeStream;
  430. m_msStartingTime= 0;
  431. m_acCheck = acCheck;
  432. m_fNeedDnsCheck = FALSE;
  433. m_dwLastReplyCode = 0;
  434. HostIpAddress = clientIpAddress;
  435. DataIpAddress = clientIpAddress;
  436. m_licbRecvd = 0;
  437. m_cchRecvBuffer = sizeof( m_recvBuffer) - sizeof(m_recvBuffer[0]);
  438. m_cchPartialReqRecvd = 0;
  439. CurrentDirHandle = INVALID_HANDLE_VALUE;
  440. RenameSourceBuffer = NULL;
  441. m_TimeAtConnection = GetCurrentTimeInSeconds();
  442. m_TimeAtLastAccess = m_TimeAtConnection;
  443. m_pvInitialRequest = pvInitialRequest;
  444. m_cbInitialRequest = cbWritten;
  445. //
  446. // clean up the stuff needed to deal async with PASV command
  447. //
  448. if ( m_pszCmd )
  449. {
  450. TCP_FREE( m_pszCmd );
  451. m_pszCmd = NULL;
  452. }
  453. if ( m_hPASVAcceptEvent )
  454. {
  455. RemovePASVAcceptEvent( TRUE );
  456. }
  457. CleanupPASVFlags();
  458. // set up the async io contexts
  459. m_AioControlConnection.SetNewSocket( sControl, pAtqContext, EndpointObject );
  460. m_AioDataConnection.SetNewSocket(INVALID_SOCKET);
  461. m_AioDataFile.SetNewSocket(INVALID_SOCKET);
  462. m_sPassiveDataListen = ( INVALID_SOCKET);
  463. if (m_AsyncTransferBuff) {
  464. TCP_FREE( m_AsyncTransferBuff);
  465. m_AsyncTransferBuff = NULL;
  466. }
  467. m_rgchFile[0] = '\0';
  468. m_szUserName[0] = '\0'; // no user name available yet.
  469. m_szCurrentDirectory.Reset(); // initialize to no virtual dir.
  470. m_dwRootDirStatus = ERROR_ACCESS_DENIED;
  471. m_pAdioReq = NULL;
  472. m_strRootDir.Reset(); // no root directory known yet
  473. m_licbSent = 0;
  474. m_pInstance->QueryStatsObj()->IncrCurrentConnections();
  475. m_liCurrentOffset = 0;
  476. m_liNextOffset = 0;
  477. //
  478. // get the local Ip address
  479. //
  480. if ( psockAddrLocal != NULL) {
  481. LocalIpAddress = psockAddrLocal->sin_addr;
  482. LocalIpPort = psockAddrLocal->sin_port;
  483. } else {
  484. SOCKADDR_IN saddrLocal;
  485. INT cbLocal;
  486. cbLocal = sizeof( saddrLocal);
  487. if ( getsockname( sControl, (SOCKADDR *) &saddrLocal, &cbLocal) != 0) {
  488. DWORD err = WSAGetLastError();
  489. fReturn = FALSE;
  490. IF_DEBUG( ERROR) {
  491. DBGPRINTF( ( DBG_CONTEXT,
  492. " Failure in getsockname( sock=%d). Error = %u\n",
  493. sControl, err));
  494. }
  495. SetLastError( err);
  496. } else {
  497. LocalIpAddress = saddrLocal.sin_addr;
  498. LocalIpPort = saddrLocal.sin_port;
  499. }
  500. }
  501. DataPort = CONN_PORT_TO_DATA_PORT(LocalIpPort);
  502. //
  503. // Success!
  504. //
  505. IF_DEBUG( CLIENT) {
  506. time_t now;
  507. time( & now);
  508. CHAR pchAddr[32];
  509. InetNtoa( clientIpAddress, pchAddr);
  510. DBGPRINTF( ( DBG_CONTEXT,
  511. " Client Connection for %s:%d starting @ %s",
  512. pchAddr, sControl,
  513. asctime( localtime( &now))));
  514. }
  515. IF_DEBUG( USER_DATABASE ) {
  516. DBGPRINTF(( DBG_CONTEXT,
  517. "user %lu reset @ %08lX.\n",
  518. QueryId(), this));
  519. }
  520. m_nControlRead = 0;
  521. FakeIOTimes = 0;
  522. return (fReturn);
  523. } // USER_DATA::Reset()
  524. VOID
  525. USER_DATA::Cleanup( VOID)
  526. /*++
  527. This cleans up data stored in the user data object.
  528. Returns:
  529. None
  530. --*/
  531. {
  532. DBG_ASSERT( QueryReference() == 0);
  533. if ( m_fCleanedup) {
  534. return;
  535. }
  536. if ( m_pMetaData != NULL )
  537. {
  538. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  539. m_pMetaData = NULL;
  540. }
  541. # if DBG
  542. if ( !IS_VALID_USER_DATA( this)) {
  543. DBGPRINTF( ( DBG_CONTEXT,
  544. "Encountering an invalid user data ( %08x)\n",
  545. this));
  546. Print();
  547. }
  548. # endif // DBG
  549. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  550. IF_DEBUG( USER_DATABASE ) {
  551. DBGPRINTF(( DBG_CONTEXT,
  552. " Cleaning up user %lu @ %08lX.\n",
  553. QueryId(), this));
  554. }
  555. DBG_ASSERT( m_nControlRead == 0);
  556. //
  557. // Clean up stuff needed to deal with PASV connections
  558. //
  559. if ( m_hPASVAcceptEvent )
  560. {
  561. RemovePASVAcceptEvent( TRUE );
  562. }
  563. if ( m_pszCmd )
  564. {
  565. TCP_FREE( m_pszCmd );
  566. m_pszCmd = NULL;
  567. }
  568. //
  569. // Close any open sockets & handles.
  570. //
  571. CloseSockets( FALSE );
  572. // invalidate the connections
  573. m_AioControlConnection.SetNewSocket(INVALID_SOCKET);
  574. m_AioDataConnection.SetNewSocket(INVALID_SOCKET);
  575. m_AioDataFile.SetNewSocket(INVALID_SOCKET);
  576. if (m_AsyncTransferBuff) {
  577. TCP_FREE( m_AsyncTransferBuff);
  578. m_AsyncTransferBuff = NULL;
  579. }
  580. //
  581. // Update the statistics.
  582. //
  583. if( IsLoggedOn()
  584. && !TEST_UF( this, WAIT_PASS ) )
  585. {
  586. if( TEST_UF( this, ANONYMOUS))
  587. {
  588. m_pInstance->QueryStatsObj()->DecrCurrentAnonymousUsers();
  589. }
  590. else
  591. {
  592. m_pInstance->QueryStatsObj()->DecrCurrentNonAnonymousUsers();
  593. }
  594. }
  595. m_pInstance->QueryStatsObj()->DecrCurrentConnections();
  596. if( UserToken != NULL )
  597. {
  598. TsDeleteUserToken( UserToken );
  599. UserToken = NULL;
  600. }
  601. if( CurrentDirHandle != INVALID_HANDLE_VALUE )
  602. {
  603. IF_DEBUG( VIRTUAL_IO )
  604. {
  605. DBGPRINTF(( DBG_CONTEXT,
  606. "closing directory handle %08lX\n",
  607. CurrentDirHandle ));
  608. }
  609. CloseHandle( CurrentDirHandle );
  610. CurrentDirHandle = INVALID_HANDLE_VALUE;
  611. }
  612. if ( m_pOpenFileInfo != NULL) {
  613. DBG_REQUIRE( CloseFileForSend());
  614. }
  615. DBG_REQUIRE( CloseFileForReceive());
  616. //
  617. // Release the memory attached to this structure.
  618. //
  619. if( RenameSourceBuffer != NULL ) {
  620. // do not free this location until end of usage.
  621. RenameSourceBuffer[0] = '\0';
  622. }
  623. m_UserId = 0; // invalid User Id
  624. //
  625. // Kill the structure signature.
  626. //
  627. KILL_USER_SIG( this );
  628. IF_DEBUG( CLIENT) {
  629. time_t now;
  630. time( & now);
  631. DBGPRINTF( ( DBG_CONTEXT,
  632. " Client Connection for %s:%d ending @ %s",
  633. inet_ntoa( HostIpAddress), QueryControlSocket(),
  634. asctime( localtime( &now))));
  635. }
  636. //
  637. // There is a possible race condition. If the socket was abruptly closed
  638. // and there was any pending Io, they will get blown away. This will
  639. // cause a call-back from the ATQ layer. That is unavoidable.
  640. // In such cases it is possible that the object was deleted.
  641. // This can lead to problems. We need to be careful.
  642. // But Reference Count protects against such disasters. So tread
  643. // carefully and use Reference count.
  644. //
  645. DBG_ASSERT( m_sPassiveDataListen == INVALID_SOCKET);
  646. m_fCleanedup = TRUE; // since we just cleaned up this object
  647. return;
  648. } // USER_DATA::Cleanup()
  649. VOID
  650. USER_DATA::ReInitializeForNewUser( VOID)
  651. /*++
  652. This function reinitializes the user data information for a new user to
  653. communicate with the server using existing control socket connection.
  654. --*/
  655. {
  656. # if DBG
  657. if ( !IS_VALID_USER_DATA( this)) {
  658. DBGPRINTF( ( DBG_CONTEXT,
  659. "Encountering an invalid user data ( %08x)\n",
  660. this));
  661. Print();
  662. }
  663. # endif // DBG
  664. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  665. //
  666. // Update the statistics.
  667. //
  668. if( IsLoggedOn())
  669. {
  670. if( TEST_UF( this, ANONYMOUS))
  671. {
  672. m_pInstance->QueryStatsObj()->DecrCurrentAnonymousUsers();
  673. }
  674. else
  675. {
  676. m_pInstance->QueryStatsObj()->DecrCurrentNonAnonymousUsers();
  677. }
  678. }
  679. CLEAR_UF_BITS( this, (UF_LOGGED_ON | UF_ANONYMOUS | UF_PASSIVE));
  680. LockUser();
  681. if( QueryState() != UserStateDisconnected ) {
  682. SetState( UserStateWaitingForUser );
  683. }
  684. UnlockUser();
  685. if ( m_pMetaData != NULL )
  686. {
  687. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  688. m_pMetaData = NULL;
  689. }
  690. m_TimeAtConnection= GetCurrentTimeInSeconds();
  691. m_TimeAtLastAccess= m_TimeAtConnection;
  692. m_xferType = XferTypeAscii;
  693. m_xferMode = XferModeStream;
  694. DataIpAddress = HostIpAddress;
  695. DataPort = CONN_PORT_TO_DATA_PORT(LocalIpPort);
  696. m_szUserName[0] = '\0';
  697. m_szCurrentDirectory.Reset();
  698. m_strRootDir.Reset();
  699. if( UserToken != NULL )
  700. {
  701. TsDeleteUserToken( UserToken );
  702. UserToken = NULL;
  703. }
  704. if( CurrentDirHandle != INVALID_HANDLE_VALUE )
  705. {
  706. IF_DEBUG( VIRTUAL_IO )
  707. {
  708. DBGPRINTF(( DBG_CONTEXT,
  709. "closing directory handle %08lX\n",
  710. CurrentDirHandle ));
  711. }
  712. CloseHandle( CurrentDirHandle );
  713. CurrentDirHandle = INVALID_HANDLE_VALUE;
  714. }
  715. if ( m_pOpenFileInfo != NULL) {
  716. DBG_REQUIRE( CloseFileForSend());
  717. }
  718. DBG_REQUIRE( CloseFileForReceive());
  719. m_licbSent = 0;
  720. m_pvInitialRequest = NULL;
  721. m_cbInitialRequest = 0;
  722. CleanupPassiveSocket( TRUE );
  723. return;
  724. } // USER_DATA::ReInitializeForNewUser()
  725. BOOL
  726. USER_DATA::ProcessAsyncIoCompletion(
  727. IN DWORD cbIo,
  728. IN DWORD dwError,
  729. IN LPASYNC_IO_CONNECTION pAioConn,
  730. IN BOOL fTimedOut)
  731. /*++
  732. This function processes the Async Io completion.
  733. ( invoked due to a callback from the ASYNC_IO_CONNECTION object)
  734. Arguments:
  735. pContext pointer to the context information ( UserData object).
  736. cbIo count of bytes transferred in Io
  737. dwError DWORD containing the error code resulting from last tfr.
  738. pAioConn pointer to AsyncIo connection object.
  739. fTimedOut flag indicating if the current call was made
  740. because of timeout.
  741. Returns:
  742. None
  743. --*/
  744. {
  745. BOOL fReturn = FALSE;
  746. AC_RESULT acDnsAccess;
  747. DWORD dwOriginalError;
  748. dwOriginalError = dwError;
  749. //
  750. // Special processing if it's an IO completion on the control connection - we might
  751. // be processing a completion we posted ourselves to signal that the data socket for the PASV
  752. // data connection is now accept()'able.
  753. //
  754. if ( pAioConn == &m_AioControlConnection && QueryInFakeIOCompletion() )
  755. {
  756. // Here is a horrible race condition:
  757. // If the FTP client closes the control socket
  758. // right after having finished receiving the transmitted file
  759. // than there may be a thread that enters this
  760. // code path (because the FakeIO flag is set, and the
  761. // Control Socket is involved) before the IO completion
  762. // for the data connection has traveled this same function,
  763. // cleaning the FakeIO flag
  764. // Here is the race condition:
  765. // A thread enter here, and see that the FakeIO is set
  766. // the normal behavior is reprocessing a command like
  767. // "RETR foo.txt", while now the command if a zero length string.
  768. // the Second thread enter this function with the DataConnection
  769. // it clears the flag (at a non specified point of the
  770. // processing of the other thread) and it exit.
  771. // the original thread is now processing a saved string
  772. // (because of the FakeIO flag) while it is not supposed to.
  773. // this causes problems to the time-out algorithm, because
  774. // of a ref-count problem in the USER_DATA
  775. LONG CurVal = InterlockedIncrement(&(this->FakeIOTimes));
  776. if (CurVal>1){
  777. goto NormalProcessing;
  778. }
  779. //
  780. // Remove the reference used to deal with the race condition between an IO
  781. // thread doing clean-up and the thread watching for the data socket to become
  782. // accept()'able and holding on to this USER_DATA object
  783. //
  784. DeReference();
  785. //
  786. // There is a race condition between the thread watching for a socket to become
  787. // accept()'able and an IO thread being woken up because the client has (unexpectedly)
  788. // disconnected. The thread watching the socket will post a fake IO completion to
  789. // indicate that an accept() on the socket will succeed; however, if the client
  790. // disconnects (the control connection) before the fake completion is processed,
  791. // we don't want to do any more processing.
  792. //
  793. if ( UserState == UserStateDisconnected )
  794. {
  795. return TRUE;
  796. }
  797. else
  798. {
  799. //
  800. // Fast-path if we know this is the second time around we're trying to process the
  801. // command, which happens when we're in PASV mode
  802. //
  803. DBG_ASSERT( ( UserState == UserStateLoggedOn ) ||
  804. (( UserState == UserStateWaitingForPass ) && QueryInFakeIOCompletion()) );
  805. goto ProcessCommand;
  806. }
  807. }
  808. NormalProcessing:
  809. if( dwError != NO_ERROR &&
  810. dwError != ERROR_SEM_TIMEOUT )
  811. {
  812. //
  813. // Geezsh, I hate my life.
  814. //
  815. // Once upon a time, there was a bug in ATQ that cause it to
  816. // always pass NO_ERROR as the status to the async completion
  817. // routine. This bug caused, among other things, FTP to never
  818. // time out idle connections, because it never saw the
  819. // ERROR_SEM_TIMEOUT status. So, I fixed the bug in ATQ.
  820. //
  821. // Now, this completion routine gets the actual status. Well,
  822. // that breaks service shutdown when there are connected users.
  823. // Basically, when a shutdown occurs, the connected sockets are
  824. // closed, causing the IO to complete with ERROR_NETNAME_DELETED.
  825. // USER_DATA::ProcessAsyncIoCompletion() is not handling this
  826. // error properly, which causes 1) an assertion failure because
  827. // USER_DATA::DisconnectUserWithError() is getting called *twice*
  828. // and 2) the service never stops because of a dangling reference
  829. // on the USER_DATA structure.
  830. //
  831. // Of course, the proper thing to do would be to fix the offending
  832. // code in USER_DATA::ProcessAsyncIoCompletion() so that it DID
  833. // handle the error properly. Unfortunately, that fix requires a
  834. // nontrivial amount of surgery, and we're a scant three days
  835. // from releasing K2 Beta 1. So...
  836. //
  837. // As a quick & dirty work around for K2 Beta 1, we'll map all
  838. // errors other than ERROR_SEM_TIMEOUT to NO_ERROR. This should
  839. // provide the lower software layers with the old ATQ behavior
  840. // they're expecting.
  841. //
  842. // REMOVE THIS POST BETA 1 AND FIX USER_DATA PROPERLY!!!!
  843. //
  844. // 3/12/98
  845. //
  846. // N.B. The debug output below has been changed to be a little
  847. // more customer friendly but I hate to prevent future developers
  848. // for enjoying the original message which read:
  849. // "Mapping error %d to NO_ERROR to mask FTP bug (FIX!)\n"
  850. //
  851. // I'm removing this message because it was the source of some
  852. // embarrasment, when a checked version of this DLL was sent to
  853. // Ernst & Young to track the now famous bug #138566.
  854. //
  855. DBGPRINTF((
  856. DBG_CONTEXT,
  857. "Mapping error %d to NO_ERROR\n",
  858. dwError
  859. ));
  860. dwError = NO_ERROR;
  861. }
  862. # if DBG
  863. if ( !IS_VALID_USER_DATA( this)) {
  864. DBGPRINTF( ( DBG_CONTEXT,
  865. "Encountering an invalid user data ( %08x)\n",
  866. this));
  867. Print();
  868. }
  869. # endif // DBG
  870. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  871. IF_DEBUG( USER_DATABASE) {
  872. DBGPRINTF( ( DBG_CONTEXT,
  873. "[%lu] Entering USER_DATA( %08x)::Process( %u, %u, %08x)."
  874. " RefCount = %d. State = %d\n",
  875. GetTickCount(),
  876. this, cbIo, dwError, pAioConn, QueryReference(),
  877. QueryState()));
  878. }
  879. if ( m_fNeedDnsCheck )
  880. {
  881. acDnsAccess = QueryAccessCheck()->CheckDnsAccess();
  882. UnbindInstanceAccessCheck();
  883. m_fNeedDnsCheck = FALSE;
  884. if ( (acDnsAccess == AC_IN_DENY_LIST) ||
  885. (acDnsAccess == AC_NOT_IN_GRANT_LIST) ||
  886. ((m_acCheck == AC_NOT_IN_GRANT_LIST) &&
  887. (acDnsAccess != AC_IN_GRANT_LIST) ) ) {
  888. ReplyToUser(this,
  889. REPLY_NOT_LOGGED_IN,
  890. "Connection refused, unknown IP address." );
  891. DisconnectUserWithError( NO_ERROR );
  892. return TRUE;
  893. }
  894. }
  895. if ( pAioConn == &m_AioDataConnection || pAioConn == &m_AioDataFile)
  896. {
  897. //
  898. // a Data transfer operation has completed.
  899. //
  900. DBG_REQUIRE( IsLoggedOn());
  901. // Update last access time
  902. m_TimeAtLastAccess = GetCurrentTimeInSeconds();
  903. if ( TEST_UF( this, ASYNC_UPLOAD)) {
  904. //
  905. // we are in the data pump loop for uploading a file, and have just completed
  906. // receiving or writing a block of data. If there were no errors, just initiate an
  907. // asynchronous write or receive for a block of data, and be done. otherwise, fall
  908. // through to normal error handling.
  909. //
  910. BOOL fAsyncOK;
  911. if ( (dwError != NO_ERROR) || (cbIo == 0)) {
  912. //
  913. // Either the transfer is complete, or an error has occured. terminate.
  914. //
  915. fAsyncOK = FALSE;
  916. } else if ( TEST_UF( this, OOB_DATA)) {
  917. //
  918. // we got an out-of-band abbort command, so bail out.
  919. //
  920. fAsyncOK = FALSE;
  921. dwOriginalError = ERROR_OPERATION_ABORTED;
  922. } else if ( pAioConn == &m_AioDataConnection) {
  923. //
  924. // received another block. update stats and initiate async write to file
  925. //
  926. IncrementCbRecvd( cbIo);
  927. fAsyncOK = m_AioDataFile.WriteFile( m_AsyncTransferBuff,
  928. cbIo,
  929. QueryCurrentOffset());
  930. dwOriginalError = GetLastError();
  931. } else {
  932. //
  933. // Write completed. update offset & start new receive
  934. //
  935. IncCurrentOffset( cbIo);
  936. fAsyncOK = m_AioDataConnection.ReadFile(m_AsyncTransferBuff,
  937. g_SocketBufferSize);
  938. dwOriginalError = GetLastError();
  939. }
  940. if (fAsyncOK) {
  941. //
  942. // we have successfully initiated the next async operation, so we are done.
  943. //
  944. return TRUE;
  945. }
  946. } else if ( TEST_UF( this, ASYNC_DOWNLOAD)) {
  947. //
  948. // we are in the datapump for downloading a large file. initiate
  949. // a transmit file for the next chunk.
  950. //
  951. BOOL fAsyncOK;
  952. BOOL fDisconnectSocket;
  953. if ( (dwError != NO_ERROR) || (cbIo < MAX_TRANSMIT_FILE_DATA_CHUNK)) {
  954. //
  955. // Either the transfer is complete, or an error has occured. terminate.
  956. //
  957. fAsyncOK = FALSE;
  958. } else if ( TEST_UF( this, OOB_DATA)) {
  959. //
  960. // we got an out-of-band abbort command, so bail out.
  961. //
  962. fAsyncOK = FALSE;
  963. dwOriginalError = ERROR_OPERATION_ABORTED;
  964. } else {
  965. LARGE_INTEGER FileSize;
  966. IncrementCbSent( cbIo);
  967. IncCurrentOffset( cbIo);
  968. m_pOpenFileInfo->QuerySize( FileSize);
  969. if (FileSize.QuadPart > QueryCurrentOffset()) {
  970. //
  971. // prepare to send the next chunk
  972. //
  973. FileSize.QuadPart -= QueryCurrentOffset();
  974. if (FileSize.QuadPart > MAX_TRANSMIT_FILE_DATA_CHUNK) {
  975. FileSize.QuadPart = MAX_TRANSMIT_FILE_DATA_CHUNK;
  976. fDisconnectSocket = FALSE;
  977. } else {
  978. //
  979. // there is nothing left to send after this chunk, so go through
  980. // normal path at next completion. Also disconnect socket after
  981. // this transmit
  982. //
  983. CLEAR_UF( this, ASYNC_DOWNLOAD);
  984. fDisconnectSocket = TRUE;
  985. }
  986. fAsyncOK = m_AioDataConnection.TransmitFileTs( m_pOpenFileInfo,
  987. FileSize,
  988. QueryCurrentOffset(),
  989. fDisconnectSocket);
  990. dwOriginalError = GetLastError();
  991. } else if (FileSize.QuadPart == QueryCurrentOffset()) {
  992. //
  993. // we have sent the entire file, done.
  994. //
  995. fAsyncOK = FALSE;
  996. } else {
  997. //
  998. // the offset is larger than the file, this is an internal error condition
  999. //
  1000. DBG_ASSERT( FileSize.QuadPart >= QueryCurrentOffset());
  1001. fAsyncOK = FALSE;
  1002. dwOriginalError = ERROR_INTERNAL_ERROR;
  1003. }
  1004. }
  1005. if (fAsyncOK) {
  1006. //
  1007. // we have successfully initiated the next async operation, so we are done.
  1008. //
  1009. return TRUE;
  1010. }
  1011. }
  1012. if ( dwError == NO_ERROR || !fTimedOut)
  1013. {
  1014. // dwError == NO_ERROR ==> No error in transmitting data
  1015. // so decrease ref count and blow away the sockets.
  1016. // if dwError != NO_ERROR then
  1017. // if timeout occured ==> ATQ will send another callback
  1018. // so do not decrease ref count now.
  1019. // if no timeout ==> then decrement ref count now.
  1020. DBG_REQUIRE( DeReference() > 0);
  1021. }
  1022. else
  1023. {
  1024. // RobSol: 8/20/01: this looks like a faulty statement. fTimeOut must be TRUE, or the
  1025. // previous "if" would be true. I am leaving this dead code commented out, in case we
  1026. // find it was supposed to exist within some other logic.
  1027. // if ( fTimedOut)
  1028. // {
  1029. // SET_UF( this, DATA_TIMEDOUT);
  1030. // }
  1031. // else
  1032. // {
  1033. // SET_UF( this, DATA_ERROR);
  1034. // }
  1035. SET_UF( this, DATA_TIMEDOUT);
  1036. }
  1037. # ifdef CHECK_DBG
  1038. if ( dwError != NO_ERROR)
  1039. {
  1040. CHAR szBuffer[100];
  1041. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  1042. " Data Socket Error = %u ", dwError) > 0);
  1043. Print( szBuffer);
  1044. }
  1045. # endif // CHECK_DBG
  1046. CLEAR_UF( this, ASYNC_TRANSFER);
  1047. CLEAR_UF( this, ASYNC_DOWNLOAD);
  1048. //
  1049. // Destroy the data connection.
  1050. // Send message accordingly to indicate if this was a failure/success
  1051. // That is done by DestroyDataConnection.
  1052. //
  1053. DBG_REQUIRE( DestroyDataConnection( dwOriginalError));
  1054. DBG_REQUIRE( CloseFileForReceive( dwOriginalError));
  1055. if ( m_pOpenFileInfo != NULL)
  1056. {
  1057. //
  1058. // set number of bytes actually sent
  1059. //
  1060. m_licbSent += cbIo;
  1061. DBG_REQUIRE( CloseFileForSend( dwOriginalError));
  1062. }
  1063. if ( dwError == NO_ERROR)
  1064. {
  1065. //
  1066. // Process any Pending commands, due to the parallel
  1067. // control channel operation for this user Connection.
  1068. // For the present, we dont buffer commands ==> No processing
  1069. // to be done effectively. NYI
  1070. // Just ensure that there is a read-operation pending on
  1071. // control channel.
  1072. //
  1073. // BOGUS: DBG_ASSERT( TEST_UF( this, CONTROL_READ));
  1074. }
  1075. fReturn = TRUE; // since this function went on well.
  1076. }
  1077. else if ( pAioConn == &m_AioControlConnection)
  1078. {
  1079. //
  1080. // a control socket operation has completed.
  1081. //
  1082. if ( dwError != NO_ERROR)
  1083. {
  1084. //
  1085. // There is an error in processing the control connection request.
  1086. // the only ASYNC_IO request we submit on control is:
  1087. // Read request on control socket
  1088. //
  1089. if ( fTimedOut)
  1090. {
  1091. if ( TEST_UF( this, TRANSFER))
  1092. {
  1093. // A data transfer is going on.
  1094. // allow client to send commands later
  1095. // (client may not be async in control/data io,so allow it)
  1096. // resubmit the control read operation
  1097. // after clearing old one
  1098. //
  1099. // Since there is a pending IO in atq.
  1100. // Just resume the timeout processing in ATQ for
  1101. // this context.
  1102. //
  1103. pAioConn->ResumeIoOperation();
  1104. fReturn = TRUE;
  1105. }
  1106. else
  1107. {
  1108. // For timeouts, ATQ sends two call backs.
  1109. // So be careful to decrement reference count only once.
  1110. DBG_ASSERT( fReturn == FALSE);
  1111. DBG_ASSERT( TEST_UF( this, CONTROL_READ));
  1112. SET_UF( this, CONTROL_TIMEDOUT);
  1113. }
  1114. }
  1115. else
  1116. {
  1117. // Either there should be a control read pending or
  1118. // control socket should have received a timeout.
  1119. DBG_ASSERT( TEST_UF( this, CONTROL_READ) ||
  1120. TEST_UF( this, CONTROL_TIMEDOUT)
  1121. );
  1122. // a non-timeout error has occured. ==> stop read operation.
  1123. StopControlRead(this);
  1124. DBG_ASSERT( fReturn == FALSE);
  1125. SET_UF( this, CONTROL_ERROR);
  1126. }
  1127. }
  1128. else
  1129. {
  1130. // If this connection had an outstanding IO on wait queue, it
  1131. // got completed. Hence get rid of the reference count.
  1132. StopControlRead( this);
  1133. switch ( UserState)
  1134. {
  1135. case UserStateEmbryonic:
  1136. fReturn = StartupSession( m_pvInitialRequest,
  1137. m_cbInitialRequest);
  1138. if ( m_pvInitialRequest == NULL)
  1139. {
  1140. // No initial buffer. Wait for read to complete
  1141. break;
  1142. }
  1143. cbIo = m_cbInitialRequest; // fake the bytes read.
  1144. // Fall Through for processing request
  1145. case UserStateWaitingForUser:
  1146. case UserStateWaitingForPass:
  1147. case UserStateLoggedOn:
  1148. ProcessCommand:
  1149. //
  1150. // Input already read. Process request and submit another read.
  1151. //
  1152. fReturn = ParseAndProcessRequest(cbIo/sizeof(CHAR));
  1153. if ( fReturn && IsDisconnected() &&
  1154. TEST_UF( this, CONTROL_TIMEDOUT))
  1155. {
  1156. // disconnect only if no pending control read
  1157. // if there is a pending control read,
  1158. // atq will pop this up for cleanup.
  1159. fReturn = !(TEST_UF( this, CONTROL_READ));
  1160. IF_DEBUG( ERROR) {
  1161. DBGPRINTF(( DBG_CONTEXT,
  1162. "%08x ::Timeout killed conn while "
  1163. " processing!\n State = %d(%x),"
  1164. " Ref = %d, Id = %d, fRet=%d\n",
  1165. this, QueryState(), Flags,
  1166. QueryReference(), QueryId(), fReturn
  1167. ));
  1168. }
  1169. FacIncrement( CacTimeoutWhenProcessing);
  1170. }
  1171. break;
  1172. case UserStateDisconnected:
  1173. fReturn = TRUE;
  1174. if ( TEST_UF( this, CONTROL_TIMEDOUT))
  1175. {
  1176. // Timeout thread raced against me :(
  1177. IF_DEBUG( ERROR) {
  1178. DBGPRINTF(( DBG_CONTEXT,
  1179. "%08x :: Conn already Disconnected !!!\n"
  1180. " State = %d(%x), Ref = %d, Id = %d\n",
  1181. this, QueryState(), Flags,
  1182. QueryReference(), QueryId()
  1183. ));
  1184. }
  1185. FacIncrement( CacTimeoutInDisconnect);
  1186. fReturn = FALSE;
  1187. }
  1188. break;
  1189. default:
  1190. DBG_ASSERT( !"Invalid UserState for processing\n");
  1191. SetLastError( ERROR_INVALID_PARAMETER);
  1192. break;
  1193. } // switch
  1194. dwError = ( fReturn) ? NO_ERROR : GetLastError();
  1195. }
  1196. if ( !fReturn)
  1197. {
  1198. DisconnectUserWithError( dwError, fTimedOut);
  1199. }
  1200. }
  1201. else
  1202. {
  1203. DBG_ASSERT( !"call to Process() with wrong parameters");
  1204. }
  1205. IF_DEBUG( USER_DATABASE) {
  1206. DBGPRINTF( ( DBG_CONTEXT,
  1207. "[%lu] Leaving USER_DATA( %08x)::Process()."
  1208. " RefCount = %d. State = %d\n",
  1209. GetTickCount(),
  1210. this, QueryReference(), QueryState())
  1211. );
  1212. }
  1213. return ( fReturn);
  1214. } // USER_DATA::ProcessAsyncIoCompletion()
  1215. # define min(a, b) (((a) < (b)) ? (a) : (b))
  1216. BOOL
  1217. USER_DATA::StartupSession(IN PVOID pvInitialRequest,
  1218. IN DWORD cbInitialRequest
  1219. )
  1220. /*++
  1221. This function allocates a buffer for receiving request from the client
  1222. and also sets up initial read from the control socket to
  1223. get client requests.
  1224. Arguments:
  1225. pvInitialRequest pointer to initial request buffer
  1226. cbInitialRequest count of bytes of data in the initial request
  1227. Returns:
  1228. TRUE on success and FALSE if there is any failure.
  1229. --*/
  1230. {
  1231. SOCKERR serr;
  1232. BOOL fReturn = FALSE;
  1233. PCSTR pszBanner;
  1234. # if DBG
  1235. if ( !IS_VALID_USER_DATA( this)) {
  1236. DBGPRINTF( ( DBG_CONTEXT,
  1237. "Encountering an invalid user data ( %08x)\n",
  1238. this));
  1239. Print();
  1240. }
  1241. # endif // DBG
  1242. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1243. DBG_ASSERT( QueryState() == UserStateEmbryonic);
  1244. //
  1245. // Reply to the initial connection message. ( Greet the new user).
  1246. //
  1247. pszBanner = QueryInstance()->QueryBannerMsg();
  1248. if( pszBanner && *pszBanner ) {
  1249. serr = SendMultilineMessage(
  1250. REPLY_SERVICE_READY,
  1251. g_FtpServiceNameString,
  1252. TRUE,
  1253. FALSE);
  1254. serr = serr || SendMultilineMessage(
  1255. REPLY_SERVICE_READY,
  1256. pszBanner,
  1257. FALSE,
  1258. TRUE);
  1259. } else {
  1260. serr = ReplyToUser( this,
  1261. REPLY_SERVICE_READY,
  1262. "%s",
  1263. g_FtpServiceNameString );
  1264. }
  1265. if ( serr != 0) {
  1266. IF_DEBUG( ERROR) {
  1267. DBGPRINTF( ( DBG_CONTEXT,
  1268. " Cannot reply with initial connection message."
  1269. " Error = %lu\n",
  1270. serr));
  1271. }
  1272. } else {
  1273. //
  1274. // enable OOB_INLINE since we are using that for our control socket
  1275. //
  1276. BOOL fOobInline = TRUE;
  1277. serr = setsockopt( QueryControlSocket(), SOL_SOCKET,
  1278. SO_OOBINLINE, (const char *) &fOobInline,
  1279. sizeof( fOobInline));
  1280. m_cchPartialReqRecvd = 0;
  1281. if ( serr == 0) {
  1282. //
  1283. // Try to set up the buffer and enter the mode for reading
  1284. // requests from the client
  1285. //
  1286. LockUser();
  1287. if( QueryState() != UserStateDisconnected ) {
  1288. SetState( UserStateWaitingForUser);
  1289. }
  1290. UnlockUser();
  1291. if ( pvInitialRequest != NULL && cbInitialRequest > 0) {
  1292. //
  1293. // No need to issue a read, since we have the data required.
  1294. // Do a safe copy to the buffer.
  1295. //
  1296. CopyMemory( QueryReceiveBuffer(), pvInitialRequest,
  1297. min( cbInitialRequest, QueryReceiveBufferSize())
  1298. );
  1299. fReturn = TRUE;
  1300. } else {
  1301. fReturn = ReadCommand();
  1302. }
  1303. } else {
  1304. IF_DEBUG( ERROR) {
  1305. DBGPRINTF((DBG_CONTEXT,
  1306. " SetsockOpt( OOB_INLINE) failed. Error = %lu\n",
  1307. WSAGetLastError()));
  1308. }
  1309. }
  1310. }
  1311. IF_DEBUG( CLIENT) {
  1312. DWORD dwError = (fReturn) ? NO_ERROR : GetLastError();
  1313. DBGPRINTF( ( DBG_CONTEXT,
  1314. " connection ( %08x)::StartupSession() returns %d."
  1315. " Error = %lu\n",
  1316. this, fReturn,
  1317. dwError));
  1318. if (fReturn) { SetLastError( dwError); }
  1319. }
  1320. return ( fReturn);
  1321. } // USER_DATA::StartupSession()
  1322. VOID
  1323. CheckAndProcessAbortOperation( IN LPUSER_DATA pUserData)
  1324. {
  1325. if ( TEST_UF( pUserData, OOB_ABORT)) {
  1326. //
  1327. // An abort was requested by client. So our processing
  1328. // has unwound and we are supposed to send some message
  1329. // to the client. ==> simulate processing ABOR command
  1330. // ABORT was not processed yet; so process now.
  1331. //
  1332. DBGPRINTF((DBG_CONTEXT,
  1333. "Executing simulated Abort for %08x\n",
  1334. pUserData));
  1335. FacIncrement( FacSimulatedAborts);
  1336. // To avoid thread races, check twice.
  1337. if ( TEST_UF( pUserData, OOB_ABORT)) {
  1338. //
  1339. // we need this stack variable (szAbort), so that
  1340. // ParseCommand() can freely modify the string!
  1341. CHAR szAbort[10];
  1342. CLEAR_UF( pUserData, OOB_ABORT);
  1343. CopyMemory( szAbort, "ABOR", sizeof("ABOR"));
  1344. ParseCommand( pUserData, szAbort);
  1345. }
  1346. }
  1347. return;
  1348. } // CheckAndProcessAbortOperation()
  1349. BOOL
  1350. USER_DATA::ParseAndProcessRequest(IN DWORD cchRequest)
  1351. /*++
  1352. This function parses the incoming request from client, identifies the
  1353. command to execute and executes the same.
  1354. Before parsing, the input is pre-processed to remove any of telnet commands
  1355. or OOB_inlined data.
  1356. Arguments:
  1357. cchRequest count of characters of request received.
  1358. --*/
  1359. {
  1360. BOOL fLineEnded = FALSE;
  1361. DWORD cchRequestRecvd = 0;
  1362. CHAR szCommandLine[ MAX_COMMAND_LENGTH + 1];
  1363. # if DBG
  1364. if ( !IS_VALID_USER_DATA( this))
  1365. {
  1366. DBGPRINTF( ( DBG_CONTEXT,
  1367. "Encountering an invalid user data ( %08x)\n",
  1368. this));
  1369. Print();
  1370. }
  1371. # endif // DBG
  1372. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1373. IF_DEBUG( CLIENT)
  1374. {
  1375. DBGPRINTF( ( DBG_CONTEXT,
  1376. "UserData(%08x)::ParseAndProcessRequest( %d chars)\n",
  1377. this, cchRequest));
  1378. }
  1379. //
  1380. // Fast-path if we're re-processing this command, which happens in PASV mode
  1381. //
  1382. if ( QueryInFakeIOCompletion() )
  1383. {
  1384. goto FastPathLabel;
  1385. }
  1386. if ( cchRequest > 0)
  1387. {
  1388. // We have a valid request. Process it
  1389. // Update last access time
  1390. m_TimeAtLastAccess = GetCurrentTimeInSeconds();
  1391. m_pInstance->QueryStatsObj()->UpdateTotalBytesReceived(
  1392. cchRequest*sizeof(CHAR));
  1393. if ( m_cchPartialReqRecvd + cchRequest >= MAX_COMMAND_LENGTH)
  1394. {
  1395. #if DBG
  1396. CHAR szCmdFailed[600];
  1397. DBG_REQUIRE( _snprintf( szCmdFailed, sizeof( szCmdFailed ),
  1398. " Command is too long: Partial=%d bytes. Now=%d \n"
  1399. " UserDb(%08p) = %s from Host: %s\n",
  1400. m_cchPartialReqRecvd, cchRequest,
  1401. this, QueryUserName(), QueryClientHostName()) > 0);
  1402. DBGPRINTF((DBG_CONTEXT, szCmdFailed));
  1403. #endif
  1404. DisconnectUserWithError( ERROR_BUSY);
  1405. return ( TRUE); // we are done with this connection.
  1406. }
  1407. CopyMemory(szCommandLine, m_recvBuffer,
  1408. m_cchPartialReqRecvd + cchRequest);
  1409. szCommandLine[m_cchPartialReqRecvd + cchRequest] = '\0';
  1410. if ( !::FilterTelnetCommands(szCommandLine,
  1411. m_cchPartialReqRecvd + cchRequest,
  1412. &fLineEnded, &cchRequestRecvd))
  1413. {
  1414. if ( TEST_UF( this, TRANSFER))
  1415. {
  1416. //
  1417. // I am in data transfer mode. Some other thread is sending
  1418. // data for this client. Just post a OOB_DATA and OOB_ABORT
  1419. // OOB_DATA will cause the call-stack of other thread to unwind
  1420. // and get out of the command.
  1421. // Then check if any async transfer was occuring. If so
  1422. // process abort with disconnect now.
  1423. //
  1424. SET_UF_BITS( this, (UF_OOB_DATA | UF_OOB_ABORT));
  1425. if ( TEST_UF( this, ASYNC_TRANSFER))
  1426. {
  1427. //
  1428. // An async transfer is occuring. Stop it
  1429. //
  1430. DestroyDataConnection( ERROR_OPERATION_ABORTED);
  1431. CheckAndProcessAbortOperation( this);
  1432. }
  1433. # ifdef CHECK_DBG
  1434. Print( " OOB_ABORT ");
  1435. # endif // CHECK_DBG
  1436. IF_DEBUG( CLIENT) {
  1437. DBGPRINTF((DBG_CONTEXT,
  1438. "[%08x]Set up the implied ABORT command\n",
  1439. this));
  1440. }
  1441. IF_DEBUG( COMMANDS) {
  1442. DBGPRINTF((DBG_CONTEXT, " ***** [%08x] OOB_ABORT Set \n",
  1443. this));
  1444. }
  1445. // Ignore the rest of the commands that may have come in.
  1446. }
  1447. else
  1448. {
  1449. //
  1450. // Since no command is getting processed.
  1451. // atleast process the abort command, otherwise clients hang.
  1452. //
  1453. //
  1454. // we need this stack variable (szAbort), so that
  1455. // ParseCommand() can freely modify the string!
  1456. CHAR szAbort[10];
  1457. CopyMemory( szAbort, "ABOR", sizeof("ABOR"));
  1458. ParseCommand( this, szAbort);
  1459. CLEAR_UF( this, OOB_ABORT); // clear the abort flag!
  1460. }
  1461. }
  1462. else
  1463. {
  1464. if ( TEST_UF( this, TRANSFER))
  1465. {
  1466. //
  1467. // we are transferring data, sorry no more commands accepted.
  1468. // This could hang clients. Hey! they asked for it :( NYI
  1469. //
  1470. // Do nothing
  1471. IF_DEBUG( COMMANDS) {
  1472. DBGPRINTF((DBG_CONTEXT,
  1473. "***** [%08x] Received Request %s during"
  1474. " transfer in progress\n",
  1475. this, szCommandLine));
  1476. }
  1477. }
  1478. else
  1479. {
  1480. //
  1481. // Let ParseCommand do the dirty work.
  1482. //
  1483. // Remember the count of partial bytes received.
  1484. m_cchPartialReqRecvd = cchRequestRecvd;
  1485. if ( !fLineEnded)
  1486. {
  1487. // In case if command was long enough to fill all buffer but
  1488. // we haven't found new line simply tell to user about the error
  1489. // and disconnect. Some ftp clients will not see that msg, becuase
  1490. // connection was disconnected, but thats a bug in client code
  1491. if ( m_cchPartialReqRecvd >= MAX_COMMAND_LENGTH - 1)
  1492. {
  1493. ReplyToUser( this,
  1494. REPLY_UNRECOGNIZED_COMMAND,
  1495. PSZ_COMMAND_TOO_LONG);
  1496. DisconnectUserWithError( ERROR_BUSY );
  1497. return ( TRUE); // we are done with this connection.
  1498. }
  1499. //
  1500. // Complete line is not received. Continue reading
  1501. // the requests, till we receive the complete request
  1502. //
  1503. }
  1504. else
  1505. {
  1506. StartProcessingTimer();
  1507. //
  1508. // set the partial received byte count to zero.
  1509. // we will not use this value till next incomplete request
  1510. //
  1511. m_cchPartialReqRecvd = 0;
  1512. FastPathLabel:
  1513. ParseCommand( this, ( QueryInFakeIOCompletion() ? QueryCmdString() :
  1514. szCommandLine ) );
  1515. CheckAndProcessAbortOperation( this);
  1516. } // if TRANSFER is not there...
  1517. } //Parse if complete
  1518. } // if FilterTelnetCommands()
  1519. }
  1520. else
  1521. {
  1522. // if (cchRequest <= 0)
  1523. SET_UF( this, CONTROL_ZERO);
  1524. //
  1525. // after a quit a client is expected to wait for quit message from
  1526. // the server. if the client prematurely closes connection, then
  1527. // the server receives it as a receive with zero byte read.
  1528. // since, we should not be having outstanding read at this time,
  1529. // atq should not be calling us. On the contrary we are getting
  1530. // called by ATQ. Let us track this down.
  1531. //
  1532. if ( !TEST_UF( this, CONTROL_QUIT))
  1533. {
  1534. DisconnectUserWithError( NO_ERROR);
  1535. }
  1536. else
  1537. {
  1538. // Quit message is received and then ZeroBytes Received!!
  1539. DBGPRINTF((DBG_CONTEXT,
  1540. " (%08x)::ZeroBytes recvd after QUIT message!!."
  1541. " State = %d(%x), Ref = %d\n",
  1542. this,
  1543. QueryState(), Flags,
  1544. QueryReference()
  1545. ));
  1546. // Do nothing. Since Quit will take care of cleanup
  1547. return (TRUE);
  1548. }
  1549. }
  1550. //
  1551. // If the connection is not yet disconnected, submit a read command.
  1552. // else return that everything is fine (someone had disconnected it).
  1553. //
  1554. return ( IsDisconnected() ? TRUE : ReadCommand());
  1555. } // USER_DATA::ParseAndProcessRequest()
  1556. BOOL
  1557. USER_DATA::ReadCommand( VOID)
  1558. {
  1559. BOOL fReturn = TRUE;
  1560. DBG_CODE(
  1561. if ( !IS_VALID_USER_DATA( this)) {
  1562. DBGPRINTF( ( DBG_CONTEXT,
  1563. "Encountering an invalid user data ( %08x)\n",
  1564. this));
  1565. Print();
  1566. }
  1567. );
  1568. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1569. if ( TEST_UF( this, CONTROL_TIMEDOUT) || IsDisconnected()) {
  1570. SetLastError( ERROR_SEM_TIMEOUT);
  1571. return (FALSE);
  1572. }
  1573. //
  1574. // Submit a read on control socket only if there is none pending!
  1575. // Otherwise, behave in idempotent manner.
  1576. //
  1577. if ( !TEST_UF( this, CONTROL_READ)) {
  1578. Reference(); // since we are going to set up async read.
  1579. InterlockedIncrement( &m_nControlRead);
  1580. DBG_ASSERT( m_nControlRead <= 1);
  1581. SET_UF( this, CONTROL_READ); // a read will be pending
  1582. if ( !m_AioControlConnection.ReadFile(QueryReceiveBuffer(),
  1583. QueryReceiveBufferSize())
  1584. ) {
  1585. CLEAR_UF( this, CONTROL_READ); // since read failed.
  1586. DBG_REQUIRE( DeReference() > 0);
  1587. InterlockedDecrement( &m_nControlRead);
  1588. DWORD dwError = GetLastError();
  1589. IF_DEBUG( ERROR) {
  1590. DBGPRINTF( ( DBG_CONTEXT,
  1591. " User( %08x)::ReadCommand() failed. Ref = %d."
  1592. " Error = %d\n",
  1593. this, QueryReference(), dwError));
  1594. }
  1595. SetLastError( dwError);
  1596. fReturn = FALSE;
  1597. }
  1598. }
  1599. return ( fReturn);
  1600. } // USER_DATA::ReadCommand()
  1601. BOOL
  1602. USER_DATA::DisconnectUserWithError(IN DWORD dwError,
  1603. IN BOOL fNextMsg OPTIONAL)
  1604. /*++
  1605. This function disconnects a user with the error code provided.
  1606. It closes down the control connection by stopping ASYNC_IO.
  1607. If the fNextMsg is not set, then it also decrements the reference count
  1608. for the user data object, to be freed soon.
  1609. --*/
  1610. {
  1611. CHAR szBuffer[120];
  1612. # if DBG
  1613. if ( !IS_VALID_USER_DATA( this)) {
  1614. DBGPRINTF( ( DBG_CONTEXT,
  1615. "Encountering an invalid user data ( %08x)\n",
  1616. this));
  1617. Print();
  1618. }
  1619. # endif // DBG
  1620. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1621. IF_DEBUG ( CLIENT) {
  1622. DBGPRINTF( ( DBG_CONTEXT,
  1623. " USER_DATA( %08x)::DisconnectUserWithError( %lu, %d)."
  1624. " RefCount = %d\n",
  1625. this, dwError, fNextMsg, QueryReference()));
  1626. }
  1627. if (!fNextMsg) {
  1628. RemoveActiveReference();
  1629. }
  1630. LockUser();
  1631. if ( QueryState() == UserStateDisconnected) {
  1632. //
  1633. // It is already in disconnected state. Do nothing for disconnect.
  1634. //
  1635. UnlockUser();
  1636. } else {
  1637. SetState( UserStateDisconnected );
  1638. UnlockUser();
  1639. //
  1640. // terminate outstanding asynchronous AD IO queries for root directory
  1641. //
  1642. if ( m_pAdioReq != NULL) {
  1643. m_pAdioReq->EndRequest();
  1644. m_pAdioReq = NULL;
  1645. }
  1646. if( dwError == ERROR_SEM_TIMEOUT) {
  1647. const CHAR * apszSubStrings[3];
  1648. IF_DEBUG( CLIENT )
  1649. {
  1650. DBGPRINTF(( DBG_CONTEXT,
  1651. "client (%08x) timed-out\n", this ));
  1652. }
  1653. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  1654. "%lu", m_pInstance->QueryConnectionTimeout() ) > 0);
  1655. apszSubStrings[0] = QueryUserName();
  1656. apszSubStrings[1] = inet_ntoa( HostIpAddress );
  1657. apszSubStrings[2] = szBuffer;
  1658. g_pInetSvc->LogEvent( FTPD_EVENT_CLIENT_TIMEOUT,
  1659. 3,
  1660. apszSubStrings,
  1661. 0 );
  1662. ReplyToUser(this,
  1663. REPLY_SERVICE_NOT_AVAILABLE,
  1664. "Timeout (%lu seconds): closing control connection.",
  1665. m_pInstance->QueryConnectionTimeout() );
  1666. }
  1667. if ( dwError != NO_ERROR) {
  1668. # ifdef CHECK_DBG
  1669. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  1670. " Control Socket Error=%u ", dwError) > 0);
  1671. Print( szBuffer);
  1672. # endif // CHECK_DBG
  1673. if( dwError != ERROR_SEM_TIMEOUT ) {
  1674. SetLastReplyCode( REPLY_TRANSFER_ABORTED );
  1675. }
  1676. // Produce a log record indicating the cause for failure.
  1677. WriteLogRecord( PSZ_CONNECTION_CLOSED_VERB, "", dwError);
  1678. }
  1679. //
  1680. // Force close the connection's sockets. This will cause the
  1681. // thread to awaken from any blocked socket operation. It
  1682. // is the destructor's responsibility to do any further cleanup.
  1683. // (such as calling UserDereference()).
  1684. //
  1685. CloseSockets(dwError != NO_ERROR);
  1686. }
  1687. return ( TRUE);
  1688. } // USER_DATA::DisconnectUserWithError()
  1689. static BOOL
  1690. DisconnectUserWorker( IN LPUSER_DATA pUserData, IN LPVOID pContext)
  1691. /*++
  1692. This disconnects (logically) a user connection, by resetting the
  1693. control connection and stopping IO. Later on the blown away socket
  1694. will cause an ATQ relinquish to occur to blow away of this connection.
  1695. Arguments:
  1696. pUserData pointer to User data object for connection to be disconnected.
  1697. pContext pointer to context information
  1698. ( in this case to DWORD containing error code indicating reasong for
  1699. disconnect).
  1700. Returns:
  1701. TRUE on success and FALSE if there is any failure.
  1702. --*/
  1703. {
  1704. DWORD dwError;
  1705. BOOL retVal;
  1706. DBG_ASSERT( pContext != NULL && pUserData != NULL);
  1707. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1708. dwError = *(LPDWORD ) pContext;
  1709. retVal = pUserData->DisconnectUserWithError( dwError, TRUE);
  1710. // fix for bug 268175 : if we disconnected user we need to do normal cleanup
  1711. // for that connection
  1712. // this check is not very necessary but I leave it for future
  1713. // DisconnectUserWithError always returns TRUE
  1714. if (retVal)
  1715. {
  1716. DereferenceUserDataAndKill(pUserData);
  1717. }
  1718. return retVal;
  1719. } // DisconnectUserWorker()
  1720. BOOL
  1721. DisconnectUser( IN DWORD UserId, FTP_SERVER_INSTANCE *pInstance )
  1722. /*++
  1723. This function disconnects a specified user identified using the UserId.
  1724. If UserId specified == 0, then all the users will be disconnected.
  1725. Arguments:
  1726. UserId user id for the connection to be disconnected.
  1727. Returns:
  1728. TRUE if atleast one of the connections is disconnected.
  1729. FALSE if no user connetion found.
  1730. History:
  1731. 06-April-1995 Created.
  1732. --*/
  1733. {
  1734. BOOL fFound;
  1735. DWORD dwContext = ERROR_SERVER_DISABLED;
  1736. pInstance->Reference();
  1737. pInstance->LockConnectionsList();
  1738. fFound = ( pInstance->
  1739. EnumerateConnection( DisconnectUserWorker,
  1740. (LPVOID ) &dwContext,
  1741. UserId));
  1742. pInstance->UnlockConnectionsList();
  1743. pInstance->Dereference();
  1744. IF_DEBUG( CLIENT) {
  1745. DWORD dwError = (fFound) ? NO_ERROR: GetLastError();
  1746. DBGPRINTF( ( DBG_CONTEXT,
  1747. " DisconnectUser( %d) returns %d. Error = %lu\n",
  1748. UserId, fFound, dwError));
  1749. if (fFound) { SetLastError( dwError); }
  1750. }
  1751. return ( fFound);
  1752. } // DisconnectUser()
  1753. static BOOL
  1754. DisconnectUserWithNoAccessWorker( IN LPUSER_DATA pUserData,
  1755. IN LPVOID pContext)
  1756. /*++
  1757. This disconnects (logically) a user connection with no access.
  1758. This occurs by resetting the control connection and stopping IO.
  1759. Later on the blown away thread
  1760. will cause an ATQ relinquish to occur to blow away of this connection.
  1761. Arguments:
  1762. pUserData pointer to User data object for connection to be disconnected.
  1763. pContext pointer to context information
  1764. ( in this case to DWORD containing error code indicating reasong for
  1765. disconnect).
  1766. Returns:
  1767. TRUE on success and FALSE if there is any failure.
  1768. --*/
  1769. {
  1770. BOOL fSuccess = TRUE;
  1771. DBG_ASSERT( pUserData != NULL);
  1772. // Ignode the pContext information.
  1773. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1774. //
  1775. // We're only interested in connected users.
  1776. //
  1777. if( pUserData->IsLoggedOn()) {
  1778. //
  1779. // If this user no longer has access to their
  1780. // current directory, blow them away.
  1781. //
  1782. if( !pUserData->VirtualPathAccessCheck(AccessTypeRead )) {
  1783. const CHAR * apszSubStrings[2];
  1784. IF_DEBUG( SECURITY ) {
  1785. DBGPRINTF(( DBG_CONTEXT,
  1786. "User %s (%lu) @ %08lX retroactively"
  1787. " denied access to %s\n",
  1788. pUserData->QueryUserName(),
  1789. pUserData->QueryId(),
  1790. pUserData,
  1791. pUserData->QueryCurrentDirectory().QueryStr() ));
  1792. }
  1793. fSuccess = ( pUserData->
  1794. DisconnectUserWithError(ERROR_ACCESS_DENIED,
  1795. TRUE)
  1796. );
  1797. //
  1798. // Log an event to tell the admin what happened.
  1799. //
  1800. apszSubStrings[0] = pUserData->QueryUserName();
  1801. apszSubStrings[1] = pUserData->QueryCurrentDirectory().QueryStr();
  1802. g_pInetSvc->LogEvent( FTPD_EVENT_RETRO_ACCESS_DENIED,
  1803. 2,
  1804. apszSubStrings,
  1805. 0 );
  1806. } // no access
  1807. } // logged on user
  1808. IF_DEBUG( CLIENT) {
  1809. DWORD dwError = (fSuccess) ? NO_ERROR: GetLastError();
  1810. DBGPRINTF( ( DBG_CONTEXT,
  1811. " DisconnectUsersWithNoAccessWorker( %d) returns %d."
  1812. " Error = %lu\n",
  1813. pUserData->QueryId(), fSuccess,
  1814. dwError)
  1815. );
  1816. if (fSuccess) { SetLastError( dwError); }
  1817. }
  1818. return ( fSuccess);
  1819. } // DisconnectUserWithNoAccessWorker()
  1820. VOID
  1821. DisconnectUsersWithNoAccess(FTP_SERVER_INSTANCE *pInstance )
  1822. /*++
  1823. This function disconnects all users who do not have read access to
  1824. their current directory. This is typically called when the access masks
  1825. have been changed.
  1826. Arguments:
  1827. None
  1828. Returns:
  1829. None.
  1830. --*/
  1831. {
  1832. BOOL fFound;
  1833. DWORD dwContext = ERROR_ACCESS_DENIED;
  1834. pInstance->Reference();
  1835. pInstance->LockConnectionsList();
  1836. fFound = ( pInstance->
  1837. EnumerateConnection( DisconnectUserWithNoAccessWorker,
  1838. (LPVOID ) &dwContext,
  1839. 0));
  1840. pInstance->UnlockConnectionsList();
  1841. pInstance->Dereference();
  1842. IF_DEBUG( CLIENT) {
  1843. DWORD dwError = (fFound) ? NO_ERROR: GetLastError();
  1844. DBGPRINTF( ( DBG_CONTEXT,
  1845. " DisconnectUsersWithNoAccess() returns %d."
  1846. " Error = %lu\n",
  1847. fFound, dwError)
  1848. );
  1849. if (fFound) { SetLastError( dwError); }
  1850. }
  1851. } // DisconnectUsersWithNoAccess
  1852. /*++
  1853. The following structure UserEnumBuffer is required to carry the context
  1854. information for enumerating the users currently connected.
  1855. It contains a pointer to array of USER_INFO structures which contain the
  1856. specific information for the user. The user name is stored in the buffer
  1857. from the end ( so that null terminated strings are formed back to back.
  1858. This permits efficient storage of variable length strings.
  1859. The member fResult is used to carry forward the partial result of
  1860. success/failure from one user to another ( since the enumeration has
  1861. to walk through all the elements to find out all user information).
  1862. History: MuraliK ( 12-April-1995)
  1863. --*/
  1864. struct USER_ENUM_BUFFER {
  1865. DWORD cbSize; // pointer to dword containing size of
  1866. IIS_USER_INFO_1 * pUserInfo; // pointer to start of array of USER_INFO
  1867. DWORD cbRequired; // incremental count of bytes required.
  1868. DWORD nEntry; // number of current entry ( index into pUserInfo)
  1869. DWORD dwCurrentTime; // current time
  1870. WCHAR * pszNext; // pointer to next string location.
  1871. BOOL fResult; // boolean flag accumulating partial results
  1872. };
  1873. typedef USER_ENUM_BUFFER * PUSER_ENUM_BUFFER;
  1874. BOOL
  1875. EnumerateUserInBufferWorker( IN LPUSER_DATA pUserData,
  1876. IN LPVOID pContext)
  1877. {
  1878. # ifdef CHECK_DBG
  1879. CHAR szBuffer[400];
  1880. # endif // CHECK_DBG
  1881. PUSER_ENUM_BUFFER pUserEnumBuffer = (PUSER_ENUM_BUFFER ) pContext;
  1882. DWORD tConnect;
  1883. DWORD cbUserName;
  1884. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1885. //
  1886. // We're only interested in connected users.
  1887. //
  1888. if( pUserData->IsDisconnected()) {
  1889. return ( TRUE);
  1890. }
  1891. //
  1892. // Determine required buffer size for current user.
  1893. //
  1894. cbUserName = ( strlen( pUserData->QueryUserName() ) + 1 ) * sizeof(WCHAR);
  1895. pUserEnumBuffer->cbRequired += sizeof(IIS_USER_INFO_1);
  1896. //
  1897. // If there's room for the user data, store it.
  1898. //
  1899. tConnect = ( pUserEnumBuffer->dwCurrentTime -
  1900. pUserData->QueryTimeAtConnection());
  1901. if( pUserEnumBuffer->fResult &&
  1902. ( pUserEnumBuffer->cbRequired <= pUserEnumBuffer->cbSize)
  1903. ) {
  1904. LPIIS_USER_INFO_1 pUserInfo =
  1905. &pUserEnumBuffer->pUserInfo[ pUserEnumBuffer->nEntry];
  1906. pUserInfo->idUser = pUserData->QueryId();
  1907. pUserInfo->pszUser = (WCHAR *)MIDL_user_allocate( cbUserName );
  1908. if( pUserInfo->pszUser ) {
  1909. pUserInfo->fAnonymous = ( pUserData->Flags & UF_ANONYMOUS ) != 0;
  1910. pUserInfo->inetHost = (DWORD)pUserData->HostIpAddress.s_addr;
  1911. pUserInfo->tConnect = tConnect;
  1912. if( !MultiByteToWideChar( CP_OEMCP,
  1913. 0,
  1914. pUserData->QueryUserName(),
  1915. -1,
  1916. pUserInfo->pszUser,
  1917. (int)cbUserName )
  1918. ) {
  1919. DBGPRINTF(( DBG_CONTEXT,
  1920. "MultiByteToWideChar failed???\n" ));
  1921. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1922. } else {
  1923. pUserEnumBuffer->nEntry++;
  1924. }
  1925. }
  1926. else {
  1927. //
  1928. // Unable to allocate memory
  1929. //
  1930. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1931. }
  1932. } else {
  1933. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1934. }
  1935. # ifdef CHECK_DBG
  1936. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  1937. " Enum tLastAction=%u; tConnect=%u. " ,
  1938. ( pUserEnumBuffer->dwCurrentTime -
  1939. pUserData->QueryTimeAtLastAccess()),
  1940. tConnect
  1941. ) > 0);
  1942. pUserData->Print( szBuffer);
  1943. # endif // CHECK_DBG
  1944. return ( TRUE);
  1945. } // EnumerateUserInBufferWorker()
  1946. BOOL
  1947. EnumerateUsers(
  1948. PCHAR pBuffer,
  1949. PDWORD pcbBuffer,
  1950. PDWORD nRead,
  1951. FTP_SERVER_INSTANCE *pInstance
  1952. )
  1953. /*++
  1954. Enumerates the current active users into the specified buffer.
  1955. Arguments:
  1956. pvEnum pointer to enumeration buffer which will receive the number of
  1957. entries and the user information.
  1958. pcbBuffer pointer to count of bytes. On entry this contains the size in
  1959. bytes of the enumeration buffer. It receives the count
  1960. of bytes for enumerating all the users.
  1961. nRead - pointer to a DWORD to return the number of user entries filled.
  1962. Returns:
  1963. TRUE if enumeration is successful ( all connected users accounted for)
  1964. FALSE otherwise
  1965. --*/
  1966. {
  1967. USER_ENUM_BUFFER userEnumBuffer;
  1968. BOOL fSuccess;
  1969. DBG_ASSERT( pcbBuffer != NULL );
  1970. IF_DEBUG( USER_DATABASE) {
  1971. DBGPRINTF( ( DBG_CONTEXT,
  1972. " Entering EnumerateUsers( %08x, %08x[%d]).\n",
  1973. pBuffer, pcbBuffer, *pcbBuffer));
  1974. }
  1975. //
  1976. // Setup the data in user enumeration buffer.
  1977. //
  1978. userEnumBuffer.cbSize = *pcbBuffer;
  1979. userEnumBuffer.cbRequired = 0;
  1980. userEnumBuffer.pUserInfo = (LPIIS_USER_INFO_1)pBuffer;
  1981. userEnumBuffer.nEntry = 0;
  1982. userEnumBuffer.dwCurrentTime = GetCurrentTimeInSeconds();
  1983. userEnumBuffer.fResult = TRUE;
  1984. //
  1985. // CODEWORK
  1986. // This field is obsolete it now points to the extra CONN_LEEWAY
  1987. // buffer.
  1988. //
  1989. userEnumBuffer.pszNext = ((WCHAR *)( pBuffer + *pcbBuffer));
  1990. //
  1991. // Scan the users and get the information required.
  1992. //
  1993. pInstance->Reference();
  1994. pInstance->LockConnectionsList();
  1995. fSuccess = (pInstance->
  1996. EnumerateConnection( EnumerateUserInBufferWorker,
  1997. (LPVOID ) &userEnumBuffer,
  1998. 0));
  1999. pInstance->UnlockConnectionsList();
  2000. pInstance->Dereference();
  2001. //
  2002. // Update enum buffer header.
  2003. //
  2004. *nRead = userEnumBuffer.nEntry;
  2005. *pcbBuffer = userEnumBuffer.cbRequired;
  2006. IF_DEBUG( USER_DATABASE) {
  2007. DBGPRINTF((DBG_CONTEXT,
  2008. " Leaving EnumerateUsers() with %d."
  2009. " Entries read =%d. BufferSize required = %d\n",
  2010. userEnumBuffer.fResult,
  2011. userEnumBuffer.nEntry, userEnumBuffer.cbRequired));
  2012. }
  2013. return ( userEnumBuffer.fResult);
  2014. } // EnumerateUsers
  2015. SOCKERR
  2016. USER_DATA::SendMultilineMessage(
  2017. IN UINT nReplyCode,
  2018. IN LPCSTR pszzMessage,
  2019. IN BOOL fIsFirst,
  2020. IN BOOL fIsLast)
  2021. /*++
  2022. Sends a multiline message to the control socket of the client.
  2023. Arguments:
  2024. nReplyCode the reply code to use for the first line of the multi-line
  2025. message.
  2026. pszzMessage pointer to double null terminated sequence of strings
  2027. containing the message to be sent.
  2028. fIsFirst flag to indicate we are starting the multiline reply. if FALSE,
  2029. don't print the code for the first line, as it was already emmited elsewhere
  2030. fIsLast flag to indicate we are finishing the multiline reply. if FALSE,
  2031. don't print the code for the first line, as it was already emmited elsewhere
  2032. If the message is empty, we do not print anything. If there is only one line, then if
  2033. fIsLast is TRUE, we only print the terminating line, otherwise we do print the openning
  2034. line if fIsFirst is TRUE.
  2035. Returns:
  2036. SOCKERR - 0 if successful, !0 if not.
  2037. History:
  2038. MuraliK 12-April-1995
  2039. --*/
  2040. {
  2041. SOCKERR serr = 0;
  2042. LPCSTR pszMsg, pszNext;
  2043. //
  2044. // return if there is nothing to send
  2045. //
  2046. if ( pszzMessage == NULL || *pszzMessage == '\0') {
  2047. return serr;
  2048. }
  2049. for ( pszMsg = pszzMessage; serr == 0 && *pszMsg != '\0'; pszMsg = pszNext) {
  2050. //
  2051. // find next message so that we can check of pszMsg is the last line
  2052. //
  2053. pszNext = pszMsg + strlen( pszMsg) + 1;
  2054. if( fIsLast && *pszNext == '\0' ) {
  2055. //
  2056. // This is globally the last line. Print it pefixed with the reply code.
  2057. //
  2058. serr = SockPrintf2(this, QueryControlSocket(),
  2059. "%u %s",
  2060. nReplyCode,
  2061. pszMsg);
  2062. } else if( fIsFirst ) {
  2063. //
  2064. // this is globally the first line of reply, and it is not globally the last one.
  2065. // print it with '-'.
  2066. //
  2067. serr = SockPrintf2(this, QueryControlSocket(),
  2068. "%u-%s",
  2069. nReplyCode,
  2070. pszMsg);
  2071. fIsFirst = FALSE;
  2072. } else {
  2073. //
  2074. // this is either an intermediate line, or the last line in this batch (but
  2075. // not globally), so print it idented without the reply code.
  2076. //
  2077. serr = SockPrintf2(this, QueryControlSocket(),
  2078. " %s",
  2079. pszMsg);
  2080. }
  2081. } // for
  2082. return ( serr);
  2083. } // USER_DATA::SendMultilineMessge()
  2084. SOCKERR
  2085. USER_DATA::SendDirectoryAnnotation( IN UINT ReplyCode, IN BOOL fIsFirst)
  2086. /*++
  2087. SYNOPSIS: Tries to open the FTPD_ANNOTATION_FILE (~~ftpsvc~~.ckm)
  2088. file in the user's current directory. If it can be
  2089. opened, it is sent to the user over the command socket
  2090. as a multi-line reply.
  2091. ENTRY:
  2092. ReplyCode - The reply code to send as the first line
  2093. of this multi-line reply.
  2094. fIsFirst - flag to indicate if this is the first line in the multi-line
  2095. reply. If not, the ReplyCode is not shown
  2096. RETURNS: SOCKERR - 0 if successful, !0 if not.
  2097. HISTORY:
  2098. KeithMo 06-May-1993 Created.
  2099. MuraliK 12-Apr-1995 Made it to be part of USER_DATA
  2100. --*/
  2101. {
  2102. FILE * pfile;
  2103. SOCKERR serr = 0;
  2104. CHAR szLine[MAX_REPLY_LENGTH+1];
  2105. //
  2106. // Try to open the annotation file.
  2107. //
  2108. pfile = Virtual_fopen( this,
  2109. FTPD_ANNOTATION_FILE,
  2110. "r" );
  2111. if( pfile == NULL )
  2112. {
  2113. //
  2114. // File not found. Blow it off.
  2115. //
  2116. return 0;
  2117. }
  2118. // protection agians attack when CKM file islarge, somebody is downloading it
  2119. // slowly on many connections and uses all ATQ threads. Note that attack is still possible
  2120. // but much more difficult to achieve
  2121. AtqSetInfo( AtqIncMaxPoolThreads, 0);
  2122. //
  2123. // While there's more text in the file, blast
  2124. // it to the user.
  2125. //
  2126. while( fgets( szLine, MAX_REPLY_LENGTH, pfile ) != NULL )
  2127. {
  2128. CHAR * pszTmp = szLine + strlen(szLine) - 1;
  2129. //
  2130. // Remove any trailing CR/LFs in the string.
  2131. //
  2132. while( ( pszTmp >= szLine ) &&
  2133. ( ( *pszTmp == '\n' ) || ( *pszTmp == '\r' ) ) )
  2134. {
  2135. *pszTmp-- = '\0';
  2136. }
  2137. //
  2138. // Ensure we send the proper prefix for the
  2139. // very *first* line of the file.
  2140. //
  2141. if( fIsFirst )
  2142. {
  2143. serr = SockPrintf2(this,
  2144. QueryControlSocket(),
  2145. "%u-%s",
  2146. ReplyCode,
  2147. szLine );
  2148. fIsFirst = FALSE;
  2149. }
  2150. else
  2151. {
  2152. serr = SockPrintf2(this,
  2153. QueryControlSocket(),
  2154. " %s",
  2155. szLine );
  2156. }
  2157. if( serr != 0 )
  2158. {
  2159. //
  2160. // Socket error sending file.
  2161. //
  2162. break;
  2163. }
  2164. }
  2165. AtqSetInfo( AtqDecMaxPoolThreads, 0);
  2166. //
  2167. // Cleanup.
  2168. //
  2169. if ( 0 != fclose( pfile )) {
  2170. IF_DEBUG( ERROR) {
  2171. DBGPRINTF(( DBG_CONTEXT,
  2172. "[%08x]::SendAnnotationFile() file close failed. "
  2173. " Error = %d\n",
  2174. this,
  2175. GetLastError()
  2176. ));
  2177. }
  2178. }
  2179. return serr;
  2180. } // USER_DATA::SendDirectoryAnnotation()
  2181. SOCKERR
  2182. USER_DATA::SendErrorToClient(
  2183. IN LPCSTR pszPath,
  2184. IN DWORD dwError,
  2185. IN LPCSTR pszDefaultErrorMsg,
  2186. IN UINT nReplyCode
  2187. )
  2188. /*++
  2189. Send an error message indicating that the path is not found or
  2190. a particular error occured in a path.
  2191. Arguments:
  2192. sock socket to be used for synchronously sending message
  2193. pszPath pointer to path to be used.
  2194. dwError DWORD containing the error code, used for getting error text.
  2195. pszDefaultErrorMsg pointer to null-terminated string containing the
  2196. error message to be used if we can't alloc error text.
  2197. nReplyCode UINT containing the FTP reply code.
  2198. Returns:
  2199. SOCKERR. 0 if successful and !0 if failure.
  2200. --*/
  2201. {
  2202. BOOL fDelete = TRUE;
  2203. LPCSTR pszText;
  2204. APIERR serr;
  2205. DBG_ASSERT( pszPath != NULL);
  2206. pszText = AllocErrorText( dwError );
  2207. if( pszText == NULL ) {
  2208. pszText = pszDefaultErrorMsg;
  2209. fDelete = FALSE;
  2210. }
  2211. serr = ReplyToUser( this,
  2212. nReplyCode,
  2213. PSZ_FILE_ERROR,
  2214. pszPath,
  2215. pszText );
  2216. if( fDelete ) {
  2217. FreeErrorText( (char *) pszText );
  2218. }
  2219. return ( serr);
  2220. } // USER_DATA::SendErrorToClient()
  2221. BOOL
  2222. USER_DATA::FreeUserToken( VOID)
  2223. /*++
  2224. This function frees the user token if present already.
  2225. Otherwise does nothing.
  2226. --*/
  2227. {
  2228. BOOL fReturn = TRUE;
  2229. if( UserToken != NULL ) {
  2230. fReturn = TsDeleteUserToken( UserToken );
  2231. UserToken = NULL;
  2232. ::RevertToSelf();
  2233. }
  2234. return ( fReturn);
  2235. } // USER_DATA::FreeUserToken()
  2236. APIERR
  2237. USER_DATA::CdToUsersHomeDirectory()
  2238. /*++
  2239. This function changes user's home directory.
  2240. First, a CD to the virtual root is attempted.
  2241. If this succeeds, a CD to pszUser is attempted.
  2242. If this fails, a CD to DEFAULT_SUB_DIRECTORY is attempted.
  2243. Returns:
  2244. APIERR. NO_ERROR on success.
  2245. --*/
  2246. {
  2247. APIERR err;
  2248. CHAR rgchRoot[MAX_PATH];
  2249. //
  2250. // Try the top-level home directory. If this fails, bag out.
  2251. // Set and try to change directory to symbolic root.
  2252. //
  2253. m_szCurrentDirectory.Reset(); // initially nothing.
  2254. m_pInstance->LockThisForRead();
  2255. DBG_ASSERT( strlen( m_pInstance->QueryRoot()) < MAX_PATH);
  2256. P_strncpy( rgchRoot, m_pInstance->QueryRoot(), MAX_PATH);
  2257. m_pInstance->UnlockThis();
  2258. err = VirtualChDir( this, rgchRoot); // change to default dir.
  2259. if ( (err == NO_ERROR) &&
  2260. (m_pInstance->QueryIsolationMode() == MD_USER_ISOLATION_NONE) ) {
  2261. //
  2262. // We successfully CD'd into the top-level home
  2263. // directory. Now see if we can CD into pszUser.
  2264. //
  2265. P_strncpy( rgchRoot, QueryRootDirectory().QueryStr(), MAX_PATH);
  2266. if ( !VirtualChDir( this, rgchRoot ) ) {
  2267. //
  2268. // Nope, try DEFAULT_SUB_DIRECTORY. If this fails, just
  2269. // hang-out at the top-level home directory.
  2270. //
  2271. P_strncpy( rgchRoot, PSZ_DEFAULT_SUB_DIRECTORY, MAX_PATH);
  2272. VirtualChDir( this, rgchRoot );
  2273. }
  2274. }
  2275. return ( err);
  2276. } // USER_DATA::CdToUsersHomeDirectory()
  2277. VOID
  2278. USER_DATA::SignalRootDirReady(
  2279. HANDLE hUserData,
  2280. DWORD dwResult)
  2281. /*++
  2282. Function that restarts processing the original command when a PASV data socket becomes
  2283. accept()'able [ie the client has made the connection]
  2284. Arguments:
  2285. pUserData - USER_DATA context attached to socket
  2286. Returns:
  2287. Nothing
  2288. --*/
  2289. {
  2290. LPUSER_DATA pUserData = (LPUSER_DATA)hUserData;
  2291. PATQ_CONTEXT pAtqContext = pUserData->QueryControlAio()->QueryAtqContext();
  2292. pUserData->SetRootDirStatus( dwResult );
  2293. pUserData->SetInFakeIOCompletion( TRUE );
  2294. pUserData->m_pAdioReq = NULL; // we no longer need this
  2295. //
  2296. // do a scary thing - fake an IO completion, to trigger re-processing of the FTP command
  2297. //
  2298. if ( !AtqPostCompletionStatus( pAtqContext,
  2299. strlen( pUserData->QueryCmdString() ) + 1 ) )
  2300. {
  2301. DBGPRINTF((DBG_CONTEXT,
  2302. "Failed to post fake completion status to deal with async ADIO : 0x%x\n",
  2303. GetLastError()));
  2304. }
  2305. }
  2306. APIERR
  2307. USER_DATA::SetRootDirectory(
  2308. LPSTR pszCmd)
  2309. /*++
  2310. this function sets the home directory for the logging in user,
  2311. based on the current isolation mode. If we are in no-isolation mode,
  2312. the user name excluding domain name is used. In basic isolation mode <domain>\<user>
  2313. is used for domain users, and local\<user> for local domain users. The
  2314. anonymous user gets 'LocalUser\public' in the isolation mode, and pszAnonymousName in
  2315. the no-isolation mode. For AD isolatin mode, the home directory is
  2316. retrieved from the AD
  2317. Arguments:
  2318. pszCmd Pointer to command line. we need to preserve it for an asynchronous call
  2319. Returns:
  2320. NO_ERROR on success, WinError otherwise.
  2321. --*/
  2322. {
  2323. APIERR RetVal = NO_ERROR;
  2324. PSTR pszUser;
  2325. PSTR pszDomain;
  2326. BOOL fIsLocalUser;
  2327. CHAR szDomainAndUser[ UNLEN + DNLEN + 2 ];
  2328. //
  2329. // handle anonymous users first
  2330. //
  2331. if( TEST_UF( this, ANONYMOUS ) ) {
  2332. switch (m_pInstance->QueryIsolationMode()) {
  2333. case MD_USER_ISOLATION_NONE:
  2334. //
  2335. // Anonymous users are mapped to a static name directory.
  2336. //
  2337. m_strRootDir.Copy( '\\' );
  2338. m_strRootDir.Append( PSZ_ANONYMOUS_NAME);
  2339. break;
  2340. case MD_USER_ISOLATION_BASIC:
  2341. //
  2342. // Anonymous users are mapped to a static named directory
  2343. // under the local accounts directory.
  2344. //
  2345. m_strRootDir.Copy( '\\' );
  2346. m_strRootDir.Append( PSZ_LOCALUSER_DIR, sizeof( PSZ_LOCALUSER_DIR ) - 1 );
  2347. m_strRootDir.Append( PSZ_ANONYMOUS_DIR, sizeof( PSZ_ANONYMOUS_DIR ) - 1 );
  2348. break;
  2349. case MD_USER_ISOLATION_AD:
  2350. DBG_ASSERT( m_pInstance->QueryAdIo() != NULL );
  2351. RetVal = m_pInstance->QueryAnonymousHomeDir( m_strRootDir );
  2352. break;
  2353. default:
  2354. DBG_ASSERT( FALSE );
  2355. RetVal = ERROR_INTERNAL_ERROR;
  2356. }
  2357. goto ExitPoint;
  2358. }
  2359. //
  2360. // now, on to authenticated users. find the logged on user full name
  2361. //
  2362. if( !ImpersonateUser()) {
  2363. //
  2364. // Impersonation failure.
  2365. //
  2366. RetVal = ERROR_ACCESS_DENIED;
  2367. goto ExitPoint;
  2368. } else {
  2369. DWORD dwSize = sizeof(szDomainAndUser);
  2370. if (!GetUserNameEx(NameSamCompatible,
  2371. szDomainAndUser,
  2372. &dwSize)) {
  2373. RetVal = GetLastError();
  2374. }
  2375. RevertToSelf();
  2376. if (RetVal != ERROR_SUCCESS) {
  2377. goto ExitPoint;
  2378. }
  2379. }
  2380. if (!ParseUserName(
  2381. szDomainAndUser,
  2382. &pszUser,
  2383. &pszDomain,
  2384. m_pInstance->QueryLocalHostName(),
  2385. &fIsLocalUser)) {
  2386. RetVal = ERROR_INVALID_PARAMETER;
  2387. goto ExitPoint;
  2388. }
  2389. switch (m_pInstance->QueryIsolationMode()) {
  2390. case MD_USER_ISOLATION_NONE:
  2391. //
  2392. // in the MD_USER_ISOLATION_NONE mode, compatible with pre 6.0 functionality, we set
  2393. // the root directory to the user name, excluding the domain (if provided).
  2394. //
  2395. m_strRootDir.Copy( '\\' );
  2396. m_strRootDir.Append( pszUser );
  2397. break;
  2398. case MD_USER_ISOLATION_BASIC:
  2399. //
  2400. // in the MD_USER_ISOLATION_BASIC mode, users logging in without specifying a domain
  2401. // are mapped to their user name under a static local directory name, while users
  2402. // logging in with a domain name are mapped to their user name directory under the
  2403. // domain name directory.
  2404. //
  2405. m_strRootDir.Copy( '\\' );
  2406. if ( fIsLocalUser ) {
  2407. //
  2408. // local user
  2409. //
  2410. m_strRootDir.Append( PSZ_LOCALUSER_DIR, sizeof( PSZ_LOCALUSER_DIR ) - 1 );
  2411. m_strRootDir.Append( pszUser );
  2412. } else {
  2413. //
  2414. // domain user
  2415. //
  2416. m_strRootDir.Append( pszDomain );
  2417. m_strRootDir.Append( "\\", 1 );
  2418. m_strRootDir.Append( pszUser );
  2419. }
  2420. break;
  2421. case MD_USER_ISOLATION_AD:
  2422. DBG_ASSERT( m_pInstance->QueryAdIo() != NULL );
  2423. if ( fIsLocalUser) {
  2424. //
  2425. // we don't allow local user in this isolation mode
  2426. //
  2427. RetVal = ERROR_INVALID_PARAMETER;
  2428. break;
  2429. }
  2430. //
  2431. // make a async call to retrieve the properties from AD
  2432. //
  2433. Reference(); // ++ reference before issuing an async call
  2434. RetVal = m_pInstance->QueryUserHomeDir(
  2435. pszUser,
  2436. pszDomain,
  2437. &m_strRootDir,
  2438. &m_pAdioReq,
  2439. &SignalRootDirReady,
  2440. (HANDLE)this);
  2441. if (RetVal != ERROR_IO_PENDING) {
  2442. //
  2443. // remove the reference as the async call failed
  2444. //
  2445. DBG_REQUIRE( DeReference() > 0 );
  2446. }
  2447. break;
  2448. default:
  2449. DBG_ASSERT( FALSE );
  2450. RetVal = ERROR_INTERNAL_ERROR;
  2451. }
  2452. ExitPoint:
  2453. SetRootDirStatus( RetVal );
  2454. return RetVal;
  2455. }
  2456. APIERR
  2457. USER_DATA::OpenFileForSend( IN LPSTR pszFile)
  2458. /*++
  2459. Open an existing file for transmission using TransmitFile.
  2460. This function converts the given relative path into canonicalized full
  2461. path and opens the file through the cached file handles manager.
  2462. Arguments:
  2463. pszFile pointer to null-terminated string containing the file name
  2464. Returns:
  2465. TRUE on success and FALSE if any failure.
  2466. --*/
  2467. {
  2468. APIERR err;
  2469. CHAR szCanonPath[MAX_PATH+1];
  2470. DWORD cbSize = sizeof(szCanonPath);
  2471. CHAR szVirtualPath[MAX_PATH+1];
  2472. DWORD ccbVirtualPath = sizeof(szVirtualPath);
  2473. DBG_ASSERT( pszFile != NULL );
  2474. //
  2475. // Close any file we might have open now
  2476. // N.B. There shouldn't be an open file; we're just
  2477. // being careful here.
  2478. //
  2479. if (m_pOpenFileInfo) {
  2480. DBGPRINTF(( DBG_CONTEXT,
  2481. "WARNING!! Closing [%08x], before opening %s\n",
  2482. pszFile
  2483. ));
  2484. DBG_REQUIRE( CloseFileForSend() );
  2485. }
  2486. //
  2487. // Open the requested file
  2488. //
  2489. err = VirtualCanonicalize(szCanonPath,
  2490. &cbSize,
  2491. pszFile,
  2492. AccessTypeRead,
  2493. NULL,
  2494. szVirtualPath,
  2495. &ccbVirtualPath);
  2496. if( err == NO_ERROR ) {
  2497. DWORD dwCreateFlags = 0;
  2498. IF_DEBUG( VIRTUAL_IO ) {
  2499. DBGPRINTF(( DBG_CONTEXT,
  2500. "Opening File: %s\n", szCanonPath ));
  2501. }
  2502. // store the virtual path name of file.
  2503. SetVirtualFileName( szVirtualPath );
  2504. dwCreateFlags = TS_FORBID_SHORT_NAMES | TS_NOT_IMPERSONATED ;
  2505. if ( !m_pMetaData || m_pMetaData->QueryDoCache() )
  2506. {
  2507. dwCreateFlags |= TS_CACHING_DESIRED;
  2508. }
  2509. m_pOpenFileInfo = TsCreateFile( m_pInstance->GetTsvcCache(),
  2510. szCanonPath,
  2511. QueryImpersonationToken(),
  2512. dwCreateFlags
  2513. );
  2514. // caching desired.
  2515. if( m_pOpenFileInfo == NULL ) {
  2516. err = GetLastError();
  2517. } else {
  2518. DWORD dwAttrib = m_pOpenFileInfo->QueryAttributes();
  2519. FacIncrement( FacFilesOpened);
  2520. DBG_ASSERT( dwAttrib != 0xffffffff);
  2521. if (dwAttrib == 0xFFFFFFFF || // invalid attributes
  2522. dwAttrib & (FILE_ATTRIBUTE_DIRECTORY |
  2523. FILE_ATTRIBUTE_HIDDEN |
  2524. FILE_ATTRIBUTE_SYSTEM)
  2525. ) {
  2526. FacIncrement( FacFilesInvalid);
  2527. err = ERROR_FILE_NOT_FOUND;
  2528. }
  2529. }
  2530. }
  2531. if( err != NO_ERROR ) {
  2532. IF_DEBUG( VIRTUAL_IO ) {
  2533. DBGPRINTF(( DBG_CONTEXT,
  2534. "cannot open %s, error %lu\n",
  2535. pszFile,
  2536. err ));
  2537. }
  2538. }
  2539. return ( err);
  2540. } // USER_DATA::OpenFileForSend()
  2541. BOOL
  2542. USER_DATA::CloseFileForSend( IN DWORD dwError, IN BOOL NoLog)
  2543. {
  2544. BOOL fReturn = TRUE;
  2545. TS_OPEN_FILE_INFO * pOpenFileInfo;
  2546. // make sure it includes the full path
  2547. DBG_ASSERT( m_rgchFile[0] == '/');
  2548. pOpenFileInfo = (TS_OPEN_FILE_INFO *) InterlockedExchangePointer(
  2549. (PVOID *) &m_pOpenFileInfo,
  2550. NULL
  2551. );
  2552. if ( pOpenFileInfo != NULL) {
  2553. //
  2554. // Fabricate an appropriate reply code based on the incoming
  2555. // error code. WriteLogRecord() will pick up this reply code
  2556. // and use it in the activity log.
  2557. //
  2558. SetLastReplyCode(
  2559. ( dwError == NO_ERROR )
  2560. ? REPLY_TRANSFER_OK
  2561. : REPLY_TRANSFER_ABORTED
  2562. );
  2563. FacIncrement( FacFilesClosed);
  2564. TsCloseHandle( m_pInstance->GetTsvcCache(), pOpenFileInfo);
  2565. if ( !NoLog) {
  2566. WriteLogRecord( PSZ_SENT_VERB, m_rgchFile, dwError);
  2567. }
  2568. }
  2569. return ( fReturn);
  2570. } // USER_DATA::CloseFileForSend()
  2571. BOOL
  2572. USER_DATA::CloseFileForReceive( IN DWORD dwError, IN BOOL NoLog)
  2573. {
  2574. BOOL fAsyncFileOp;
  2575. fAsyncFileOp = TEST_UF( this, ASYNC_UPLOAD);
  2576. CLEAR_UF( this, ASYNC_UPLOAD );
  2577. if ( fAsyncFileOp) {
  2578. m_AioDataFile.StopIo(dwError);
  2579. //
  2580. // Fabricate an appropriate reply code based on the incoming
  2581. // error code. WriteLogRecord() will pick up this reply code
  2582. // and use it in the activity log.
  2583. //
  2584. if ( !NoLog) {
  2585. SetLastReplyCode(
  2586. ( dwError == NO_ERROR )
  2587. ? REPLY_TRANSFER_OK
  2588. : REPLY_TRANSFER_ABORTED
  2589. );
  2590. WriteLogRecord( m_pszCmdVerb, m_rgchFile, dwError);
  2591. }
  2592. }
  2593. return TRUE;
  2594. } // USER_DATA::CloseFileForReceive()
  2595. # define MAX_ERROR_MESSAGE_LEN ( 500)
  2596. VOID
  2597. USER_DATA::WriteLogRecord( IN LPCSTR pszVerb,
  2598. IN LPCSTR pszPath,
  2599. IN DWORD dwError)
  2600. /*++
  2601. This function writes the log record for current request made to the
  2602. Ftp server by the client.
  2603. Arguments:
  2604. pszVerb - pointer to null-terminated string containing the verb
  2605. of operation done
  2606. pszPath - pointer to string containing the path for the verb
  2607. dwError - DWORD containing the error code for operation
  2608. Returns:
  2609. None.
  2610. --*/
  2611. {
  2612. INETLOG_INFORMATION ilRequest;
  2613. DWORD dwLog;
  2614. CHAR pszClientHostName[50];
  2615. CHAR pszServerIpAddress[50];
  2616. CHAR rgchRequest[MAX_PATH + 20];
  2617. INT cch;
  2618. static CHAR szFTPVersion[]="FTP";
  2619. BOOL fDontLog = m_pMetaData && m_pMetaData->DontLog();
  2620. if (!fDontLog)
  2621. {
  2622. //
  2623. // Fill in the information that needs to be logged.
  2624. //
  2625. ZeroMemory(&ilRequest, sizeof(ilRequest));
  2626. P_strncpy( pszClientHostName, (char *)QueryClientHostName(), 50);
  2627. ilRequest.pszClientHostName = pszClientHostName;
  2628. ilRequest.cbClientHostName = strlen(pszClientHostName);
  2629. ilRequest.pszClientUserName = (char *)QueryUserName();
  2630. P_strncpy( pszServerIpAddress, inet_ntoa( LocalIpAddress ), 50);
  2631. ilRequest.pszServerAddress = pszServerIpAddress;
  2632. ilRequest.msTimeForProcessing = QueryProcessingTime();
  2633. ilRequest.dwBytesSent = (DWORD)m_licbSent; // BUGBUG: need to log 64 bit
  2634. ilRequest.dwBytesRecvd = (DWORD)m_licbRecvd; // BUGBUG: need to log 64 bit
  2635. ilRequest.dwProtocolStatus = GetLastReplyCode();
  2636. ilRequest.dwWin32Status = dwError;
  2637. ilRequest.dwPort = ntohs ((WORD)LocalIpPort);
  2638. cch = _snprintf( rgchRequest, sizeof( rgchRequest ), "[%d]%s", QueryId(), pszVerb);
  2639. rgchRequest[ sizeof( rgchRequest) - 1] = '\0';
  2640. DBG_ASSERT( cch > 0 );
  2641. DBG_ASSERT( cch < MAX_PATH + 20);
  2642. ilRequest.pszOperation = rgchRequest;
  2643. if ( rgchRequest != NULL ) {
  2644. ilRequest.cbOperation = strlen(rgchRequest);
  2645. } else {
  2646. ilRequest.cbOperation = 0;
  2647. }
  2648. ilRequest.pszTarget = (char *)pszPath;
  2649. if ( pszPath != NULL ) {
  2650. ilRequest.cbTarget = strlen((char *)pszPath);
  2651. } else {
  2652. ilRequest.cbTarget = 0;
  2653. }
  2654. ilRequest.pszParameters = "";
  2655. ilRequest.pszVersion = szFTPVersion;
  2656. dwLog = m_pInstance->m_Logging.LogInformation( &ilRequest);
  2657. if ( dwLog != NO_ERROR) {
  2658. IF_DEBUG( ERROR) {
  2659. DBGPRINTF((DBG_CONTEXT,
  2660. " Unable to log information to logger. Error = %u\n",
  2661. dwLog));
  2662. DBGPRINTF((DBG_CONTEXT,
  2663. " Request From %s, User %s. Request = %s %s\n",
  2664. ilRequest.pszClientHostName,
  2665. ilRequest.pszClientUserName,
  2666. ilRequest.pszOperation,
  2667. ilRequest.pszTarget));
  2668. }
  2669. }
  2670. //
  2671. // LogInformation() should not fail.
  2672. // If it does fail, the TsvcInfo will gracefully suspend logging
  2673. // for now.
  2674. // We may want to gracefully handle the same.
  2675. //
  2676. }
  2677. m_licbRecvd = 0; // reset since we wrote the record
  2678. m_pInstance->QueryStatsObj()->UpdateTotalBytesSent( m_licbSent );
  2679. m_licbSent = 0;
  2680. return;
  2681. } // USER_DATA::WriteLogRecord()
  2682. VOID
  2683. USER_DATA::WriteLogRecordForSendError( DWORD dwError )
  2684. {
  2685. //
  2686. // We put this into its own method in this file so it can access
  2687. // the common PSZ_SENT_VERB global.
  2688. //
  2689. WriteLogRecord(
  2690. PSZ_SENT_VERB,
  2691. m_rgchFile,
  2692. dwError
  2693. );
  2694. } // USER_DATA::WriteLogRecordForSendError
  2695. //
  2696. // Private functions.
  2697. //
  2698. VOID
  2699. USER_DATA::CloseSockets(IN BOOL fWarnUser)
  2700. /*++
  2701. Closes sockets (data and control) opened by the user for this session.
  2702. Arguments:
  2703. fWarnUser - If TRUE, send the user a warning shot before closing
  2704. the sockets.
  2705. --*/
  2706. {
  2707. SOCKET ControlSocket;
  2708. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  2709. //
  2710. // Close any open sockets. It is very important to set
  2711. // PassiveDataListen socket & ControlSocket to INVALID_SOCKET
  2712. // *before* we actually close the sockets.
  2713. // Since this routine is called to
  2714. // disconnect a user, and may be called from the RPC thread,
  2715. // closing one of the sockets may cause the client thread
  2716. // to unblock and try to access the socket. Setting the
  2717. // values in the per-user area to INVALID_SOCKET before
  2718. // closing the sockets keeps this from being a problem.
  2719. //
  2720. // This was a problem created by the Select or WaitForMultipleObjects()
  2721. // Investigate if such race conditions occur with Asynchronous IO?
  2722. // NYI
  2723. //
  2724. CleanupPassiveSocket( TRUE );
  2725. //
  2726. // Get rid of the async io connection used for data transfer.
  2727. //
  2728. m_AioDataConnection.StopIo( NO_ERROR);
  2729. ControlSocket = QueryControlSocket();
  2730. if( ControlSocket != INVALID_SOCKET )
  2731. {
  2732. if( fWarnUser )
  2733. {
  2734. //
  2735. // Since this may be called in a context other than
  2736. // the user we're disconnecting, we cannot rely
  2737. // on the USER_DATA fields. So, we cannot call
  2738. // SockReply, so we'll kludge one together with
  2739. // SockPrintf2.
  2740. //
  2741. SockPrintf2( this,
  2742. ControlSocket,
  2743. "%d Terminating connection.",
  2744. REPLY_SERVICE_NOT_AVAILABLE );
  2745. }
  2746. StopControlIo(); // to stop the io on control socket.
  2747. }
  2748. return;
  2749. } // USER_DATA::CloseSockets()
  2750. /*******************************************************************
  2751. NAME: UserpGetNextId
  2752. SYNOPSIS: Returns the next available user id.
  2753. RETURNS: DWORD - The user id.
  2754. HISTORY:
  2755. KeithMo 23-Mar-1993 Created.
  2756. ********************************************************************/
  2757. DWORD
  2758. UserpGetNextId(
  2759. VOID
  2760. )
  2761. {
  2762. DWORD userId;
  2763. // Increment the global counter, avoiding it from becoming 0.
  2764. InterlockedIncrement( (LPLONG ) &p_NextUserId);
  2765. if ((userId = p_NextUserId) == 0) {
  2766. InterlockedIncrement( (LPLONG ) &p_NextUserId);
  2767. userId = p_NextUserId;
  2768. }
  2769. DBG_ASSERT( userId != 0);
  2770. return userId;
  2771. } // UserpGetNextId
  2772. VOID
  2773. USER_DATA::Print( IN LPCSTR pszMsg) const
  2774. /*++
  2775. Prints the UserData object in debug mode.
  2776. History:
  2777. MuraliK 28-March-1995 Created.
  2778. --*/
  2779. {
  2780. # ifdef CHECK_DBG
  2781. CHAR szBuffer[1000];
  2782. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  2783. "[%d] %s: {%u} \"%s\" State=%u. Ref=%u.\n"
  2784. " Ctrl sock=%u; Atq=%x. Data sock=%u; Atq=%x. CtrlRead=%u\n"
  2785. " LastCmd= \"%s\"\n",
  2786. GetCurrentThreadId(), pszMsg,
  2787. QueryId(), QueryUserName(),
  2788. QueryState(), QueryReference(),
  2789. QueryControlSocket(), m_AioControlConnection.QueryAtqContext(),
  2790. QueryDataSocket(), m_AioDataConnection.QueryAtqContext(),
  2791. TEST_UF( this, CONTROL_READ), m_recvBuffer
  2792. ) > 0);
  2793. OutputDebugString( szBuffer);
  2794. # endif // CHECK_DBG
  2795. CHKINFO( ( DBG_CONTEXT,
  2796. " Printing USER_DATA( %08x) Signature: %08x\n"
  2797. " RefCount = %08x; UserState = %08x;\n"
  2798. " ControlSocket = %08x; PassiveL = %08x\n"
  2799. " FileInfo@ = %08x; CurDir( %s) Handle = %08x\n"
  2800. " UserName = %s; UserToken = %08x; UserId = %u\n"
  2801. " Behaviour Flags = %08x; XferType = %d; XferMode = %d\n",
  2802. this, Signature, m_References, UserState,
  2803. QueryControlSocket(), m_sPassiveDataListen,
  2804. m_pOpenFileInfo, QueryCurrentDirectory().QueryStr(), CurrentDirHandle,
  2805. QueryUserName(), UserToken, QueryId(),
  2806. Flags, m_xferType, m_xferMode));
  2807. DBGPRINTF( ( DBG_CONTEXT,
  2808. " Local IpAddr = %s; HostIpAddr = %s; DataIpAddr = %s;\n"
  2809. " Port = %d; TimeAtConnection = %08x;\n",
  2810. inet_ntoa( LocalIpAddress), inet_ntoa( HostIpAddress),
  2811. inet_ntoa( DataIpAddress),
  2812. DataPort,
  2813. m_TimeAtConnection));
  2814. DBGPRINTF(( DBG_CONTEXT, " ASYNC_IO_CONN Control=%08x; Data=%08x\n",
  2815. &m_AioControlConnection, m_AioDataConnection));
  2816. IF_DEBUG( ASYNC_IO) {
  2817. # if DBG
  2818. m_AioControlConnection.Print();
  2819. m_AioDataConnection.Print();
  2820. # endif // DBG
  2821. }
  2822. return;
  2823. } // USER_DATA::Print()
  2824. BOOL
  2825. USER_DATA::VirtualPathAccessCheck(IN ACCESS_TYPE _access, IN char * pszPath)
  2826. /*++
  2827. checks to see if the access is allowed for accessing the path
  2828. using pszPath after canonicalizing it.
  2829. Arguments:
  2830. access the access desired
  2831. pszPath pointer to string containing the path
  2832. Returns:
  2833. TRUE on success and FALSE if there is any failure.
  2834. --*/
  2835. {
  2836. DWORD dwError;
  2837. CHAR rgchPath[MAX_PATH+1];
  2838. DWORD dwSize = sizeof(rgchPath);
  2839. // this following call converts the symbolic path into absolute
  2840. // and also does path access check.
  2841. dwError = VirtualCanonicalize(rgchPath, &dwSize,
  2842. pszPath, _access);
  2843. return ( dwError);
  2844. } // USER_DATA::VirtualPathAccessCheck()
  2845. APIERR
  2846. USER_DATA::VirtualCanonicalize(
  2847. OUT CHAR * pszDest,
  2848. IN OUT LPDWORD lpdwSize,
  2849. IN OUT CHAR * pszSearchPath,
  2850. IN ACCESS_TYPE _access,
  2851. OUT LPDWORD pdwAccessMask,
  2852. OUT CHAR * pchVirtualPath, /* OPTIONAL */
  2853. IN OUT LPDWORD lpccbVirtualPath /* OPTIONAL */
  2854. )
  2855. /*++
  2856. This function canonicalizes the path, taking into account the current
  2857. user's current directory value.
  2858. Arguments:
  2859. pszDest string that will on return contain the complete
  2860. canonicalized path. This buffer will be of size
  2861. specified in *lpdwSize.
  2862. lpdwSize Contains the size of the buffer pszDest on entry.
  2863. On return contains the number of bytes written
  2864. into the buffer or number of bytes required (incl. \0).
  2865. pszSearchPath pointer to string containing the path to be converted.
  2866. IF NULL, use the current directory only
  2867. accesss Access type for this path ( read, write, etc.)
  2868. pdwAccessMask pointer to DWORD which on succesful deciphering
  2869. will contain the access mask.
  2870. pchVirtualPath pointer to string which will contain the sanitized
  2871. virtual path on return (on success)
  2872. lpccbVirtualPath pointer to DWORD containing the length of buffer
  2873. (contains the length on return) incl. \0.
  2874. Returns:
  2875. Win32 Error Code - NO_ERROR on success
  2876. MuraliK 24-Apr-1995 Created.
  2877. --*/
  2878. {
  2879. DWORD dwError = NO_ERROR;
  2880. CHAR rgchVirtual[MAX_PATH+1];
  2881. DBG_ASSERT( pszDest != NULL);
  2882. DBG_ASSERT( lpdwSize != NULL);
  2883. DBG_ASSERT( pszSearchPath != NULL);
  2884. IF_DEBUG( VIRTUAL_IO) {
  2885. DBGPRINTF(( DBG_CONTEXT,
  2886. "UserData(%08x)::VirtualCanonicalize(%08x, %08x[%u],"
  2887. " %s, %d)\n",
  2888. this, pszDest, lpdwSize, *lpdwSize, pszSearchPath, _access));
  2889. }
  2890. if ( pdwAccessMask != NULL) {
  2891. *pdwAccessMask = 0;
  2892. }
  2893. //
  2894. // Form the virtual path for the given path.
  2895. //
  2896. if ( !IS_PATH_SEP( *pszSearchPath)) {
  2897. const CHAR * pszNewDir = QueryCurrentDirectory().QueryStr(); // get virtual dir.
  2898. //
  2899. // This is a relative path. append it to currrent directory
  2900. //
  2901. if ( _snprintf( rgchVirtual, sizeof(rgchVirtual) - 1, "%s/%s",
  2902. pszNewDir, pszSearchPath) < 0) {
  2903. // long path --> is not supported.
  2904. DBGPRINTF((DBG_CONTEXT, "Long Virtual Path %s---%s\n",
  2905. pszNewDir, pszSearchPath));
  2906. dwError = ERROR_PATH_NOT_FOUND;
  2907. } else {
  2908. rgchVirtual[ sizeof(rgchVirtual) - 1 ] = '\0';
  2909. pszSearchPath = rgchVirtual;
  2910. }
  2911. } else {
  2912. // This is an absolute virtual path.
  2913. // need to overwrite this virtual path with absolute
  2914. // path of the root. Do nothing.
  2915. }
  2916. if ( dwError == NO_ERROR) {
  2917. DWORD dwAccessMask = 0;
  2918. DBG_ASSERT( IS_PATH_SEP(*pszSearchPath));
  2919. //
  2920. // Now we have the complete symbolic path to the target file.
  2921. // Translate it into the absolute path
  2922. //
  2923. VirtualpSanitizePath( pszSearchPath);
  2924. if ( !LookupVirtualRoot( pszSearchPath,
  2925. pszDest,
  2926. lpdwSize,
  2927. &dwAccessMask ) ) {
  2928. dwError = GetLastError();
  2929. DBGPRINTF(( DBG_CONTEXT,
  2930. "LookupVirtualRoot Failed. Error = %d. pszDest = %s. BReq=%d\n",
  2931. dwError, pszDest, *lpdwSize));
  2932. } else if ( !PathAccessCheck( _access, dwAccessMask,
  2933. TEST_UF( this, READ_ACCESS),
  2934. TEST_UF( this, WRITE_ACCESS))
  2935. ) {
  2936. dwError = GetLastError();
  2937. DBGPRINTF(( DBG_CONTEXT,
  2938. "PathAccessCheck Failed. Error = %d. pszDest = %s\n",
  2939. dwError, pszDest));
  2940. } else if ( lpccbVirtualPath != NULL) {
  2941. // successful in getting the path.
  2942. DWORD cchVPath = strlen( pszSearchPath);
  2943. *lpccbVirtualPath = cchVPath + 1; // set the length to required size.
  2944. if ( *lpccbVirtualPath > cchVPath && pchVirtualPath != NULL) {
  2945. // copy the virtual path, since we have space.
  2946. CopyMemory( pchVirtualPath, pszSearchPath, cchVPath + 1);
  2947. } else {
  2948. dwError = ERROR_INSUFFICIENT_BUFFER;
  2949. }
  2950. }
  2951. if ( dwError == NO_ERROR ) {
  2952. // IP check
  2953. AC_RESULT acIpAccess;
  2954. AC_RESULT acDnsAccess;
  2955. BOOL fNeedDnsCheck;
  2956. BindPathAccessCheck();
  2957. acIpAccess = QueryAccessCheck()->CheckIpAccess( &fNeedDnsCheck );
  2958. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  2959. ((acIpAccess == AC_NOT_IN_GRANT_LIST) && !fNeedDnsCheck) ) {
  2960. dwError = ERROR_INCORRECT_ADDRESS;
  2961. }
  2962. else if ( fNeedDnsCheck ) {
  2963. if ( !QueryAccessCheck()->IsDnsResolved() ) {
  2964. BOOL fSync;
  2965. LPSTR pDns;
  2966. if ( !QueryAccessCheck()->QueryDnsName( &fSync,
  2967. (ADDRCHECKFUNCEX)NULL,
  2968. (ADDRCHECKARG)NULL,
  2969. &pDns ) ) {
  2970. dwError = ERROR_INCORRECT_ADDRESS;
  2971. }
  2972. }
  2973. if ( dwError == NO_ERROR ) {
  2974. acDnsAccess = QueryAccessCheck()->CheckDnsAccess();
  2975. if ( (acDnsAccess == AC_IN_DENY_LIST) ||
  2976. (acDnsAccess == AC_NOT_IN_GRANT_LIST) ||
  2977. ((m_acCheck == AC_NOT_IN_GRANT_LIST) &&
  2978. (acDnsAccess != AC_IN_GRANT_LIST) ) ) {
  2979. dwError = ERROR_INCORRECT_ADDRESS;
  2980. }
  2981. }
  2982. }
  2983. UnbindPathAccessCheck();
  2984. }
  2985. if ( pdwAccessMask != NULL) {
  2986. *pdwAccessMask = dwAccessMask;
  2987. }
  2988. }
  2989. IF_DEBUG( VIRTUAL_IO) {
  2990. if ( dwError != NO_ERROR) {
  2991. DBGPRINTF(( DBG_CONTEXT,
  2992. " Cannot Canonicalize %s -- %s, Error = %lu\n",
  2993. QueryCurrentDirectory().QueryStr(),
  2994. pszSearchPath,
  2995. dwError));
  2996. } else {
  2997. DBGPRINTF(( DBG_CONTEXT,
  2998. "Canonicalized path is: %s\n",
  2999. pszDest));
  3000. }
  3001. }
  3002. if ( dwError != NO_ERROR ) {
  3003. SetLastError( dwError );
  3004. }
  3005. return ( dwError);
  3006. } // USER_DATA::VirtualCanonicalize()
  3007. /*******************************************************************
  3008. ********************************************************************/
  3009. SOCKERR
  3010. USER_DATA::EstablishDataConnection(
  3011. IN LPCSTR pszReason,
  3012. IN LPCSTR pszSize
  3013. )
  3014. /*++
  3015. Connects to the client's data socket.
  3016. Arguments:
  3017. pszReason - The reason for the transfer (file list, get, put, etc).
  3018. pszSize - size of data being transferred.
  3019. Returns:
  3020. socket error code on any error.
  3021. --*/
  3022. {
  3023. SOCKERR serr = 0;
  3024. SOCKET DataSocket = INVALID_SOCKET;
  3025. BOOL fPassive;
  3026. BOOL fAcceptableSocket = FALSE;
  3027. //
  3028. // if we're in passive mode and aren't dealing with a fake IO completion [ie reprocessing
  3029. // the command], we just set up the event that will get signalled when the client
  3030. // actually connects.
  3031. //
  3032. if ( TEST_UF( this, PASSIVE ) &&
  3033. !QueryInFakeIOCompletion() )
  3034. {
  3035. //
  3036. // Ensure we actually created a passive listen data socket.
  3037. // no data transfer socket is in AsyncIo object.
  3038. //
  3039. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  3040. //
  3041. // To avoid blocking while waiting for the client to connect, we're going to use
  3042. // WSAEventSelect() to wait for the socket to be accept()'able.
  3043. //
  3044. //
  3045. if ( ( serr = AddPASVAcceptEvent( &fAcceptableSocket ) ) != 0 )
  3046. {
  3047. ReplyToUser( this,
  3048. REPLY_LOCAL_ERROR,
  3049. PSZ_TOO_MANY_PASV_USERS );
  3050. return ( serr );
  3051. }
  3052. //
  3053. // No need to wait around, we can call accept() on the socket right now
  3054. //
  3055. if ( fAcceptableSocket )
  3056. {
  3057. goto continue_label;
  3058. }
  3059. m_fWaitingForPASVConn = TRUE;
  3060. m_fHavePASVConn = FALSE;
  3061. return ERROR_IO_PENDING;
  3062. }
  3063. DBG_ASSERT( !TEST_UF(this, PASSIVE) || QueryInFakeIOCompletion() );
  3064. continue_label:
  3065. //
  3066. // Reset any oob flag.
  3067. //
  3068. CLEAR_UF( this, OOB_DATA );
  3069. //
  3070. // Capture the user's passive flag, then reset to FALSE.
  3071. //
  3072. fPassive = TEST_UF( this, PASSIVE );
  3073. CLEAR_UF( this, PASSIVE );
  3074. //
  3075. // If we're in passive mode, then accept a connection to
  3076. // the data socket.
  3077. //
  3078. // Calling accept() on this socket should -not- block because shouldn't get this
  3079. // far without being sure that calling accept() won't block - that's the point of
  3080. // jumping through the WSAEventSelect() hoops mentioned above
  3081. //
  3082. if( fPassive )
  3083. {
  3084. SOCKADDR_IN saddrClient;
  3085. //
  3086. // Ensure we actually created a passive listen data socket.
  3087. // no data transfer socket is in AsyncIo object.
  3088. //
  3089. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  3090. //
  3091. // Wait for a connection.
  3092. //
  3093. IF_DEBUG( CLIENT )
  3094. {
  3095. DBGPRINTF(( DBG_CONTEXT,
  3096. "waiting for passive connection on socket %d\n",
  3097. m_sPassiveDataListen ));
  3098. }
  3099. serr = AcceptSocket( m_sPassiveDataListen,
  3100. &DataSocket,
  3101. &saddrClient,
  3102. TRUE,
  3103. m_pInstance ); // enforce timeouts
  3104. //
  3105. // We can kill m_sPassiveDataListen now.
  3106. // We only allow one connection in passive mode.
  3107. //
  3108. CleanupPassiveSocket( TRUE );
  3109. // PASV Theft is disabled, so you MUST have the same IP
  3110. // address ad the Control Connection. The flag is renamed to
  3111. // enable_pasv_conn_from_3rdip
  3112. if (!(QueryInstance()->IsEnablePasvConnFrom3rdIP()))
  3113. {
  3114. if (!(HostIpAddress.S_un.S_addr == saddrClient.sin_addr.S_un.S_addr))
  3115. {
  3116. DBGPRINTF(( DBG_CONTEXT,
  3117. "Unmatching IP - Control: %d.%d.%d.%d Data: %d.%d.%d.%d \n",
  3118. HostIpAddress.S_un.S_un_b.s_b1,
  3119. HostIpAddress.S_un.S_un_b.s_b2,
  3120. HostIpAddress.S_un.S_un_b.s_b3,
  3121. HostIpAddress.S_un.S_un_b.s_b4,
  3122. saddrClient.sin_addr.S_un.S_un_b.s_b1,
  3123. saddrClient.sin_addr.S_un.S_un_b.s_b2,
  3124. saddrClient.sin_addr.S_un.S_un_b.s_b3,
  3125. saddrClient.sin_addr.S_un.S_un_b.s_b4));
  3126. CloseSocket( DataSocket);
  3127. DataSocket = INVALID_SOCKET;
  3128. serr = WSA_OPERATION_ABORTED;
  3129. };
  3130. };
  3131. if( serr == 0 )
  3132. {
  3133. //
  3134. // Got one.
  3135. //
  3136. DBG_ASSERT( DataSocket != INVALID_SOCKET );
  3137. m_fHavePASVConn = TRUE;
  3138. m_fWaitingForPASVConn = FALSE;
  3139. FacIncrement( FacPassiveDataConnections);
  3140. if ( m_AioDataConnection.SetNewSocket( DataSocket))
  3141. {
  3142. ReplyToUser(this,
  3143. REPLY_TRANSFER_STARTING,
  3144. PSZ_TRANSFER_STARTING);
  3145. }
  3146. else
  3147. {
  3148. //
  3149. // We are possibly running low on resources. Send error.
  3150. //
  3151. ReplyToUser( this,
  3152. REPLY_LOCAL_ERROR,
  3153. PSZ_INSUFFICIENT_RESOURCES);
  3154. CloseSocket( DataSocket);
  3155. DataSocket = INVALID_SOCKET;
  3156. serr = WSAENOBUFS;
  3157. }
  3158. }
  3159. else
  3160. {
  3161. IF_DEBUG( CLIENT )
  3162. {
  3163. DBGPRINTF(( DBG_CONTEXT,
  3164. "cannot wait for connection, error %d\n",
  3165. serr ));
  3166. }
  3167. ReplyToUser(this,
  3168. REPLY_TRANSFER_ABORTED,
  3169. PSZ_TRANSFER_ABORTED);
  3170. }
  3171. }
  3172. else
  3173. {
  3174. //
  3175. // Announce our intentions of establishing a connection.
  3176. //
  3177. ReplyToUser(this,
  3178. REPLY_OPENING_CONNECTION,
  3179. PSZ_OPENING_DATA_CONNECTION,
  3180. TransferType(m_xferType ),
  3181. pszReason,
  3182. pszSize);
  3183. //
  3184. // Open data socket.
  3185. //
  3186. serr = CreateDataSocket(&DataSocket, // Will receive socket
  3187. LocalIpAddress.s_addr, // Local address
  3188. CONN_PORT_TO_DATA_PORT(LocalIpPort),
  3189. DataIpAddress.s_addr,// RemoteAddr
  3190. DataPort ); // Remote port
  3191. if ( serr == 0 )
  3192. {
  3193. DBG_ASSERT( DataSocket != INVALID_SOCKET );
  3194. FacIncrement( FacActiveDataConnections);
  3195. if ( !m_AioDataConnection.SetNewSocket( DataSocket))
  3196. {
  3197. CloseSocket( DataSocket);
  3198. DataSocket = INVALID_SOCKET;
  3199. serr = WSAENOBUFS;
  3200. }
  3201. }
  3202. if ( serr != 0)
  3203. {
  3204. ReplyToUser(this,
  3205. REPLY_CANNOT_OPEN_CONNECTION,
  3206. PSZ_CANNOT_OPEN_DATA_CONNECTION);
  3207. IF_DEBUG( COMMANDS )
  3208. {
  3209. DBGPRINTF(( DBG_CONTEXT,
  3210. "could not create data socket, error %d\n",
  3211. serr ));
  3212. }
  3213. }
  3214. }
  3215. if( serr == 0 )
  3216. {
  3217. // set this to indicate a transfer might start
  3218. SET_UF( this, TRANSFER );
  3219. //
  3220. // Submit a read command on control socket, since we
  3221. // have to await possibility of an abort on OOB_INLINE.
  3222. // Can we ignore possibility of an error on read request?
  3223. //
  3224. if ( !ReadCommand())
  3225. {
  3226. DWORD dwError = GetLastError();
  3227. # ifdef CHECK_DBG
  3228. CHAR szBuffer[100];
  3229. DBG_REQUIRE( _snprintf( szBuffer, sizeof( szBuffer ),
  3230. " Read while DataTfr failed Error = %u. ", dwError) > 0);
  3231. Print( szBuffer);
  3232. # endif // CHECK_DBG
  3233. IF_DEBUG(CLIENT) {
  3234. DBGPRINTF((DBG_CONTEXT,
  3235. " %08x::ReadCommand() failed. Error = %u\n",
  3236. this, dwError));
  3237. SetLastError( dwError);
  3238. }
  3239. }
  3240. }
  3241. return ( serr);
  3242. } // USER_DATA::EstablishDataConnection()
  3243. BOOL
  3244. USER_DATA::DestroyDataConnection( IN DWORD dwError)
  3245. /*++
  3246. Tears down the connection to the client's data socket that was created
  3247. using EstablishDataConnection()
  3248. Arguments:
  3249. dwError = NO_ERROR if data is transferred successfully.
  3250. Win32 error code otherwise
  3251. --*/
  3252. {
  3253. UINT replyCode;
  3254. LPCSTR pszReply;
  3255. BOOL fTransfer;
  3256. fTransfer = TEST_UF( this, TRANSFER);
  3257. CLEAR_UF( this, TRANSFER );
  3258. CleanupPASVFlags();
  3259. //
  3260. // Close the data socket.
  3261. //
  3262. DBG_ASSERT( m_sPassiveDataListen == INVALID_SOCKET);
  3263. // Stop Io occuring on data connection
  3264. m_AioDataConnection.StopIo(dwError);
  3265. if ( fTransfer) {
  3266. //
  3267. // Tell the client we're done with the transfer.
  3268. //
  3269. if ( dwError == NO_ERROR) {
  3270. replyCode = REPLY_TRANSFER_OK;
  3271. pszReply = PSZ_TRANSFER_COMPLETE;
  3272. } else {
  3273. replyCode = REPLY_TRANSFER_ABORTED;
  3274. pszReply = PSZ_TRANSFER_ABORTED;
  3275. }
  3276. ReplyToUser(this, replyCode, pszReply);
  3277. }
  3278. return (TRUE);
  3279. } // USER_DATA::DestroyDataConnection()
  3280. APIERR
  3281. USER_DATA::GetFileSize()
  3282. {
  3283. LARGE_INTEGER FileSize;
  3284. DWORD dwError = NO_ERROR;
  3285. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3286. CHAR rgchSize[MAX_FILE_SIZE_SPEC];
  3287. pOpenFileInfo = m_pOpenFileInfo;
  3288. if ( pOpenFileInfo == NULL) {
  3289. return ( ERROR_FILE_NOT_FOUND);
  3290. }
  3291. if ( !pOpenFileInfo->QuerySize(FileSize)) {
  3292. dwError = GetLastError();
  3293. if( dwError != NO_ERROR ) {
  3294. return ( dwError);
  3295. }
  3296. }
  3297. IsLargeIntegerToDecimalChar( &FileSize, rgchSize);
  3298. ReplyToUser( this, REPLY_FILE_STATUS, rgchSize );
  3299. return(dwError);
  3300. }
  3301. APIERR
  3302. USER_DATA::GetFileModTime(LPSYSTEMTIME lpSystemTime)
  3303. {
  3304. DWORD dwError = NO_ERROR;
  3305. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3306. FILETIME FileTime;
  3307. pOpenFileInfo = m_pOpenFileInfo;
  3308. DBG_ASSERT( pOpenFileInfo != NULL );
  3309. if ( !pOpenFileInfo->QueryLastWriteTime(&FileTime)) {
  3310. dwError = GetLastError();
  3311. return ( dwError);
  3312. }
  3313. if (!FileTimeToSystemTime(&FileTime, lpSystemTime)) {
  3314. return GetLastError();
  3315. }
  3316. return NO_ERROR;
  3317. }
  3318. APIERR
  3319. USER_DATA::SendFileToUser( IN LPSTR pszFileName,
  3320. IN OUT LPBOOL pfErrorSent)
  3321. /*++
  3322. This is a worker function for RETR command of FTP. It will establish
  3323. connection via the ( new ) data socket, then send a file over that
  3324. socket. This uses Async io for transmitting the file.
  3325. Arguments:
  3326. pszFileName pointer to null-terminated string containing the filename
  3327. pfErrorSent pointer to boolean flag indicating if an error has
  3328. been already sent to client.
  3329. The flag should be used only when return value is error.
  3330. Returns:
  3331. NO_ERROR on success and Win32 error code if error.
  3332. History:
  3333. 30-April-1995 MuraliK
  3334. --*/
  3335. {
  3336. LARGE_INTEGER FileSize;
  3337. DWORD dwError = NO_ERROR;
  3338. BOOL fTransmit;
  3339. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3340. CHAR rgchSize[MAX_FILE_SIZE_SPEC];
  3341. CHAR rgchBuffer[MAX_FILE_SIZE_SPEC + 10];
  3342. BOOL fDisconnectSocket = TRUE;
  3343. DBG_ASSERT( pszFileName != NULL && pfErrorSent != NULL);
  3344. *pfErrorSent = FALSE;
  3345. IF_DEBUG( SEND) {
  3346. DBGPRINTF( ( DBG_CONTEXT,
  3347. " USER_DATA ( %08x)::SendFileToUser( %s,"
  3348. " pfErrorSent = %08x).\n",
  3349. this, pszFileName, pfErrorSent));
  3350. }
  3351. //
  3352. // Get file size.
  3353. //
  3354. pOpenFileInfo = m_pOpenFileInfo;
  3355. if ( pOpenFileInfo == NULL) {
  3356. return ( ERROR_FILE_NOT_FOUND);
  3357. }
  3358. // Get the file size
  3359. if ( !pOpenFileInfo->QuerySize(FileSize)) {
  3360. dwError = GetLastError();
  3361. if( dwError != NO_ERROR ) {
  3362. return ( dwError);
  3363. }
  3364. }
  3365. FileSize.QuadPart -= QueryCurrentOffset();
  3366. IsLargeIntegerToDecimalChar( &FileSize, rgchSize);
  3367. DBG_REQUIRE( _snprintf( rgchBuffer, sizeof( rgchBuffer ), "(%s bytes)", rgchSize) > 0);
  3368. m_pInstance->QueryStatsObj()->IncrTotalFilesSent();
  3369. //
  3370. // Blast the file from a local file to the user.
  3371. //
  3372. Reference(); // incr ref since async data transfer is started
  3373. SET_UF( this, ASYNC_TRANSFER);
  3374. if (FileSize.QuadPart > MAX_TRANSMIT_FILE_DATA_CHUNK) {
  3375. //
  3376. // TransmitFile does not handle more than 2GB very well, so we'll break it into
  3377. // 2GB chunks
  3378. //
  3379. FileSize.QuadPart = MAX_TRANSMIT_FILE_DATA_CHUNK;
  3380. SET_UF( this, ASYNC_DOWNLOAD);
  3381. fDisconnectSocket = FALSE;
  3382. }
  3383. fTransmit = ( m_AioDataConnection.
  3384. TransmitFileTs( pOpenFileInfo,
  3385. FileSize, // cbToSend ( send entire file)
  3386. QueryCurrentOffset(),
  3387. fDisconnectSocket)
  3388. );
  3389. if ( !fTransmit) {
  3390. dwError = GetLastError();
  3391. IF_DEBUG( SEND) {
  3392. DBGPRINTF( ( DBG_CONTEXT,
  3393. " Unable to transmit file ( %s) (pOpenFile = %p)."
  3394. " Error = %u\n",
  3395. pszFileName,
  3396. pOpenFileInfo,
  3397. dwError));
  3398. }
  3399. // decr refcount since async tfr failed.
  3400. DBG_REQUIRE( DeReference() > 0);
  3401. }
  3402. //
  3403. // Disconnect from client.
  3404. // ( will be done at the call back after completion of IO).
  3405. //
  3406. return ( dwError);
  3407. } // USER_DATA::SendFileToUser()
  3408. APIERR
  3409. USER_DATA::ReceiveFileFromUser(
  3410. LPSTR pszFileName,
  3411. LPHANDLE phFile
  3412. )
  3413. /*++
  3414. NAME: ReceiveFileFromUser
  3415. SYNOPSIS: Worker function for STOR, STOU, and APPE commands.
  3416. Will establish a connection via the (new) data
  3417. socket, then initiate asynchronous receive a file.
  3418. ENTRY: pszFileName - The name of the file to receive.
  3419. phFile - An handle to the file being received.
  3420. This handle is closed before this
  3421. routine returns.
  3422. Returns:
  3423. Win32 Error codes (or socket errors) as DWORD
  3424. HISTORY:
  3425. KeithMo 16-Mar-1993 Created.
  3426. MuraliK 05-April-1995 Dont free hFile here +
  3427. Alloc IoTransBuffer locally
  3428. RobSol 04-September-2001 convert file upload to be asynchronous
  3429. --*/
  3430. {
  3431. BOOL fCloseFileOnError = TRUE;
  3432. DWORD dwError = NO_ERROR;
  3433. DBG_ASSERT( pszFileName != NULL );
  3434. DBG_ASSERT( *phFile != INVALID_HANDLE_VALUE );
  3435. //
  3436. // Allocate an i/o buffer if not already allocated.
  3437. //
  3438. if (m_AsyncTransferBuff == NULL) {
  3439. m_AsyncTransferBuff = TCP_ALLOC( g_SocketBufferSize );
  3440. }
  3441. if( m_AsyncTransferBuff == NULL ) {
  3442. ReplyToUser(this,
  3443. REPLY_LOCAL_ERROR,
  3444. PSZ_INSUFFICIENT_RESOURCES);
  3445. dwError = ERROR_NOT_ENOUGH_MEMORY;
  3446. goto ReceiveFileFromUser_exit;
  3447. }
  3448. if ( !m_AioDataFile.SetNewFile( *phFile)) {
  3449. ReplyToUser(this,
  3450. REPLY_LOCAL_ERROR,
  3451. PSZ_INSUFFICIENT_RESOURCES);
  3452. dwError = GetLastError();
  3453. goto ReceiveFileFromUser_exit;
  3454. }
  3455. //
  3456. // after setting the file handle in m_AioDataFile, it will be closed by DestroyDataConnection
  3457. // so we no longer close it.
  3458. //
  3459. fCloseFileOnError = FALSE;
  3460. Reference(); // incr ref since async data transfer is started
  3461. SET_UF( this, ASYNC_TRANSFER);
  3462. SET_UF( this, ASYNC_UPLOAD);
  3463. //
  3464. // kick of the data pump by initiating read of the first data block.
  3465. //
  3466. if ( !m_AioDataConnection.ReadFile(m_AsyncTransferBuff,
  3467. g_SocketBufferSize) ) {
  3468. dwError = GetLastError();
  3469. DBG_ASSERT( dwError != NO_ERROR );
  3470. DBGPRINTF( ( DBG_CONTEXT,
  3471. " Unable to receive file ( %s)."
  3472. " Error = %u\n",
  3473. pszFileName,
  3474. dwError));
  3475. // decr refcount since async recv failed.
  3476. DBG_REQUIRE( DeReference() > 0);
  3477. }
  3478. ReceiveFileFromUser_exit:
  3479. if (dwError != NO_ERROR) {
  3480. //
  3481. // Close file handle before disconnecting from client. This is to serialize
  3482. // requests. If we disconnect first, then an append to this file may follow
  3483. // which may result in a sharing violation of the file (if this write has
  3484. // not been flushed and closed yet).
  3485. //
  3486. if( *phFile != INVALID_HANDLE_VALUE ) {
  3487. if ( fCloseFileOnError ) {
  3488. DBG_REQUIRE( CloseHandle( *phFile ) );
  3489. }
  3490. *phFile = INVALID_HANDLE_VALUE;
  3491. }
  3492. //
  3493. // Disconnect from client.
  3494. //
  3495. DBG_REQUIRE( DestroyDataConnection( dwError));
  3496. DBG_REQUIRE( CloseFileForReceive( dwError));
  3497. } else {
  3498. QueryInstance()->QueryStatsObj()->IncrTotalFilesReceived();
  3499. }
  3500. return (dwError);
  3501. } // ReceiveFileFromUser()
  3502. VOID
  3503. USER_DATA::SetPassiveSocket( IN SOCKET sPassive )
  3504. /*++
  3505. This function frees up an old Passive socket and resets the
  3506. passive socket to the new Passive socket.
  3507. Arguments:
  3508. sPassive - new passive socket to use
  3509. --*/
  3510. {
  3511. SOCKET sPassiveOld;
  3512. sPassiveOld = (SOCKET) InterlockedExchangePointer ( (PVOID *) &m_sPassiveDataListen,
  3513. (PVOID) sPassive);
  3514. if ( sPassiveOld != INVALID_SOCKET) {
  3515. FacDecrement( FacPassiveDataListens);
  3516. DBG_REQUIRE( CloseSocket( sPassiveOld) == 0);
  3517. }
  3518. if ( sPassive != INVALID_SOCKET) {
  3519. FacIncrement(FacPassiveDataListens);
  3520. }
  3521. return;
  3522. } // USER_DATA::SetPassiveSocket()
  3523. VOID
  3524. USER_DATA::CleanupPassiveSocket( BOOL fTellWatchThread )
  3525. /*++
  3526. This function cleans up the resources associated with the current passive socket
  3527. Arguments:
  3528. fTellWatchThread - flag indicating whether to tell thread waiting for an event on
  3529. the current passive socket to clean up as well
  3530. Returns:
  3531. Nothing
  3532. --*/
  3533. {
  3534. LockUser();
  3535. if ( m_sPassiveDataListen == INVALID_SOCKET )
  3536. {
  3537. UnlockUser();
  3538. return;
  3539. }
  3540. RemovePASVAcceptEvent( fTellWatchThread );
  3541. DBG_REQUIRE( CloseSocket( m_sPassiveDataListen ) == 0 );
  3542. m_sPassiveDataListen = INVALID_SOCKET;
  3543. UnlockUser();
  3544. }
  3545. BOOL
  3546. USER_DATA::SetCommand( IN LPSTR pszCmd )
  3547. /*++
  3548. Routine Description:
  3549. Used to set pointer to FTP cmd
  3550. Arguments :
  3551. pszArgs - pointer to command to execute
  3552. Returns :
  3553. BOOL indicating success/failure to set values
  3554. --*/
  3555. {
  3556. BOOL fReturn = TRUE;
  3557. if ( !pszCmd )
  3558. {
  3559. return FALSE;
  3560. }
  3561. //
  3562. // Free any previous allocations
  3563. //
  3564. if ( m_pszCmd )
  3565. {
  3566. TCP_FREE( m_pszCmd );
  3567. m_pszCmd = NULL;
  3568. }
  3569. if ( m_pszCmd = ( LPSTR ) TCP_ALLOC( strlen(pszCmd) + 1 ) )
  3570. {
  3571. strcpy( m_pszCmd, pszCmd );
  3572. }
  3573. else
  3574. {
  3575. DBGPRINTF((DBG_CONTEXT,
  3576. "Failed to allocate memory for command args !\n"));
  3577. fReturn = FALSE;
  3578. }
  3579. return fReturn;
  3580. }
  3581. /************************************************************
  3582. * Auxiliary Functions
  3583. ************************************************************/
  3584. VOID
  3585. ProcessUserAsyncIoCompletion(IN LPVOID pContext,
  3586. IN DWORD cbIo,
  3587. IN DWORD dwError,
  3588. IN LPASYNC_IO_CONNECTION pAioConn,
  3589. IN BOOL fTimedOut
  3590. )
  3591. /*++
  3592. This function processes the Async Io completion ( invoked as
  3593. a callback from the ASYNC_IO_CONNECTION object).
  3594. Arguments:
  3595. pContext pointer to the context information ( UserData object).
  3596. cbIo count of bytes transferred in Io
  3597. dwError DWORD containing the error code resulting from last tfr.
  3598. pAioConn pointer to AsyncIo connection object.
  3599. Returns:
  3600. None
  3601. --*/
  3602. {
  3603. LPUSER_DATA pUserData = (LPUSER_DATA ) pContext;
  3604. DBG_ASSERT( pUserData != NULL);
  3605. DBG_ASSERT( pAioConn != NULL);
  3606. IF_SPECIAL_DEBUG( CRITICAL_PATH) {
  3607. CHAR rgchBuffer[100];
  3608. DBG_REQUIRE( _snprintf( rgchBuffer, sizeof( rgchBuffer ),
  3609. " ProcessAio( cb=%u, err=%u, Aio=%p). ",
  3610. cbIo, dwError, pAioConn) > 0);
  3611. pUserData->Print( rgchBuffer);
  3612. }
  3613. DBG_REQUIRE( pUserData->Reference() > 0);
  3614. # if DBG
  3615. if ( !IS_VALID_USER_DATA( pUserData)) {
  3616. DBGPRINTF( ( DBG_CONTEXT,
  3617. "Encountering an invalid user data ( %08x)\n",
  3618. pUserData));
  3619. pUserData->Print();
  3620. }
  3621. # endif // DBG
  3622. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  3623. pUserData->ProcessAsyncIoCompletion( cbIo, dwError, pAioConn, fTimedOut);
  3624. DereferenceUserDataAndKill(pUserData);
  3625. return;
  3626. } // ProcessUserAsyncIoCompletion()
  3627. VOID
  3628. USER_DATA::RemovePASVAcceptEvent( BOOL fTellWatchThread )
  3629. /*++
  3630. Routine Description:
  3631. Routine that cleans up the state associated with a PASV accept event
  3632. Arguments:
  3633. fTellWatchThread - BOOL indicating whether or not to inform the thread waiting on
  3634. the event to stop waiting on it
  3635. Returns:
  3636. Nothing
  3637. --*/
  3638. {
  3639. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  3640. if ( m_hPASVAcceptEvent == NULL )
  3641. {
  3642. return;
  3643. }
  3644. //
  3645. // Remove all network notifications for the PASV socket
  3646. //
  3647. if ( WSAEventSelect( m_sPassiveDataListen,
  3648. m_hPASVAcceptEvent,
  3649. 0 ) )
  3650. {
  3651. DBGPRINTF((DBG_CONTEXT,
  3652. "WSAEventSelect on socket %d failed : 0x%x\n",
  3653. m_sPassiveDataListen, WSAGetLastError()));
  3654. }
  3655. //
  3656. // Stop watching for the event
  3657. //
  3658. if ( fTellWatchThread )
  3659. {
  3660. RemoveAcceptEvent( m_hPASVAcceptEvent,
  3661. this );
  3662. }
  3663. WSACloseEvent( m_hPASVAcceptEvent );
  3664. m_hPASVAcceptEvent = NULL;
  3665. }
  3666. SOCKERR
  3667. USER_DATA::AddPASVAcceptEvent( BOOL *pfAcceptableSocket )
  3668. /*++
  3669. Routine Description:
  3670. Routine that sets up the event to signal that the PASV socket is an accept()'able
  3671. state
  3672. Arguments:
  3673. pfAcceptableSocket - BOOL set to TRUE if socket can be accept()'ed at once, FALSE if
  3674. NOT
  3675. Returns:
  3676. Error code indicating success/failure
  3677. --*/
  3678. {
  3679. DWORD dwRet = 0;
  3680. SOCKERR serr = 0;
  3681. BOOL fRegistered = FALSE;
  3682. *pfAcceptableSocket = FALSE;
  3683. if ( ( m_hPASVAcceptEvent = WSACreateEvent() ) == WSA_INVALID_EVENT )
  3684. {
  3685. DBGPRINTF((DBG_CONTEXT,
  3686. "Failed to create event to wait for accept() : 0x%x\n",
  3687. WSAGetLastError()));
  3688. return WSAGetLastError();
  3689. }
  3690. //
  3691. // specify that we want to be alerted when the socket is accept()'able =)
  3692. //
  3693. if ( WSAEventSelect( m_sPassiveDataListen,
  3694. m_hPASVAcceptEvent,
  3695. FD_ACCEPT ) )
  3696. {
  3697. DBGPRINTF((DBG_CONTEXT,
  3698. "WSAEventSelect failed : 0x%x\n",
  3699. WSAGetLastError()));
  3700. serr = WSAGetLastError();
  3701. goto exit;
  3702. }
  3703. else
  3704. {
  3705. fRegistered = TRUE;
  3706. }
  3707. //
  3708. // In order to deal as quickly as possible with legitimate clients and avoid rejecting them
  3709. // because the queue is full, we'll wait for 0.1 sec to see whether the socket becomes
  3710. // accept()'able before queueing it
  3711. //
  3712. dwRet = WSAWaitForMultipleEvents( 1,
  3713. &m_hPASVAcceptEvent,
  3714. FALSE,
  3715. 100,
  3716. FALSE );
  3717. switch ( dwRet )
  3718. {
  3719. case WSA_WAIT_EVENT_0:
  3720. //
  3721. // we can call accept() at once on the socket, no need to muck around with waiting
  3722. // for it
  3723. //
  3724. *pfAcceptableSocket = TRUE;
  3725. break;
  3726. case WSA_WAIT_TIMEOUT:
  3727. //
  3728. // Need to queue the socket
  3729. //
  3730. serr = AddAcceptEvent( m_hPASVAcceptEvent,
  3731. this );
  3732. break;
  3733. default:
  3734. serr = WSAGetLastError();
  3735. break;
  3736. }
  3737. exit:
  3738. //
  3739. // clean up if something failed or the socket is acceptible
  3740. //
  3741. if ( (serr != 0) || *pfAcceptableSocket )
  3742. {
  3743. if ( m_hPASVAcceptEvent )
  3744. {
  3745. if ( fRegistered )
  3746. {
  3747. WSAEventSelect( m_sPassiveDataListen,
  3748. m_hPASVAcceptEvent,
  3749. 0 );
  3750. }
  3751. WSACloseEvent( m_hPASVAcceptEvent );
  3752. m_hPASVAcceptEvent = NULL;
  3753. }
  3754. }
  3755. return serr;
  3756. }
  3757. VOID
  3758. DereferenceUserDataAndKill(IN OUT LPUSER_DATA pUserData)
  3759. /*++
  3760. This function dereferences User data and kills the UserData object if the
  3761. reference count hits 0. Before killing the user data, it also removes
  3762. the connection from the list of active connections.
  3763. --*/
  3764. {
  3765. FTP_SERVER_INSTANCE * pinstance;
  3766. IF_SPECIAL_DEBUG( CRITICAL_PATH) {
  3767. pUserData->Print( " Deref ");
  3768. }
  3769. //
  3770. // We must capture the instance pointer from the user data, as
  3771. // USER_DATA::RemoveConnection() will set the pointer to NULL.
  3772. // We must also reference the instance before locking it, as
  3773. // removing the last user from the instance will cause the instance
  3774. // to be destroyed. We'll defer this destruction until we're done
  3775. // with the instance.
  3776. //
  3777. pinstance = pUserData->QueryInstance();
  3778. pinstance->Reference();
  3779. pinstance->LockConnectionsList();
  3780. if ( !pUserData->DeReference()) {
  3781. //
  3782. // Deletion of the object USER_DATA is required.
  3783. //
  3784. IF_DEBUG( USER_DATABASE) {
  3785. DBGPRINTF( ( DBG_CONTEXT,
  3786. " UserData( %08x) is being deleted.\n",
  3787. pUserData));
  3788. }
  3789. pinstance->UnlockConnectionsList();
  3790. pUserData->Cleanup();
  3791. DBG_ASSERT( pUserData->QueryControlSocket() == INVALID_SOCKET );
  3792. DBG_ASSERT( pUserData->QueryDataSocket() == INVALID_SOCKET );
  3793. pinstance->RemoveConnection( pUserData);
  3794. }
  3795. else {
  3796. pinstance->UnlockConnectionsList();
  3797. }
  3798. pinstance->Dereference();
  3799. } // DereferenceUserDataAndKill()
  3800. BOOL
  3801. PathAccessCheck(IN ACCESS_TYPE _access,
  3802. IN DWORD dwVrootAccessMask,
  3803. IN BOOL fUserRead,
  3804. IN BOOL fUserWrite
  3805. )
  3806. /*++
  3807. This function determines if the required privilege to access the specified
  3808. virtual root with a given access mask exists.
  3809. Arguments:
  3810. access - specifies type of acces desired.
  3811. dwVrootAccessMask - DWORD containing the access mask for the virtual root.
  3812. fUserRead - user's permission to read (general)
  3813. fUserWrite - user's permission to write (general)
  3814. Returns:
  3815. BOOL - TRUE if access is to be granted, else FALSE.
  3816. History:
  3817. MuraliK 20-Sept-1995
  3818. --*/
  3819. {
  3820. BOOL fAccessGranted = FALSE;
  3821. DBG_ASSERT( IS_VALID_ACCESS_TYPE( _access ) );
  3822. //
  3823. // Perform the actual access check.
  3824. //
  3825. switch( _access ) {
  3826. case AccessTypeRead :
  3827. fAccessGranted = (fUserRead &&
  3828. ((dwVrootAccessMask & VROOT_MASK_READ)
  3829. == VROOT_MASK_READ)
  3830. );
  3831. break;
  3832. case AccessTypeWrite :
  3833. case AccessTypeCreate :
  3834. case AccessTypeDelete :
  3835. fAccessGranted = (fUserWrite &&
  3836. ((dwVrootAccessMask & VROOT_MASK_WRITE)
  3837. == VROOT_MASK_WRITE)
  3838. );
  3839. break;
  3840. default :
  3841. DBGPRINTF(( DBG_CONTEXT,
  3842. "PathAccessCheck - invalid access type %d\n",
  3843. _access ));
  3844. DBG_ASSERT( FALSE );
  3845. break;
  3846. }
  3847. if (!fAccessGranted) {
  3848. SetLastError( ERROR_ACCESS_DENIED);
  3849. }
  3850. return ( fAccessGranted);
  3851. } // PathAccessCheck()
  3852. VOID SignalAcceptableSocket( LPUSER_DATA pUserData )
  3853. /*++
  3854. Function that restarts processing the original command when a PASV data socket becomes
  3855. accept()'able [ie the client has made the connection]
  3856. Arguments:
  3857. pUserData - USER_DATA context attached to socket
  3858. Returns:
  3859. Nothing
  3860. --*/
  3861. {
  3862. PATQ_CONTEXT pAtqContext = pUserData->QueryControlAio()->QueryAtqContext();
  3863. //
  3864. // Stop waiting for events on this socket
  3865. //
  3866. pUserData->RemovePASVAcceptEvent( FALSE );
  3867. pUserData->SetInFakeIOCompletion( TRUE );
  3868. //
  3869. // do a scary thing - fake an IO completion, to trigger re-processing of the FTP command
  3870. //
  3871. if ( !AtqPostCompletionStatus( pAtqContext,
  3872. strlen( pUserData->QueryCmdString() ) + 1 ) )
  3873. {
  3874. DBGPRINTF((DBG_CONTEXT,
  3875. "Failed to post fake completion status to deal with PASV event : 0x%x\n",
  3876. GetLastError()));
  3877. return;
  3878. }
  3879. }
  3880. VOID CleanupTimedOutSocketContext( LPUSER_DATA pUserData )
  3881. /*++
  3882. Function used to do cleanup when timeout for waiting for a PASV connection expires
  3883. Arguments:
  3884. pUserData - context pointer
  3885. Returns:
  3886. Nothing
  3887. --*/
  3888. {
  3889. DBG_ASSERT( pUserData );
  3890. pUserData->LockUser();
  3891. pUserData->CleanupPassiveSocket( FALSE );
  3892. CLEAR_UF( pUserData, PASSIVE );
  3893. pUserData->SetHavePASVConn( FALSE );
  3894. pUserData->SetWaitingForPASVConn( FALSE );
  3895. ReplyToUser( pUserData,
  3896. REPLY_CANNOT_OPEN_CONNECTION,
  3897. PSZ_CANNOT_OPEN_DATA_CONNECTION );
  3898. //
  3899. // Remove our reference to this USER_DATA object
  3900. //
  3901. pUserData->DeReference();
  3902. pUserData->UnlockUser();
  3903. }
  3904. /*******************************************************************
  3905. NAME: FtpMetaDataFree
  3906. SYNOPSIS: Frees a formatted meta data object when it's not in use.
  3907. ENTRY: pObject - Pointer to the meta data object.
  3908. RETURNS:
  3909. NOTES:
  3910. ********************************************************************/
  3911. VOID
  3912. FtpMetaDataFree(
  3913. PVOID pObject
  3914. )
  3915. {
  3916. PFTP_METADATA pMD;
  3917. pMD = (PFTP_METADATA)pObject;
  3918. delete pMD;
  3919. }
  3920. BOOL
  3921. FTP_METADATA::HandlePrivateProperty(
  3922. LPSTR pszURL,
  3923. PIIS_SERVER_INSTANCE pInstance,
  3924. METADATA_GETALL_INTERNAL_RECORD *pMDRecord,
  3925. LPVOID pDataPointer,
  3926. BUFFER *pBuffer,
  3927. DWORD *pdwBytesUsed,
  3928. PMETADATA_ERROR_INFO pMDErrorInfo
  3929. )
  3930. /*++
  3931. Routine Description:
  3932. Handle metabase properties private to FTP service
  3933. Arguments:
  3934. pszURL - URL of the requested object
  3935. pInstance - FTP server instance
  3936. pMDRecord - metadata record
  3937. pDataPointer - pointer to metabase data
  3938. pBuffer - Buffer available for storage space
  3939. pdwBytesUsed - Pointer to bytes used in *pBuffer
  3940. Returns:
  3941. BOOL - TRUE success ( or not handled ), otherwise FALSE.
  3942. --*/
  3943. {
  3944. return TRUE;
  3945. }
  3946. BOOL
  3947. FTP_METADATA::FinishPrivateProperties(
  3948. BUFFER *pBuffer,
  3949. DWORD dwBytesUsed,
  3950. BOOL bSucceeded
  3951. )
  3952. /*++
  3953. Routine Description:
  3954. Handles completion of reading metabase properties private to FTP.
  3955. Arguments:
  3956. pBuffer - Buffer previously used for storage space
  3957. dwBytesUsed - bytes used in *pBuffer
  3958. Returns:
  3959. BOOL - TRUE success ( or not handled ), otherwise FALSE.
  3960. --*/
  3961. {
  3962. return TRUE;
  3963. }
  3964. BOOL
  3965. USER_DATA::LookupVirtualRoot(
  3966. IN const CHAR * pszURL,
  3967. OUT CHAR * pszPath,
  3968. OUT DWORD * pccbDirRoot,
  3969. OUT DWORD * pdwAccessMask
  3970. )
  3971. /*++
  3972. Routine Description:
  3973. Looks up the virtual root to find the physical drive mapping. If an
  3974. Accept-Language header was sent by the client, we look for a virtual
  3975. root prefixed by the language tag
  3976. Arguments:
  3977. pstrPath - Receives physical drive path
  3978. pszURL - URL to look for
  3979. pccbDirRoot - On entry, size of pstrPath buffer in bytes. On return,
  3980. Number of characters in the found physical path (incl. \0)
  3981. pdwMask - Access mask for the specified URL
  3982. Returns:
  3983. BOOL - TRUE if success, otherwise FALSE.
  3984. --*/
  3985. {
  3986. PFTP_METADATA pMD;
  3987. DWORD dwDataSetNumber;
  3988. PVOID pCacheInfo;
  3989. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  3990. STACK_STR( strFullPath, MAX_PATH );
  3991. METADATA_ERROR_INFO MDErrorInfo;
  3992. BOOL fOk;
  3993. DWORD dwError = NO_ERROR;
  3994. if ( m_pMetaData != NULL )
  3995. {
  3996. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  3997. m_pMetaData = NULL;
  3998. }
  3999. // First read the data set number, and see if we already have it
  4000. // cached. We don't do a full open in this case.
  4001. if ( !strFullPath.Copy( m_pInstance->QueryMDVRPath() ) ||
  4002. !strFullPath.Append( ( *pszURL == '/' ) ? pszURL + 1 : pszURL ) )
  4003. {
  4004. goto LookupVirtualRoot_Error;
  4005. }
  4006. if (!mb.GetDataSetNumber( strFullPath.QueryStr(),
  4007. &dwDataSetNumber ))
  4008. {
  4009. goto LookupVirtualRoot_Error;
  4010. }
  4011. // See if we can find a matching data set already formatted.
  4012. pMD = (PFTP_METADATA)TsFindMetaData(dwDataSetNumber, METACACHE_FTP_SERVER_ID);
  4013. if (pMD == NULL)
  4014. {
  4015. pMD = new FTP_METADATA;
  4016. if (pMD == NULL)
  4017. {
  4018. goto LookupVirtualRoot_Error;
  4019. }
  4020. if ( !pMD->ReadMetaData( m_pInstance,
  4021. &mb,
  4022. (LPSTR)pszURL,
  4023. &MDErrorInfo) )
  4024. {
  4025. delete pMD;
  4026. goto LookupVirtualRoot_Error;
  4027. }
  4028. // We were succesfull, so try and add this metadata. There is a race
  4029. // condition where someone else could have added it while we were
  4030. // formatting. This is OK - we'll have two cached, but they should be
  4031. // consistent, and one of them will eventually time out. We could have
  4032. // AddMetaData check for this, and free the new one while returning a
  4033. // pointer to the old one if it finds one, but that isn't worthwhile
  4034. // now.
  4035. pCacheInfo = TsAddMetaData(pMD, FtpMetaDataFree,
  4036. dwDataSetNumber, METACACHE_FTP_SERVER_ID);
  4037. }
  4038. m_pMetaData = pMD;
  4039. if ( m_pMetaData->QueryVrError() )
  4040. {
  4041. dwError = m_pMetaData->QueryVrError();
  4042. goto LookupVirtualRoot_Error;
  4043. }
  4044. //
  4045. // Build physical path from VR_PATH & portion of URI not used to define VR_PATH
  4046. //
  4047. //
  4048. // for URLs containing a Virtual Root, as well as MD_USER_ISOLATION_NONE mode, and StandAlone
  4049. // mode, concatenate the VR physical path and the URL to form a full physical path. In
  4050. // StandAlone where the path does not contain a VR alias, contatenate the physical root path,
  4051. // the user home directory, and the URL. In MD_USER_ISOLATION_AD mode (with no VR prefix),
  4052. // concatenate the user physical home directory (in the USER_DATA objcet) and the relative
  4053. // path.
  4054. //
  4055. if (( m_pInstance->QueryIsolationMode() == MD_USER_ISOLATION_AD ) &&
  4056. ( m_pMetaData->QueryVrLevel() == 0 )) {
  4057. //
  4058. // we get here in AD isolation mode, where the VR has no path mapping
  4059. //
  4060. if ( !strFullPath.Copy( QueryRootDirectory().QueryStr(),
  4061. QueryRootDirectory().QueryCCH() ) ||
  4062. !strFullPath.Append( pszURL ) )
  4063. {
  4064. goto LookupVirtualRoot_Error;
  4065. }
  4066. FlipSlashes( strFullPath.QueryStr(), FLIP_SLASHES_TO_DOS );
  4067. fOk = TRUE;
  4068. } else {
  4069. //
  4070. // for stand-alone UIM (no VROOT), we use the homeDirectory as a relative path prefix.
  4071. // no-isolation and all VROOTs use the VR lookup path.
  4072. //
  4073. PCSTR pszHomeDir = NULL;
  4074. if ((m_pMetaData->QueryVrLevel() == 0) &&
  4075. (m_pInstance->QueryIsolationMode() == MD_USER_ISOLATION_BASIC) ) {
  4076. pszHomeDir = m_strRootDir.QueryStr();
  4077. }
  4078. fOk = pMD->BuildPhysicalPathWithAltRoot( (LPSTR)pszURL, &strFullPath, pszHomeDir );
  4079. }
  4080. if ( fOk ) {
  4081. if ( *pccbDirRoot > strFullPath.QueryCCH() )
  4082. {
  4083. memcpy( pszPath, strFullPath.QueryStr(), strFullPath.QueryCCH()+1 );
  4084. *pccbDirRoot = strFullPath.QueryCCH()+1;
  4085. if ( pdwAccessMask )
  4086. {
  4087. *pdwAccessMask = m_pMetaData->QueryAccessPerms();
  4088. }
  4089. return TRUE;
  4090. }
  4091. }
  4092. LookupVirtualRoot_Error:
  4093. if (dwError == NO_ERROR) {
  4094. //
  4095. // best error message to send to client
  4096. //
  4097. dwError = ERROR_FILE_NOT_FOUND;
  4098. }
  4099. SetLastError( dwError );
  4100. return FALSE;
  4101. }
  4102. BOOL
  4103. USER_DATA::BindInstanceAccessCheck(
  4104. )
  4105. /*++
  4106. Routine Description:
  4107. Bind IP/DNS access check for this request to instance data
  4108. Arguments:
  4109. None
  4110. Returns:
  4111. BOOL - TRUE if success, otherwise FALSE.
  4112. --*/
  4113. {
  4114. if ( m_rfAccessCheck.CopyFrom( m_pInstance->QueryMetaDataRefHandler() ) )
  4115. {
  4116. m_acAccessCheck.BindCheckList( (LPBYTE)m_rfAccessCheck.GetPtr(), m_rfAccessCheck.GetSize() );
  4117. return TRUE;
  4118. }
  4119. return FALSE;
  4120. }
  4121. VOID
  4122. USER_DATA::UnbindInstanceAccessCheck()
  4123. /*++
  4124. Routine Description:
  4125. Unbind IP/DNS access check for this request to instance data
  4126. Arguments:
  4127. None
  4128. Returns:
  4129. Nothing
  4130. --*/
  4131. {
  4132. m_acAccessCheck.UnbindCheckList();
  4133. m_rfAccessCheck.Reset( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  4134. }
  4135. BOOL
  4136. USER_DATA::IsFileNameShort( IN LPSTR pszFile)
  4137. /*++
  4138. Check file name beeing short or not.
  4139. Arguments:
  4140. pszFile pointer to null-terminated string containing the file name
  4141. Returns:
  4142. TRUE if filename is short.
  4143. --*/
  4144. {
  4145. APIERR err;
  4146. CHAR szCanonPath[MAX_PATH+1];
  4147. DWORD cbSize = sizeof(szCanonPath);
  4148. BOOL fShort;
  4149. BOOL fRet = FALSE;
  4150. DBG_ASSERT( pszFile != NULL );
  4151. //
  4152. // Close any file we might have open now
  4153. // N.B. There shouldn't be an open file; we're just
  4154. // being careful here.
  4155. //
  4156. if (m_pOpenFileInfo) {
  4157. DBGPRINTF(( DBG_CONTEXT,
  4158. "WARNING!! Closing [%08x], before opening %s\n",
  4159. pszFile
  4160. ));
  4161. DBG_REQUIRE( CloseFileForSend() );
  4162. }
  4163. //
  4164. // Open the requested file
  4165. //
  4166. err = VirtualCanonicalize(szCanonPath,
  4167. &cbSize,
  4168. pszFile,
  4169. AccessTypeRead);
  4170. if( err == NO_ERROR )
  4171. {
  4172. if ( strchr( szCanonPath, '~' ))
  4173. {
  4174. err = CheckIfShortFileName( (UCHAR *) szCanonPath, TsTokenToImpHandle( QueryUserToken()), &fShort );
  4175. if ( !err && fShort)
  4176. {
  4177. DBGPRINTF(( DBG_CONTEXT,
  4178. "Short filename being rejected \"%s\"\n",
  4179. szCanonPath ));
  4180. fRet = TRUE;
  4181. }
  4182. }
  4183. }
  4184. return fRet;
  4185. } // USER_DATA::IsFileNameShort()
  4186. DWORD
  4187. USER_DATA::CheckIfShortFileName(
  4188. IN CONST UCHAR * pszPath,
  4189. IN HANDLE hImpersonation,
  4190. OUT BOOL * pfShort
  4191. )
  4192. /*++
  4193. Description:
  4194. This function takes a suspected NT/Win95 short filename and checks if there's
  4195. an equivalent long filename. For example, c:\foobar\ABCDEF~1.ABC is the same
  4196. as c:\foobar\abcdefghijklmnop.abc.
  4197. NOTE: This function should be called unimpersonated - the FindFirstFile() must
  4198. be called in the system context since most systems have traverse checking turned
  4199. off - except for the UNC case where we must be impersonated to get network access.
  4200. Arguments:
  4201. pszPath - Path to check
  4202. hImpersonation - Impersonation handle if this is a UNC path - can be NULL if not UNC
  4203. pfShort - Set to TRUE if an equivalent long filename is found
  4204. Returns:
  4205. Win32 error on failure
  4206. --*/
  4207. {
  4208. DWORD err = NO_ERROR;
  4209. WIN32_FIND_DATA FindData;
  4210. UCHAR * psz;
  4211. BOOL fUNC;
  4212. psz = _mbschr( (UCHAR *) pszPath, '~' );
  4213. *pfShort = FALSE;
  4214. fUNC = (*pszPath == '\\');
  4215. //
  4216. // Loop for multiple tildas - watch for a # after the tilda
  4217. //
  4218. while ( psz++ )
  4219. {
  4220. if ( *psz >= '0' && *psz <= '9' )
  4221. {
  4222. UCHAR achTmp[MAX_PATH];
  4223. UCHAR * pchEndSeg;
  4224. UCHAR * pchBeginSeg;
  4225. HANDLE hFind;
  4226. //
  4227. // Isolate the path up to the segment with the
  4228. // '~' and do the FindFirst with that path
  4229. //
  4230. pchEndSeg = _mbschr( psz, '\\' );
  4231. if ( !pchEndSeg )
  4232. {
  4233. pchEndSeg = psz + _mbslen( psz );
  4234. }
  4235. //
  4236. // If the string is beyond MAX_PATH then we allow it through
  4237. //
  4238. if ( ((INT) (pchEndSeg - pszPath)) >= sizeof( achTmp ))
  4239. {
  4240. return NO_ERROR;
  4241. }
  4242. memcpy( achTmp, pszPath, (INT) (pchEndSeg - pszPath) );
  4243. achTmp[pchEndSeg - pszPath] = '\0';
  4244. if ( fUNC && hImpersonation )
  4245. {
  4246. if ( !ImpersonateLoggedOnUser( hImpersonation ))
  4247. {
  4248. return GetLastError();
  4249. }
  4250. }
  4251. hFind = FindFirstFile( (CHAR *) achTmp, &FindData );
  4252. if ( fUNC && hImpersonation )
  4253. {
  4254. RevertToSelf();
  4255. }
  4256. if ( hFind == INVALID_HANDLE_VALUE )
  4257. {
  4258. err = GetLastError();
  4259. DBGPRINTF(( DBG_CONTEXT,
  4260. "FindFirst failed!! - \"%s\", error %d\n",
  4261. achTmp,
  4262. GetLastError() ));
  4263. //
  4264. // If the FindFirstFile() fails to find the file then return
  4265. // success - the path doesn't appear to be a valid path which
  4266. // is ok.
  4267. //
  4268. if ( err == ERROR_FILE_NOT_FOUND ||
  4269. err == ERROR_PATH_NOT_FOUND )
  4270. {
  4271. return NO_ERROR;
  4272. }
  4273. return err;
  4274. }
  4275. DBG_REQUIRE( FindClose( hFind ));
  4276. //
  4277. // Isolate the last segment of the string which should be
  4278. // the potential short name equivalency
  4279. //
  4280. pchBeginSeg = _mbsrchr( achTmp, '\\' );
  4281. DBG_ASSERT( pchBeginSeg );
  4282. pchBeginSeg++;
  4283. //
  4284. // If the last segment doesn't match the long name then this is
  4285. // the short name version of the path
  4286. //
  4287. if ( _mbsicmp( (UCHAR *) FindData.cFileName, pchBeginSeg ))
  4288. {
  4289. *pfShort = TRUE;
  4290. return NO_ERROR;
  4291. }
  4292. }
  4293. psz = _mbschr( psz, '~' );
  4294. }
  4295. return err;
  4296. }
  4297. /******************************* End Of File *************************/