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.

702 lines
18 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name :
  4. w3ssl.cxx
  5. Abstract:
  6. SSL service for W3SVC.
  7. New SSL service is introduced to IIS6.
  8. In the dedicated (new process mode) it will run
  9. in lsass. That should boost ssl performance by eliminating
  10. context switches and interprocess communication during
  11. SSL handshakes.
  12. In the backward compatibility mode it will not do much.
  13. Service will register with http for ssl processing, but w3svc
  14. will register it's strmfilt and http.sys always uses the
  15. last registered filter so that the one loaded by inetinfo.exe
  16. will be used.
  17. Note: W3SSL service was renamed to HTTPFilter.
  18. Author:
  19. Jaroslav Dunajsky (Jaroslad) 04-16-2001
  20. Environment:
  21. Win32 - User Mode
  22. Project:
  23. Stream Filter Worker Process
  24. --*/
  25. #include "precomp.hxx"
  26. #include <inetsvcs.h>
  27. HRESULT RunInSameProcessAsIISADMIN (
  28. BOOL * pfInIISAdminProcess
  29. )
  30. /*++
  31. Routine:
  32. Find out if HttpFilter service executes in the same process
  33. as IISADMIN service
  34. Arguments:
  35. pfInIISAdminProcess - OUT parameter - set to TRUE current process is
  36. running in the same process as IISADMIN
  37. Returns:
  38. HRESULT
  39. --*/
  40. {
  41. SC_HANDLE schSCManager = NULL;
  42. SC_HANDLE schIISADMINService = NULL;
  43. LPSERVICE_STATUS_PROCESS pServiceStatus = NULL;
  44. DWORD dwBytesNeeded = 0;
  45. HRESULT hr = E_FAIL;
  46. *pfInIISAdminProcess = FALSE;
  47. // Open a handle to the SC Manager database.
  48. schSCManager = OpenSCManagerW(
  49. NULL, // local machine
  50. NULL, // ServicesActive database
  51. SC_MANAGER_CONNECT );
  52. if ( schSCManager == NULL )
  53. {
  54. hr = HRESULT_FROM_WIN32( GetLastError() );
  55. goto Finished;
  56. }
  57. schIISADMINService = OpenServiceW(
  58. schSCManager, // SCM database
  59. L"IISADMIN", // IISADMIN service name
  60. SERVICE_QUERY_STATUS );
  61. if ( schIISADMINService == NULL )
  62. {
  63. hr = HRESULT_FROM_WIN32( GetLastError() );
  64. goto Finished;
  65. }
  66. //
  67. // Get the configuration information.
  68. // - to find out the current image path of the
  69. // HTTPFilter service
  70. //
  71. if ( !QueryServiceStatusEx(
  72. schIISADMINService,
  73. SC_STATUS_PROCESS_INFO,
  74. (LPBYTE) pServiceStatus,
  75. 0,
  76. &dwBytesNeeded ) )
  77. {
  78. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  79. {
  80. pServiceStatus = reinterpret_cast<LPSERVICE_STATUS_PROCESS> (
  81. new char[dwBytesNeeded] );
  82. if ( pServiceStatus == NULL )
  83. {
  84. hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  85. goto Finished;
  86. }
  87. if ( !QueryServiceStatusEx(
  88. schIISADMINService,
  89. SC_STATUS_PROCESS_INFO,
  90. (LPBYTE) pServiceStatus,
  91. dwBytesNeeded,
  92. &dwBytesNeeded ) )
  93. {
  94. hr = HRESULT_FROM_WIN32( GetLastError() );
  95. goto Finished;
  96. }
  97. }
  98. else
  99. {
  100. hr = HRESULT_FROM_WIN32( GetLastError() );
  101. goto Finished;
  102. }
  103. }
  104. *pfInIISAdminProcess = ( pServiceStatus->dwProcessId == GetCurrentProcessId() );
  105. hr = S_OK;
  106. Finished:
  107. if ( pServiceStatus != NULL )
  108. {
  109. delete [] pServiceStatus;
  110. pServiceStatus = NULL;
  111. }
  112. if ( schIISADMINService != NULL )
  113. {
  114. DBG_REQUIRE( CloseServiceHandle( schIISADMINService ) );
  115. schIISADMINService = NULL;
  116. }
  117. if ( schSCManager != NULL )
  118. {
  119. DBG_REQUIRE( CloseServiceHandle( schSCManager ) );
  120. schSCManager = NULL;
  121. }
  122. return hr;
  123. }
  124. //
  125. // Implementation of W3SSL_SERVICE methods
  126. //
  127. //virtual
  128. HRESULT
  129. W3SSL_SERVICE::OnServiceStart(
  130. VOID
  131. )
  132. /*++
  133. Routine:
  134. Initialize HTTPFilter and start service.
  135. It initializes necessary structures and modules. If there is no error then
  136. after returning from function HTTPFilter service will be in SERVICE_STARTED state
  137. Arguments:
  138. Returns:
  139. HRESULT
  140. --*/
  141. {
  142. HRESULT hr = S_OK;
  143. DBG_ASSERT( _InitStatus == INIT_NONE );
  144. hr = LoadStrmfilt();
  145. if ( FAILED ( hr ) )
  146. {
  147. goto Finished;
  148. }
  149. _InitStatus = INIT_STRMFILT_LOADED;
  150. //
  151. // Initialize stream filter
  152. //
  153. hr = _fnStreamFilterInitialize();
  154. if ( FAILED( hr ) )
  155. {
  156. DBGPRINTF(( DBG_CONTEXT,
  157. "Error in StreamFilterInitialize(). hr = %x\n",
  158. hr ));
  159. goto Finished;
  160. }
  161. _InitStatus = INIT_STREAMFILTER;
  162. //
  163. // Start listening
  164. //
  165. hr = _fnStreamFilterStart();
  166. if ( FAILED( hr ) )
  167. {
  168. DBGPRINTF(( DBG_CONTEXT,
  169. "Error in StreamFilterStart(). hr = %x\n",
  170. hr ));
  171. goto Finished;
  172. }
  173. _InitStatus = INIT_STREAMFILTER_STARTED;
  174. //
  175. // Write current mode of w3ssl
  176. // ( if it is executing in inetinfo.exe then it supports raw read filters )
  177. // This setting will be used by WAS to determine what mode to execute in.
  178. // That way W3SSL and W3SVC will always be running in the same mode
  179. //
  180. // CODEWORK: in the case that writing to registry fails we may want to do more
  181. // than just ignoring it. However to halt startup may not necessarily be the best way
  182. // to go in that case
  183. DWORD dwRunningInInetinfo = 0;
  184. BOOL fRunningInInetinfo = FALSE;
  185. DWORD dwErr = NO_ERROR;
  186. HKEY hKeyParam;
  187. if ( SUCCEEDED( hr = RunInSameProcessAsIISADMIN( &fRunningInInetinfo ) ) )
  188. {
  189. if ( ( dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  190. REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
  191. 0,
  192. KEY_WRITE,
  193. &hKeyParam ) ) == NO_ERROR )
  194. {
  195. if ( fRunningInInetinfo )
  196. {
  197. dwRunningInInetinfo = 1;
  198. }
  199. else
  200. {
  201. dwRunningInInetinfo = 0;
  202. }
  203. dwErr = RegSetValueExW( hKeyParam,
  204. L"CurrentMode",
  205. NULL,
  206. REG_DWORD,
  207. ( LPBYTE )&dwRunningInInetinfo,
  208. sizeof( dwRunningInInetinfo )
  209. );
  210. if ( dwErr != NO_ERROR )
  211. {
  212. //
  213. // Ignore the error.
  214. //
  215. DBGPRINTF(( DBG_CONTEXT,
  216. "Failed to write registry value \"RunningInInetinfo\". Win32 = %d\n",
  217. dwErr ));
  218. }
  219. RegCloseKey( hKeyParam );
  220. }
  221. else
  222. {
  223. //
  224. // Ignore the error.
  225. //
  226. DBGPRINTF(( DBG_CONTEXT,
  227. "Failed to open registry key %s. Win32 = %d\n",
  228. REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
  229. dwErr ));
  230. hr = S_OK;
  231. }
  232. }
  233. else
  234. {
  235. //
  236. // Ignore the error.
  237. //
  238. DBGPRINTF(( DBG_CONTEXT,
  239. "Failed to call RunInSameProcessAsIISADMIN(). hr = %x\n",
  240. hr ));
  241. hr = S_OK;
  242. }
  243. Finished:
  244. if (FAILED ( hr ) )
  245. {
  246. //
  247. // OnServiceStop() will assure proper cleanup
  248. //
  249. OnServiceStop();
  250. }
  251. return hr;
  252. }
  253. //virtual
  254. HRESULT
  255. W3SSL_SERVICE::OnServiceStop(
  256. VOID
  257. )
  258. /*++
  259. Routine:
  260. Terminate HTTPFilter service. Performs cleanup
  261. _InitStatus is used to determine where to start with cleanup.
  262. OnServiceStop() is also used in OnServiceStart() for cleanup in the error case
  263. Arguments:
  264. Returns:
  265. HRESULT - will be reported to SCM
  266. --*/
  267. {
  268. HRESULT hr = S_OK;
  269. //
  270. // Cases in the switch command don't have "break" command
  271. // on purpose. _InitStatus indicates how for the initialization has gone
  272. // and that indicates where cleanup should start
  273. //
  274. switch( _InitStatus )
  275. {
  276. case INIT_STREAMFILTER_STARTED:
  277. //
  278. // Stop Listening
  279. //
  280. hr = _fnStreamFilterStop();
  281. if ( FAILED( hr ) )
  282. {
  283. DBGPRINTF(( DBG_CONTEXT,
  284. "Error in StreamFilterStop(). hr = %x\n",
  285. hr ));
  286. }
  287. case INIT_STREAMFILTER:
  288. _fnStreamFilterTerminate();
  289. case INIT_STRMFILT_LOADED:
  290. //
  291. // time to unload strmfilt
  292. //
  293. UnloadStrmfilt();
  294. case INIT_NONE:
  295. break;
  296. default:
  297. DBG_ASSERT( FALSE );
  298. }
  299. _InitStatus = INIT_NONE;
  300. return hr;
  301. }
  302. HRESULT
  303. W3SSL_SERVICE::LoadStrmfilt(
  304. VOID
  305. )
  306. /*++
  307. Routine:
  308. Dynamically load strmfilt.dll
  309. Arguments:
  310. Returns:
  311. HRESULT
  312. --*/
  313. {
  314. HRESULT hr = E_FAIL;
  315. DBG_ASSERT( _hStrmfilt == NULL );
  316. _hStrmfilt = LoadLibraryEx( L"strmfilt.dll",
  317. NULL,
  318. 0 );
  319. if ( _hStrmfilt != NULL )
  320. {
  321. _fnStreamFilterInitialize = (PFN_STREAM_FILTER_INITIALIZE)
  322. GetProcAddress(_hStrmfilt, "StreamFilterInitialize");
  323. if ( _fnStreamFilterInitialize == NULL)
  324. {
  325. hr = HRESULT_FROM_WIN32( GetLastError() );
  326. DBGPRINTF(( DBG_CONTEXT,
  327. "Error in loading strmfilt %s entrypoint(). hr = %x\n",
  328. "StreamFilterInitialize",
  329. hr ));
  330. goto Failed;
  331. }
  332. _fnStreamFilterStart = (PFN_STREAM_FILTER_START)
  333. GetProcAddress(_hStrmfilt, "StreamFilterStart");
  334. if ( _fnStreamFilterStart == NULL)
  335. {
  336. hr = HRESULT_FROM_WIN32( GetLastError() );
  337. DBGPRINTF(( DBG_CONTEXT,
  338. "Error in loading strmfilt %s entrypoint(). hr = %x\n",
  339. "StreamFilterStart",
  340. hr ));
  341. goto Failed;
  342. }
  343. _fnStreamFilterStop = (PFN_STREAM_FILTER_STOP)
  344. GetProcAddress(_hStrmfilt, "StreamFilterStop");
  345. if ( _fnStreamFilterStop == NULL)
  346. {
  347. hr = HRESULT_FROM_WIN32( GetLastError() );
  348. DBGPRINTF(( DBG_CONTEXT,
  349. "Error in loading strmfilt %s entrypoint(). hr = %x\n",
  350. "StreamFilterStop",
  351. hr ));
  352. goto Failed;
  353. }
  354. _fnStreamFilterTerminate = (PFN_STREAM_FILTER_TERMINATE)
  355. GetProcAddress(_hStrmfilt, "StreamFilterTerminate");
  356. if ( _fnStreamFilterTerminate == NULL)
  357. {
  358. hr = HRESULT_FROM_WIN32( GetLastError() );
  359. DBGPRINTF(( DBG_CONTEXT,
  360. "Error in loading strmfilt %s entrypoint(). hr = %x\n",
  361. "StreamFilterTerminate",
  362. hr ));
  363. goto Failed;
  364. }
  365. return S_OK;
  366. }
  367. else
  368. {
  369. hr = HRESULT_FROM_WIN32( GetLastError() );
  370. DBGPRINTF(( DBG_CONTEXT,
  371. "Failed to load strmfilt.dll. hr = %x\n",
  372. hr ));
  373. goto Failed;
  374. }
  375. Failed:
  376. UnloadStrmfilt();
  377. return hr;
  378. }
  379. HRESULT
  380. W3SSL_SERVICE::UnloadStrmfilt(
  381. VOID
  382. )
  383. /*++
  384. Routine:
  385. Dynamically load strmfilt.dll
  386. Arguments:
  387. Returns:
  388. HRESULT
  389. --*/
  390. {
  391. if ( _hStrmfilt != NULL )
  392. {
  393. BOOL fRet = FreeLibrary( _hStrmfilt );
  394. _fnStreamFilterInitialize = NULL;
  395. _fnStreamFilterStart = NULL;
  396. _fnStreamFilterStop = NULL;
  397. _fnStreamFilterTerminate = NULL;
  398. if ( !fRet )
  399. {
  400. return HRESULT_FROM_WIN32( GetLastError() );
  401. }
  402. }
  403. _hStrmfilt = NULL;
  404. return S_OK;
  405. }
  406. DWORD WINAPI
  407. HTTPFilterServiceThreadProc(
  408. LPVOID /*lpParameter*/
  409. )
  410. /*++
  411. Routine:
  412. This is the "real" proc for the service.
  413. When HTTPFilterServiceMain is called, to prevent stack overflows,
  414. it creates a thread that will begin executing this routine.
  415. Arguments:
  416. lpParameter - Not used, should be NULL.
  417. Returns:
  418. Win32 error. Does not return until service is stopped.
  419. --*/
  420. {
  421. W3SSL_SERVICE * pHTTPFilterServiceInstance;
  422. DWORD dwError = ERROR_SUCCESS;
  423. //
  424. // There is a possibility that after service reported it stopped
  425. // there is a new request to start the service again.
  426. // That may cause new thread to enter this function
  427. // before previous one fully cleaned up and returned.
  428. //
  429. pHTTPFilterServiceInstance = new W3SSL_SERVICE;
  430. if( pHTTPFilterServiceInstance == NULL )
  431. {
  432. //
  433. // report fatal error to SCM
  434. // (otherwise service would hang on START_PENDING)
  435. //
  436. dwError = ERROR_OUTOFMEMORY;
  437. SCM_MANAGER::ReportFatalServiceStartupError(
  438. HTTPFILTER_SERVICE_NAME_W,
  439. dwError );
  440. return dwError;
  441. }
  442. pHTTPFilterServiceInstance->RunService();
  443. //
  444. // return value will be ignored.
  445. // Return value has only informational value
  446. // if anything failed, SCM would be informed about the error
  447. // within RunService() call
  448. //
  449. delete pHTTPFilterServiceInstance;
  450. pHTTPFilterServiceInstance = NULL;
  451. return dwError;
  452. }
  453. VOID
  454. HTTPFilterServiceMain(
  455. DWORD /*argc*/,
  456. LPWSTR /*argv*/[]
  457. )
  458. /*++
  459. Routine:
  460. This is the "real" entrypoint for the service. When
  461. the Service Controller dispatcher is requested to
  462. start a service, it creates a thread that will begin
  463. executing this routine.
  464. Note: HTTPFilterServiceMain name is recognized by lsass as entrypoint
  465. for HTTPFilter service
  466. Arguments:
  467. argc - Number of command line arguments to this service.
  468. argv - Pointers to the command line arguments.
  469. Returns:
  470. None. Does not return until service is stopped.
  471. --*/
  472. {
  473. HRESULT hr = S_OK;
  474. DWORD dwError = ERROR_SUCCESS;
  475. HANDLE hThread = NULL;
  476. DWORD dwThreadId = 0;
  477. // Create a separate thread
  478. hThread = CreateThread( NULL,
  479. // Big initial size to prevent stack overflows
  480. IIS_DEFAULT_INITIAL_STACK_SIZE,
  481. HTTPFilterServiceThreadProc,
  482. NULL,
  483. 0,
  484. &dwThreadId );
  485. // If failed
  486. if ( !hThread )
  487. {
  488. // Get the error
  489. dwError=GetLastError();
  490. // Convert it to HRESULT
  491. hr=HRESULT_FROM_WIN32(dwError);
  492. DBGPRINTF(( DBG_CONTEXT,
  493. "CreateThread() failed. hr = %x\n",
  494. hr ));
  495. //
  496. // We have to report the through SCM
  497. // since otherwise service may end up
  498. // in START_PENDING forever
  499. //
  500. SCM_MANAGER::ReportFatalServiceStartupError(
  501. HTTPFILTER_SERVICE_NAME_W,
  502. dwError );
  503. goto exit;
  504. }
  505. // Wait for the service shutdown
  506. if ( WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED )
  507. {
  508. dwError = GetLastError();
  509. hr = HRESULT_FROM_WIN32( dwError );
  510. DBGPRINTF((
  511. DBG_CONTEXT,
  512. "WaitForSingleObject() failed. hr = %x\n",
  513. hr
  514. ));
  515. goto exit;
  516. }
  517. // Get the exit code
  518. if ( !GetExitCodeThread(hThread, &dwError) )
  519. {
  520. dwError = GetLastError();
  521. hr = HRESULT_FROM_WIN32(dwError);
  522. DBGPRINTF(( DBG_CONTEXT,
  523. "GetExitCodeThread() failed. hr = %x\n",
  524. hr ));
  525. goto exit;
  526. }
  527. DBG_ASSERT( dwError != STILL_ACTIVE );
  528. // If HTTPFilterServiceThreadProc failed
  529. if ( dwError != ERROR_SUCCESS )
  530. {
  531. // Convert it to HRESULT
  532. hr = HRESULT_FROM_WIN32(dwError);
  533. DBGPRINTF(( DBG_CONTEXT,
  534. "HTTPFilterServiceThreadProc() failed. hr = %x\n",
  535. hr ));
  536. }
  537. exit:
  538. if ( hThread )
  539. {
  540. // Close the handle
  541. CloseHandle(hThread);
  542. }
  543. }
  544. VOID
  545. ServiceEntry(
  546. DWORD /*cArgs*/, // unused
  547. LPSTR /*pArgs[]*/, // unused
  548. PTCPSVCS_GLOBAL_DATA /*pGlobalData*/ // unused
  549. )
  550. /*++
  551. Routine:
  552. IIS services use ServiceEntry for entry point while HTTPFilter has
  553. HTTPFilterServiceMain name chosen for default entry point by preference of lsass
  554. Let's support both entry points so that HTTPFilter gets easily loaded
  555. by both lsass and inetinfo.exe
  556. --*/
  557. {
  558. HTTPFilterServiceMain( 0,
  559. NULL );
  560. }