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.

2818 lines
74 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. ftpconf.cxx
  5. Abstract:
  6. This module contains functions for FTP Server configuration
  7. class (FTP_SERVER_CONFIG).
  8. Author:
  9. Murali R. Krishnan (MuraliK) 21-March-1995
  10. Project:
  11. FTP Server DLL
  12. Functions Exported:
  13. FTP_SERVER_CONFIG::FTP_SERVER_CONFIG()
  14. FTP_SERVER_CONFIG::~FTP_SERVER_CONFIG()
  15. FTP_SERVER_CONFIG::InitFromRegistry()
  16. FTP_SERVER_CONFIG::GetConfigInformation()
  17. FTP_SERVER_CONFIG::SetConfigInformation()
  18. FTP_SERVER_CONFIG::AllocNewConnection()
  19. FTP_SERVER_CONFIG::RemoveConnection()
  20. FTP_SERVER_CONFIG::DisconnectAllConnections()
  21. FTP_SERVER_CONFIG::Print()
  22. Revisions:
  23. MuraliK 26-July-1995 Added Allocation caching of client conns.
  24. --*/
  25. # include "ftpdp.hxx"
  26. #include <ole2.h>
  27. #include <imd.h>
  28. #include <iiscnfgp.h>
  29. #include <mb.hxx>
  30. #include <mbstring.h>
  31. # include <tchar.h>
  32. #include <timer.h>
  33. extern "C"
  34. {
  35. #include "ntlsa.h"
  36. } // extern "C"
  37. /************************************************************
  38. * Symbolic Constants
  39. ************************************************************/
  40. #define DEFAULT_ALLOW_ANONYMOUS TRUE
  41. #define DEFAULT_ALLOW_GUEST_ACCESS TRUE
  42. #define DEFAULT_ANONYMOUS_ONLY FALSE
  43. #define DEFAULT_READ_ACCESS_MASK 0
  44. #define DEFAULT_WRITE_ACCESS_MASK 0
  45. #define DEFAULT_MSDOS_DIR_OUTPUT TRUE
  46. #define DEFAULT_USE_SUBAUTH FALSE
  47. #define DEFAULT_LOGON_METHOD LOGON32_LOGON_NETWORK_CLEARTEXT
  48. #define DEFAULT_ANONYMOUS_PWD ""
  49. #define DEFAULT_AD_CONNECTIONS_USERNAME ""
  50. #define DEFAULT_AD_CONNECTIONS_PASSWORD ""
  51. const TCHAR DEFAULT_EXIT_MESSAGE[] = TEXT("Goodbye.");
  52. # define CCH_DEFAULT_EXIT_MESSAGE (lstrlen( DEFAULT_EXIT_MESSAGE) + 1)
  53. const TCHAR DEFAULT_MAX_CLIENTS_MSG[] =
  54. TEXT("Maximum clients reached, service unavailable.");
  55. # define CCH_DEFAULT_MAX_CLIENTS_MSG (lstrlen( DEFAULT_MAX_CLIENTS_MSG) + 1)
  56. // this should be a double null terminated null terminated sequence.
  57. const TCHAR DEFAULT_GREETING_MESSAGE[2] = { '\0', '\0' };
  58. # define CCH_DEFAULT_GREETING_MESSAGE ( 2)
  59. // this should be a double null terminated null terminated sequence.
  60. const TCHAR DEFAULT_BANNER_MESSAGE[2] = { '\0', '\0' };
  61. # define CCH_DEFAULT_BANNER_MESSAGE ( 2)
  62. #define DEFAULT_ANNOTATE_DIRS FALSE
  63. #define DEFAULT_LOWERCASE_FILES FALSE
  64. #define DEFAULT_LISTEN_BACKLOG 1 /* reduce listen backlog */
  65. #define DEFAULT_ENABLE_LICENSING FALSE
  66. #define DEFAULT_DEFAULT_LOGON_DOMAIN NULL // NULL == use primary domain
  67. #define DEFAULT_ENABLE_CONN_TO_3RDIP FALSE
  68. #define DEFAULT_ENABLE_PASV_FROM_3RDIP FALSE
  69. #define DEFAULT_ALLOW_REPLACE_ON_RENAME FALSE
  70. #define DEFAULT_SHOW_4_DIGIT_YEAR FALSE
  71. #define DEFAULT_USER_ISOLATION MD_USER_ISOLATION_NONE
  72. #define DEFAULT_LOG_IN_UTF_8 FALSE
  73. # define SC_NOTIFY_INTERVAL 3000 // milliseconds
  74. # define CLEANUP_POLL_INTERVAL 2000 // milliseconds
  75. # define CLEANUP_RETRY_COUNT 12 // iterations
  76. //
  77. // Private Prototypes
  78. //
  79. APIERR
  80. GetDefaultDomainName(
  81. CHAR * pszDomainName,
  82. DWORD cchDomainName
  83. );
  84. BOOL
  85. FtpdReadRegString(
  86. IN HKEY hkey,
  87. OUT TCHAR * * ppchstr,
  88. IN LPCTSTR pchValue,
  89. IN LPCTSTR pchDefault,
  90. IN DWORD cchDefault
  91. );
  92. BOOL
  93. GenMessageWithLineFeed(IN LPSTR pszzMessage,
  94. IN LPSTR * ppszMessageWithLineFeed);
  95. #if DBG
  96. static CHAR * p_AccessTypes[] = { "read",
  97. "write",
  98. "create",
  99. "delete" };
  100. #endif // DBG
  101. /************************************************************
  102. * Member Functions of FTP_SERVER_INSTANCE
  103. ************************************************************/
  104. FTP_SERVER_INSTANCE::FTP_SERVER_INSTANCE(
  105. IN PFTP_IIS_SERVICE pService,
  106. IN DWORD dwInstanceId,
  107. IN USHORT sPort,
  108. IN LPCSTR lpszRegParamKey,
  109. IN LPWSTR lpwszAnonPasswordSecretName,
  110. IN LPWSTR lpwszRootPasswordSecretName,
  111. IN BOOL fMigrateVroots
  112. )
  113. /*++
  114. Description:
  115. Constructor Function for Ftp server Configuration object
  116. ( Initializes all members to be NULL)
  117. The valid flag may be initialized to TRUE only after reading values
  118. from registry.
  119. --*/
  120. : IIS_SERVER_INSTANCE(
  121. pService,
  122. dwInstanceId,
  123. sPort,
  124. lpszRegParamKey,
  125. lpwszAnonPasswordSecretName,
  126. lpwszRootPasswordSecretName,
  127. fMigrateVroots
  128. ),
  129. m_cCurrentConnections ( 0),
  130. m_cMaxCurrentConnections ( 0),
  131. m_fValid ( FALSE),
  132. m_fAllowAnonymous ( TRUE),
  133. m_fAnonymousOnly ( FALSE),
  134. m_fAllowGuestAccess ( TRUE),
  135. m_fAnnotateDirectories ( FALSE),
  136. m_fLowercaseFiles ( FALSE),
  137. m_fMsdosDirOutput ( FALSE),
  138. m_fFourDigitYear ( FALSE),
  139. m_fEnableLicensing ( FALSE),
  140. m_fEnableDataConnTo3rdIP ( FALSE),
  141. m_fEnablePasvConnFrom3rdIP( FALSE),
  142. m_pszGreetingMessageWithLineFeed( NULL),
  143. m_pszBannerMessageWithLineFeed( NULL),
  144. m_pszLocalHostName ( NULL),
  145. m_dwUserFlags ( 0),
  146. m_ListenBacklog ( DEFAULT_LISTEN_BACKLOG),
  147. m_pFTPStats ( NULL),
  148. m_UserIsolationMode ( DEFAULT_USER_ISOLATION),
  149. m_fLogInUtf8 ( DEFAULT_LOG_IN_UTF_8),
  150. m_pAdIo ( NULL )
  151. {
  152. InitializeListHead( &m_ActiveConnectionsList);
  153. INITIALIZE_CRITICAL_SECTION( &m_csLock);
  154. InitializeListHead( &m_FreeConnectionsList);
  155. INITIALIZE_CRITICAL_SECTION( &m_csConnectionsList);
  156. if( QueryServerState() == MD_SERVER_STATE_INVALID ) {
  157. return;
  158. }
  159. m_pFTPStats = new FTP_SERVER_STATISTICS;
  160. if ( m_pFTPStats == NULL )
  161. {
  162. SetServerState( MD_SERVER_STATE_INVALID, ERROR_NOT_ENOUGH_MEMORY );
  163. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  164. }
  165. return;
  166. } // FTP_SERVER_INSTANCE::FTP_SERVER_INSTANCE()
  167. FTP_SERVER_INSTANCE::~FTP_SERVER_INSTANCE( VOID)
  168. /*++
  169. Description:
  170. Destructor function for server config object.
  171. ( Frees all dynamically allocated storage space)
  172. --*/
  173. {
  174. //
  175. // delete statistics object
  176. //
  177. if( m_pFTPStats != NULL )
  178. {
  179. delete m_pFTPStats;
  180. m_pFTPStats = NULL;
  181. }
  182. //
  183. // The strings are automatically freed by a call to destructor
  184. //
  185. if ( m_pszLocalHostName != NULL) {
  186. delete [] ( m_pszLocalHostName);
  187. }
  188. if ( m_pszGreetingMessageWithLineFeed != NULL) {
  189. TCP_FREE( m_pszGreetingMessageWithLineFeed);
  190. m_pszGreetingMessageWithLineFeed = NULL;
  191. }
  192. if ( m_pszBannerMessageWithLineFeed != NULL) {
  193. TCP_FREE( m_pszBannerMessageWithLineFeed);
  194. m_pszBannerMessageWithLineFeed = NULL;
  195. }
  196. m_rfAccessCheck.Reset( (IMDCOM*)m_Service->QueryMDObject() );
  197. DBG_ASSERT( m_cCurrentConnections == 0);
  198. DBG_ASSERT( IsListEmpty( &m_ActiveConnectionsList));
  199. LockConnectionsList();
  200. DBG_REQUIRE(FreeAllocCachedClientConn());
  201. UnlockConnectionsList();
  202. DBG_ASSERT( IsListEmpty( &m_FreeConnectionsList));
  203. if ( m_pAdIo ) {
  204. delete m_pAdIo;
  205. m_pAdIo = NULL;
  206. }
  207. //
  208. // Delete the critical section object
  209. //
  210. DeleteCriticalSection( &m_csLock);
  211. DeleteCriticalSection( &m_csConnectionsList);
  212. } /* FTP_SERVER_INSTANCE::~FTP_SERVER_INSTANCE() */
  213. DWORD
  214. FTP_SERVER_INSTANCE::StartInstance()
  215. {
  216. IF_DEBUG(INSTANCE) {
  217. DBGPRINTF((
  218. DBG_CONTEXT,
  219. "FTP_SERVER_INSTANCE::StartInstance called for %p. Current state %d\n",
  220. this,
  221. QueryServerState()
  222. ));
  223. }
  224. DBG_ASSERT(m_pFTPStats);
  225. m_pFTPStats->UpdateStopTime();
  226. DWORD dwError = IIS_SERVER_INSTANCE::StartInstance();
  227. if ( dwError)
  228. {
  229. IF_DEBUG(INSTANCE) {
  230. DBGPRINTF((
  231. DBG_CONTEXT,
  232. "FTO_SERVER_INSTANCE - IIS_SERVER_INSTANCE Failed. StartInstance returned 0x%x",
  233. dwError
  234. ));
  235. }
  236. return dwError;
  237. }
  238. dwError = InitFromRegistry( FC_FTP_ALL );
  239. if( dwError != NO_ERROR ) {
  240. goto StartInstance_Exit;
  241. }
  242. dwError = ReadAuthentInfo();
  243. if( dwError != NO_ERROR ) {
  244. goto StartInstance_Exit;
  245. }
  246. dwError = SetAdIoInfo();
  247. m_pFTPStats->UpdateStartTime();
  248. StartInstance_Exit:
  249. return dwError;
  250. }
  251. DWORD
  252. FTP_SERVER_INSTANCE::StopInstance()
  253. {
  254. DBG_ASSERT(m_pFTPStats);
  255. m_pFTPStats->UpdateStopTime();
  256. return IIS_SERVER_INSTANCE::StopInstance();
  257. }
  258. DWORD
  259. FTP_SERVER_INSTANCE::SetLocalHostName(IN LPCSTR pszHost)
  260. /*++
  261. This function copies the host name specified in the given string to
  262. configuration object.
  263. Arguments:
  264. pszHost pointer to string containing the local host name.
  265. Returns:
  266. NO_ERROR on success and ERROR_NOT_ENOUGH_MEMORY when no memory.
  267. ERROR_ALREADY_ASSIGNED if value is already present.
  268. --*/
  269. {
  270. //
  271. // if already a host name exists, return error.
  272. // otherwise allocate memory and copy the local host name.
  273. //
  274. if ( m_pszLocalHostName != NULL) {
  275. return (ERROR_ALREADY_ASSIGNED);
  276. } else {
  277. m_pszLocalHostName = new CHAR[lstrlenA(pszHost) + 1];
  278. if ( m_pszLocalHostName == NULL) {
  279. return (ERROR_NOT_ENOUGH_MEMORY);
  280. }
  281. lstrcpyA( m_pszLocalHostName, pszHost);
  282. }
  283. return (NO_ERROR);
  284. } // FTP_SERVER_INSTANCE::SetLocalHostName()
  285. DWORD
  286. FTP_SERVER_INSTANCE::InitFromRegistry(
  287. IN FIELD_CONTROL FieldsToRead)
  288. /*++
  289. Description:
  290. Initializes server configuration data from registry.
  291. Some values are also initialized with constants.
  292. If invalid registry key or load data from registry fails,
  293. then use default values.
  294. Arguments:
  295. hkeyReg handle to registry key
  296. FieldsToRead
  297. bitmask indicating the fields to read from the registry.
  298. This is useful when we try to read the new values after
  299. modifying the registry information as a result of
  300. SetAdminInfo call from the Admin UI
  301. Returns:
  302. NO_ERROR if there are no errors.
  303. Win32 error codes otherwise
  304. Limitations:
  305. No validity check is performed on the data present in registry.
  306. --*/
  307. {
  308. BOOL fSuccess = TRUE;
  309. DWORD err = NO_ERROR;
  310. IMDCOM* pMBCom;
  311. METADATA_HANDLE hMB;
  312. HRESULT hRes;
  313. METADATA_RECORD mdRecord;
  314. DWORD dwRequiredLen;
  315. HKEY hkeyReg = NULL;
  316. MB mb( (IMDCOM*)g_pInetSvc->QueryMDObject() );
  317. DWORD tmp;
  318. err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  319. QueryRegParamKey(),
  320. 0,
  321. KEY_READ,
  322. &hkeyReg );
  323. if ( hkeyReg == INVALID_HANDLE_VALUE ||
  324. hkeyReg == NULL) {
  325. //
  326. // Invalid Registry handle given
  327. //
  328. SetLastError( ERROR_INVALID_PARAMETER);
  329. return ( FALSE);
  330. }
  331. LockConfig();
  332. //
  333. // Read metabase data.
  334. //
  335. if( !mb.Open( QueryMDPath() ) ) {
  336. RegCloseKey( hkeyReg );
  337. UnLockConfig();
  338. return FALSE;
  339. }
  340. if( IsFieldSet( FieldsToRead, FC_FTP_EXIT_MESSAGE ) ) {
  341. if( !mb.GetStr( "",
  342. MD_EXIT_MESSAGE,
  343. IIS_MD_UT_SERVER,
  344. &m_ExitMessage,
  345. METADATA_INHERIT,
  346. DEFAULT_EXIT_MESSAGE ) ) {
  347. fSuccess = FALSE;
  348. err = GetLastError();
  349. }
  350. }
  351. if( fSuccess && IsFieldSet( FieldsToRead, FC_FTP_GREETING_MESSAGE ) ) {
  352. if( !mb.GetMultisz( "",
  353. MD_GREETING_MESSAGE,
  354. IIS_MD_UT_SERVER,
  355. &m_GreetingMessage ) ) {
  356. if( !m_GreetingMessage.Copy(
  357. DEFAULT_GREETING_MESSAGE,
  358. CCH_DEFAULT_GREETING_MESSAGE
  359. ) ) {
  360. fSuccess = FALSE;
  361. err = GetLastError();
  362. }
  363. }
  364. //
  365. // The m_pszGreetingMessage as read is a double null terminated
  366. // seq of strings (with one string per line)
  367. // A local copy of the string in the form suited for RPC Admin
  368. // should be generated.
  369. //
  370. if( fSuccess ) {
  371. fSuccess = GenMessageWithLineFeed( m_GreetingMessage.QueryStr(),
  372. &m_pszGreetingMessageWithLineFeed);
  373. if( !fSuccess ) {
  374. err = GetLastError();
  375. }
  376. }
  377. }
  378. if( fSuccess && IsFieldSet( FieldsToRead, FC_FTP_BANNER_MESSAGE ) ) {
  379. if( !mb.GetMultisz( "",
  380. MD_BANNER_MESSAGE,
  381. IIS_MD_UT_SERVER,
  382. &m_BannerMessage ) ) {
  383. if( !m_BannerMessage.Copy(
  384. DEFAULT_BANNER_MESSAGE,
  385. CCH_DEFAULT_BANNER_MESSAGE
  386. ) ) {
  387. fSuccess = FALSE;
  388. err = GetLastError();
  389. }
  390. }
  391. //
  392. // The m_pszBannerMessage as read is a double null terminated
  393. // seq of strings (with one string per line)
  394. // A local copy of the string in the form suited for RPC Admin
  395. // should be generated.
  396. //
  397. if( fSuccess ) {
  398. fSuccess = GenMessageWithLineFeed( m_BannerMessage.QueryStr(),
  399. &m_pszBannerMessageWithLineFeed);
  400. if( !fSuccess ) {
  401. err = GetLastError();
  402. }
  403. }
  404. }
  405. if( fSuccess && IsFieldSet( FieldsToRead, FC_FTP_MAX_CLIENTS_MESSAGE ) ) {
  406. if( !mb.GetStr( "",
  407. MD_MAX_CLIENTS_MESSAGE,
  408. IIS_MD_UT_SERVER,
  409. &m_MaxClientsMessage,
  410. METADATA_INHERIT,
  411. DEFAULT_MAX_CLIENTS_MSG ) ) {
  412. fSuccess = FALSE;
  413. err = GetLastError();
  414. }
  415. }
  416. if( IsFieldSet( FieldsToRead, FC_FTP_MSDOS_DIR_OUTPUT ) ) {
  417. if( !mb.GetDword( "",
  418. MD_MSDOS_DIR_OUTPUT,
  419. IIS_MD_UT_SERVER,
  420. &tmp ) ) {
  421. tmp = DEFAULT_MSDOS_DIR_OUTPUT;
  422. }
  423. m_fMsdosDirOutput = !!tmp;
  424. // clear and then set the MSDOS_DIR_OUTPUT in user flags
  425. m_dwUserFlags &= ~UF_MSDOS_DIR_OUTPUT;
  426. m_dwUserFlags |= (m_fMsdosDirOutput) ? UF_MSDOS_DIR_OUTPUT : 0;
  427. }
  428. if( IsFieldSet( FieldsToRead, FC_FTP_SHOW_4_DIGIT_YEAR) ) {
  429. if( !mb.GetDword( "",
  430. MD_SHOW_4_DIGIT_YEAR,
  431. IIS_MD_UT_SERVER,
  432. &tmp ) ) {
  433. tmp = DEFAULT_SHOW_4_DIGIT_YEAR;
  434. }
  435. m_fFourDigitYear = !!tmp;
  436. // clear and then set the 4_DIGIT_YEAR in user flags
  437. m_dwUserFlags &= ~UF_4_DIGIT_YEAR;
  438. m_dwUserFlags |= (m_fFourDigitYear) ? UF_4_DIGIT_YEAR : 0;
  439. }
  440. if( IsFieldSet( FieldsToRead, FC_FTP_ALLOW_ANONYMOUS ) ) {
  441. if( !mb.GetDword( "",
  442. MD_ALLOW_ANONYMOUS,
  443. IIS_MD_UT_SERVER,
  444. &tmp ) ) {
  445. tmp = DEFAULT_ALLOW_ANONYMOUS;
  446. }
  447. m_fAllowAnonymous = !!tmp;
  448. }
  449. if( IsFieldSet( FieldsToRead, FC_FTP_ANONYMOUS_ONLY ) ) {
  450. if( !mb.GetDword( "",
  451. MD_ANONYMOUS_ONLY,
  452. IIS_MD_UT_SERVER,
  453. &tmp ) ) {
  454. tmp = DEFAULT_ANONYMOUS_ONLY;
  455. }
  456. m_fAnonymousOnly = !!tmp;
  457. }
  458. if( IsFieldSet( FieldsToRead, FC_FTP_ALLOW_REPLACE_ON_RENAME ) ) {
  459. if( !mb.GetDword( "",
  460. MD_ALLOW_REPLACE_ON_RENAME,
  461. IIS_MD_UT_SERVER,
  462. &tmp ) ) {
  463. tmp = DEFAULT_ALLOW_REPLACE_ON_RENAME;
  464. }
  465. m_fAllowReplaceOnRename = !!tmp;
  466. }
  467. if( IsFieldSet( FieldsToRead, FC_FTP_USER_ISOLATION ) ) {
  468. if( !mb.GetDword( "",
  469. MD_USER_ISOLATION,
  470. IIS_MD_UT_SERVER,
  471. &tmp ) ) {
  472. tmp = DEFAULT_USER_ISOLATION;
  473. }
  474. m_UserIsolationMode = tmp;
  475. if (m_UserIsolationMode > MD_USER_ISOLATION_LAST) {
  476. m_UserIsolationMode = DEFAULT_USER_ISOLATION;
  477. }
  478. }
  479. if( IsFieldSet( FieldsToRead, FC_FTP_LOG_IN_UTF_8 ) ) {
  480. if( !mb.GetDword( "",
  481. MD_FTP_LOG_IN_UTF_8,
  482. IIS_MD_UT_SERVER,
  483. &tmp ) ) {
  484. tmp = DEFAULT_LOG_IN_UTF_8;
  485. }
  486. m_fLogInUtf8 = !!tmp;
  487. }
  488. //
  489. // Read registry data.
  490. //
  491. if( IsFieldSet( FieldsToRead, FC_FTP_LISTEN_BACKLOG ) )
  492. {
  493. m_ListenBacklog = ReadRegistryDword( hkeyReg,
  494. FTPD_LISTEN_BACKLOG,
  495. DEFAULT_LISTEN_BACKLOG );
  496. }
  497. if( IsFieldSet( FieldsToRead, FC_FTP_ALLOW_GUEST_ACCESS ) ) {
  498. m_fAllowGuestAccess = !!ReadRegistryDword( hkeyReg,
  499. FTPD_ALLOW_GUEST_ACCESS,
  500. DEFAULT_ALLOW_GUEST_ACCESS );
  501. }
  502. if( IsFieldSet( FieldsToRead, FC_FTP_ANNOTATE_DIRECTORIES ) ) {
  503. m_fAnnotateDirectories = !!ReadRegistryDword( hkeyReg,
  504. FTPD_ANNOTATE_DIRS,
  505. DEFAULT_ANNOTATE_DIRS );
  506. // clear and then set the ANNOTATE_DIRS in user flags
  507. m_dwUserFlags &= ~UF_ANNOTATE_DIRS;
  508. m_dwUserFlags |= (m_fAnnotateDirectories) ? UF_ANNOTATE_DIRS : 0;
  509. }
  510. if( IsFieldSet( FieldsToRead, FC_FTP_LOWERCASE_FILES ) ) {
  511. m_fLowercaseFiles = !!ReadRegistryDword( hkeyReg,
  512. FTPD_LOWERCASE_FILES,
  513. DEFAULT_LOWERCASE_FILES );
  514. }
  515. // fEnableDataConnTo3rdIP is not controlled by RPC yet.
  516. m_fEnableDataConnTo3rdIP = !!ReadRegistryDword(hkeyReg,
  517. FTPD_ENABLE_CONN_TO_3RDIP,
  518. DEFAULT_ENABLE_CONN_TO_3RDIP);
  519. // fEnablePasvConnFrom3rdIP is not controlled by RPC yet.
  520. m_fEnablePasvConnFrom3rdIP = !!ReadRegistryDword(hkeyReg,
  521. FTPD_ENABLE_PASV_FROM_3RDIP,
  522. DEFAULT_ENABLE_PASV_FROM_3RDIP);
  523. if( fSuccess ) {
  524. //
  525. // The following field is not supported in the admin API.
  526. //
  527. m_fEnableLicensing = !!ReadRegistryDword( hkeyReg,
  528. FTPD_ENABLE_LICENSING,
  529. DEFAULT_ENABLE_LICENSING );
  530. }
  531. if ( fSuccess )
  532. {
  533. m_rfAccessCheck.Reset( (IMDCOM*)m_Service->QueryMDObject() );
  534. pMBCom = (IMDCOM*)m_Service->QueryMDObject();
  535. hRes = pMBCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
  536. (BYTE *) QueryMDVRPath(),
  537. METADATA_PERMISSION_READ,
  538. 5000,
  539. &hMB );
  540. if ( SUCCEEDED( hRes ) )
  541. {
  542. mdRecord.dwMDIdentifier = MD_IP_SEC;
  543. mdRecord.dwMDAttributes = METADATA_INHERIT | METADATA_REFERENCE;
  544. mdRecord.dwMDUserType = IIS_MD_UT_FILE;
  545. mdRecord.dwMDDataType = BINARY_METADATA;
  546. mdRecord.dwMDDataLen = 0;
  547. mdRecord.pbMDData = (PBYTE)NULL;
  548. hRes = pMBCom->ComMDGetMetaData( hMB,
  549. (LPBYTE)"",
  550. &mdRecord,
  551. &dwRequiredLen );
  552. if ( SUCCEEDED( hRes ) && mdRecord.dwMDDataTag )
  553. {
  554. m_rfAccessCheck.Set( mdRecord.pbMDData,
  555. mdRecord.dwMDDataLen,
  556. mdRecord.dwMDDataTag );
  557. }
  558. DBG_REQUIRE( SUCCEEDED(pMBCom->ComMDCloseMetaObject( hMB )) );
  559. }
  560. else
  561. if( HRESULTTOWIN32( hRes ) != ERROR_PATH_NOT_FOUND )
  562. {
  563. fSuccess = FALSE;
  564. err = HRESULTTOWIN32( hRes );
  565. }
  566. }
  567. UnLockConfig();
  568. IF_DEBUG( CONFIG) {
  569. Print();
  570. }
  571. m_fValid = TRUE;
  572. RegCloseKey( hkeyReg );
  573. return ( err);
  574. } // FTP_SERVER_INSTANCE::InitFromRegistry()
  575. VOID
  576. FTP_SERVER_INSTANCE::Print( VOID) const
  577. /*++
  578. Description:
  579. Prints the configuration information for this server.
  580. To be used in debugging mode for verification.
  581. Returns:
  582. None.
  583. --*/
  584. {
  585. DBGPRINTF(( DBG_CONTEXT,
  586. "FTP Server Configuration ( %08x).\n", this ));
  587. #if 0
  588. READ_LOCK_INST();
  589. DBGPRINTF(( DBG_CONTEXT,
  590. " AnonymousUser = %s\n",
  591. g_pInetSvc->QueryAnonUserName() ));
  592. UNLOCK_INST();
  593. DBGPRINTF(( DBG_CONTEXT,
  594. " %s = %d\n"
  595. " %s = %d\n"
  596. " %s = %u\n"
  597. " %s = %u\n"
  598. " %s = %u\n"
  599. " %s = %u\n"
  600. " %s = %u\n",
  601. FTPD_ALLOW_ANONYMOUS,
  602. m_fAllowAnonymous,
  603. FTPD_ALLOW_GUEST_ACCESS,
  604. m_fAllowGuestAccess,
  605. FTPD_ANONYMOUS_ONLY,
  606. m_fAnonymousOnly,
  607. FTPD_ENABLE_CONN_TO_3RDIP,
  608. m_fEnableDtaConnTo3rdIP,
  609. FTPD_ENABLE_PASV_FROM_3RDIP,
  610. m_fEnablePasvConnFrom3rdIP,
  611. "LogAnonymous",
  612. g_pInetSvc->QueryLogAnonymous(),
  613. "LogNonAnonymous",
  614. g_pInetSvc->QueryLogNonAnonymous()
  615. ));
  616. DBGPRINTF(( DBG_CONTEXT,
  617. " %s = %d\n",
  618. FTPD_ENABLE_LICENSING,
  619. m_fEnableLicensing ));
  620. DBGPRINTF(( DBG_CONTEXT,
  621. " MaxConnections = %lu\n",
  622. g_pInetSvc->QueryMaxConnections() ));
  623. DBGPRINTF(( DBG_CONTEXT,
  624. " ConnectionTimeout = %lu\n",
  625. g_pInetSvc->QueryConnectionTimeout() ));
  626. DBGPRINTF(( DBG_CONTEXT,
  627. " %s = %d\n",
  628. FTPD_MSDOS_DIR_OUTPUT,
  629. m_fMsdosDirOutput ));
  630. DBGPRINTF(( DBG_CONTEXT,
  631. " %s = %d\n",
  632. FTPD_4_DIGIT_YEAR,
  633. m_f4DigitYear ));
  634. DBGPRINTF(( DBG_CONTEXT,
  635. " %s = %d\n",
  636. FTPD_ANNOTATE_DIRS,
  637. m_fAnnotateDirectories ));
  638. DBGPRINTF(( DBG_CONTEXT,
  639. " %s = %08lX\n",
  640. FTPD_DEBUG_FLAGS,
  641. GET_DEBUG_FLAGS()));
  642. READ_LOCK_INST();
  643. DBGPRINTF(( DBG_CONTEXT,
  644. " LogFileDirectory = %s\n",
  645. g_pInetSvc->QueryLogFileDirectory() ));
  646. UNLOCK_INST();
  647. DBGPRINTF(( DBG_CONTEXT,
  648. " LogFileAccess = %lu\n",
  649. g_pInetSvc->QueryLogFileType() ));
  650. DBGPRINTF(( DBG_CONTEXT,
  651. " %s = %u\n",
  652. FTPD_LISTEN_BACKLOG,
  653. m_ListenBacklog ));
  654. DBGPRINTF(( DBG_CONTEXT,
  655. " DefaultLogonDomain = %s\n",
  656. m_DefaultLogonDomain ));
  657. DBGPRINTF(( DBG_CONTEXT,
  658. " %s = %u\n",
  659. FTPD_USER_ISOLATION,
  660. m_UserIsolation ));
  661. DBGPRINTF(( DBG_CONTEXT,
  662. " %s = %u\n",
  663. FTPD_LOG_IN_UTF_8,
  664. m_fLogInUtf8 ));
  665. #endif
  666. return;
  667. } // FTP_SERVER_INSTANCE::Print()
  668. PICLIENT_CONNECTION
  669. FTP_SERVER_INSTANCE::AllocNewConnection()
  670. /*++
  671. This function first checks that there is room for more connections
  672. as per the configured max connections.
  673. If there is no more connections allowed, it returns NULL
  674. with *pfMaxExceeded = TRUE
  675. Otherwise:
  676. This function creates a new CLIENT_CONNECTION (USER_DATA) object.
  677. The creation maybe fresh from heap or from cached free list.
  678. It increments the counter of currnet connections and returns
  679. the allocated object (if non NULL).
  680. We enter a critical section to avoid race condition
  681. among different threads. (this can be improved NYI).
  682. Returns:
  683. pointer to new connection object on success and
  684. NULL if there is max Connections exceeded.
  685. --*/
  686. {
  687. PICLIENT_CONNECTION pConn = NULL;
  688. LockConnectionsList();
  689. //
  690. // We can add this new connection
  691. //
  692. pConn = AllocClientConnFromAllocCache();
  693. if ( pConn != NULL) {
  694. //
  695. // Increment the count of connected users
  696. //
  697. m_cCurrentConnections++;
  698. IF_DEBUG( CLIENT) {
  699. DBGPRINTF((DBG_CONTEXT, " CurrentConnections = %u\n",
  700. m_cCurrentConnections));
  701. }
  702. //
  703. // Update the current maximum connections
  704. //
  705. if ( m_cCurrentConnections > m_cMaxCurrentConnections) {
  706. m_cMaxCurrentConnections = m_cCurrentConnections;
  707. }
  708. //
  709. // Insert into the list of connected users.
  710. //
  711. DBG_ASSERT( !pConn->IsOnActiveConnectionList() );
  712. InsertTailList( &m_ActiveConnectionsList, &pConn->QueryListEntry());
  713. pConn->SetOnActiveConnectionList();
  714. }
  715. UnlockConnectionsList();
  716. return ( pConn);
  717. } // FTP_SERVER_INSTANCE::AllocNewConnection()
  718. VOID
  719. FTP_SERVER_INSTANCE::RemoveConnection(
  720. IN OUT PICLIENT_CONNECTION pcc
  721. )
  722. /*++
  723. --*/
  724. {
  725. LockConnectionsList();
  726. //
  727. // this assert is here just to see if we ever get in here with the connection not on the list
  728. // if we do, then checking for this condition was not in vein...
  729. //
  730. DBG_ASSERT( pcc->IsOnActiveConnectionList() );
  731. if (pcc->IsOnActiveConnectionList()) {
  732. //
  733. // Remove from list of connections
  734. //
  735. RemoveEntryList( &pcc->QueryListEntry());
  736. pcc->ResetOnActiveConnectionList();
  737. //
  738. // Decrement count of current users
  739. //
  740. m_cCurrentConnections--;
  741. IF_DEBUG( CLIENT) {
  742. DBGPRINTF((DBG_CONTEXT, " CurrentConnections = %u\n",
  743. m_cCurrentConnections));
  744. }
  745. //
  746. // move the free connection to free list
  747. //
  748. FreeClientConnToAllocCache( pcc);
  749. }
  750. UnlockConnectionsList();
  751. } // FTP_SERVER_INSTANCE::RemoveConnection()
  752. VOID
  753. FTP_SERVER_INSTANCE::DisconnectAllConnections( VOID)
  754. /*++
  755. Disconnects all user connections.
  756. Arguments:
  757. Instance - If NULL, then all users are disconnected. If !NULL, then
  758. only those users associated with the specified instance are
  759. disconnected.
  760. --*/
  761. {
  762. #ifdef CHECK_DBG
  763. CHAR rgchBuffer[90];
  764. #endif // CHECK_DBG
  765. DWORD dwLastTick = GetTickCount();
  766. DWORD dwCurrentTick;
  767. PLIST_ENTRY pEntry;
  768. PLIST_ENTRY pEntryNext;
  769. DBGPRINTF( ( DBG_CONTEXT,
  770. "Entering FTP_SERVER_INSTANCE::DisconnectAllConnections()\n"));
  771. //
  772. // Let's empty the connection list immediately while in the lock to avoid a
  773. // shutdown deadlock
  774. //
  775. LockConnectionsList();
  776. pEntry = m_ActiveConnectionsList.Flink;
  777. InitializeListHead( &m_ActiveConnectionsList);
  778. UnlockConnectionsList();
  779. //
  780. // close down all the active sockets.
  781. //
  782. for( ;
  783. pEntry != &m_ActiveConnectionsList;
  784. pEntry = pEntryNext) {
  785. PICLIENT_CONNECTION pConn =
  786. GET_USER_DATA_FROM_LIST_ENTRY( pEntry);
  787. pEntryNext = pEntry->Flink; // cache next entry since pConn may die
  788. ASSERT( pConn != NULL);
  789. # ifdef CHECK_DBG
  790. DBG_REQUIRE( _snprintf( rgchBuffer, sizeof( rgcBuffer ), "Kill UID=%u. Ref=%u\n",
  791. pConn->QueryId(), pConn->QueryReference()) > 0);
  792. rgchBuffer[ sizeof( rgcBuffer ) - 1 ] = '\0';
  793. OutputDebugString( rgchBuffer);
  794. # endif // CHECK_DBG
  795. dwCurrentTick = GetTickCount();
  796. if ( (dwCurrentTick - dwLastTick) >= ( SC_NOTIFY_INTERVAL)) {
  797. //
  798. // We seem to take longer time for cleaning up than
  799. // expected. Let us ask service controller to wait for us.
  800. //
  801. g_pInetSvc->
  802. DelayCurrentServiceCtrlOperation(SC_NOTIFY_INTERVAL * 2);
  803. dwLastTick = dwCurrentTick;
  804. }
  805. pConn->Reference();
  806. pConn->DisconnectUserWithError( ERROR_SERVER_DISABLED, TRUE);
  807. DBG_REQUIRE( pConn->DeReference() > 0 ); // remove ref added above
  808. if( pConn->RemoveActiveReference() ) {
  809. //
  810. // take the lock, as we are messing with the lists. The FreeList is still live,
  811. // and the connection may also be terminating spontaneously, so that remove can
  812. // occur concurrently.
  813. //
  814. LockConnectionsList();
  815. if (pConn->IsOnActiveConnectionList()) {
  816. //
  817. // This connection is due for deletion. Kill it.
  818. //
  819. // Remove from list of connections
  820. //
  821. pConn->Cleanup();
  822. RemoveEntryList( &pConn->QueryListEntry());
  823. pConn->ResetOnActiveConnectionList();
  824. //
  825. // Decrement count of current users
  826. //
  827. m_cCurrentConnections--;
  828. // move the connection to free list
  829. FreeClientConnToAllocCache( pConn);
  830. }
  831. UnlockConnectionsList();
  832. }
  833. } // for
  834. //
  835. // Wait for the users to die.
  836. // The connection objects should be automatically freed because the
  837. // socket has been closed. Subsequent requests will fail
  838. // and cause a blowaway of the connection objects.
  839. //
  840. //
  841. // Wait for the users to die.
  842. //
  843. for( int i = 0 ;
  844. ( i < CLEANUP_RETRY_COUNT ) && ( m_cCurrentConnections > 0);
  845. i++ )
  846. {
  847. DBGPRINTF(( DBG_CONTEXT, "Sleep Iteration %d; Time=%u millisecs."
  848. " CurrentConn=%d.\n",
  849. i, CLEANUP_POLL_INTERVAL, m_cCurrentConnections));
  850. g_pInetSvc->
  851. DelayCurrentServiceCtrlOperation( CLEANUP_POLL_INTERVAL * 2);
  852. Sleep( CLEANUP_POLL_INTERVAL );
  853. }
  854. return;
  855. } // FTP_SERVER_INSTANCE::DisconnectAllConnections()
  856. BOOL
  857. FTP_SERVER_INSTANCE::EnumerateConnection(
  858. IN PFN_CLIENT_CONNECTION_ENUM pfnConnEnum,
  859. IN LPVOID pContext,
  860. IN DWORD dwConnectionId)
  861. /*++
  862. This function iterates through all the connections in the current connected
  863. users list and enumerates each of them. If the connectionId matches then
  864. given callback function is called. If the ConnectionId is 0, then the
  865. callback is called for each and every connection active currently.
  866. During such a call the reference count of the connection is bumped up.
  867. Call this function after obtaining the ConnectionsList Lock.
  868. Arguments:
  869. pfnConnEnum pointer to function to be called when a match is found.
  870. pContext pointer to context information to be passed in
  871. for callback
  872. dwConnectionId DWORD containing the Connection Id. IF 0 match all the
  873. connections.
  874. Returns:
  875. FALSE if no match is found
  876. TRUE if atleast one match is found.
  877. --*/
  878. {
  879. BOOL fReturn = FALSE;
  880. BOOL fFoundOne = FALSE;
  881. PLIST_ENTRY pEntry;
  882. PLIST_ENTRY pEntryNext;
  883. DBG_ASSERT( pfnConnEnum != NULL);
  884. //
  885. // Loop through the list of connections and call the callback
  886. // for each connection that matches condition
  887. //
  888. for ( pEntry = m_ActiveConnectionsList.Flink,
  889. pEntryNext = &m_ActiveConnectionsList;
  890. pEntry != &m_ActiveConnectionsList;
  891. pEntry = pEntryNext
  892. ) {
  893. PICLIENT_CONNECTION pConn =
  894. GET_USER_DATA_FROM_LIST_ENTRY( pEntry);
  895. pEntryNext = pEntry->Flink; // cache next entry since pConn may die
  896. if ( dwConnectionId == 0 || dwConnectionId == pConn->QueryId()) {
  897. pConn->Reference();
  898. fReturn = ( pfnConnEnum)( pConn, pContext);
  899. if ( !pConn->DeReference()) {
  900. // Blowaway the connection and update the count of entries.
  901. //
  902. // Remove from list of connections
  903. //
  904. if (pConn->IsOnActiveConnectionList()) {
  905. pConn->Cleanup();
  906. RemoveEntryList( &pConn->QueryListEntry());
  907. pConn->ResetOnActiveConnectionList();
  908. //
  909. // Decrement count of current users
  910. //
  911. m_cCurrentConnections--;
  912. IF_DEBUG( CLIENT) {
  913. DBGPRINTF((DBG_CONTEXT, " CurrentConnections = %u\n",
  914. m_cCurrentConnections));
  915. }
  916. // move the free connection to free list
  917. FreeClientConnToAllocCache( pConn);
  918. }
  919. }
  920. if (!fReturn) {
  921. break;
  922. }
  923. fFoundOne = TRUE;
  924. }
  925. } // for
  926. //
  927. // If we didn't find any, assume that there was no match.
  928. //
  929. if ( !fFoundOne ) {
  930. SetLastError( ERROR_NO_MORE_ITEMS );
  931. fReturn = FALSE;
  932. }
  933. return ( fReturn);
  934. } // FTP_SERVER_INSTANCE::EnumerateConnection()
  935. DWORD
  936. FTP_SERVER_INSTANCE::GetConfigInformation(OUT LPFTP_CONFIG_INFO pConfig)
  937. /*++
  938. This function copies the ftp server configuration into the given
  939. structure (pointed to).
  940. Arguments:
  941. pConfig -- pointer to FTP_CONFIG_INFO which on success will contain
  942. the ftp server configuration
  943. Returns:
  944. Win32 error code. NO_ERROR on success.
  945. --*/
  946. {
  947. DWORD dwError = NO_ERROR;
  948. memset( pConfig, 0, sizeof(*pConfig) );
  949. pConfig->FieldControl = FC_FTP_ALL;
  950. LockConfig();
  951. pConfig->fAllowAnonymous = m_fAllowAnonymous;
  952. pConfig->fAllowGuestAccess = m_fAllowGuestAccess;
  953. pConfig->fAnnotateDirectories = m_fAnnotateDirectories;
  954. pConfig->fAnonymousOnly = m_fAnonymousOnly;
  955. pConfig->dwListenBacklog = m_ListenBacklog;
  956. pConfig->fLowercaseFiles = m_fLowercaseFiles;
  957. pConfig->fMsdosDirOutput = m_fMsdosDirOutput;
  958. pConfig->dwUserIsolationMode = m_UserIsolationMode;
  959. pConfig->fLogInUtf8 = m_fLogInUtf8;
  960. if( !ConvertStringToRpc( &pConfig->lpszExitMessage,
  961. QueryExitMsg() ) ||
  962. !ConvertStringToRpc( &pConfig->lpszGreetingMessage,
  963. m_pszGreetingMessageWithLineFeed ) ||
  964. !ConvertStringToRpc( &pConfig->lpszBannerMessage,
  965. m_pszBannerMessageWithLineFeed ) ||
  966. !ConvertStringToRpc( &pConfig->lpszMaxClientsMessage,
  967. QueryMaxClientsMsg() ) )
  968. {
  969. dwError = GetLastError();
  970. }
  971. UnLockConfig();
  972. if ( dwError == NO_ERROR) {
  973. pConfig->lpszHomeDirectory = NULL; // use query virtual roots.
  974. }
  975. if ( dwError != NO_ERROR) {
  976. FreeRpcString( pConfig->lpszExitMessage );
  977. FreeRpcString( pConfig->lpszGreetingMessage );
  978. FreeRpcString( pConfig->lpszBannerMessage );
  979. FreeRpcString( pConfig->lpszHomeDirectory );
  980. FreeRpcString( pConfig->lpszMaxClientsMessage );
  981. }
  982. return (dwError);
  983. } // FTP_SERVER_INSTANCE::GetConfigurationInformation()
  984. // Private Functions ...
  985. BOOL
  986. FTP_SERVER_INSTANCE::FreeAllocCachedClientConn( VOID)
  987. /*++
  988. This function frees all the alloc cached client connections
  989. It walks through the list of alloc cached entries and frees them.
  990. This function should be called when Server module is terminated and when
  991. no other thread can interfere in processing a shared object.
  992. Arguments:
  993. NONE
  994. Returns:
  995. TRUE on success and FALSE on failure.
  996. --*/
  997. {
  998. register PLIST_ENTRY pEntry;
  999. register PLIST_ENTRY pEntryNext;
  1000. for( pEntry = m_FreeConnectionsList.Flink;
  1001. pEntry != &m_FreeConnectionsList; ) {
  1002. PICLIENT_CONNECTION pConn =
  1003. GET_USER_DATA_FROM_LIST_ENTRY( pEntry);
  1004. pEntryNext = pEntry->Flink; // cache next entry since pConn may die
  1005. DBG_ASSERT( pConn->QueryReference() == 0);
  1006. RemoveEntryList( pEntry ); // Remove this context from list
  1007. // delete the object itself
  1008. delete pConn;
  1009. pEntry = pEntryNext;
  1010. } // for
  1011. return (TRUE);
  1012. } // USER_DATA::FreeAllocCachedClientConn()
  1013. PICLIENT_CONNECTION
  1014. FTP_SERVER_INSTANCE::AllocClientConnFromAllocCache(
  1015. VOID
  1016. )
  1017. /*++
  1018. This function attempts to allocate a client connection object from
  1019. the allocation cache, using the free list of connections available.
  1020. If none is available, then a new object is allocated using new ()
  1021. and returned to the caller.
  1022. Eventually the object will enter free list and will be available
  1023. for free use.
  1024. Arguments:
  1025. None
  1026. Returns:
  1027. On success a valid pointer to client connection object.
  1028. Issues:
  1029. This function should be called while holding the ConnectionsLock.
  1030. --*/
  1031. {
  1032. PLIST_ENTRY pEntry = m_FreeConnectionsList.Flink;
  1033. PICLIENT_CONNECTION pConn;
  1034. if ( pEntry != &m_FreeConnectionsList) {
  1035. pConn = GET_USER_DATA_FROM_LIST_ENTRY( pEntry);
  1036. DBG_ASSERT( pConn != NULL);
  1037. RemoveEntryList( pEntry); // remove entry from free list
  1038. DBG_ASSERT( pConn->QueryInstance() == NULL );
  1039. pConn->SetInstance( this );
  1040. } else {
  1041. //
  1042. // create a new object, since allocation cache is empty
  1043. //
  1044. pConn = new USER_DATA( this );
  1045. }
  1046. return (pConn);
  1047. } // FTP_SERVER_INSTANCE::AllocClientConnFromAllocCache()
  1048. VOID
  1049. FTP_SERVER_INSTANCE::FreeClientConnToAllocCache(
  1050. IN PICLIENT_CONNECTION pClient
  1051. )
  1052. /*++
  1053. This function releases the given Client connection to the allocation cache.
  1054. It adds the given object to allocation cache.
  1055. Arguments:
  1056. pClient pointer to client connection object which needs to be freed.
  1057. Returns:
  1058. None
  1059. Issues:
  1060. This function should be called after holding the ConnectionsList
  1061. critical section.
  1062. Should we limit the number of items that can be on free list and
  1063. to release the remaining to global pool? NYI (depends on # CPUs)
  1064. --*/
  1065. {
  1066. PLIST_ENTRY pEntry = &pClient->QueryListEntry();
  1067. //
  1068. // empty out instance pointer
  1069. //
  1070. pClient->QueryInstance()->DecrementCurrentConnections();
  1071. pClient->QueryInstance()->Dereference( );
  1072. pClient->SetInstance( NULL );
  1073. InsertHeadList( &m_FreeConnectionsList, pEntry);
  1074. return;
  1075. } // FTP_SERVER_INSTANCE::FreeClientConnToAllocCache()
  1076. DWORD
  1077. FTP_SERVER_INSTANCE::SetAdIoInfo()
  1078. /*++
  1079. Create, update or delete the AD IO related objects when starting the service or when
  1080. configuration parameters change
  1081. Arguments:
  1082. None.
  1083. Returns:
  1084. NO_ERROR on success, WinError on failure.
  1085. --*/
  1086. {
  1087. DWORD dwError = NO_ERROR;
  1088. //
  1089. // ADIO is needed in AD Isolation mode
  1090. //
  1091. BOOL Need_ADIO = (QueryIsolationMode() == MD_USER_ISOLATION_AD);
  1092. //
  1093. // Anonym Cache is needed in AD isolation, if anonymous access is allowed
  1094. //
  1095. BOOL Need_AnonymCache = Need_ADIO && IsAllowedUser( TRUE );
  1096. //
  1097. // we are checking if the relevant configuration changed, to determine if we need to delete,
  1098. // create, or update either of the AD access objects
  1099. //
  1100. switch ( (m_pAdIo ? 1 : 0) + (Need_ADIO ? 2 : 0) ) {
  1101. case 0: // No ADIO before or after
  1102. break;
  1103. case 1: // We had ADIO, no longer need it (isolation mode changed from enterprise)
  1104. delete m_pAdIo;
  1105. m_pAdIo = NULL;
  1106. break;
  1107. case 2: // we didn't have ADIO, now we need it. Startup in enterprise iolation
  1108. m_pAdIo = new AD_IO;
  1109. if (m_pAdIo == NULL) {
  1110. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1111. } else {
  1112. //
  1113. // allocated OK. check for init errors
  1114. //
  1115. dwError = GetLastError();
  1116. }
  1117. if( dwError != NO_ERROR ) {
  1118. break;
  1119. }
  1120. // fall through to configuring the object
  1121. case 3: // AD or anonymous user credentials changed
  1122. {
  1123. PSTR pszUser = NULL, pszDomain = NULL;
  1124. //
  1125. // if we need it, get the anonymous user details
  1126. //
  1127. if (Need_AnonymCache) {
  1128. BOOL fIsLocalUser;
  1129. CHAR szDomainAndUser[ UNLEN + DNLEN + 2 ];
  1130. if (
  1131. (QueryAuthentInfo()->strAnonUserName.QueryCCH() >= sizeof(szDomainAndUser)) ||
  1132. !strcpy( szDomainAndUser, QueryAuthentInfo()->strAnonUserName.QueryStr()) || // this is always false
  1133. !ParseUserName( szDomainAndUser, &pszUser, &pszDomain,
  1134. QueryLocalHostName(), &fIsLocalUser) ||
  1135. fIsLocalUser ) {
  1136. dwError = ERROR_INVALID_PARAMETER;
  1137. break;
  1138. }
  1139. }
  1140. dwError = m_pAdIo->Configure(
  1141. QueryADConnAuthentInfo()->strName,
  1142. QueryADConnAuthentInfo()->strDomain,
  1143. QueryADConnAuthentInfo()->strPassword,
  1144. pszUser,
  1145. pszDomain);
  1146. break;
  1147. }
  1148. default:
  1149. DBG_ASSERT( FALSE );
  1150. dwError = ERROR_INTERNAL_ERROR;
  1151. }
  1152. return dwError;
  1153. } // FTP_SERVER_INSTANCE::SetAdIoInfo()
  1154. /*******************************************************************
  1155. NAME: GetDefaultDomainName
  1156. SYNOPSIS: Fills in the given array with the name of the default
  1157. domain to use for logon validation.
  1158. ENTRY: pszDomainName - Pointer to a buffer that will receive
  1159. the default domain name.
  1160. cchDomainName - The size (in charactesr) of the domain
  1161. name buffer.
  1162. RETURNS: APIERR - 0 if successful, !0 if not.
  1163. HISTORY:
  1164. KeithMo 05-Dec-1994 Created.
  1165. ********************************************************************/
  1166. APIERR
  1167. GetDefaultDomainName(
  1168. STR * pstrDomainName
  1169. )
  1170. {
  1171. OBJECT_ATTRIBUTES ObjectAttributes;
  1172. NTSTATUS NtStatus;
  1173. INT Result;
  1174. APIERR err = 0;
  1175. LSA_HANDLE LsaPolicyHandle = NULL;
  1176. PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
  1177. //
  1178. // Open a handle to the local machine's LSA policy object.
  1179. //
  1180. InitializeObjectAttributes( &ObjectAttributes, // object attributes
  1181. NULL, // name
  1182. 0L, // attributes
  1183. NULL, // root directory
  1184. NULL ); // security descriptor
  1185. NtStatus = LsaOpenPolicy( NULL, // system name
  1186. &ObjectAttributes, // object attributes
  1187. POLICY_EXECUTE, // access mask
  1188. &LsaPolicyHandle ); // policy handle
  1189. if( !NT_SUCCESS( NtStatus ) )
  1190. {
  1191. DBGPRINTF(( DBG_CONTEXT,
  1192. "cannot open lsa policy, error %08lX\n",
  1193. NtStatus ));
  1194. err = LsaNtStatusToWinError( NtStatus );
  1195. goto Cleanup;
  1196. }
  1197. //
  1198. // Query the domain information from the policy object.
  1199. //
  1200. NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle,
  1201. PolicyAccountDomainInformation,
  1202. (PVOID *)&DomainInfo );
  1203. if( !NT_SUCCESS( NtStatus ) )
  1204. {
  1205. DBGPRINTF(( DBG_CONTEXT,
  1206. "cannot query lsa policy info, error %08lX\n",
  1207. NtStatus ));
  1208. err = LsaNtStatusToWinError( NtStatus );
  1209. goto Cleanup;
  1210. }
  1211. //
  1212. // Compute the required length of the ANSI name.
  1213. //
  1214. Result = WideCharToMultiByte( CP_ACP,
  1215. 0, // dwFlags
  1216. (LPCWSTR)DomainInfo->DomainName.Buffer,
  1217. DomainInfo->DomainName.Length /sizeof(WCHAR),
  1218. NULL, // lpMultiByteStr
  1219. 0, // cchMultiByte
  1220. NULL, // lpDefaultChar
  1221. NULL // lpUsedDefaultChar
  1222. );
  1223. if( Result <= 0 )
  1224. {
  1225. err = GetLastError();
  1226. goto Cleanup;
  1227. }
  1228. //
  1229. // Resize the output string as appropriate, including room for the
  1230. // terminating '\0'.
  1231. //
  1232. if( !pstrDomainName->Resize( (UINT)Result + 1 ) )
  1233. {
  1234. err = GetLastError();
  1235. goto Cleanup;
  1236. }
  1237. //
  1238. // Convert the name from UNICODE to ANSI.
  1239. //
  1240. Result = WideCharToMultiByte( CP_ACP,
  1241. 0, // flags
  1242. (LPCWSTR)DomainInfo->DomainName.Buffer,
  1243. DomainInfo->DomainName.Length /sizeof(WCHAR),
  1244. pstrDomainName->QueryStr(),
  1245. pstrDomainName->QuerySize() - 1, // for '\0'
  1246. NULL,
  1247. NULL
  1248. );
  1249. if( Result <= 0 )
  1250. {
  1251. err = GetLastError();
  1252. DBGPRINTF(( DBG_CONTEXT,
  1253. "cannot convert domain name to ANSI, error %d\n",
  1254. err ));
  1255. goto Cleanup;
  1256. }
  1257. //
  1258. // Ensure the ANSI string is zero terminated.
  1259. //
  1260. DBG_ASSERT( (DWORD)Result < pstrDomainName->QuerySize() );
  1261. pstrDomainName->QueryStr()[Result] = '\0';
  1262. //
  1263. // Success!
  1264. //
  1265. DBG_ASSERT( err == 0 );
  1266. IF_DEBUG( CONFIG )
  1267. {
  1268. DBGPRINTF(( DBG_CONTEXT,
  1269. "GetDefaultDomainName: default domain = %s\n",
  1270. pstrDomainName->QueryStr() ));
  1271. }
  1272. Cleanup:
  1273. if( DomainInfo != NULL )
  1274. {
  1275. LsaFreeMemory( (PVOID)DomainInfo );
  1276. }
  1277. if( LsaPolicyHandle != NULL )
  1278. {
  1279. LsaClose( LsaPolicyHandle );
  1280. }
  1281. return err;
  1282. } // GetDefaultDomainName()
  1283. BOOL
  1284. GenMessageWithLineFeed(IN LPTSTR pszzMessage,
  1285. IN LPTSTR * ppszMessageWithLineFeed)
  1286. {
  1287. DWORD cbLen;
  1288. TCHAR *pszDst;
  1289. DBG_ASSERT( ppszMessageWithLineFeed != NULL);
  1290. //
  1291. // 1. Find the length of the complete message. Each itteration adds
  1292. // the length of the string plus one, which is either a \n or a \0
  1293. // at the end
  1294. //
  1295. for ( cbLen = 0;
  1296. *(pszzMessage + cbLen) != TEXT('\0');
  1297. cbLen += _tcslen( pszzMessage + cbLen) + 1 )
  1298. ;
  1299. //
  1300. // 2. Allocate sufficient space to hold the data
  1301. //
  1302. if ( *ppszMessageWithLineFeed != NULL) {
  1303. TCP_FREE( *ppszMessageWithLineFeed);
  1304. }
  1305. *ppszMessageWithLineFeed = (TCHAR *) TCP_ALLOC((cbLen) * sizeof(TCHAR));
  1306. if ( *ppszMessageWithLineFeed == NULL) {
  1307. SetLastError( ERROR_NOT_ENOUGH_MEMORY);
  1308. return (FALSE);
  1309. }
  1310. //
  1311. // 3.
  1312. // Copy the entire message, then going backword (skipping the last \0)
  1313. // replace all \0's with \n's
  1314. //
  1315. CopyMemory( *ppszMessageWithLineFeed, pszzMessage, cbLen);
  1316. for(pszDst = *ppszMessageWithLineFeed + cbLen - 2;
  1317. pszDst > *ppszMessageWithLineFeed;
  1318. pszDst--) {
  1319. if (*pszDst == '\0') {
  1320. *pszDst = '\n';
  1321. }
  1322. }
  1323. return ( TRUE);
  1324. } // GenMessageWithLineFeed()
  1325. TCHAR *
  1326. FtpdReadRegistryString(IN HKEY hkey,
  1327. IN LPCTSTR pszValueName,
  1328. IN LPCTSTR pchDefaultValue,
  1329. IN DWORD cbDefaultValue)
  1330. /*++
  1331. This function reads a string (REG_SZ/REG_MULTI_SZ/REG_EXPAND_SZ) without
  1332. expanding the same. It allocates memory for reading the data from registry.
  1333. Arguments:
  1334. hkey handle for the registry key.
  1335. pszValueName pointer to string containing the name of value to be read.
  1336. pchDefaultValue pointer to default value to be used for reading the string
  1337. this may be double null terminated sequence of string for
  1338. REG_MULTI_SZ strings
  1339. cchDefaultValue count of characters in default value string,
  1340. including double null characters.
  1341. Return:
  1342. pointer to newly allocated string containing the data read from registry
  1343. or the default string.
  1344. --*/
  1345. {
  1346. TCHAR * pszBuffer1 = NULL;
  1347. DWORD err;
  1348. if( hkey == NULL ) {
  1349. //
  1350. // Pretend the key wasn't found.
  1351. //
  1352. err = ERROR_FILE_NOT_FOUND;
  1353. } else {
  1354. DWORD cbBuffer = 0;
  1355. DWORD dwType;
  1356. //
  1357. // Determine the buffer size.
  1358. //
  1359. err = RegQueryValueEx( hkey,
  1360. pszValueName,
  1361. NULL,
  1362. &dwType,
  1363. NULL,
  1364. &cbBuffer );
  1365. if( ( err == NO_ERROR ) || ( err == ERROR_MORE_DATA ) ) {
  1366. if(( dwType != REG_SZ ) &&
  1367. ( dwType != REG_MULTI_SZ ) &&
  1368. ( dwType != REG_EXPAND_SZ )
  1369. ) {
  1370. //
  1371. // Type mismatch, registry data NOT a string.
  1372. // Use default.
  1373. //
  1374. err = ERROR_FILE_NOT_FOUND;
  1375. } else {
  1376. //
  1377. // Item found, allocate a buffer.
  1378. //
  1379. pszBuffer1 = (TCHAR *) TCP_ALLOC( cbBuffer+sizeof(TCHAR) );
  1380. if( pszBuffer1 == NULL ) {
  1381. err = GetLastError();
  1382. } else {
  1383. //
  1384. // Now read the value into the buffer.
  1385. //
  1386. err = RegQueryValueEx( hkey,
  1387. pszValueName,
  1388. NULL,
  1389. NULL,
  1390. (LPBYTE)pszBuffer1,
  1391. &cbBuffer );
  1392. }
  1393. }
  1394. }
  1395. }
  1396. if( err == ERROR_FILE_NOT_FOUND ) {
  1397. //
  1398. // Item not found, use default value.
  1399. //
  1400. err = NO_ERROR;
  1401. if( pchDefaultValue != NULL ) {
  1402. if ( pszBuffer1 != NULL) {
  1403. TCP_FREE( pszBuffer1);
  1404. }
  1405. pszBuffer1 = (TCHAR *)TCP_ALLOC((cbDefaultValue) *
  1406. sizeof(TCHAR));
  1407. if( pszBuffer1 == NULL ) {
  1408. err = GetLastError();
  1409. } else {
  1410. memcpy(pszBuffer1, pchDefaultValue,
  1411. cbDefaultValue*sizeof(TCHAR) );
  1412. }
  1413. }
  1414. }
  1415. if( err != NO_ERROR ) {
  1416. //
  1417. // Something tragic happend; free any allocated buffers
  1418. // and return NULL to the caller, indicating failure.
  1419. //
  1420. if( pszBuffer1 != NULL ) {
  1421. TCP_FREE( pszBuffer1 );
  1422. pszBuffer1 = NULL;
  1423. }
  1424. SetLastError( err);
  1425. }
  1426. return pszBuffer1;
  1427. } // FtpdReadRegistryString()
  1428. BOOL
  1429. FtpdReadRegString(
  1430. IN HKEY hkey,
  1431. OUT TCHAR * * ppchstr,
  1432. IN LPCTSTR pchValue,
  1433. IN LPCTSTR pchDefault,
  1434. IN DWORD cchDefault
  1435. )
  1436. /*++
  1437. Description
  1438. Gets the specified string from the registry. If *ppchstr is not NULL,
  1439. then the value is freed. If the registry call fails, *ppchstr is
  1440. restored to its previous value.
  1441. Arguments:
  1442. hkey - Handle to open key
  1443. ppchstr - Receives pointer of allocated memory of the new value of the
  1444. string
  1445. pchValue - Which registry value to retrieve
  1446. pchDefault - Default string if value isn't found
  1447. cchDefault - count of characters in default value
  1448. Note:
  1449. --*/
  1450. {
  1451. CHAR * pch = *ppchstr;
  1452. *ppchstr = FtpdReadRegistryString(hkey,
  1453. pchValue,
  1454. pchDefault,
  1455. cchDefault);
  1456. if ( !*ppchstr )
  1457. {
  1458. *ppchstr = pch;
  1459. return FALSE;
  1460. }
  1461. if ( pch ) {
  1462. //
  1463. // use TCP_FREE since FtpdReadRegistryString() uses TCP_ALLOC
  1464. // to allocate the chunk of memory
  1465. //
  1466. TCP_FREE( pch );
  1467. }
  1468. return TRUE;
  1469. } // FtpdReadRegString()
  1470. BOOL
  1471. FTP_IIS_SERVICE::AddInstanceInfo(
  1472. IN DWORD dwInstance,
  1473. IN BOOL fMigrateRoots
  1474. )
  1475. {
  1476. PFTP_SERVER_INSTANCE pInstance;
  1477. CHAR szHost[MAXGETHOSTSTRUCT];
  1478. IF_DEBUG(SERVICE_CTRL) {
  1479. DBGPRINTF(( DBG_CONTEXT,
  1480. "AddInstanceInfo: instance %d reg %s\n", dwInstance, QueryRegParamKey() ));
  1481. }
  1482. // Guard against startup race where another thread might be adding
  1483. // instances before the MSFTPSVC thread is finished with initializing
  1484. // the g_pInetSvc pointer.
  1485. if ( g_pInetSvc == NULL )
  1486. {
  1487. SetLastError( ERROR_NOT_SUPPORTED );
  1488. return FALSE;
  1489. }
  1490. //
  1491. // Get the current host name.
  1492. //
  1493. if( gethostname( szHost, sizeof(szHost) ) < 0 ) {
  1494. return FALSE;
  1495. }
  1496. //
  1497. // Create the new instance
  1498. //
  1499. pInstance = new FTP_SERVER_INSTANCE(
  1500. this,
  1501. dwInstance,
  1502. IPPORT_FTP,
  1503. QueryRegParamKey(),
  1504. FTPD_ANONYMOUS_SECRET_W,
  1505. FTPD_ROOT_SECRET_W,
  1506. fMigrateRoots
  1507. );
  1508. if (pInstance == NULL) {
  1509. SetLastError( ERROR_NOT_ENOUGH_MEMORY );
  1510. return FALSE;
  1511. }
  1512. //
  1513. // must be before AddInstanceInfoHelper() as the local host name
  1514. // is needed when initializing the authentication info under AD isolation
  1515. //
  1516. if( pInstance->SetLocalHostName( szHost ) != NO_ERROR ) {
  1517. return FALSE;
  1518. }
  1519. if( !AddInstanceInfoHelper( pInstance ) ) {
  1520. return FALSE;
  1521. }
  1522. return TRUE;
  1523. } // FTP_IIS_SERVICE::AddInstanceInfo
  1524. DWORD
  1525. FTP_IIS_SERVICE::DisconnectUsersByInstance(
  1526. IN IIS_SERVER_INSTANCE * pInstance
  1527. )
  1528. /*++
  1529. Virtual callback invoked by IIS_SERVER_INSTANCE::StopInstance() to
  1530. disconnect all users associated with the given instance.
  1531. Arguments:
  1532. pInstance - All users associated with this instance will be
  1533. forcibly disconnected.
  1534. --*/
  1535. {
  1536. ((FTP_SERVER_INSTANCE *)pInstance)->DisconnectAllConnections();
  1537. return NO_ERROR;
  1538. } // FTP_IIS_SERVICE::DisconnectUsersByInstance
  1539. VOID
  1540. FTP_SERVER_INSTANCE::MDChangeNotify(
  1541. MD_CHANGE_OBJECT * pco
  1542. )
  1543. /*++
  1544. Handles metabase change notifications.
  1545. Arguments:
  1546. pco - Path and ID that changed.
  1547. --*/
  1548. {
  1549. FIELD_CONTROL control = 0;
  1550. DWORD i;
  1551. DWORD err;
  1552. DWORD id;
  1553. PCSTR pszURL;
  1554. DWORD dwURLLength;
  1555. //
  1556. // Let the parent deal with it.
  1557. //
  1558. IIS_SERVER_INSTANCE::MDChangeNotify( pco );
  1559. //
  1560. // Now flush the metacache and relevant file handle cache entries.
  1561. //
  1562. TsFlushMetaCache(METACACHE_FTP_SERVER_ID, FALSE);
  1563. if ( !_mbsnbicmp((PUCHAR)pco->pszMDPath, (PUCHAR)QueryMDVRPath(),
  1564. _mbslen( (PUCHAR)QueryMDVRPath() )) )
  1565. {
  1566. pszURL = (CHAR *)pco->pszMDPath + QueryMDVRPathLen() - 1;
  1567. //
  1568. // Figure out the length of the URL. Unless this is the root,
  1569. // we want to strip the trailing slash.
  1570. if (memcmp(pszURL, "/", sizeof("/")) != 0)
  1571. {
  1572. dwURLLength = strlen(pszURL) - 1;
  1573. }
  1574. else
  1575. {
  1576. dwURLLength = sizeof("/") - 1;
  1577. }
  1578. }
  1579. else
  1580. {
  1581. //
  1582. // Presumably this is for a change above the root URL level, i.e. a
  1583. // change of a property at the service level. Since this affects
  1584. // everything, flush starting at the root.
  1585. //
  1586. pszURL = "/";
  1587. dwURLLength = sizeof("/") - 1;
  1588. }
  1589. DBG_ASSERT(pszURL != NULL);
  1590. DBG_ASSERT(*pszURL != '\0');
  1591. TsFlushURL(GetTsvcCache(), pszURL, dwURLLength, RESERVED_DEMUX_URI_INFO);
  1592. //
  1593. // Interpret the changes.
  1594. //
  1595. for( i = 0 ; i < pco->dwMDNumDataIDs ; i++ ) {
  1596. id = pco->pdwMDDataIDs[i];
  1597. switch( id ) {
  1598. case MD_EXIT_MESSAGE :
  1599. control |= FC_FTP_EXIT_MESSAGE;
  1600. break;
  1601. case MD_GREETING_MESSAGE :
  1602. control |= FC_FTP_GREETING_MESSAGE;
  1603. break;
  1604. case MD_BANNER_MESSAGE :
  1605. control |= FC_FTP_BANNER_MESSAGE;
  1606. break;
  1607. case MD_MAX_CLIENTS_MESSAGE :
  1608. control |= FC_FTP_MAX_CLIENTS_MESSAGE;
  1609. break;
  1610. case MD_MSDOS_DIR_OUTPUT :
  1611. control |= FC_FTP_MSDOS_DIR_OUTPUT;
  1612. break;
  1613. case MD_ALLOW_ANONYMOUS :
  1614. control |= FC_FTP_ALLOW_ANONYMOUS;
  1615. break;
  1616. case MD_ANONYMOUS_ONLY :
  1617. control |= FC_FTP_ANONYMOUS_ONLY;
  1618. break;
  1619. case MD_ALLOW_REPLACE_ON_RENAME :
  1620. control |= FC_FTP_ALLOW_REPLACE_ON_RENAME;
  1621. break;
  1622. case MD_SHOW_4_DIGIT_YEAR :
  1623. control |= FC_FTP_SHOW_4_DIGIT_YEAR;
  1624. break;
  1625. case MD_USER_ISOLATION :
  1626. // Don't do this --- control |= FC_FTP_USER_ISOLATION;
  1627. // UserIsolation cannot change on the fly, so do not update this field
  1628. break;
  1629. case MD_FTP_LOG_IN_UTF_8 :
  1630. control |= FC_FTP_LOG_IN_UTF_8;
  1631. break;
  1632. case MD_ANONYMOUS_USER_NAME :
  1633. case MD_ANONYMOUS_PWD :
  1634. case MD_ANONYMOUS_USE_SUBAUTH :
  1635. case MD_AD_CONNECTIONS_USERNAME:
  1636. case MD_AD_CONNECTIONS_PASSWORD:
  1637. case MD_DEFAULT_LOGON_DOMAIN :
  1638. case MD_LOGON_METHOD :
  1639. err = ReadAuthentInfo( FALSE, id );
  1640. if( err != NO_ERROR ) {
  1641. DBGPRINTF((
  1642. DBG_CONTEXT,
  1643. "FTP_SERVER_INSTANCE::MDChangeNotify() cannot read authentication info, error %d\n",
  1644. err
  1645. ));
  1646. }
  1647. break;
  1648. }
  1649. }
  1650. if( control != 0 ) {
  1651. err = InitFromRegistry( control );
  1652. if( err != NO_ERROR ) {
  1653. DBGPRINTF((
  1654. DBG_CONTEXT,
  1655. "FTP_SERVER_INSTANCE::MDChangeNotify() cannot read config, error %lx\n",
  1656. err
  1657. ));
  1658. }
  1659. }
  1660. } // FTP_SERVER_INSTANCE::MDChangeNotify
  1661. DWORD
  1662. FTP_SERVER_INSTANCE::ReadAuthentInfo(
  1663. IN BOOL ReadAll,
  1664. IN DWORD SingleItemToRead
  1665. )
  1666. /*++
  1667. Reads per-instance authentication info from the metabase.
  1668. Arguments:
  1669. ReadAll - If TRUE, then all authentication related items are
  1670. read from the metabase. If FALSE, then only a single item
  1671. is read.
  1672. SingleItemToRead - The single authentication item to read if
  1673. ReadAll is FALSE. Ignored if ReadAll is TRUE.
  1674. Returns:
  1675. DWORD - 0 if successful, !0 otherwise.
  1676. --*/
  1677. {
  1678. DWORD tmp;
  1679. DWORD err = NO_ERROR;
  1680. BOOL rebuildAcctDesc = FALSE;
  1681. STACK_STATSTR (strADConn, MAX_PATH + 1);
  1682. MB mb( (IMDCOM*)g_pInetSvc->QueryMDObject() );
  1683. //
  1684. // Lock our configuration (since we'll presumably be making changes)
  1685. // and open the metabase.
  1686. //
  1687. LockConfig();
  1688. if( !mb.Open( QueryMDPath() ) ) {
  1689. err = GetLastError();
  1690. DBGPRINTF((
  1691. DBG_CONTEXT,
  1692. "ReadAuthentInfo: cannot open metabase, error %lx\n",
  1693. err
  1694. ));
  1695. }
  1696. //
  1697. // Read the anonymous username if necessary. Note this is a REQUIRED
  1698. // property. If it is missing from the metabase, bail.
  1699. //
  1700. if( err == NO_ERROR &&
  1701. ( ReadAll || SingleItemToRead == MD_ANONYMOUS_USER_NAME ) ) {
  1702. if( mb.GetStr(
  1703. "",
  1704. MD_ANONYMOUS_USER_NAME,
  1705. IIS_MD_UT_SERVER,
  1706. &m_TcpAuthentInfo.strAnonUserName
  1707. ) ) {
  1708. rebuildAcctDesc = TRUE;
  1709. } else {
  1710. err = GetLastError();
  1711. DBGPRINTF((
  1712. DBG_CONTEXT,
  1713. "ReadAuthentInfo: cannot read MD_ANONYMOUS_USER_NAME, error %lx\n",
  1714. err
  1715. ));
  1716. }
  1717. }
  1718. //
  1719. // Read the "use subauthenticator" flag if necessary. This is an
  1720. // optional property.
  1721. //
  1722. if( err == NO_ERROR &&
  1723. ( ReadAll || SingleItemToRead == MD_ANONYMOUS_USE_SUBAUTH ) ) {
  1724. if( !mb.GetDword(
  1725. "",
  1726. MD_ANONYMOUS_USE_SUBAUTH,
  1727. IIS_MD_UT_SERVER,
  1728. &tmp
  1729. ) ) {
  1730. tmp = DEFAULT_USE_SUBAUTH;
  1731. }
  1732. m_TcpAuthentInfo.fDontUseAnonSubAuth = !tmp;
  1733. }
  1734. //
  1735. // Read the anonymous password if necessary. This is an optional
  1736. // property.
  1737. //
  1738. if( err == NO_ERROR &&
  1739. ( ReadAll || SingleItemToRead == MD_ANONYMOUS_PWD ) ) {
  1740. if( mb.GetStr(
  1741. "",
  1742. MD_ANONYMOUS_PWD,
  1743. IIS_MD_UT_SERVER,
  1744. &m_TcpAuthentInfo.strAnonUserPassword,
  1745. METADATA_INHERIT,
  1746. DEFAULT_ANONYMOUS_PWD
  1747. ) ) {
  1748. //
  1749. // hide this from curious memory dump analizers
  1750. //
  1751. m_TcpAuthentInfo.strAnonUserPassword.Hash();
  1752. m_TcpAuthentInfo.fPwdIsHashed = TRUE;
  1753. rebuildAcctDesc = TRUE;
  1754. } else {
  1755. //
  1756. // Since we provided a default value to mb.GetStr(), it should
  1757. // only fail if something catastrophic occurred, such as an
  1758. // out of memory condition.
  1759. //
  1760. err = GetLastError();
  1761. DBGPRINTF((
  1762. DBG_CONTEXT,
  1763. "ReadAuthentInfo: cannot read MD_ANONYMOUS_PWD, error %lx\n",
  1764. err
  1765. ));
  1766. }
  1767. }
  1768. //
  1769. // Read the AD Access username if necessary. This is an optional
  1770. // property.
  1771. //
  1772. if( err == NO_ERROR &&
  1773. ( ReadAll || SingleItemToRead == MD_AD_CONNECTIONS_USERNAME ) ) {
  1774. if( mb.GetStr(
  1775. "",
  1776. MD_AD_CONNECTIONS_USERNAME,
  1777. IIS_MD_UT_SERVER,
  1778. &strADConn,
  1779. METADATA_INHERIT,
  1780. DEFAULT_AD_CONNECTIONS_USERNAME
  1781. ) ) {
  1782. //
  1783. // if a property exists, parse it into separat username and domain tokens
  1784. //
  1785. if (strADConn.IsEmpty()) {
  1786. m_ADConnAuthentInfo.strName.Reset();
  1787. m_ADConnAuthentInfo.strDomain.Reset();
  1788. } else {
  1789. BOOL fIsLocalUser;
  1790. CHAR szDomainAndUser[ UNLEN + DNLEN + 2 ];
  1791. PSTR pszUser = NULL, pszDomain = NULL;
  1792. if (
  1793. (strADConn.QueryCCH() >= sizeof(szDomainAndUser)) ||
  1794. !strcpy( szDomainAndUser, strADConn.QueryStr()) || // this is always false
  1795. !ParseUserName( szDomainAndUser, &pszUser, &pszDomain,
  1796. QueryLocalHostName(), &fIsLocalUser) ||
  1797. fIsLocalUser ) {
  1798. err = ERROR_INVALID_PARAMETER;
  1799. } else {
  1800. m_ADConnAuthentInfo.strName.Copy( pszUser);
  1801. m_ADConnAuthentInfo.strDomain.Copy( pszDomain);
  1802. }
  1803. }
  1804. } else {
  1805. //
  1806. // Since we provided a default value to mb.GetStr(), it should
  1807. // only fail if something catastrophic occurred, such as an
  1808. // out of memory condition.
  1809. //
  1810. err = GetLastError();
  1811. DBGPRINTF((
  1812. DBG_CONTEXT,
  1813. "ReadAuthentInfo: cannot read MD_AD_CONNECTIONS_USERNAME, error %lx\n",
  1814. err
  1815. ));
  1816. }
  1817. }
  1818. //
  1819. // Read the AD Access password if necessary. This is an optional
  1820. // property.
  1821. //
  1822. if( err == NO_ERROR &&
  1823. ( ReadAll || SingleItemToRead == MD_AD_CONNECTIONS_PASSWORD ) ) {
  1824. if( mb.GetStr(
  1825. "",
  1826. MD_AD_CONNECTIONS_PASSWORD,
  1827. IIS_MD_UT_SERVER,
  1828. &m_ADConnAuthentInfo.strPassword,
  1829. METADATA_INHERIT,
  1830. DEFAULT_AD_CONNECTIONS_PASSWORD
  1831. ) ) {
  1832. //
  1833. // hide this from curious memory dump analizers
  1834. //
  1835. m_ADConnAuthentInfo.strPassword.Hash();
  1836. } else {
  1837. //
  1838. // Since we provided a default value to mb.GetStr(), it should
  1839. // only fail if something catastrophic occurred, such as an
  1840. // out of memory condition.
  1841. //
  1842. err = GetLastError();
  1843. DBGPRINTF((
  1844. DBG_CONTEXT,
  1845. "ReadAuthentInfo: cannot read MD_AD_CONNECTIONS_PASSWORD, error %lx\n",
  1846. err
  1847. ));
  1848. }
  1849. }
  1850. //
  1851. // Read the default logon domain if necessary. This is an optional
  1852. // property.
  1853. //
  1854. if( err == NO_ERROR &&
  1855. ( ReadAll || SingleItemToRead == MD_DEFAULT_LOGON_DOMAIN ) ) {
  1856. if( !mb.GetStr(
  1857. "",
  1858. MD_DEFAULT_LOGON_DOMAIN,
  1859. IIS_MD_UT_SERVER,
  1860. &m_TcpAuthentInfo.strDefaultLogonDomain
  1861. ) ) {
  1862. //
  1863. // Generate a default domain name.
  1864. //
  1865. err = GetDefaultDomainName( &m_TcpAuthentInfo.strDefaultLogonDomain );
  1866. if( err != NO_ERROR ) {
  1867. DBGPRINTF((
  1868. DBG_CONTEXT,
  1869. "ReadAuthentInfo: cannot get default domain name, error %d\n",
  1870. err
  1871. ));
  1872. }
  1873. }
  1874. }
  1875. //
  1876. // Read the logon method if necessary. This is an optional property.
  1877. //
  1878. if( err == NO_ERROR &&
  1879. ( ReadAll || SingleItemToRead == MD_LOGON_METHOD ) ) {
  1880. if( !mb.GetDword(
  1881. "",
  1882. MD_LOGON_METHOD,
  1883. IIS_MD_UT_SERVER,
  1884. &tmp
  1885. ) ) {
  1886. tmp = DEFAULT_LOGON_METHOD;
  1887. }
  1888. m_TcpAuthentInfo.dwLogonMethod = tmp;
  1889. }
  1890. //
  1891. // If anything changed that could affect the anonymous account
  1892. // descriptor, then rebuild the descriptor.
  1893. //
  1894. if( err == NO_ERROR && rebuildAcctDesc ) {
  1895. if( !BuildAnonymousAcctDesc( &m_TcpAuthentInfo ) ) {
  1896. DBGPRINTF((
  1897. DBG_CONTEXT,
  1898. "ReadAuthentInfo: BuildAnonymousAcctDesc() failed\n"
  1899. ));
  1900. err = ERROR_NOT_ENOUGH_MEMORY; // SWAG
  1901. }
  1902. }
  1903. UnLockConfig();
  1904. return err;
  1905. } // FTP_SERVER_INSTANCE::ReadAuthentInfo
  1906. BOOL
  1907. FTP_IIS_SERVICE::GetGlobalStatistics(
  1908. IN DWORD dwLevel,
  1909. OUT PCHAR *pBuffer
  1910. )
  1911. {
  1912. APIERR err = NO_ERROR;
  1913. switch( dwLevel ) {
  1914. case 0 : {
  1915. LPFTP_STATISTICS_0 pstats0;
  1916. pstats0 = (LPFTP_STATISTICS_0)
  1917. MIDL_user_allocate(sizeof(FTP_STATISTICS_0));
  1918. if( pstats0 == NULL ) {
  1919. err = ERROR_NOT_ENOUGH_MEMORY;
  1920. } else {
  1921. ZeroMemory( pstats0, sizeof( *pstats0 ) );
  1922. g_pFTPStats->CopyToStatsBuffer( pstats0 );
  1923. *pBuffer = (PCHAR)pstats0;
  1924. }
  1925. }
  1926. break;
  1927. default :
  1928. err = ERROR_INVALID_LEVEL;
  1929. break;
  1930. }
  1931. SetLastError(err);
  1932. return(err == NO_ERROR);
  1933. } // FTP_IIS_SERVICE::GetGlobalStatistics
  1934. BOOL
  1935. FTP_IIS_SERVICE::AggregateStatistics(
  1936. IN PCHAR pDestination,
  1937. IN PCHAR pSource
  1938. )
  1939. {
  1940. LPFTP_STATISTICS_0 pStatDest = (LPFTP_STATISTICS_0) pDestination;
  1941. LPFTP_STATISTICS_0 pStatSrc = (LPFTP_STATISTICS_0) pSource;
  1942. if ((NULL == pDestination) || (NULL == pSource))
  1943. {
  1944. return FALSE;
  1945. }
  1946. pStatDest->TotalBytesSent.QuadPart += pStatSrc->TotalBytesSent.QuadPart;
  1947. pStatDest->TotalBytesReceived.QuadPart += pStatSrc->TotalBytesReceived.QuadPart;
  1948. pStatDest->TotalFilesSent += pStatSrc->TotalFilesSent;
  1949. pStatDest->TotalFilesReceived += pStatSrc->TotalFilesReceived;
  1950. pStatDest->CurrentAnonymousUsers += pStatSrc->CurrentAnonymousUsers;
  1951. pStatDest->CurrentNonAnonymousUsers += pStatSrc->CurrentNonAnonymousUsers;
  1952. pStatDest->TotalAnonymousUsers += pStatSrc->TotalAnonymousUsers;
  1953. pStatDest->MaxAnonymousUsers += pStatSrc->MaxAnonymousUsers;
  1954. pStatDest->MaxNonAnonymousUsers += pStatSrc->MaxNonAnonymousUsers;
  1955. pStatDest->CurrentConnections += pStatSrc->CurrentConnections;
  1956. pStatDest->MaxConnections += pStatSrc->MaxConnections;
  1957. pStatDest->ConnectionAttempts += pStatSrc->ConnectionAttempts;
  1958. pStatDest->LogonAttempts += pStatSrc->LogonAttempts;
  1959. pStatDest->ServiceUptime = pStatSrc->ServiceUptime;
  1960. // bandwidth throttling info. Not relevant for FTP
  1961. /*
  1962. pStatDest->CurrentBlockedRequests += pStatSrc->CurrentBlockedRequests;
  1963. pStatDest->TotalBlockedRequests += pStatSrc->TotalBlockedRequests;
  1964. pStatDest->TotalAllowedRequests += pStatSrc->TotalAllowedRequests;
  1965. pStatDest->TotalRejectedRequests += pStatSrc->TotalRejectedRequests;
  1966. pStatDest->MeasuredBandwidth += pStatSrc->MeasuredBandwidth;
  1967. */
  1968. return TRUE;
  1969. }
  1970. ////////////////////////////////////////////////////
  1971. //
  1972. // FTP_PASV_PORT
  1973. // Site level configuration of passive data port range
  1974. //
  1975. ////////////////////////////////////////////////////
  1976. CRITICAL_SECTION FTP_PASV_PORT::m_csPasvPortRange;
  1977. PORT FTP_PASV_PORT::m_dwPasvPortRangeStart = 0;
  1978. PORT FTP_PASV_PORT::m_dwPasvPortRangeEnd = 0;
  1979. LONG FTP_PASV_PORT::m_dwNextPasvPort = 0;
  1980. BOOL
  1981. FTP_PASV_PORT::Configure()
  1982. {
  1983. MB mb( (IMDCOM*)g_pInetSvc->QueryMDObject() );
  1984. STACK_STR(strPortRange, 16);
  1985. ULONG RangeStart, RangeEnd;
  1986. PSTR pCh;
  1987. const CHAR * sz[1];
  1988. if( !mb.Open( g_pInetSvc->QueryMDPath() ) ||
  1989. !mb.GetStr( "",
  1990. MD_PASSIVE_PORT_RANGE,
  1991. IIS_MD_UT_SERVER,
  1992. &strPortRange,
  1993. METADATA_INHERIT,
  1994. "" ) ) {
  1995. sz[0] = g_pInetSvc->QueryMDPath();
  1996. g_pInetSvc->LogEvent( FTP_PASVPORT_MB_ACCESS_FAILED,
  1997. 1,
  1998. sz,
  1999. GetLastError() );
  2000. return FALSE;
  2001. }
  2002. // empty value is OK - it's the default
  2003. if (strPortRange.IsEmpty()) {
  2004. return TRUE;
  2005. }
  2006. RangeStart = strtoul(strPortRange.QueryStr(), &pCh, 10);
  2007. if (*pCh == '-') {
  2008. RangeEnd = strtoul(pCh+1, &pCh, 10);
  2009. } else {
  2010. RangeEnd = RangeStart;
  2011. }
  2012. // validate the range: over 5000, under 64K, and start <= end
  2013. if ((RangeStart <= 5000) || (RangeEnd <= 5000) ||
  2014. (RangeStart > 0xFFFF) || (RangeEnd > 0xFFFF) ||
  2015. (RangeStart > RangeEnd) ||
  2016. (*pCh != '\0') ) {
  2017. sz[0] = strPortRange.QueryStr();
  2018. g_pInetSvc->LogEvent( FTP_PASVPORT_INVALID_VALUE,
  2019. 1,
  2020. sz,
  2021. ERROR_INVALID_DATA );
  2022. return FALSE;
  2023. }
  2024. m_dwPasvPortRangeStart = (PORT)RangeStart;
  2025. m_dwPasvPortRangeEnd = (PORT)RangeEnd;
  2026. m_dwNextPasvPort = RangeStart;
  2027. return TRUE;
  2028. };