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.

670 lines
19 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. iiscert.cxx
  5. Abstract:
  6. Code to handle retrieving/storing/using CAPI2 certificate contexts for IIS server
  7. certificates.
  8. Author:
  9. Alex Mallet (amallet) 02-Dec-1997
  10. --*/
  11. #include "tcpdllp.hxx"
  12. #pragma hdrstop
  13. #include <dbgutil.h>
  14. #include <buffer.hxx>
  15. #include <ole2.h>
  16. #include <imd.h>
  17. #include <mb.hxx>
  18. //
  19. // Local includes
  20. //
  21. #include "iiscert.hxx"
  22. #include "capiutil.hxx"
  23. #define SHA1_HASH_SIZE 20 //size of SHA1 hash
  24. HCRYPTPROV IIS_SERVER_CERT::m_hFortezzaCSP = NULL;
  25. HCRYPTDEFAULTCONTEXT IIS_SERVER_CERT::m_hFortezzaCtxt = NULL;
  26. IIS_SERVER_CERT::IIS_SERVER_CERT( IN IMDCOM *pMDObject,
  27. IN LPTSTR pszMBPath ) :
  28. m_strMBPath( pszMBPath ),
  29. m_pStoreInfo( NULL ),
  30. m_hCryptProv( NULL ),
  31. m_pCertContext( NULL ),
  32. m_fIsFortezzaCert( FALSE )
  33. /*++
  34. Routine Description:
  35. Constructor for server cert that reads all necessary info out of metabase
  36. Arguments:
  37. pMDObject - pointer to metabase object
  38. pszMBPath - full path in metabase to information for this virtual server instance
  39. Returns:
  40. Nothing
  41. --*/
  42. {
  43. DBG_ASSERT( pMDObject );
  44. DBG_ASSERT( pszMBPath );
  45. DWORD dwSize = 0;
  46. METADATA_HANDLE hInstanceInfoHandle = NULL;
  47. int iLengthNeeded = 0;
  48. PBYTE pbCertHash = NULL;
  49. DWORD dwHashSize = 0;
  50. DWORD dwPathLength = 0;
  51. MB mb( pMDObject );
  52. LPTSTR pszCSPString = NULL;
  53. LPTSTR pszCSPStringCopy = NULL;
  54. BOOL fProgPINEntry = FALSE;
  55. m_dwStatus = CERT_ERR_NONE;
  56. if ( !m_strMBPath.IsValid() )
  57. {
  58. SetLastError( ERROR_OUTOFMEMORY );
  59. goto EndRetrieveCertContext;
  60. }
  61. if ( mb.Open( m_strMBPath.QueryStr(),
  62. METADATA_PERMISSION_READ ))
  63. {
  64. DWORD dwReqDataLen = 0;
  65. METADATA_RECORD mdr;
  66. DWORD dwFortezza = 0;
  67. //
  68. // Retrieve cert hash
  69. //
  70. MD_SET_DATA_RECORD(&mdr, MD_SSL_CERT_HASH, METADATA_NO_ATTRIBUTES,
  71. IIS_MD_UT_SERVER, BINARY_METADATA, NULL,
  72. 0);
  73. if ( !RetrieveBlobFromMetabase(&mb,
  74. NULL,
  75. &mdr,
  76. SHA1_HASH_SIZE ) )
  77. {
  78. m_dwStatus = CERT_ERR_MB;
  79. goto EndRetrieveCertContext;
  80. }
  81. else
  82. {
  83. DBG_ASSERT( mdr.dwMDDataLen == SHA1_HASH_SIZE );
  84. pbCertHash = mdr.pbMDData;
  85. dwHashSize = mdr.dwMDDataLen;
  86. }
  87. //
  88. // Retrieve flag indicating whether it's a Fortezza cert or not
  89. //
  90. if ( !mb.GetDword( NULL,
  91. MD_SSL_CERT_IS_FORTEZZA,
  92. IIS_MD_UT_SERVER,
  93. &(dwFortezza),
  94. METADATA_NO_ATTRIBUTES ) )
  95. {
  96. if ( GetLastError() != MD_ERROR_DATA_NOT_FOUND )
  97. {
  98. m_dwStatus = CERT_ERR_MB;
  99. goto EndRetrieveCertContext;
  100. }
  101. else
  102. {
  103. m_fIsFortezzaCert = FALSE;
  104. }
  105. }
  106. else
  107. {
  108. m_fIsFortezzaCert = (BOOL) dwFortezza;
  109. }
  110. //
  111. // If it's a Fortezza cert and we're supposed to do programmatic PIN entry,
  112. // we need to get a handle to the CSP ourselves by providing the
  113. // PIN in the call to CryptAcquireContext(), to avoid the "Enter PIN" dialog
  114. //
  115. if ( m_fIsFortezzaCert && ( fProgPINEntry = UseProgrammaticPINEntry( &mb ) ) )
  116. {
  117. LPTSTR pszPIN = NULL;
  118. LPTSTR pszSerialNumber = NULL;
  119. LPTSTR pszPersonality = NULL;
  120. DWORD cbPIN = 0;
  121. DWORD cbSerialNumber = 0;
  122. DWORD cbPersonality = 0;
  123. DWORD cbLen = 0;
  124. DWORD iPos = 0;
  125. if ( !RetrievePINInfo( &mb,
  126. &pszPIN,
  127. &pszSerialNumber,
  128. &pszPersonality ) )
  129. {
  130. DBGPRINTF((DBG_CONTEXT,
  131. "Couldn't retrieve PIN info for Fortezza card\n"));
  132. m_dwStatus = CERT_ERR_MB;
  133. goto EndRetrieveCertContext;
  134. }
  135. DBG_ASSERT( pszPIN && pszSerialNumber && pszPersonality );
  136. //
  137. // Construct string to be passed to CryptAcquireContext - it's
  138. // <card serial #>\n<personality>\n<PIN>.
  139. //
  140. cbPIN = strlen( pszPIN );
  141. cbSerialNumber = strlen( pszSerialNumber );
  142. cbPersonality = strlen( pszPersonality );
  143. cbLen = cbPIN + cbSerialNumber + cbPersonality + 10; //add a few bytes for CR
  144. //and null terminating char
  145. pszCSPString = new CHAR[cbLen];
  146. pszCSPStringCopy = new CHAR[cbLen];
  147. if ( !pszCSPString || !pszCSPStringCopy )
  148. {
  149. DBGPRINTF((DBG_CONTEXT,
  150. "Couldn't allocate memory for CSP string\n"));
  151. //
  152. // Clean out the PIN info - the less time we leave it lying around,
  153. // the better
  154. //
  155. memset( pszPIN, 0, cbPIN );
  156. delete [] pszPIN;
  157. memset( pszSerialNumber, 0, cbSerialNumber );
  158. delete [] pszSerialNumber;
  159. memset( pszPersonality, 0, cbPersonality );
  160. delete [] pszPersonality;
  161. m_dwStatus = CERT_ERR_INTERNAL;
  162. goto EndRetrieveCertContext;
  163. }
  164. //
  165. // Build the magic string that will unlock the Fortezza secret ...
  166. //
  167. strcpy( pszCSPString, pszSerialNumber );
  168. strcat( pszCSPString, "\n" );
  169. strcat( pszCSPString, pszPersonality );
  170. strcat( pszCSPString, "\n" );
  171. strcat( pszCSPString, pszPIN );
  172. //
  173. // Make a copy we can reuse later
  174. //
  175. strcpy( pszCSPStringCopy, pszCSPString );
  176. //
  177. // Clean out the PIN & serial number - the less time we leave it lying around,
  178. // the better
  179. //
  180. memset( pszPIN, 0, cbPIN );
  181. delete [] pszPIN;
  182. memset( pszSerialNumber, 0, cbSerialNumber );
  183. delete [] pszSerialNumber;
  184. memset( pszPersonality, 0, cbPersonality );
  185. delete [] pszPersonality;
  186. //
  187. // Get a handle to the CSP, passing in the serial # etc and the
  188. // CRYPT_SILENT flag to avoid any UI
  189. //
  190. if ( !CryptAcquireContext( &m_hCryptProv,
  191. pszCSPString,
  192. NULL,
  193. PROV_FORTEZZA,
  194. CRYPT_SILENT | CRYPT_MACHINE_KEYSET ) )
  195. {
  196. DBGPRINTF((DBG_CONTEXT,
  197. "Couldn't get handle to Fortezza CSP : 0x%d\n",
  198. GetLastError()));
  199. m_dwStatus = CERT_ERR_CAPI;
  200. goto EndRetrieveCertContext;
  201. }
  202. if ( !IIS_SERVER_CERT::m_hFortezzaCSP )
  203. {
  204. //
  205. // There's apparently no way to duplicate an HCRYPTPROV handle,
  206. // so we have to call CryptAcquireContext again. Also, rumour
  207. // has it that the Fortezza CSP nulls out the string passed to it,
  208. // so we have to use a copy of it
  209. //
  210. if ( !CryptAcquireContext( &IIS_SERVER_CERT::m_hFortezzaCSP,
  211. pszCSPStringCopy,
  212. NULL,
  213. PROV_FORTEZZA,
  214. CRYPT_SILENT | CRYPT_MACHINE_KEYSET ) )
  215. {
  216. DBGPRINTF((DBG_CONTEXT,
  217. "Couldn't get handle to Fortezza CSP : 0x%d\n",
  218. GetLastError()));
  219. m_dwStatus = CERT_ERR_CAPI;
  220. goto EndRetrieveCertContext;
  221. }
  222. //
  223. // Install the handler used to verify Fortezza signatures.
  224. //
  225. CRYPT_DEFAULT_CONTEXT_MULTI_OID_PARA CDCMOP;
  226. LPSTR rgszOID[2];
  227. CDCMOP.cOID = 2;
  228. CDCMOP.rgpszOID = rgszOID;
  229. CDCMOP.rgpszOID[0] = szOID_INFOSEC_mosaicUpdatedSig;
  230. CDCMOP.rgpszOID[1] = szOID_INFOSEC_mosaicKMandUpdSig;
  231. if (!CryptInstallDefaultContext( IIS_SERVER_CERT::m_hFortezzaCSP,
  232. CRYPT_DEFAULT_CONTEXT_MULTI_CERT_SIGN_OID,
  233. &CDCMOP,
  234. CRYPT_DEFAULT_CONTEXT_PROCESS_FLAG,
  235. NULL,
  236. &IIS_SERVER_CERT::m_hFortezzaCtxt ) )
  237. {
  238. DBGPRINTF((DBG_CONTEXT,
  239. "Failed to install Fortezza context : 0x%d\n",
  240. GetLastError()));
  241. m_dwStatus = CERT_ERR_CAPI;
  242. goto EndRetrieveCertContext;
  243. }
  244. }
  245. }
  246. }
  247. mb.Close();
  248. //
  249. // Read store name etc out of MB
  250. //
  251. m_pStoreInfo = ReadCertStoreInfoFromMB( pMDObject,
  252. m_strMBPath.QueryStr(),
  253. FALSE );
  254. if ( !m_pStoreInfo )
  255. {
  256. m_dwStatus = CERT_ERR_MB;
  257. goto EndRetrieveCertContext;
  258. }
  259. //
  260. // Open store in which to look for the cert
  261. //
  262. if ( !(m_pStoreInfo->hCertStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_A,
  263. 0,
  264. (fProgPINEntry ? m_hCryptProv : NULL ),
  265. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  266. m_pStoreInfo->pszStoreName ) ) )
  267. {
  268. m_dwStatus = CERT_ERR_CAPI;
  269. goto EndRetrieveCertContext;
  270. }
  271. //
  272. // Try to find the cert in the store
  273. //
  274. CRYPT_HASH_BLOB HashBlob;
  275. HashBlob.cbData = dwHashSize;
  276. HashBlob.pbData = pbCertHash;
  277. m_pCertContext = CertFindCertificateInStore( m_pStoreInfo->hCertStore,
  278. X509_ASN_ENCODING,
  279. 0,
  280. CERT_FIND_SHA1_HASH,
  281. (VOID *) &HashBlob,
  282. NULL );
  283. if ( !m_pCertContext )
  284. {
  285. m_dwStatus = CERT_ERR_CERT_NOT_FOUND;
  286. goto EndRetrieveCertContext;
  287. }
  288. else
  289. {
  290. #if DBG
  291. CHAR szSubjectName[1024];
  292. if ( CertGetNameString( m_pCertContext,
  293. CERT_NAME_SIMPLE_DISPLAY_TYPE,
  294. 0,
  295. NULL,
  296. szSubjectName,
  297. 1024 ) )
  298. {
  299. DBGPRINTF((DBG_CONTEXT,
  300. "Retrieved cert for %s \n",
  301. szSubjectName));
  302. }
  303. #endif
  304. }
  305. if ( fProgPINEntry )
  306. {
  307. //
  308. // If this is a Fortezza cert, then we should set the
  309. // CERT_KEY_PROV_HANDLE_PROP_ID property of the context to point to
  310. // the HCRYPTPROV retrieved by CryptAcquireContext(). Why? Because
  311. // if we don't set this property, Schannel will try to acquire the
  312. // provider handle itself, and will NOT set the CRYPT_MACHINE_KEY
  313. // flag when doing the call to CryptAcquireContext() and thus we
  314. // will not be able to acquire the credential handle in SSPIFILT.
  315. //
  316. // Fortezza rocks
  317. //
  318. if ( !CertSetCertificateContextProperty( m_pCertContext,
  319. CERT_KEY_PROV_HANDLE_PROP_ID,
  320. CERT_STORE_NO_CRYPT_RELEASE_FLAG,
  321. (VOID*) m_hCryptProv ) )
  322. {
  323. m_dwStatus = CERT_ERR_CAPI;
  324. goto EndRetrieveCertContext;
  325. }
  326. }
  327. //
  328. // If we got this far, everything is happy
  329. //
  330. m_dwStatus = CERT_ERR_NONE;
  331. EndRetrieveCertContext:
  332. //
  333. // This is where all the cleanup that takes place only if we fail needs to
  334. // happen
  335. //
  336. DBG_ASSERT( m_dwStatus < CERT_ERR_END );
  337. if ( m_dwStatus != CERT_ERR_NONE )
  338. {
  339. DBGPRINTF((DBG_CONTEXT,
  340. "IIS_SERVER cert constructor, Error occurred : 0x%x\n",
  341. GetLastError()));
  342. if ( m_pCertContext != NULL )
  343. {
  344. CertFreeCertificateContext( m_pCertContext );
  345. m_pCertContext = NULL;
  346. }
  347. if ( m_pStoreInfo )
  348. {
  349. DeallocateCertStoreInfo( m_pStoreInfo );
  350. m_pStoreInfo = NULL;
  351. }
  352. if ( m_hCryptProv != NULL )
  353. {
  354. CryptReleaseContext( m_hCryptProv,
  355. 0 );
  356. m_hCryptProv = NULL;
  357. }
  358. }
  359. //
  360. // Cleanup we do regardless of success/failure
  361. //
  362. //
  363. // clean out memory holding PIN
  364. //
  365. if ( pszCSPString )
  366. {
  367. memset( pszCSPString, 0, strlen( pszCSPString ) );
  368. delete [] pszCSPString;
  369. }
  370. if ( pszCSPStringCopy )
  371. {
  372. memset( pszCSPStringCopy, 0, strlen( pszCSPStringCopy ) );
  373. delete [] pszCSPStringCopy;
  374. }
  375. mb.Close();
  376. } //IIS_SERVER_CERT::IIS_SERVER_CERT
  377. IIS_SERVER_CERT::~IIS_SERVER_CERT()
  378. /*++
  379. Routine Description:
  380. Destructor
  381. Arguments :
  382. None
  383. Returns :
  384. Nothing
  385. --*/
  386. {
  387. if ( m_pCertContext )
  388. {
  389. CertFreeCertificateContext( m_pCertContext );
  390. m_pCertContext = NULL;
  391. }
  392. if ( m_pStoreInfo )
  393. {
  394. DeallocateCertStoreInfo( m_pStoreInfo );
  395. m_pStoreInfo = NULL;
  396. }
  397. if ( m_hCryptProv )
  398. {
  399. CryptReleaseContext( m_hCryptProv,
  400. 0 );
  401. m_hCryptProv = NULL;
  402. }
  403. } //~IIS_SERVER_CERT::IIS_SERVER_CERT
  404. BOOL IIS_SERVER_CERT::RetrievePINInfo( IN MB *pMB,
  405. OUT LPTSTR *ppszPIN,
  406. OUT LPTSTR *ppszSerialNumber,
  407. OUT LPTSTR *ppszPersonality )
  408. /*++
  409. Routine Description:
  410. Retrieve PIN information for Fortezza certificates
  411. Arguments:
  412. pMB - pointer to MB object, open for reading
  413. ppszPIN - pointer to pointer to PIN for Fortezza card, updated on success
  414. ppszSerialNumber - pointer to pointer to card serial number, updated on success
  415. ppszPersonality - pointer to pointer to Fortezza "personality", updated on sucess
  416. Returns:
  417. BOOL indicating success/failure
  418. --*/
  419. {
  420. DBG_ASSERT( pMB &&
  421. ppszPIN &&
  422. ppszSerialNumber &&
  423. ppszPersonality );
  424. DWORD cbLen = 0;
  425. BOOL fSuccess = FALSE;
  426. DWORD dwReqDataLen = 0;
  427. METADATA_RECORD mdr;
  428. //
  429. // Retrieve PIN
  430. //
  431. cbLen = 0;
  432. if ( ( !pMB->GetString( NULL,
  433. MD_SSL_CERT_FORTEZZA_PIN,
  434. IIS_MD_UT_SERVER,
  435. NULL,
  436. &cbLen,
  437. METADATA_SECURE ) &&
  438. GetLastError() != ERROR_INSUFFICIENT_BUFFER ) ||
  439. !cbLen )
  440. {
  441. goto end_pin_info;
  442. }
  443. *ppszPIN = new CHAR[cbLen + 1];
  444. if ( !*ppszPIN ||
  445. !pMB->GetString( NULL,
  446. MD_SSL_CERT_FORTEZZA_PIN,
  447. IIS_MD_UT_SERVER,
  448. *ppszPIN,
  449. &cbLen,
  450. METADATA_SECURE ) )
  451. {
  452. goto end_pin_info;
  453. }
  454. //
  455. // Retrieve serial #
  456. //
  457. cbLen = 0;
  458. if ( ( !pMB->GetString( NULL,
  459. MD_SSL_CERT_FORTEZZA_SERIAL_NUMBER,
  460. IIS_MD_UT_SERVER,
  461. NULL,
  462. &cbLen,
  463. METADATA_SECURE ) &&
  464. GetLastError() != ERROR_INSUFFICIENT_BUFFER ) ||
  465. !cbLen )
  466. {
  467. goto end_pin_info;
  468. }
  469. *ppszSerialNumber = new CHAR[cbLen + 1];
  470. if ( !*ppszSerialNumber ||
  471. !pMB->GetString( NULL,
  472. MD_SSL_CERT_FORTEZZA_SERIAL_NUMBER,
  473. IIS_MD_UT_SERVER,
  474. *ppszSerialNumber,
  475. &cbLen,
  476. METADATA_SECURE ) )
  477. {
  478. goto end_pin_info;
  479. }
  480. //
  481. // Retrieve personality
  482. //
  483. cbLen = 0;
  484. if ( ( !pMB->GetString( NULL,
  485. MD_SSL_CERT_FORTEZZA_PERSONALITY,
  486. IIS_MD_UT_SERVER,
  487. NULL,
  488. &cbLen,
  489. METADATA_SECURE ) &&
  490. GetLastError() != ERROR_INSUFFICIENT_BUFFER ) ||
  491. !cbLen )
  492. {
  493. goto end_pin_info;
  494. }
  495. *ppszPersonality = new CHAR[cbLen + 1];
  496. if ( !*ppszPersonality ||
  497. !pMB->GetString( NULL,
  498. MD_SSL_CERT_FORTEZZA_PERSONALITY,
  499. IIS_MD_UT_SERVER,
  500. *ppszPersonality,
  501. &cbLen,
  502. METADATA_SECURE ) )
  503. {
  504. goto end_pin_info;
  505. }
  506. fSuccess = TRUE;
  507. end_pin_info:
  508. if ( !fSuccess )
  509. {
  510. DBGPRINTF((DBG_CONTEXT,
  511. "RetrievePINInfo failed : 0x%x\n",
  512. GetLastError()));
  513. //
  514. // Clean out all the PIN info, making sure to erase the memory
  515. //
  516. if ( *ppszPIN )
  517. {
  518. /* INTRINSA suppress = uninitialized */
  519. memset( *ppszPIN, 0, strlen(*ppszPIN) );
  520. delete [] *ppszPIN;
  521. }
  522. if ( *ppszSerialNumber )
  523. {
  524. memset( *ppszSerialNumber, 0, strlen(*ppszSerialNumber) );
  525. delete [] *ppszSerialNumber;
  526. }
  527. if ( *ppszPersonality )
  528. {
  529. memset( *ppszPersonality, 0, strlen(*ppszPersonality) );
  530. delete [] *ppszPersonality;
  531. }
  532. }
  533. return fSuccess;
  534. }
  535. inline
  536. BOOL IIS_SERVER_CERT::IsValid()
  537. {
  538. return ( m_dwStatus == CERT_ERR_NONE ? TRUE : FALSE ) ;
  539. }