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.

567 lines
13 KiB

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