Source code of Windows XP (NT5)
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.

739 lines
21 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 (duh)
  44. Return Value:
  45. HRESULT
  46. --*/
  47. {
  48. MB mb( g_pW3Server->QueryMDObject() );
  49. WCHAR achMBPath[ 256 ];
  50. HRESULT hr = NO_ERROR;
  51. WCHAR achMappingName[ MAX_PATH + 1 ];
  52. BOOL fRet;
  53. DWORD dwIndex;
  54. BYTE abBuffer[ 1024 ];
  55. BUFFER buff( abBuffer, sizeof( abBuffer ) );
  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 NSEPM path to get at 1-1 mappings
  64. //
  65. _snwprintf( achMBPath,
  66. sizeof( achMBPath ) / sizeof( WCHAR ) - 1,
  67. L"/LM/W3SVC/%d/<nsepm>/Cert11/Mappings",
  68. dwSiteId );
  69. //
  70. // Open the metabase and read 1-1 mapping properties
  71. //
  72. fRet = mb.Open( achMBPath,
  73. METADATA_PERMISSION_READ );
  74. if ( fRet )
  75. {
  76. dwIndex = 0;
  77. achMappingName[ 0 ] = L'/';
  78. for ( ; ; )
  79. {
  80. //
  81. // We will need to prepend the object name with '/'. Hence
  82. // goofyness of sending an offseted pointed to name
  83. //
  84. fRet = mb.EnumObjects( L"",
  85. achMappingName + 1,
  86. dwIndex );
  87. if ( !fRet )
  88. {
  89. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  90. break;
  91. }
  92. //
  93. // Get certificate blob
  94. //
  95. cbRequired = buff.QuerySize();
  96. fRet = mb.GetData( achMappingName,
  97. MD_MAPCERT,
  98. IIS_MD_UT_SERVER,
  99. BINARY_METADATA,
  100. buff.QueryPtr(),
  101. &cbRequired,
  102. METADATA_NO_ATTRIBUTES );
  103. if ( !fRet )
  104. {
  105. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  106. {
  107. DBG_ASSERT( cbRequired > buff.QuerySize() );
  108. if ( !buff.Resize( cbRequired ) )
  109. {
  110. hr = HRESULT_FROM_WIN32( GetLastError() );
  111. break;
  112. }
  113. fRet = mb.GetData( achMappingName,
  114. MD_MAPCERT,
  115. IIS_MD_UT_SERVER,
  116. BINARY_METADATA,
  117. buff.QueryPtr(),
  118. &cbRequired,
  119. METADATA_NO_ATTRIBUTES );
  120. if ( !fRet )
  121. {
  122. DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
  123. hr = HRESULT_FROM_WIN32( GetLastError() );
  124. break;
  125. }
  126. }
  127. else
  128. {
  129. hr = HRESULT_FROM_WIN32( GetLastError() );
  130. break;
  131. }
  132. }
  133. //
  134. // Get NT account name
  135. //
  136. if ( !mb.GetStr( achMappingName,
  137. MD_MAPNTACCT,
  138. IIS_MD_UT_SERVER,
  139. &strUserName ) )
  140. {
  141. hr = HRESULT_FROM_WIN32( GetLastError() );
  142. break;
  143. }
  144. //
  145. // Get NT password
  146. //
  147. if ( !mb.GetStr( achMappingName,
  148. MD_MAPNTPWD,
  149. IIS_MD_UT_SERVER,
  150. &strPassword ) )
  151. {
  152. hr = HRESULT_FROM_WIN32( GetLastError() );
  153. break;
  154. }
  155. //
  156. // Is this mapping enabled?
  157. //
  158. if ( !mb.GetDword( achMappingName,
  159. MD_MAPENABLED,
  160. IIS_MD_UT_SERVER,
  161. &dwEnabled ) )
  162. {
  163. hr = HRESULT_FROM_WIN32( GetLastError() );
  164. break;
  165. }
  166. //
  167. // If this mapping is enabled, add it to 1-1 mapper
  168. //
  169. if ( dwEnabled )
  170. {
  171. if ( _pCert11Mapper == NULL )
  172. {
  173. _pCert11Mapper = new CIisCert11Mapper();
  174. if ( _pCert11Mapper == NULL )
  175. {
  176. hr = HRESULT_FROM_WIN32( GetLastError() );
  177. break;
  178. }
  179. //
  180. // Reset() will configure default hierarchies
  181. // If hierarchies are not configured then comparison (CIisMapping::Cmp() function
  182. // implemented in iismap.cxx) will fail
  183. // (and mapper will always incorrectly map to first available 1to1 mapping)
  184. //
  185. if(!_pCert11Mapper->Reset())
  186. {
  187. delete _pCert11Mapper;
  188. _pCert11Mapper = NULL;
  189. hr = HRESULT_FROM_WIN32( GetLastError() );
  190. break;
  191. }
  192. }
  193. DBG_ASSERT( _pCert11Mapper != NULL );
  194. pCertMapping = _pCert11Mapper->CreateNewMapping();
  195. if ( pCertMapping == NULL )
  196. {
  197. hr = HRESULT_FROM_WIN32( GetLastError() );
  198. break;
  199. }
  200. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_CERT,
  201. (CHAR*)buff.QueryPtr(),
  202. cbRequired,
  203. FALSE ) )
  204. {
  205. hr = HRESULT_FROM_WIN32( GetLastError() );
  206. break;
  207. }
  208. //
  209. // Encrypted metabase stuff returned as ANSI (LAME!!!!!)
  210. //
  211. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_ACCT,
  212. (CHAR*) strUserName.QueryStr() ) )
  213. {
  214. hr = HRESULT_FROM_WIN32( GetLastError() );
  215. break;
  216. }
  217. if ( !pCertMapping->MappingSetField( IISMDB_INDEX_CERT11_NT_PWD,
  218. (CHAR*) strPassword.QueryStr() ) )
  219. {
  220. hr = HRESULT_FROM_WIN32( GetLastError() );
  221. break;
  222. }
  223. if ( !((CIisAcctMapper*)_pCert11Mapper)->Add( pCertMapping, FALSE ) )
  224. {
  225. hr = HRESULT_FROM_WIN32( GetLastError() );
  226. break;
  227. }
  228. }
  229. dwIndex++;
  230. }
  231. }
  232. else
  233. {
  234. hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  235. }
  236. return hr;
  237. }
  238. HRESULT
  239. IIS_CERTIFICATE_MAPPING::ReadWildcardMappings(
  240. DWORD dwSiteId
  241. )
  242. /*++
  243. Routine Description:
  244. Read wildcard mappings from metabase
  245. Arguments:
  246. dwSiteId - Site ID (duh)
  247. Return Value:
  248. HRESULT
  249. --*/
  250. {
  251. MB mb( g_pW3Server->QueryMDObject() );
  252. WCHAR achMBPath[ 256 ];
  253. BOOL fRet;
  254. BYTE abBuffer[ 1024 ];
  255. BUFFER buff( abBuffer, sizeof( abBuffer ) );
  256. DWORD cbRequired;
  257. PUCHAR pLamePointer;
  258. //
  259. // Setup the NSEPM path to get at wildcard mappings
  260. //
  261. _snwprintf( achMBPath,
  262. sizeof( achMBPath ) / sizeof( WCHAR ) - 1,
  263. L"/LM/W3SVC/%d/<nsepm>/CertW",
  264. dwSiteId );
  265. //
  266. // Open the metabase and read wildcard mappings
  267. //
  268. fRet = mb.Open( achMBPath,
  269. METADATA_PERMISSION_READ );
  270. if ( fRet )
  271. {
  272. cbRequired = buff.QuerySize();
  273. fRet = mb.GetData( L"",
  274. MD_SERIAL_CERTW,
  275. IIS_MD_UT_SERVER,
  276. BINARY_METADATA,
  277. buff.QueryPtr(),
  278. &cbRequired,
  279. METADATA_NO_ATTRIBUTES );
  280. if ( !fRet )
  281. {
  282. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  283. {
  284. DBG_ASSERT( cbRequired > buff.QuerySize() );
  285. if ( !buff.Resize( cbRequired ) )
  286. {
  287. return HRESULT_FROM_WIN32( GetLastError() );
  288. }
  289. fRet = mb.GetData( L"",
  290. MD_SERIAL_CERTW,
  291. IIS_MD_UT_SERVER,
  292. BINARY_METADATA,
  293. buff.QueryPtr(),
  294. &cbRequired,
  295. METADATA_NO_ATTRIBUTES );
  296. if ( !fRet )
  297. {
  298. DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
  299. return HRESULT_FROM_WIN32( GetLastError() );
  300. }
  301. }
  302. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  303. }
  304. //
  305. // Thanx to the man, I can just unserialize. XBF rocks ;-)
  306. //
  307. DBG_ASSERT( _pCertWildcardMapper == NULL );
  308. _pCertWildcardMapper = new CIisRuleMapper();
  309. if ( _pCertWildcardMapper == NULL )
  310. {
  311. return HRESULT_FROM_WIN32( GetLastError() );
  312. }
  313. pLamePointer = (PUCHAR) buff.QueryPtr();
  314. fRet = _pCertWildcardMapper->Unserialize( &pLamePointer,
  315. &cbRequired );
  316. if ( !fRet )
  317. {
  318. return HRESULT_FROM_WIN32( GetLastError() );
  319. }
  320. }
  321. else
  322. {
  323. return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
  324. }
  325. return NO_ERROR;
  326. }
  327. //static
  328. HRESULT
  329. IIS_CERTIFICATE_MAPPING::GetCertificateMapping(
  330. DWORD dwSiteId,
  331. IIS_CERTIFICATE_MAPPING ** ppCertificateMapping
  332. )
  333. /*++
  334. Routine Description:
  335. Read appropriate metabase configuration to get configured IIS
  336. certificate mapping
  337. Arguments:
  338. dwSiteId - Site ID (duh)
  339. ppCertificateMapping - Filled with certificate mapping descriptor on
  340. success
  341. Return Value:
  342. HRESULT
  343. --*/
  344. {
  345. IIS_CERTIFICATE_MAPPING * pCertMapping = NULL;
  346. HRESULT hr = NO_ERROR;
  347. if ( ppCertificateMapping == NULL )
  348. {
  349. DBG_ASSERT( FALSE );
  350. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  351. }
  352. *ppCertificateMapping = NULL;
  353. //
  354. // Create a certificate mapping descriptor
  355. //
  356. pCertMapping = new IIS_CERTIFICATE_MAPPING();
  357. if ( pCertMapping == NULL )
  358. {
  359. hr = HRESULT_FROM_WIN32( GetLastError() );
  360. goto Finished;
  361. }
  362. //
  363. // Read 1-1 mappings
  364. //
  365. hr = pCertMapping->Read11Mappings( dwSiteId );
  366. if ( FAILED( hr ) &&
  367. hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  368. {
  369. goto Finished;
  370. }
  371. hr = NO_ERROR;
  372. //
  373. // Read wildcards
  374. //
  375. hr = pCertMapping->ReadWildcardMappings( dwSiteId );
  376. if ( FAILED( hr ) &&
  377. hr != HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
  378. {
  379. goto Finished;
  380. }
  381. hr = NO_ERROR;
  382. Finished:
  383. if ( FAILED( hr ) )
  384. {
  385. if ( pCertMapping != NULL )
  386. {
  387. delete pCertMapping;
  388. }
  389. }
  390. else
  391. {
  392. DBG_ASSERT( pCertMapping != NULL );
  393. *ppCertificateMapping = pCertMapping;
  394. }
  395. return hr;
  396. }
  397. HRESULT
  398. IIS_CERTIFICATE_MAPPING::DoMapCredential(
  399. PBYTE pClientCertBlob,
  400. DWORD cbClientCertBlob,
  401. TOKEN_CACHE_ENTRY ** ppCachedToken,
  402. BOOL * pfClientCertDeniedByMapper
  403. )
  404. {
  405. CIisMapping * pQuery;
  406. CIisMapping * pResult;
  407. CHAR achUserName[ UNLEN + 1 ];
  408. LPSTR pszUserName;
  409. DWORD cbUserName;
  410. CHAR achPassword[ PWLEN + 1 ];
  411. LPSTR pszPassword;
  412. DWORD cbPassword;
  413. CHAR * pszDomain;
  414. DWORD dwEnabled = 0;
  415. DWORD cbEnabled;
  416. BOOL fMatch = FALSE;
  417. DWORD dwLogonError = NO_ERROR;
  418. HRESULT hr = S_OK;
  419. //
  420. // add 1 to strUserDomainW for separator "\"
  421. //
  422. STACK_STRU( strUserDomainW, UNLEN + IIS_DNLEN + 1 + 1 );
  423. STACK_STRU( strUserNameW, UNLEN + 1 );
  424. STACK_STRU( strPasswordW, PWLEN + 1 );
  425. DBG_ASSERT( pClientCertBlob != NULL );
  426. DBG_ASSERT( cbClientCertBlob != 0 );
  427. DBG_ASSERT( ppCachedToken != NULL );
  428. DBG_ASSERT( pfClientCertDeniedByMapper != NULL );
  429. //
  430. // First try the 1-1 mapper
  431. //
  432. if ( _pCert11Mapper != NULL )
  433. {
  434. //
  435. // Build a query mapping to check
  436. //
  437. pQuery = _pCert11Mapper->CreateNewMapping( pClientCertBlob,
  438. cbClientCertBlob );
  439. if ( pQuery == NULL )
  440. {
  441. return SEC_E_INTERNAL_ERROR;
  442. }
  443. _pCert11Mapper->Lock();
  444. if ( _pCert11Mapper->FindMatch( pQuery,
  445. &pResult ) )
  446. {
  447. //
  448. // Awesome. We found a match. Do the deed if the rule is
  449. // enabled
  450. //
  451. if ( pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_ACCT,
  452. &pszUserName,
  453. &cbUserName,
  454. FALSE ) &&
  455. pResult->MappingGetField( IISMDB_INDEX_CERT11_NT_PWD,
  456. &pszPassword,
  457. &cbPassword,
  458. FALSE ) )
  459. {
  460. //
  461. // No need to check for Enabled (IISMDB_INDEX_CERT11_ENABLED)
  462. // since Read11Mappings() will build mapping table consisting only
  463. // of enabled mappings
  464. //
  465. strncpy( achUserName,
  466. pszUserName,
  467. UNLEN );
  468. achUserName[ UNLEN ] = '\0';
  469. strncpy( achPassword,
  470. pszPassword,
  471. PWLEN );
  472. achPassword[ PWLEN ] = '\0';
  473. fMatch = TRUE;
  474. }
  475. }
  476. _pCert11Mapper->Unlock();
  477. delete pQuery;
  478. pQuery = NULL;
  479. }
  480. //
  481. // Try the wildcard mapper if we still haven't found a match
  482. //
  483. if ( !fMatch &&
  484. _pCertWildcardMapper != NULL )
  485. {
  486. PCCERT_CONTEXT pClientCert = CertCreateCertificateContext(
  487. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  488. pClientCertBlob,
  489. cbClientCertBlob );
  490. if ( pClientCert == NULL )
  491. {
  492. DBG_ASSERT( pClientCert != NULL );
  493. }
  494. else if ( !_pCertWildcardMapper->Match( (PCERT_CONTEXT) (pClientCert),
  495. NULL, // legacy value
  496. achUserName,
  497. achPassword ) )
  498. {
  499. //
  500. // If the mapping rule is denied then return
  501. // a NULL pointer through ppCachedToken with SEC_E_OK.
  502. // That indicated to caller that mapping was denied
  503. //
  504. if ( GetLastError() == ERROR_ACCESS_DENIED )
  505. {
  506. *ppCachedToken = NULL;
  507. *pfClientCertDeniedByMapper = TRUE;
  508. if ( pClientCert != NULL )
  509. {
  510. CertFreeCertificateContext( pClientCert );
  511. pClientCert = NULL;
  512. }
  513. return SEC_E_OK;
  514. }
  515. }
  516. else
  517. {
  518. fMatch = TRUE;
  519. }
  520. if ( pClientCert != NULL )
  521. {
  522. CertFreeCertificateContext( pClientCert );
  523. pClientCert = NULL;
  524. }
  525. }
  526. //
  527. // If we still haven't found a match, then return error
  528. //
  529. if ( fMatch )
  530. {
  531. //
  532. // Split up the user name into domain/user if needed
  533. //
  534. pszUserName = (LPSTR) _mbspbrk( (UCHAR*) achUserName, (UCHAR*) "/\\" );
  535. if ( pszUserName == NULL )
  536. {
  537. //
  538. // No domain in the user name. First try the metabase domain
  539. // name
  540. //
  541. //
  542. // BUGBUG: DefaultLogonDomain is not used for cert mapping at all
  543. //
  544. pszDomain = "";
  545. pszUserName = achUserName;
  546. }
  547. else
  548. {
  549. *pszUserName = '\0';
  550. pszDomain = achUserName;
  551. pszUserName = pszUserName + 1;
  552. }
  553. //
  554. // Convert domain, user and password to unicode
  555. //
  556. hr = strUserNameW.CopyA( pszUserName );
  557. if ( FAILED( hr ) )
  558. {
  559. return hr;
  560. }
  561. hr = strUserDomainW.CopyA( pszDomain );
  562. if ( FAILED( hr ) )
  563. {
  564. return hr;
  565. }
  566. hr = strPasswordW.CopyA( achPassword );
  567. if ( FAILED( hr ) )
  568. {
  569. return hr;
  570. }
  571. //
  572. // We should have valid credentials to call LogonUser()
  573. //
  574. DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
  575. hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
  576. strUserNameW.QueryStr(),
  577. strUserDomainW.QueryStr(),
  578. strPasswordW.QueryStr(),
  579. LOGON32_LOGON_INTERACTIVE,
  580. FALSE, // BUGBUG not UPN logon
  581. ppCachedToken,
  582. &dwLogonError );
  583. if ( FAILED( hr ) )
  584. {
  585. return SEC_E_UNKNOWN_CREDENTIALS;
  586. }
  587. //
  588. // If *ppCachedToken is NULL, then logon failed
  589. //
  590. if ( *ppCachedToken == NULL )
  591. {
  592. //
  593. // Note: With IIS5 we used to log logon failure to event log
  594. // however it doesn't seem to be necessary because if logon/logoff auditing is enabled
  595. // then security log would have relevant information about the logon failure
  596. //
  597. DBG_ASSERT( dwLogonError != ERROR_SUCCESS );
  598. return SEC_E_UNKNOWN_CREDENTIALS;
  599. }
  600. DBG_ASSERT( (*ppCachedToken)->CheckSignature() );
  601. *pfClientCertDeniedByMapper = FALSE;
  602. return SEC_E_OK;
  603. }
  604. return SEC_E_UNKNOWN_CREDENTIALS;
  605. }