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.

579 lines
12 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. certcontext.cxx
  5. Abstract:
  6. Simple wrapper of a certificate blob
  7. Author:
  8. Bilal Alam (balam) 5-Sept-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. HCRYPTPROV CERTIFICATE_CONTEXT::sm_CryptProvider;
  16. ALLOC_CACHE_HANDLER * CERTIFICATE_CONTEXT::sm_pachCertContexts;
  17. CERTIFICATE_CONTEXT::CERTIFICATE_CONTEXT(
  18. HTTP_SSL_CLIENT_CERT_INFO * pClientCertInfo
  19. ) : _fCertDecoded( FALSE ),
  20. _pClientCertInfo( pClientCertInfo ),
  21. _buffCertInfo( (PBYTE) &_CertInfo, sizeof( _CertInfo ) )
  22. {
  23. DBG_ASSERT( _pClientCertInfo != NULL );
  24. }
  25. CERTIFICATE_CONTEXT::~CERTIFICATE_CONTEXT(
  26. VOID
  27. )
  28. {
  29. _pClientCertInfo = NULL;
  30. }
  31. HRESULT
  32. CERTIFICATE_CONTEXT::GetIssuer(
  33. STRA * pstrIssuer
  34. )
  35. /*++
  36. Routine Description:
  37. Get the issuer of the client certificate
  38. Arguments:
  39. pstrIssuer - Filled with issuer string
  40. Return Value:
  41. HRESULT
  42. --*/
  43. {
  44. STACK_BUFFER ( buffIssuer, 256 );
  45. HRESULT hr;
  46. if ( pstrIssuer == NULL )
  47. {
  48. DBG_ASSERT( FALSE );
  49. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  50. }
  51. //
  52. // Decoding deferred until needed
  53. //
  54. if ( !_fCertDecoded )
  55. {
  56. hr = DecodeCert();
  57. if ( FAILED( hr ) )
  58. {
  59. return hr;
  60. }
  61. DBG_ASSERT( _fCertDecoded );
  62. }
  63. CertNameToStrA( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  64. &(QueryCertInfo()->Issuer),
  65. CERT_X500_NAME_STR,
  66. (CHAR *) buffIssuer.QueryPtr(),
  67. buffIssuer.QuerySize());
  68. return pstrIssuer->Copy( (CHAR *) buffIssuer.QueryPtr() );
  69. }
  70. HRESULT
  71. CERTIFICATE_CONTEXT::GetFlags(
  72. STRA * pstrFlags
  73. )
  74. /*++
  75. Routine Description:
  76. Get certificate validity flags for CERT_FLAGS
  77. Arguments:
  78. pstrFlags - Filled with string representation of flags
  79. Return Value:
  80. HRESULT
  81. --*/
  82. {
  83. CHAR * pszNumber;
  84. if ( pstrFlags == NULL )
  85. {
  86. DBG_ASSERT( FALSE );
  87. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  88. }
  89. //
  90. // Bit 0 indicates whether a cert is available. If this routine is
  91. // being called, then it must be TRUE
  92. //
  93. pszNumber = "1";
  94. //
  95. // Bit 1 indicates whether the authority is trusted
  96. //
  97. if ( _pClientCertInfo->CertFlags & CERT_TRUST_IS_UNTRUSTED_ROOT )
  98. {
  99. pszNumber = "3";
  100. }
  101. return pstrFlags->Copy( pszNumber, 1 );
  102. }
  103. HRESULT
  104. CERTIFICATE_CONTEXT::GetSerialNumber(
  105. STRA * pstrSerialNumber
  106. )
  107. /*++
  108. Routine Description:
  109. Stringize the certificate's serial number for filling in the
  110. CERT_SERIAL_NUMBER
  111. Arguments:
  112. pstrSerialNumber - Filled with serial number string
  113. Return Value:
  114. HRESULT
  115. --*/
  116. {
  117. HRESULT hr = NO_ERROR;
  118. INT i;
  119. DWORD cbSerialNumber;
  120. PBYTE pbSerialNumber;
  121. CHAR achDigit[ 2 ] = { '\0', '\0' };
  122. if ( pstrSerialNumber == NULL )
  123. {
  124. DBG_ASSERT( FALSE );
  125. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  126. }
  127. //
  128. // Decoding deferred until needed
  129. //
  130. if ( !_fCertDecoded )
  131. {
  132. hr = DecodeCert();
  133. if ( FAILED( hr ) )
  134. {
  135. return hr;
  136. }
  137. DBG_ASSERT( _fCertDecoded );
  138. }
  139. cbSerialNumber = QueryCertInfo()->SerialNumber.cbData;
  140. pbSerialNumber = QueryCertInfo()->SerialNumber.pbData;
  141. for ( i = cbSerialNumber-1; i >=0; i-- )
  142. {
  143. //
  144. // Just like IIS 5.0, we make the serial number in reverse byte order
  145. //
  146. achDigit[ 0 ] = HEX_DIGIT( ( pbSerialNumber[ i ] >> 4 ) );
  147. hr = pstrSerialNumber->Append( achDigit, 1 );
  148. if ( FAILED( hr ) )
  149. {
  150. return hr;
  151. }
  152. achDigit[ 0 ] = HEX_DIGIT( ( pbSerialNumber[ i ] & 0xF ) );
  153. hr = pstrSerialNumber->Append( achDigit, 1 );
  154. if ( FAILED( hr ) )
  155. {
  156. return hr;
  157. }
  158. //
  159. // Do not append "-" after last digit of Serial Number
  160. //
  161. if( i != 0 )
  162. {
  163. hr = pstrSerialNumber->Append( "-", 1 );
  164. if ( FAILED( hr ) )
  165. {
  166. return hr;
  167. }
  168. }
  169. }
  170. return NO_ERROR;
  171. }
  172. HRESULT
  173. CERTIFICATE_CONTEXT::DecodeCert(
  174. VOID
  175. )
  176. /*++
  177. Routine Description:
  178. Decode client certificate into stuff needed to fill in some server
  179. variables
  180. Arguments:
  181. None
  182. Return Value:
  183. HRESULT
  184. --*/
  185. {
  186. DWORD cbBuffer;
  187. cbBuffer = _buffCertInfo.QuerySize();
  188. if ( !CryptDecodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  189. X509_CERT_TO_BE_SIGNED,
  190. _pClientCertInfo->pCertEncoded,
  191. _pClientCertInfo->CertEncodedSize,
  192. CRYPT_DECODE_NOCOPY_FLAG,
  193. NULL,
  194. _buffCertInfo.QueryPtr(),
  195. &cbBuffer ) )
  196. {
  197. if ( GetLastError() == ERROR_MORE_DATA )
  198. {
  199. DBG_ASSERT( cbBuffer > _buffCertInfo.QuerySize() );
  200. if ( !_buffCertInfo.Resize( cbBuffer ) )
  201. {
  202. return HRESULT_FROM_WIN32( GetLastError() );
  203. }
  204. if ( !CryptDecodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  205. X509_CERT_TO_BE_SIGNED,
  206. _pClientCertInfo->pCertEncoded,
  207. _pClientCertInfo->CertEncodedSize,
  208. CRYPT_DECODE_NOCOPY_FLAG,
  209. NULL,
  210. _buffCertInfo.QueryPtr(),
  211. &cbBuffer ) )
  212. {
  213. DBG_ASSERT( GetLastError() != ERROR_MORE_DATA );
  214. return HRESULT_FROM_WIN32( GetLastError() );
  215. }
  216. }
  217. else
  218. {
  219. return HRESULT_FROM_WIN32( GetLastError() );
  220. }
  221. }
  222. _fCertDecoded = TRUE;
  223. return NO_ERROR;
  224. }
  225. HRESULT
  226. CERTIFICATE_CONTEXT::GetSubject(
  227. STRA * pstrSubject
  228. )
  229. /*++
  230. Routine Description:
  231. Get subject string for cert
  232. Arguments:
  233. pstrSubject - Filled with subject string
  234. Return Value:
  235. HRESULT
  236. --*/
  237. {
  238. STACK_BUFFER ( buffIssuer, 256);
  239. HRESULT hr;
  240. if ( pstrSubject == NULL )
  241. {
  242. DBG_ASSERT( FALSE );
  243. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  244. }
  245. //
  246. // Decoding deferred until needed
  247. //
  248. if ( !_fCertDecoded )
  249. {
  250. hr = DecodeCert();
  251. if ( FAILED( hr ) )
  252. {
  253. return hr;
  254. }
  255. DBG_ASSERT( _fCertDecoded );
  256. }
  257. CertNameToStrA( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  258. &(QueryCertInfo()->Subject),
  259. CERT_X500_NAME_STR,
  260. (CHAR *) buffIssuer.QueryPtr(),
  261. buffIssuer.QuerySize());
  262. return pstrSubject->Copy( (CHAR *) buffIssuer.QueryPtr() );
  263. }
  264. HRESULT
  265. CERTIFICATE_CONTEXT::GetCookie(
  266. STRA * pstrCookie
  267. )
  268. /*++
  269. Routine Description:
  270. CERT_COOKIE server variable
  271. Arguments:
  272. pstrCookie - Filled with CERT_COOKIE
  273. Return Value:
  274. HRESULT
  275. --*/
  276. {
  277. HRESULT hr;
  278. HCRYPTHASH cryptHash;
  279. STACK_STRA( strIssuer, 256 );
  280. DWORD cbHashee;
  281. STACK_BUFFER ( buffFinal, 256);
  282. BYTE * pbFinal;
  283. DWORD cbFinal;
  284. CHAR achDigit[ 2 ] = { '\0', '\0' };
  285. if ( pstrCookie == NULL )
  286. {
  287. DBG_ASSERT( FALSE );
  288. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  289. }
  290. //
  291. // Decoding deferred until needed
  292. //
  293. if ( !_fCertDecoded )
  294. {
  295. hr = DecodeCert();
  296. if ( FAILED( hr ) )
  297. {
  298. return hr;
  299. }
  300. DBG_ASSERT( _fCertDecoded );
  301. }
  302. //
  303. // Cookie is MD5(<issuer string> <serial number in binary>)
  304. //
  305. hr = GetIssuer( &strIssuer );
  306. if ( FAILED( hr ) )
  307. {
  308. return hr;
  309. }
  310. //
  311. // Begin the hashing
  312. //
  313. if ( !CryptCreateHash( sm_CryptProvider,
  314. CALG_MD5,
  315. 0,
  316. 0,
  317. &cryptHash ) )
  318. {
  319. return HRESULT_FROM_WIN32( GetLastError() );
  320. }
  321. if ( !CryptHashData( cryptHash,
  322. (BYTE*) strIssuer.QueryStr(),
  323. strIssuer.QueryCCH(),
  324. 0 ) )
  325. {
  326. hr = HRESULT_FROM_WIN32( GetLastError() );
  327. CryptDestroyHash( cryptHash );
  328. return hr;
  329. }
  330. if ( !CryptHashData( cryptHash,
  331. QueryCertInfo()->SerialNumber.pbData,
  332. QueryCertInfo()->SerialNumber.cbData,
  333. 0 ) )
  334. {
  335. hr = HRESULT_FROM_WIN32( GetLastError() );
  336. CryptDestroyHash( cryptHash );
  337. return hr;
  338. }
  339. //
  340. // Get the final hash value
  341. //
  342. cbFinal = buffFinal.QuerySize();
  343. if ( !CryptGetHashParam( cryptHash,
  344. HP_HASHVAL,
  345. (BYTE*) buffFinal.QueryPtr(),
  346. &cbFinal,
  347. 0 ) )
  348. {
  349. hr = HRESULT_FROM_WIN32( GetLastError() );
  350. CryptDestroyHash( cryptHash );
  351. return hr;
  352. }
  353. CryptDestroyHash( cryptHash );
  354. //
  355. // Now ascii'ize the final hex string
  356. //
  357. pbFinal = (BYTE*) buffFinal.QueryPtr();
  358. for ( DWORD i = 0; i < cbFinal; i++ )
  359. {
  360. achDigit[ 0 ] = HEX_DIGIT( ( pbFinal[ i ] & 0xF0 ) >> 4 );
  361. hr = pstrCookie->Append( achDigit, 1 );
  362. if ( FAILED( hr ) )
  363. {
  364. return hr;
  365. }
  366. achDigit[ 0 ] = HEX_DIGIT( ( pbFinal[ i ] & 0xF ) );
  367. hr = pstrCookie->Append( achDigit, 1 );
  368. if ( FAILED( hr ) )
  369. {
  370. return hr;
  371. }
  372. }
  373. return NO_ERROR;
  374. }
  375. //static
  376. HRESULT
  377. CERTIFICATE_CONTEXT::Initialize(
  378. VOID
  379. )
  380. /*++
  381. Routine Description:
  382. Do one time initialization of CRYPTO provider for doing MD5 hashes
  383. Arguments:
  384. None
  385. Return Value:
  386. HRESULT
  387. --*/
  388. {
  389. ALLOC_CACHE_CONFIGURATION acConfig;
  390. if ( !CryptAcquireContext( &sm_CryptProvider,
  391. NULL,
  392. NULL,
  393. PROV_RSA_FULL,
  394. CRYPT_VERIFYCONTEXT ) )
  395. {
  396. return HRESULT_FROM_WIN32( GetLastError() );
  397. }
  398. DBG_ASSERT( sm_CryptProvider != NULL );
  399. //
  400. // Setup allocation lookaside
  401. //
  402. acConfig.nConcurrency = 1;
  403. acConfig.nThreshold = 100;
  404. acConfig.cbSize = sizeof( CERTIFICATE_CONTEXT );
  405. DBG_ASSERT( sm_pachCertContexts == NULL );
  406. sm_pachCertContexts = new ALLOC_CACHE_HANDLER( "CERTIFICATE_CONTEXT",
  407. &acConfig );
  408. if ( sm_pachCertContexts == NULL )
  409. {
  410. CryptReleaseContext( sm_CryptProvider, 0 );
  411. sm_CryptProvider = NULL;
  412. return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  413. }
  414. return NO_ERROR;
  415. }
  416. //static
  417. VOID
  418. CERTIFICATE_CONTEXT::Terminate(
  419. VOID
  420. )
  421. /*++
  422. Routine Description:
  423. Global cleanup
  424. Arguments:
  425. None
  426. Return Value:
  427. None
  428. --*/
  429. {
  430. if ( sm_pachCertContexts != NULL )
  431. {
  432. delete sm_pachCertContexts;
  433. sm_pachCertContexts = NULL;
  434. }
  435. if ( sm_CryptProvider != NULL )
  436. {
  437. CryptReleaseContext( sm_CryptProvider, 0 );
  438. sm_CryptProvider = NULL;
  439. }
  440. }