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.

817 lines
25 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. iiscertmap.cxx
  5. Abstract:
  6. IIS Certificate mapping
  7. Author:
  8. Bilal Alam (BAlam) 19-Apr-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Stream Filter Worker Process
  13. --*/
  14. #include "precomp.hxx"
  15. #include <mbstring.h>
  16. IIS_CERTIFICATE_MAPPING::IIS_CERTIFICATE_MAPPING( VOID )
  17. : _pCert11Mapper( NULL ),
  18. _pCertWildcardMapper( NULL ),
  19. _cRefs( 1 )
  20. {
  21. }
  22. IIS_CERTIFICATE_MAPPING::~IIS_CERTIFICATE_MAPPING( VOID )
  23. {
  24. if ( _pCert11Mapper != NULL )
  25. {
  26. delete _pCert11Mapper;
  27. _pCert11Mapper = NULL;
  28. }
  29. if ( _pCertWildcardMapper != NULL )
  30. {
  31. delete _pCertWildcardMapper;
  32. _pCertWildcardMapper = NULL;
  33. }
  34. }
  35. HRESULT
  36. IIS_CERTIFICATE_MAPPING::Read11Mappings(
  37. DWORD dwSiteId
  38. )
  39. /*++
  40. Routine Description:
  41. Read 1-1 mappings from metabase
  42. Arguments:
  43. dwSiteId - Site ID
  44. Return Value:
  45. HRESULT
  46. --*/
  47. {
  48. MB mb( g_pW3Server->QueryMDObject() );
  49. WCHAR achMBPath[ 256 ];
  50. HRESULT hr = NO_ERROR;
  51. // besides terminating '\0' one extra char is needed for leading slash
  52. WCHAR achMappingName[ ADMINDATA_MAX_NAME_LEN + 1 + 1 ];
  53. BOOL fRet;
  54. DWORD dwIndex;
  55. STACK_BUFFER( buff, 1024 );
  56. DWORD cbRequired;
  57. STACK_STRU( strTemp, 64 );
  58. STACK_STRU( strUserName, 64 );
  59. STACK_STRU( strPassword, 64 );
  60. DWORD dwEnabled;
  61. CIisMapping * pCertMapping;
  62. //
  63. // Setup the metabase path to get at 1-1 mappings
  64. //
  65. _snwprintf( achMBPath,
  66. sizeof( achMBPath ) / sizeof( WCHAR ) - 1,
  67. L"/LM/W3SVC/%d/Cert11/Mappings",
  68. dwSiteId );
  69. achMBPath[ sizeof( achMBPath ) / sizeof( WCHAR ) - 1 ] = '\0';
  70. //
  71. // Open the metabase and read 1-1 mapping properties
  72. //
  73. fRet = mb.Open( achMBPath,
  74. METADATA_PERMISSION_READ );
  75. if ( fRet )
  76. {
  77. dwIndex = 0;
  78. achMappingName[ 0 ] = L'/';
  79. for ( ; ; ) // loop un
  80. {
  81. dwEnabled = FALSE;
  82. //
  83. // We will need to prepend the object name with '/'. Hence
  84. // goofyness of sending an offseted pointed to name
  85. //
  86. fRet = mb.EnumObjects( L"",
  87. achMappingName + 1,
  88. dwIndex );
  89. if ( !fRet )
  90. {
  91. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  92. break;
  93. }
  94. //
  95. // Get certificate blob
  96. //
  97. cbRequired = buff.QuerySize();
  98. fRet = mb.GetData( achMappingName,
  99. MD_MAPCERT,
  100. IIS_MD_UT_SERVER,
  101. BINARY_METADATA,
  102. buff.QueryPtr(),
  103. &cbRequired,
  104. METADATA_NO_ATTRIBUTES );
  105. if ( !fRet )
  106. {
  107. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  108. {
  109. DBG_ASSERT( cbRequired > buff.QuerySize() );
  110. if ( !buff.Resize( cbRequired ) )
  111. {
  112. hr = HRESULT_FROM_WIN32( GetLastError() );
  113. break;
  114. }
  115. fRet = mb.GetData( achMappingName,
  116. MD_MAPCERT,
  117. IIS_MD_UT_SERVER,
  118. BINARY_METADATA,
  119. buff.QueryPtr(),
  120. &cbRequired,
  121. METADATA_NO_ATTRIBUTES );
  122. if ( !fRet )
  123. {
  124. DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
  125. hr = HRESULT_FROM_WIN32( GetLastError() );
  126. if ( hr == MD_ERROR_DATA_NOT_FOUND )
  127. {
  128. // if cert blob is not present we skip this entry
  129. goto NextEntry;
  130. }
  131. else
  132. {
  133. break;
  134. }
  135. }
  136. }
  137. else
  138. {
  139. hr = HRESULT_FROM_WIN32( GetLastError() );
  140. if ( hr == MD_ERROR_DATA_NOT_FOUND )
  141. {
  142. // if cert blob is not present we skip this entry
  143. goto NextEntry;
  144. }
  145. else
  146. {
  147. break;
  148. }
  149. }
  150. }
  151. //
  152. // Get NT account name
  153. //
  154. if ( !mb.GetStr( achMappingName,
  155. MD_MAPNTACCT,
  156. IIS_MD_UT_SERVER,
  157. &strUserName ) )
  158. {
  159. hr = HRESULT_FROM_WIN32( GetLastError() );
  160. if ( hr == MD_ERROR_DATA_NOT_FOUND )
  161. {
  162. // if account name is not present we skip this entry
  163. goto NextEntry;
  164. }
  165. else
  166. {
  167. break;
  168. }
  169. break;
  170. }
  171. //
  172. // Get NT password
  173. //
  174. if ( !mb.GetStr( achMappingName,
  175. MD_MAPNTPWD,
  176. IIS_MD_UT_SERVER,
  177. &strPassword ) )
  178. {
  179. hr = HRESULT_FROM_WIN32( GetLastError() );
  180. if ( hr == MD_ERROR_DATA_NOT_FOUND )
  181. {
  182. // we assume default password - empty;
  183. strPassword.Reset();
  184. }
  185. else
  186. {
  187. break;
  188. }
  189. }
  190. //
  191. // Is this mapping enabled?
  192. //
  193. if ( !mb.GetDword( achMappingName,
  194. MD_MAPENABLED,
  195. IIS_MD_UT_SERVER,
  196. &dwEnabled ) )
  197. {
  198. hr = HRESULT_FROM_WIN32( GetLastError() );
  199. if ( hr == MD_ERROR_DATA_NOT_FOUND )
  200. {
  201. // we assume default 0 (FALSE);
  202. dwEnabled = 0;
  203. }
  204. else
  205. {
  206. break;
  207. }
  208. }
  209. //
  210. // If this mapping is enabled, add it to 1-1 mapper
  211. //
  212. if ( dwEnabled )
  213. {
  214. if ( _pCert11Mapper == NULL )
  215. {
  216. _pCert11Mapper = new CIisCert11Mapper();
  217. if ( _pCert11Mapper == NULL )
  218. {
  219. hr = HRESULT_FROM_WIN32( GetLastError() );
  220. break;
  221. }
  222. //
  223. // Reset() will configure default hierarchies
  224. // If hierarchies are not configured then comparison (CIisMapping::Cmp() function
  225. // implemented in iismap.cxx) will fail
  226. // (and mapper will always incorrectly map to first available 1to1 mapping)
  227. //
  228. if(!_pCert11Mapper->Reset())
  229. {
  230. delete _pCert11Mapper;
  231. _pCert11Mapper = NULL;
  232. hr = HRESULT_FROM_WIN32( GetLastError() );
  233. break;
  234. }
  235. }
  236. DBG_ASSERT( _pCert11Mapper != NULL );
  237. pCertMapping = _pCert11Mapper->CreateNewMapping();
  238. if ( pCertMapping == NULL )
  239. {
  240. hr = HRESULT_FROM_WIN32( GetLastError() );
  241. break;
  242. }
  243. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_CERT,
  244. (PBYTE) buff.QueryPtr(),
  245. cbRequired,
  246. FALSE ) )
  247. {
  248. hr = HRESULT_FROM_WIN32( GetLastError() );
  249. break;
  250. }
  251. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_ACCT,
  252. (PBYTE) strUserName.QueryStr(),
  253. strUserName.QueryCB() + sizeof (WCHAR),
  254. FALSE ) )
  255. {
  256. hr = HRESULT_FROM_WIN32( GetLastError() );
  257. delete pCertMapping;
  258. pCertMapping = NULL;
  259. break;
  260. }
  261. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_PWD,
  262. (PBYTE) strPassword.QueryStr(),
  263. strPassword.QueryCB() + sizeof (WCHAR),
  264. FALSE ) )
  265. {
  266. hr = HRESULT_FROM_WIN32( GetLastError() );
  267. delete pCertMapping;
  268. pCertMapping = NULL;
  269. break;
  270. }
  271. if ( !((CIisAcctMapper*)_pCert11Mapper)->Add( pCertMapping, FALSE ) )
  272. {
  273. hr = HRESULT_FROM_WIN32( GetLastError() );
  274. delete pCertMapping;
  275. pCertMapping = NULL;
  276. break;
  277. }
  278. }
  279. NextEntry:
  280. dwIndex++;
  281. }
  282. }
  283. else
  284. {
  285. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  286. }
  287. return hr;
  288. }
  289. HRESULT
  290. IIS_CERTIFICATE_MAPPING::ReadWildcardMappings(
  291. DWORD dwSiteId
  292. )
  293. /*++
  294. Routine Description:
  295. Read wildcard mappings from metabase
  296. Arguments:
  297. dwSiteId - Site ID (duh)
  298. Return Value:
  299. HRESULT
  300. --*/
  301. {
  302. MB mb( g_pW3Server->QueryMDObject() );
  303. WCHAR achMBPath[ 256 ];
  304. BOOL fRet;
  305. BYTE abBuffer[ 1024 ];
  306. BUFFER buff( abBuffer, sizeof( abBuffer ) );
  307. DWORD cbRequired;
  308. PUCHAR pSerializedMapping;
  309. //
  310. // Setup the metabase path to get at wildcard mappings
  311. //
  312. _snwprintf( achMBPath,
  313. sizeof( achMBPath ) / sizeof( WCHAR ) - 1,
  314. L"/LM/W3SVC/%d/",
  315. dwSiteId );
  316. achMBPath[ sizeof( achMBPath ) / sizeof( WCHAR ) - 1 ] = '\0';
  317. //
  318. // Open the metabase and read wildcard mappings
  319. //
  320. fRet = mb.Open( achMBPath,
  321. METADATA_PERMISSION_READ );
  322. if ( fRet )
  323. {
  324. cbRequired = buff.QuerySize();
  325. fRet = mb.GetData( L"",
  326. MD_SERIAL_CERTW,
  327. IIS_MD_UT_SERVER,
  328. BINARY_METADATA,
  329. buff.QueryPtr(),
  330. &cbRequired,
  331. METADATA_NO_ATTRIBUTES );
  332. if ( !fRet )
  333. {
  334. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  335. {
  336. DBG_ASSERT( cbRequired > buff.QuerySize() );
  337. if ( !buff.Resize( cbRequired ) )
  338. {
  339. return HRESULT_FROM_WIN32( GetLastError() );
  340. }
  341. fRet = mb.GetData( L"",
  342. MD_SERIAL_CERTW,
  343. IIS_MD_UT_SERVER,
  344. BINARY_METADATA,
  345. buff.QueryPtr(),
  346. &cbRequired,
  347. METADATA_NO_ATTRIBUTES );
  348. if ( !fRet )
  349. {
  350. DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
  351. return HRESULT_FROM_WIN32( GetLastError() );
  352. }
  353. }
  354. else
  355. {
  356. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  357. }
  358. }
  359. //
  360. // Thanx to the man, I can just unserialize. XBF rocks ;-)
  361. //
  362. DBG_ASSERT( _pCertWildcardMapper == NULL );
  363. _pCertWildcardMapper = new CIisRuleMapper();
  364. if ( _pCertWildcardMapper == NULL )
  365. {
  366. return HRESULT_FROM_WIN32( GetLastError() );
  367. }
  368. if( !_pCertWildcardMapper->IsValid() )
  369. {
  370. //
  371. // creation of _pCertWildcardMapper failed
  372. // assume out of memory
  373. //
  374. return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  375. }
  376. pSerializedMapping = (PUCHAR) buff.QueryPtr();
  377. // Unserialize will change the value of pSerializedMapping
  378. // It will point to the end of the unserialized data
  379. // We don't need that modified pointer. But remember not to use
  380. // pSerializedMapping any more after Unserialize call
  381. //
  382. fRet = _pCertWildcardMapper->Unserialize( &pSerializedMapping,
  383. &cbRequired );
  384. if ( !fRet )
  385. {
  386. return HRESULT_FROM_WIN32( GetLastError() );
  387. }
  388. }
  389. else
  390. {
  391. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  392. }
  393. return NO_ERROR;
  394. }
  395. //static
  396. HRESULT
  397. IIS_CERTIFICATE_MAPPING::GetCertificateMapping(
  398. DWORD dwSiteId,
  399. IIS_CERTIFICATE_MAPPING ** ppCertificateMapping
  400. )
  401. /*++
  402. Routine Description:
  403. Read appropriate metabase configuration to get configured IIS
  404. certificate mapping
  405. Arguments:
  406. dwSiteId - Site ID (duh)
  407. ppCertificateMapping - Filled with certificate mapping descriptor on
  408. success
  409. Return Value:
  410. HRESULT
  411. --*/
  412. {
  413. IIS_CERTIFICATE_MAPPING * pCertMapping = NULL;
  414. HRESULT hr = NO_ERROR;
  415. if ( ppCertificateMapping == NULL )
  416. {
  417. DBG_ASSERT( FALSE );
  418. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  419. }
  420. *ppCertificateMapping = NULL;
  421. //
  422. // Create a certificate mapping descriptor
  423. //
  424. pCertMapping = new IIS_CERTIFICATE_MAPPING();
  425. if ( pCertMapping == NULL )
  426. {
  427. hr = HRESULT_FROM_WIN32( GetLastError() );
  428. goto Finished;
  429. }
  430. //
  431. // Read 1-1 mappings
  432. //
  433. hr = pCertMapping->Read11Mappings( dwSiteId );
  434. if ( FAILED( hr ) &&
  435. hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  436. {
  437. DBGPRINTF(( DBG_CONTEXT,
  438. "Error reading 1to1 Certificate Mappings. hr = %x\n",
  439. hr ));
  440. goto Finished;
  441. }
  442. hr = NO_ERROR;
  443. //
  444. // Read wildcards
  445. //
  446. hr = pCertMapping->ReadWildcardMappings( dwSiteId );
  447. if ( FAILED( hr ) &&
  448. hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  449. {
  450. DBGPRINTF(( DBG_CONTEXT,
  451. "Error reading Wildcard Certificate Mappings. hr = %x\n",
  452. hr ));
  453. goto Finished;
  454. }
  455. hr = NO_ERROR;
  456. Finished:
  457. if ( FAILED( hr ) )
  458. {
  459. if ( pCertMapping != NULL )
  460. {
  461. delete pCertMapping;
  462. pCertMapping = NULL;
  463. }
  464. }
  465. else
  466. {
  467. DBG_ASSERT( pCertMapping != NULL );
  468. *ppCertificateMapping = pCertMapping;
  469. }
  470. return hr;
  471. }
  472. HRESULT
  473. IIS_CERTIFICATE_MAPPING::DoMapCredential(
  474. W3_MAIN_CONTEXT * pMainContext,
  475. PBYTE pClientCertBlob,
  476. DWORD cbClientCertBlob,
  477. TOKEN_CACHE_ENTRY ** ppCachedToken,
  478. BOOL * pfClientCertDeniedByMapper
  479. )
  480. {
  481. CIisMapping * pQuery;
  482. CIisMapping * pResult;
  483. WCHAR wszUserName[ UNLEN + IIS_DNLEN + 1 + 1 ];
  484. LPWSTR pwszUserName;
  485. DWORD cbUserName;
  486. WCHAR wszPassword[ PWLEN + 1 ];
  487. LPWSTR pwszPassword;
  488. DWORD cbPassword;
  489. CHAR * pszDomain;
  490. BOOL fMatch = FALSE;
  491. DWORD dwLogonError = NO_ERROR;
  492. HRESULT hr = S_OK;
  493. BOOL fPossibleUPNLogon = FALSE;
  494. //
  495. // add 1 to strUserDomainW for separator "\"
  496. //
  497. STACK_STRU( strUserDomainW, UNLEN + IIS_DNLEN + 1 + 1 );
  498. STACK_STRU( strUserNameW, UNLEN + IIS_DNLEN + 1 + 1 );
  499. STACK_STRU( strDomainNameW, IIS_DNLEN + 1 );
  500. STACK_STRU( strPasswordW, PWLEN + 1 );
  501. DBG_ASSERT( pClientCertBlob != NULL );
  502. DBG_ASSERT( cbClientCertBlob != 0 );
  503. DBG_ASSERT( ppCachedToken != NULL );
  504. DBG_ASSERT( pfClientCertDeniedByMapper != NULL );
  505. //
  506. // First try the 1-1 mapper
  507. //
  508. if ( _pCert11Mapper != NULL )
  509. {
  510. //
  511. // Build a query mapping to check
  512. //
  513. pQuery = _pCert11Mapper->CreateNewMapping( pClientCertBlob,
  514. cbClientCertBlob );
  515. if ( pQuery == NULL )
  516. {
  517. return SEC_E_INTERNAL_ERROR;
  518. }
  519. //
  520. // no need to lock cert mapper because this is read only copy
  521. // used for mapping execution in worker process
  522. if ( _pCert11Mapper->FindMatch( pQuery,
  523. &pResult ) )
  524. {
  525. //
  526. // Awesome. We found a match. Do the deed if the rule is
  527. // enabled
  528. //
  529. if ( pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_ACCT,
  530. (PBYTE *)&pwszUserName,
  531. &cbUserName,
  532. FALSE ) &&
  533. pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_PWD,
  534. (PBYTE *)&pwszPassword,
  535. &cbPassword,
  536. FALSE ) )
  537. {
  538. //
  539. // No need to check for Enabled (IISMDB_INDEX_CERT11_ENABLED)
  540. // since Read11Mappings() will build mapping table consisting only
  541. // of enabled mappings
  542. //
  543. //
  544. // Make copy of user and password
  545. //
  546. hr = strUserDomainW.Copy( pwszUserName );
  547. if ( FAILED( hr ) )
  548. {
  549. return hr;
  550. }
  551. hr = strPasswordW.Copy( pwszPassword );
  552. if ( FAILED( hr ) )
  553. {
  554. return hr;
  555. }
  556. fMatch = TRUE;
  557. }
  558. }
  559. delete pQuery;
  560. pQuery = NULL;
  561. }
  562. //
  563. // Try the wildcard mapper if we still haven't found a match
  564. //
  565. if ( !fMatch &&
  566. _pCertWildcardMapper != NULL )
  567. {
  568. PCCERT_CONTEXT pClientCert = CertCreateCertificateContext(
  569. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  570. pClientCertBlob,
  571. cbClientCertBlob );
  572. if ( pClientCert == NULL )
  573. {
  574. DBG_ASSERT( pClientCert != NULL );
  575. }
  576. // Wildcard mapper assumes that
  577. // achUserName buffer is greater than UNLEN+IIS_DNLEN+1 and
  578. // achPassword buffer is greater then PWLEN
  579. //
  580. else if ( !_pCertWildcardMapper->Match(
  581. (PCERT_CONTEXT) pClientCert,
  582. NULL, // legacy value
  583. wszUserName,
  584. wszPassword ) )
  585. {
  586. //
  587. // If the mapping rule is denied then return
  588. // a NULL pointer through ppCachedToken with SEC_E_OK.
  589. // That indicated to caller that mapping was denied
  590. //
  591. if ( GetLastError() == ERROR_ACCESS_DENIED )
  592. {
  593. *ppCachedToken = NULL;
  594. *pfClientCertDeniedByMapper = TRUE;
  595. if ( pClientCert != NULL )
  596. {
  597. CertFreeCertificateContext( pClientCert );
  598. pClientCert = NULL;
  599. }
  600. return SEC_E_OK;
  601. }
  602. }
  603. else
  604. {
  605. fMatch = TRUE;
  606. //
  607. // Copy user and password (user name may be fully qualified with domain name in it)
  608. //
  609. hr = strUserDomainW.Copy( wszUserName );
  610. if ( FAILED( hr ) )
  611. {
  612. return hr;
  613. }
  614. hr = strPasswordW.Copy( wszPassword );
  615. if ( FAILED( hr ) )
  616. {
  617. return hr;
  618. }
  619. }
  620. if ( pClientCert != NULL )
  621. {
  622. CertFreeCertificateContext( pClientCert );
  623. pClientCert = NULL;
  624. }
  625. }
  626. //
  627. // If we still haven't found a match, then return error
  628. //
  629. if ( fMatch )
  630. {
  631. //
  632. // Split up the user name into domain/user if needed
  633. // Note: DefaultLogonDomain is not used for cert mapping at all
  634. // This is to keep behaviour equivalent with former versions of IIS
  635. //
  636. hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomainW,
  637. &strUserNameW,
  638. &strDomainNameW,
  639. NULL, // no default domain
  640. &fPossibleUPNLogon );
  641. if ( FAILED( hr ) )
  642. {
  643. return hr;
  644. }
  645. //
  646. // We should have valid credentials to call LogonUser()
  647. //
  648. DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
  649. hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
  650. strUserNameW.QueryStr(),
  651. strDomainNameW.QueryStr(),
  652. strPasswordW.QueryStr(),
  653. LOGON32_LOGON_NETWORK_CLEARTEXT,
  654. FALSE, //don't use subauth
  655. fPossibleUPNLogon,
  656. pMainContext->QueryRequest()->
  657. QueryRemoteSockAddress(),
  658. ppCachedToken,
  659. &dwLogonError );
  660. if ( FAILED( hr ) )
  661. {
  662. return SEC_E_UNKNOWN_CREDENTIALS;
  663. }
  664. //
  665. // If *ppCachedToken is NULL, then logon failed
  666. //
  667. if ( *ppCachedToken == NULL )
  668. {
  669. //
  670. // Note: With IIS5 we used to log logon failure to event log
  671. // however it doesn't seem to be necessary because if logon/logoff auditing is enabled
  672. // then security log would have relevant information about the logon failure
  673. //
  674. DBG_ASSERT( dwLogonError != ERROR_SUCCESS );
  675. return SEC_E_UNKNOWN_CREDENTIALS;
  676. }
  677. DBG_ASSERT( (*ppCachedToken)->CheckSignature() );
  678. *pfClientCertDeniedByMapper = FALSE;
  679. return SEC_E_OK;
  680. }
  681. return SEC_E_UNKNOWN_CREDENTIALS;
  682. }