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.

724 lines
18 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name :
  4. dll_manager.cxx
  5. Abstract:
  6. IIS Plus ISAPI Handler. Dll management classes.
  7. Author:
  8. Taylor Weiss (TaylorW) 03-Feb-2000
  9. Wade A. Hilmo (WadeH) 08-Mar-2001
  10. Project:
  11. w3isapi.dll
  12. --*/
  13. #include "precomp.hxx"
  14. #include "dll_manager.h"
  15. ISAPI_DLL_MANAGER * g_pDllManager = NULL;
  16. PTRACE_LOG ISAPI_DLL::sm_pTraceLog;
  17. ALLOC_CACHE_HANDLER * ISAPI_DLL::sm_pachIsapiDlls;
  18. //
  19. // Generic mapping for Application access check
  20. //
  21. GENERIC_MAPPING g_FileGenericMapping =
  22. {
  23. FILE_GENERIC_READ,
  24. FILE_GENERIC_WRITE,
  25. FILE_GENERIC_EXECUTE,
  26. FILE_ALL_ACCESS
  27. };
  28. //static
  29. HRESULT
  30. ISAPI_DLL::Initialize(
  31. VOID
  32. )
  33. {
  34. ALLOC_CACHE_CONFIGURATION acConfig;
  35. #if DBG
  36. sm_pTraceLog = CreateRefTraceLog( 2000, 0 );
  37. #endif
  38. //
  39. // Initialize a lookaside for this structure
  40. //
  41. acConfig.nConcurrency = 1;
  42. acConfig.nThreshold = 100;
  43. acConfig.cbSize = sizeof( ISAPI_DLL );
  44. DBG_ASSERT( sm_pachIsapiDlls == NULL );
  45. sm_pachIsapiDlls = new ALLOC_CACHE_HANDLER( "ISAPI_DLL",
  46. &acConfig );
  47. if ( sm_pachIsapiDlls == NULL )
  48. {
  49. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  50. }
  51. return NO_ERROR;
  52. }
  53. //static
  54. VOID
  55. ISAPI_DLL::Terminate(
  56. VOID
  57. )
  58. {
  59. if ( sm_pTraceLog != NULL )
  60. {
  61. DestroyRefTraceLog( sm_pTraceLog );
  62. sm_pTraceLog = NULL;
  63. }
  64. if ( sm_pachIsapiDlls != NULL )
  65. {
  66. delete sm_pachIsapiDlls;
  67. sm_pachIsapiDlls = NULL;
  68. }
  69. }
  70. HRESULT
  71. ISAPI_DLL::SetName(
  72. const WCHAR * szModuleName,
  73. HANDLE hImpersonation
  74. )
  75. {
  76. HRESULT hr;
  77. BOOL fImpersonateForUnc = FALSE;
  78. DBG_ASSERT( CheckSignature() );
  79. hr = m_strModuleName.Copy( szModuleName );
  80. if ( FAILED( hr ) )
  81. {
  82. return hr;
  83. }
  84. //
  85. // Store a copy of the anti-canon module name
  86. //
  87. hr = MakePathCanonicalizationProof(
  88. const_cast<LPWSTR>( szModuleName ),
  89. &m_strAntiCanonModuleName
  90. );
  91. if ( SUCCEEDED( hr ) )
  92. {
  93. //
  94. // Verify that the file exists by getting its attributes.
  95. //
  96. // Note that in the UNC case, we have to do an impersonation
  97. // before we can make this call.
  98. //
  99. if ( wcsncmp( szModuleName, L"\\\\", 2 ) == 0 )
  100. {
  101. fImpersonateForUnc = SetThreadToken( NULL, hImpersonation );
  102. if ( !fImpersonateForUnc )
  103. {
  104. hr = HRESULT_FROM_WIN32( GetLastError() );
  105. DBGPRINTF((
  106. DBG_CONTEXT,
  107. "Attempt impersonate to get file attributes on %S failed "
  108. "with error 0x%08x.\r\n",
  109. szModuleName,
  110. hr
  111. ));
  112. return hr;
  113. }
  114. }
  115. if ( GetFileAttributes( m_strAntiCanonModuleName.QueryStr() ) ==
  116. 0xffffffff )
  117. {
  118. IF_DEBUG( DLL_MANAGER )
  119. {
  120. DBGPRINTF((
  121. DBG_CONTEXT,
  122. "Attempt to get file attributes on %S failed "
  123. "with error %d.\r\n",
  124. szModuleName,
  125. GetLastError()
  126. ));
  127. }
  128. //
  129. // CODEWORK - This smoke and mirrors game with the last
  130. // error is to preserve the old behavior that extensions
  131. // that are not found return 500 errors to the client
  132. // instead of 404.
  133. //
  134. if ( GetLastError() == ERROR_FILE_NOT_FOUND )
  135. {
  136. SetLastError( ERROR_MOD_NOT_FOUND );
  137. }
  138. hr = HRESULT_FROM_WIN32( GetLastError() );
  139. }
  140. }
  141. if ( fImpersonateForUnc )
  142. {
  143. RevertToSelf();
  144. }
  145. return hr;
  146. }
  147. HRESULT
  148. ISAPI_DLL::SetFastSid(
  149. IN PSID pSid
  150. )
  151. {
  152. DBG_ASSERT( pSid != NULL );
  153. if ( GetLengthSid( pSid ) <= sizeof( m_abFastSid ) )
  154. {
  155. memcpy( m_abFastSid,
  156. pSid,
  157. GetLengthSid( pSid ) );
  158. m_pFastSid = m_abFastSid;
  159. }
  160. return NO_ERROR;
  161. }
  162. HRESULT
  163. ISAPI_DLL::Load(
  164. IN HANDLE hImpersonation,
  165. IN PSID pSid
  166. )
  167. {
  168. HRESULT hr;
  169. HSE_VERSION_INFO VersionInfo;
  170. BOOL fImpersonatingForUnc = FALSE;
  171. DBG_ASSERT( CheckSignature() );
  172. //
  173. // Check to see if the dll is already loaded.
  174. // If so, just return NO_ERROR.
  175. //
  176. if ( m_fIsLoaded )
  177. {
  178. return NO_ERROR;
  179. }
  180. //
  181. // So the dll is not loaded. Grab the lock and
  182. // check again (another thread may have snuck in between
  183. // the first test and now and already loaded it...
  184. //
  185. Lock();
  186. if ( !m_fIsLoaded )
  187. {
  188. //
  189. // Load the ACL for the dll file and do an access check
  190. // before loading the dll itself. This is a slightly different
  191. // approach than what earlier versions of IIS did. We are
  192. // not going to be actually doing impersonation at the time
  193. // we load the dll as earlier versions did. As a result,
  194. // DllMain's DLL_PROCESS_ATTACH will run in the context of
  195. // the worker process primary token instead of as the
  196. // authenticated user.
  197. //
  198. // This new approach is the right way to do it, but it might
  199. // introduce an incompatibility with any extensions that implement
  200. // code in DllMain that depends on running as the authenticated
  201. // user. It is very, very unlikely that we'll see this problem.
  202. // If we do, the solution is to impersonate the token before
  203. // calling LoadLibraryEx.
  204. //
  205. //
  206. // Before calling LoadAcl, we should check to see if this
  207. // dll lives on a UNC path. If it does, then we need to
  208. // do the impersonation before attempting to load the ACL,
  209. // since we can't assume that the inetinfo or w3wp process
  210. // token can jump off the box.
  211. //
  212. if ( wcsncmp( m_strModuleName.QueryStr(), L"\\\\", 2 ) == 0 )
  213. {
  214. fImpersonatingForUnc = SetThreadToken( NULL, hImpersonation );
  215. if ( !fImpersonatingForUnc )
  216. {
  217. hr = HRESULT_FROM_WIN32( GetLastError() );
  218. DBGPRINTF(( DBG_CONTEXT,
  219. "[ISAPI_DLL::Load] SetThreadToken failed for %S. Error %0x08x\n",
  220. m_strModuleName.QueryStr(),
  221. hr
  222. ));
  223. goto Failed;
  224. }
  225. }
  226. hr = LoadAcl( m_strAntiCanonModuleName );
  227. if ( FAILED( hr ) )
  228. {
  229. goto Failed;
  230. }
  231. if ( !AccessCheck( hImpersonation,
  232. pSid ) )
  233. {
  234. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  235. goto Failed;
  236. }
  237. //
  238. // Since we've just passed an AccessCheck, we can cache
  239. // the fast SID now.
  240. //
  241. if ( pSid != NULL )
  242. {
  243. hr = SetFastSid( pSid );
  244. if ( FAILED( hr ) )
  245. {
  246. goto Failed;
  247. }
  248. }
  249. m_hModule = LoadLibraryEx( m_strAntiCanonModuleName.QueryStr(),
  250. NULL,
  251. LOAD_WITH_ALTERED_SEARCH_PATH
  252. );
  253. if ( m_hModule == NULL )
  254. {
  255. hr = HRESULT_FROM_WIN32( GetLastError() );
  256. DBGPRINTF(( DBG_CONTEXT,
  257. "[ISAPI_DLL::Load] LoadLibrary %S failed. Error %d\n",
  258. m_strModuleName.QueryStr(),
  259. GetLastError()
  260. ));
  261. goto Failed;
  262. }
  263. //
  264. // Lose the impersonation token before we call any more entry points...
  265. //
  266. if ( fImpersonatingForUnc )
  267. {
  268. RevertToSelf();
  269. fImpersonatingForUnc = FALSE;
  270. }
  271. //
  272. // Get the entry points
  273. //
  274. m_pfnGetExtensionVersion =
  275. (PFN_GETEXTENSIONVERSION)GetProcAddress( m_hModule,
  276. "GetExtensionVersion"
  277. );
  278. m_pfnTerminateExtension =
  279. (PFN_TERMINATEEXTENSION)GetProcAddress( m_hModule,
  280. "TerminateExtension"
  281. );
  282. m_pfnHttpExtensionProc =
  283. (PFN_HTTPEXTENSIONPROC)GetProcAddress( m_hModule,
  284. "HttpExtensionProc"
  285. );
  286. //
  287. // HttpExtensionProc and GetExtensionVersion are required
  288. //
  289. if( !m_pfnGetExtensionVersion ||
  290. !m_pfnHttpExtensionProc )
  291. {
  292. hr = HRESULT_FROM_WIN32( ERROR_PROC_NOT_FOUND );
  293. //
  294. // Make sure we dont call TerminateExtension on
  295. // cleanup.
  296. //
  297. m_pfnTerminateExtension = NULL;
  298. goto Failed;
  299. }
  300. if( !m_pfnGetExtensionVersion( &VersionInfo ) )
  301. {
  302. hr = GetLastError() == ERROR_SUCCESS ?
  303. E_FAIL :
  304. HRESULT_FROM_WIN32( GetLastError() );
  305. goto Failed;
  306. }
  307. DBGPRINTF(( DBG_CONTEXT,
  308. "ISAPI_DLL::Load() Loaded extension %S, "
  309. " description \"%s\"\n",
  310. m_strModuleName.QueryStr(),
  311. VersionInfo.lpszExtensionDesc ));
  312. m_fIsLoaded = TRUE;
  313. }
  314. Unlock();
  315. return NO_ERROR;
  316. Failed:
  317. DBG_ASSERT( FAILED( hr ) );
  318. //
  319. // Clean up after the failed load attempt
  320. //
  321. if ( fImpersonatingForUnc )
  322. {
  323. RevertToSelf();
  324. fImpersonatingForUnc = FALSE;
  325. }
  326. if ( m_pfnHttpExtensionProc )
  327. {
  328. m_pfnHttpExtensionProc = NULL;
  329. }
  330. if ( m_pfnGetExtensionVersion )
  331. {
  332. m_pfnGetExtensionVersion = NULL;
  333. }
  334. if ( m_pfnTerminateExtension )
  335. {
  336. m_pfnTerminateExtension = NULL;
  337. }
  338. if ( m_hModule )
  339. {
  340. FreeLibrary( m_hModule );
  341. m_hModule = NULL;
  342. }
  343. Unlock();
  344. return hr;
  345. }
  346. VOID
  347. ISAPI_DLL::Unload( VOID )
  348. {
  349. if( m_pfnTerminateExtension )
  350. {
  351. m_pfnTerminateExtension( HSE_TERM_MUST_UNLOAD );
  352. m_pfnTerminateExtension = NULL;
  353. }
  354. m_pfnGetExtensionVersion = NULL;
  355. m_pfnHttpExtensionProc = NULL;
  356. if( m_hModule )
  357. {
  358. FreeLibrary( m_hModule );
  359. }
  360. }
  361. HRESULT
  362. ISAPI_DLL::LoadAcl( STRU &strModuleName )
  363. {
  364. DWORD cbSecDesc = m_buffSD.QuerySize();
  365. DWORD dwError;
  366. DBG_ASSERT( CheckSignature() );
  367. //
  368. // Force an access check on the next request
  369. //
  370. while ( !GetFileSecurity( strModuleName.QueryStr(),
  371. (OWNER_SECURITY_INFORMATION |
  372. GROUP_SECURITY_INFORMATION |
  373. DACL_SECURITY_INFORMATION),
  374. m_buffSD.QueryPtr(),
  375. m_buffSD.QuerySize(),
  376. &cbSecDesc ))
  377. {
  378. if ( ( dwError = GetLastError() ) != ERROR_INSUFFICIENT_BUFFER )
  379. {
  380. return HRESULT_FROM_WIN32( dwError );
  381. }
  382. if ( !m_buffSD.Resize( cbSecDesc ) )
  383. {
  384. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  385. }
  386. //
  387. // Hopefully, we have sufficient buffer now, retry
  388. //
  389. }
  390. return NOERROR;
  391. }
  392. BOOL
  393. ISAPI_DLL::AccessCheck(
  394. IN HANDLE hImpersonation,
  395. IN PSID pSid
  396. )
  397. {
  398. BOOL fRet = TRUE;
  399. DWORD dwGrantedAccess = 0;
  400. BYTE PrivSet[400];
  401. DWORD cbPrivilegeSet = sizeof(PrivSet);
  402. BOOL fAccessGranted;
  403. DBG_ASSERT( CheckSignature() );
  404. //
  405. // First compare to the fast check SID if possible
  406. //
  407. if ( pSid != NULL && QueryFastSid() != NULL )
  408. {
  409. if ( EqualSid( pSid, QueryFastSid() ) )
  410. {
  411. return TRUE;
  412. }
  413. }
  414. //
  415. // Ok, just do the real access check
  416. //
  417. fRet = ( ::AccessCheck( QuerySecDesc(),
  418. hImpersonation,
  419. FILE_GENERIC_EXECUTE,
  420. &g_FileGenericMapping,
  421. (PRIVILEGE_SET *) &PrivSet,
  422. &cbPrivilegeSet,
  423. &dwGrantedAccess,
  424. &fAccessGranted )
  425. && fAccessGranted);
  426. return fRet;
  427. }
  428. HRESULT
  429. ISAPI_DLL_MANAGER::GetIsapi(
  430. IN const WCHAR * szModuleName,
  431. OUT ISAPI_DLL ** ppIsapiDll,
  432. IN HANDLE hImpersonation,
  433. IN PSID pSid
  434. )
  435. {
  436. HRESULT hr = NOERROR;
  437. ISAPI_DLL * pIsapiDll = NULL;
  438. LK_RETCODE lkrc;
  439. IF_DEBUG( DLL_MANAGER )
  440. {
  441. DBGPRINTF((
  442. DBG_CONTEXT,
  443. "DllManager looking for %S.\r\n",
  444. szModuleName
  445. ));
  446. }
  447. //
  448. // Check for the ISAPI in the hash table. If we don't
  449. // find it, then we'll need to create an entry in the
  450. // hash for it.
  451. //
  452. lkrc = m_IsapiHash.FindKey( szModuleName, &pIsapiDll );
  453. if ( lkrc == LK_SUCCESS )
  454. {
  455. IF_DEBUG( DLL_MANAGER )
  456. {
  457. DBGPRINTF((
  458. DBG_CONTEXT,
  459. "Found ISAPI_DLL %p (%S).\r\n",
  460. pIsapiDll,
  461. pIsapiDll->QueryModuleName()
  462. ));
  463. }
  464. }
  465. else
  466. {
  467. //
  468. // Create a new entry
  469. //
  470. IF_DEBUG( DLL_MANAGER )
  471. {
  472. DBGPRINTF((
  473. DBG_CONTEXT,
  474. "Creating new ISAPI_DLL object for %S.\r\n",
  475. szModuleName
  476. ));
  477. }
  478. pIsapiDll = new ISAPI_DLL;
  479. if ( !pIsapiDll )
  480. {
  481. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  482. goto Failed;
  483. }
  484. if ( FAILED( hr = pIsapiDll->SetName( szModuleName, hImpersonation ) ) )
  485. {
  486. pIsapiDll->DereferenceIsapiDll();
  487. pIsapiDll = NULL;
  488. goto Failed;
  489. }
  490. //
  491. // Insert the new object into the hash table. If someone
  492. // else beat us to it, then we'll just release our new one.
  493. //
  494. lkrc = m_IsapiHash.InsertRecord( pIsapiDll );
  495. if ( lkrc == LK_SUCCESS )
  496. {
  497. IF_DEBUG( DLL_MANAGER )
  498. {
  499. DBGPRINTF((
  500. DBG_CONTEXT,
  501. "Added ISAPI_DLL %p to table for %S.\r\n",
  502. pIsapiDll,
  503. szModuleName
  504. ));
  505. }
  506. }
  507. else
  508. {
  509. pIsapiDll->DereferenceIsapiDll();
  510. pIsapiDll = NULL;
  511. if ( lkrc == LK_KEY_EXISTS )
  512. {
  513. IF_DEBUG( DLL_MANAGER )
  514. {
  515. DBGPRINTF((
  516. DBG_CONTEXT,
  517. "InsertRecord for %S returned LK_KEY_EXISTS.\r\n",
  518. szModuleName
  519. ));
  520. }
  521. //
  522. // Ok, so let's get the existing one
  523. //
  524. lkrc = m_IsapiHash.FindKey( szModuleName, &pIsapiDll );
  525. }
  526. if ( lkrc != LK_SUCCESS )
  527. {
  528. hr = HRESULT_FROM_WIN32( lkrc );
  529. goto Failed;
  530. }
  531. }
  532. }
  533. DBG_ASSERT( pIsapiDll );
  534. //
  535. // Call the Load function for the ISAPI_DLL. Note that the ISAPI_DLL
  536. // function is smart enough to deal with locking on GetExtensionVersion
  537. // and in only allowing it to happen one time.
  538. //
  539. hr = pIsapiDll->Load( hImpersonation,
  540. pSid );
  541. if ( FAILED( hr ) )
  542. {
  543. goto Failed;
  544. }
  545. //
  546. // We've got the extension, but we still need to do
  547. // an access check to make sure that the client
  548. // is allowed to run it.
  549. //
  550. DBG_ASSERT( pIsapiDll );
  551. if ( !pIsapiDll->AccessCheck( hImpersonation,
  552. pSid ) )
  553. {
  554. hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
  555. goto Failed;
  556. }
  557. //
  558. // We're about to return successfully. Set the out parameter now.
  559. //
  560. *ppIsapiDll = pIsapiDll;
  561. DBG_ASSERT( SUCCEEDED( hr ) );
  562. return hr;
  563. Failed:
  564. DBG_ASSERT( FAILED( hr ) );
  565. IF_DEBUG( DLL_MANAGER )
  566. {
  567. DBGPRINTF((
  568. DBG_CONTEXT,
  569. "Error %d(0x%08x) occurred getting ISAPI_DLL object for %S.\r\n",
  570. hr,
  571. hr,
  572. szModuleName
  573. ));
  574. }
  575. if ( pIsapiDll )
  576. {
  577. pIsapiDll->DereferenceIsapiDll();
  578. pIsapiDll = NULL;
  579. }
  580. return hr;
  581. }