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.

1596 lines
35 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. base.cxx
  5. Abstract:
  6. This module implements the IIS_CRYPTO_BASE class.
  7. Author:
  8. Keith Moore (keithmo) 02-Dec-1996
  9. Revision History:
  10. --*/
  11. #include "precomp.hxx"
  12. #pragma hdrstop
  13. //
  14. // Private constants.
  15. //
  16. //
  17. // Private types.
  18. //
  19. //
  20. // Private globals.
  21. //
  22. //
  23. // Private prototypes.
  24. //
  25. //
  26. // Public functions.
  27. //
  28. IIS_CRYPTO_BASE::IIS_CRYPTO_BASE()
  29. /*++
  30. Routine Description:
  31. IIS_CRYPTO_BASE class constructor. Just sets the member variables
  32. to known values; does nothing that can actually fail. All of the
  33. hard work is in the Initialize() method.
  34. Arguments:
  35. None.
  36. Return Value:
  37. None.
  38. --*/
  39. {
  40. //
  41. // Set the handles to known values so we know what to cleanup
  42. // in the destructor.
  43. //
  44. m_hProv = CRYPT_NULL;
  45. m_fCloseProv = FALSE;
  46. m_hKeyExchangeKey = CRYPT_NULL;
  47. m_hSignatureKey = CRYPT_NULL;
  48. } // IIS_CRYPTO_BASE::IIS_CRYPTO_BASE
  49. IIS_CRYPTO_BASE::~IIS_CRYPTO_BASE()
  50. /*++
  51. Routine Description:
  52. IIS_CRYPTO_BASE class destructor. Performs any necessary cleanup.
  53. Arguments:
  54. None.
  55. Return Value:
  56. None.
  57. --*/
  58. {
  59. //
  60. // Close any open keys.
  61. //
  62. CLOSE_KEY( m_hSignatureKey );
  63. CLOSE_KEY( m_hKeyExchangeKey );
  64. //
  65. // Close the container.
  66. //
  67. if( m_fCloseProv && m_hProv != CRYPT_NULL ) {
  68. (VOID)::IISCryptoCloseContainer( m_hProv );
  69. m_hProv = CRYPT_NULL;
  70. }
  71. } // IIS_CRYPTO_BASE::~IIS_CRYPTO_BASE
  72. HRESULT
  73. IIS_CRYPTO_BASE::GetCryptoContainerByName(
  74. OUT HCRYPTPROV * phProv,
  75. IN LPTSTR pszContainerName,
  76. IN DWORD dwAdditionalFlags,
  77. IN BOOL fApplyAcl
  78. )
  79. /*++
  80. Routine Description:
  81. Creates or opens the specified crypto container. This method plays
  82. games to ensure the container can be opened regardless of the
  83. current security impersonation context.
  84. Arguments:
  85. phProv - Receives the handle to the provider.
  86. pszContainerName - The name of the container to open/create.
  87. (NULL means temporary container)
  88. dwAdditionalFlags - Flags to pass into IISCryptoGetStandardContainer().
  89. fApplyAcl - If TRUE, then an ACL is applied to the container.
  90. Return Value:
  91. HRESULT - 0 if successful, !0 otherwise.
  92. --*/
  93. {
  94. HRESULT result = NO_ERROR;
  95. HANDLE token = NULL;
  96. BOOL resetToken = FALSE;
  97. //
  98. // Sanity check.
  99. //
  100. DBG_ASSERT( phProv != NULL );
  101. //
  102. // If the caller is not already asking for CRYPT_MACHINE_KEYSET
  103. // *and* the current process is running in the local system context,
  104. // then forcibly set CRYPT_MACHINE_KEYSET and note that we need to
  105. // apply an ACL to the container. This is to workaround an issue
  106. // with the NT5 Protected Storage service that causes the client-side
  107. // containers to fail after NT5 GUI Setup. Apparently, there is some
  108. // as-yet unidentified interaction between creating the client-side
  109. // container under NT5 GUI Setup (which runs in the Local System
  110. // security context) and later attempts to create containers in
  111. // lesser security contexts.
  112. //
  113. if( !( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) ) {
  114. if( AmIRunningInTheLocalSystemContext() ) {
  115. dwAdditionalFlags |= CRYPT_MACHINE_KEYSET;
  116. fApplyAcl = TRUE;
  117. }
  118. }
  119. //
  120. // Step 1: Just try to open/create the container.
  121. //
  122. result = ::IISCryptoGetContainerByName(
  123. phProv,
  124. pszContainerName,
  125. dwAdditionalFlags,
  126. fApplyAcl
  127. );
  128. if( SUCCEEDED(result) ) {
  129. goto complete;
  130. }
  131. //
  132. // NTE_BAD_KEYSET is typically returned when the caller has
  133. // insufficient privilege to access the key container registry
  134. // tree. If we failed for any other reason, bail.
  135. //
  136. if( result != NTE_BAD_KEYSET ) {
  137. goto complete;
  138. }
  139. //
  140. // Step 2: If the caller didn't ask for CRYPT_MACHINE_KEYSET, then
  141. // retry the operation with this flag set.
  142. //
  143. if( ( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) == 0 ) {
  144. result = ::IISCryptoGetContainerByName(
  145. phProv,
  146. pszContainerName,
  147. dwAdditionalFlags | CRYPT_MACHINE_KEYSET,
  148. fApplyAcl
  149. );
  150. if( SUCCEEDED(result) ) {
  151. goto complete;
  152. }
  153. }
  154. //
  155. // OK, now things get a bit complex.
  156. //
  157. // If the current thread has an impersonation token, then
  158. // (temporarily) remove it and retry the container operation.
  159. // This is mainly here so that ISAPI applications (like, say, ASP)
  160. // can access the DCOM interface while impersonating a non-privileged
  161. // security context.
  162. //
  163. // Note that, after we remove the impersonation token, we first try
  164. // the operation with CRYPT_MACHINE_KEYSET ORed in. We do this on
  165. // the assumption that, if the thread had an impersonation token,
  166. // then we're probably running in the context of a server process.
  167. // If this operation fails, we try again without forcing the flag.
  168. //
  169. result = GetThreadImpersonationToken( &token );
  170. if( FAILED(result) ) {
  171. goto complete;
  172. }
  173. if( token != NULL ) {
  174. result = SetThreadImpersonationToken( NULL );
  175. if( FAILED(result) ) {
  176. goto complete;
  177. }
  178. resetToken = TRUE;
  179. //
  180. // Step 3: With the token removed, retry the operation with the
  181. // CRYPT_MACHINE_KEYSET flag set if not already set.
  182. //
  183. if( ( dwAdditionalFlags & CRYPT_MACHINE_KEYSET ) == 0 ) {
  184. result = ::IISCryptoGetContainerByName(
  185. phProv,
  186. pszContainerName,
  187. dwAdditionalFlags | CRYPT_MACHINE_KEYSET,
  188. fApplyAcl
  189. );
  190. if( SUCCEEDED(result) ) {
  191. goto complete;
  192. }
  193. }
  194. //
  195. // Step 4: With the token removed, try to open/create the container.
  196. //
  197. result = ::IISCryptoGetContainerByName(
  198. phProv,
  199. pszContainerName,
  200. dwAdditionalFlags,
  201. fApplyAcl
  202. );
  203. if( SUCCEEDED(result) ) {
  204. goto complete;
  205. }
  206. }
  207. //
  208. // If we made it this far, then the container cannot be opened.
  209. //
  210. result = NTE_BAD_KEYSET;
  211. complete:
  212. if( resetToken ) {
  213. HRESULT result2;
  214. DBG_ASSERT( token != NULL );
  215. result2 = SetThreadImpersonationToken( token );
  216. if( FAILED(result2) ) {
  217. //
  218. // This is really, really, really bad news. The current
  219. // thread, which does not have an impersonation token
  220. // (and is therefore running in the system context)
  221. // cannot reset its impersonation token to the original
  222. // value.
  223. //
  224. DBG_ASSERT( SUCCEEDED( result2 ) );
  225. }
  226. }
  227. if( token != NULL ) {
  228. DBG_REQUIRE( CloseHandle( token ) );
  229. }
  230. return result;
  231. } // IIS_CRYPTO_BASE::GetCryptoContainerByName
  232. HRESULT
  233. IIS_CRYPTO_BASE::Initialize(
  234. IN HCRYPTPROV hProv,
  235. IN HCRYPTKEY hKeyExchangeKey,
  236. IN HCRYPTKEY hSignatureKey,
  237. IN BOOL fUseMachineKeyset
  238. )
  239. /*++
  240. Routine Description:
  241. Performs any complex initialization.
  242. Arguments:
  243. hProv - Optional pre-opened handle to a crypto provider. If this
  244. is not present, then the standard container is opened. If this
  245. is present, then it is used and it is the responsibility of the
  246. caller to close the handle when no longer needed.
  247. hKeyExchangeKey - Optional pre-opened handle to a key exchange key. If
  248. this is not present, then the local key exchange key is used.
  249. hSignatureKey - Optional pre-opened handle to a signature key. If this
  250. is not present, then the local signature key is used.
  251. fUseMachineKeyset - TRUE if the per-machine keyset container should
  252. be used, as opposed to the per-user keyset container.
  253. Return Value:
  254. HRESULT - Completion status, 0 if successful, !0 otherwise.
  255. --*/
  256. {
  257. HRESULT result;
  258. //
  259. // Sanity check.
  260. //
  261. DBG_ASSERT( m_hProv == CRYPT_NULL );
  262. DBG_ASSERT( m_hKeyExchangeKey == CRYPT_NULL );
  263. DBG_ASSERT( m_hSignatureKey == CRYPT_NULL );
  264. IcpAcquireGlobalLock();
  265. if( hProv == CRYPT_NULL ) {
  266. //
  267. // Open the container.
  268. //
  269. result = ::IISCryptoGetStandardContainer(
  270. &m_hProv,
  271. fUseMachineKeyset
  272. ? CRYPT_MACHINE_KEYSET
  273. : 0
  274. );
  275. m_fCloseProv = TRUE;
  276. } else {
  277. m_hProv = hProv;
  278. m_fCloseProv = FALSE;
  279. result = NO_ERROR;
  280. }
  281. if( SUCCEEDED(result) ) {
  282. if( hKeyExchangeKey == CRYPT_NULL ) {
  283. //
  284. // Get the key exchange key.
  285. //
  286. result = ::IISCryptoGetKeyExchangeKey(
  287. &m_hKeyExchangeKey,
  288. m_hProv
  289. );
  290. } else {
  291. m_hKeyExchangeKey = hKeyExchangeKey;
  292. }
  293. }
  294. if( SUCCEEDED(result) ) {
  295. if( hSignatureKey == CRYPT_NULL ) {
  296. //
  297. // Get the signature key.
  298. //
  299. result = ::IISCryptoGetSignatureKey(
  300. &m_hSignatureKey,
  301. m_hProv
  302. );
  303. } else {
  304. m_hSignatureKey = hSignatureKey;
  305. }
  306. }
  307. IcpReleaseGlobalLock();
  308. return result;
  309. } // IIS_CRYPTO_BASE::Initialize
  310. HRESULT
  311. IIS_CRYPTO_BASE::Initialize2(
  312. IN HCRYPTPROV hProv
  313. )
  314. /*++
  315. Routine Description:
  316. Performs any complex initialization.
  317. Arguments:
  318. hProv - Optional pre-opened handle to a crypto provider. If this
  319. is not present, then the standard container is opened. If this
  320. is present, then it is used and it is the responsibility of the
  321. caller to close the handle when no longer needed.
  322. Return Value:
  323. HRESULT - Completion status, 0 if successful, !0 otherwise.
  324. --*/
  325. {
  326. HRESULT result;
  327. //
  328. // Sanity check.
  329. //
  330. DBG_ASSERT( m_hProv == CRYPT_NULL );
  331. DBG_ASSERT( m_hKeyExchangeKey == CRYPT_NULL );
  332. DBG_ASSERT( m_hSignatureKey == CRYPT_NULL );
  333. IcpAcquireGlobalLock();
  334. if( hProv == CRYPT_NULL ) {
  335. //
  336. // Open the container.
  337. //
  338. result = ::IISCryptoGetStandardContainer2(
  339. &m_hProv
  340. );
  341. m_fCloseProv = TRUE;
  342. } else {
  343. m_hProv = hProv;
  344. m_fCloseProv = FALSE;
  345. result = NO_ERROR;
  346. }
  347. IcpReleaseGlobalLock();
  348. return result;
  349. } // IIS_CRYPTO_BASE::Initialize2
  350. HRESULT
  351. IIS_CRYPTO_BASE::GetKeyExchangeKeyBlob(
  352. OUT PIIS_CRYPTO_BLOB * ppKeyExchangeKeyBlob
  353. )
  354. /*++
  355. Routine Description:
  356. Exports the key exchange key as a public blob.
  357. Arguments:
  358. ppKeyExchangeKeyBlob - Receives a pointer to the key exchange key
  359. public blob if successful.
  360. Return Value:
  361. HRESULT - Completion status, 0 if successful, !0 otherwise.
  362. --*/
  363. {
  364. HRESULT result;
  365. //
  366. // Sanity check.
  367. //
  368. DBG_ASSERT( ValidateState() );
  369. DBG_ASSERT( ppKeyExchangeKeyBlob != NULL );
  370. //
  371. // Let the IIS Crypto APIs do the dirty work.
  372. //
  373. result = ::IISCryptoExportPublicKeyBlob(
  374. ppKeyExchangeKeyBlob,
  375. m_hProv,
  376. m_hKeyExchangeKey
  377. );
  378. return result;
  379. } // IIS_CRYPTO_BASE::GetKeyExchangeKeyBlob
  380. HRESULT
  381. IIS_CRYPTO_BASE::GetSignatureKeyBlob(
  382. OUT PIIS_CRYPTO_BLOB * ppSignatureKeyBlob
  383. )
  384. /*++
  385. Routine Description:
  386. Exports the signature key as a public blob.
  387. Arguments:
  388. ppSignatureKeyBlob - Receives a pointer to the signature key
  389. public blob if successful.
  390. Return Value:
  391. HRESULT - Completion status, 0 if successful, !0 otherwise.
  392. --*/
  393. {
  394. HRESULT result;
  395. //
  396. // Sanity check.
  397. //
  398. DBG_ASSERT( ValidateState() );
  399. DBG_ASSERT( ppSignatureKeyBlob != NULL );
  400. //
  401. // Let the IIS Crypto APIs do the dirty work.
  402. //
  403. result = ::IISCryptoExportPublicKeyBlob(
  404. ppSignatureKeyBlob,
  405. m_hProv,
  406. m_hSignatureKey
  407. );
  408. return result;
  409. } // IIS_CRYPTO_BASE::GetSignatureKeyBlob
  410. //
  411. // Private functions.
  412. //
  413. #if DBG
  414. BOOL
  415. IIS_CRYPTO_BASE::ValidateState()
  416. /*++
  417. Routine Description:
  418. This debug-only routine validates the current object state.
  419. Arguments:
  420. None.
  421. Return Value:
  422. BOOL - TRUE if state is valid, FALSE otherwise.
  423. --*/
  424. {
  425. if( m_hProv != CRYPT_NULL &&
  426. m_hKeyExchangeKey != CRYPT_NULL &&
  427. m_hSignatureKey != CRYPT_NULL ) {
  428. return TRUE;
  429. }
  430. return FALSE;
  431. } // IIS_CRYPTO_BASE::ValidateState
  432. BOOL
  433. IIS_CRYPTO_BASE::ValidateState2()
  434. /*++
  435. Routine Description:
  436. This debug-only routine validates the current object state.
  437. Arguments:
  438. None.
  439. Return Value:
  440. BOOL - TRUE if state is valid, FALSE otherwise.
  441. --*/
  442. {
  443. if( m_hProv != CRYPT_NULL )
  444. {
  445. return TRUE;
  446. }
  447. return FALSE;
  448. } // IIS_CRYPTO_BASE::ValidateState2
  449. #endif // DBG
  450. HRESULT
  451. IIS_CRYPTO_BASE::SafeImportSessionKeyBlob(
  452. OUT HCRYPTKEY * phSessionKey,
  453. IN PIIS_CRYPTO_BLOB pSessionKeyBlob,
  454. IN HCRYPTPROV hProv,
  455. IN HCRYPTKEY hSignatureKey
  456. )
  457. /*++
  458. Routine Description:
  459. This routine takes the specified session key blob and creates the
  460. corresponding session key, iff the encrypted session key can be
  461. decrypted and the digital signature can be validated.
  462. Arguments:
  463. phSessionKey - Receives a pointer to the newly created session key
  464. if successful.
  465. pSessionKeyBlob - Pointer to a key blob created with
  466. IISCryptoExportSessionKeyBlob().
  467. hProv - A handle to a crypto service provider.
  468. hSignatureKey - Handle to the encryption key to use when validating
  469. the digital signature.
  470. Return Value:
  471. HRESULT - Completion status, 0 if successful, !0 otherwise.
  472. --*/
  473. {
  474. HRESULT result;
  475. HCRYPTKEY sessionKey = CRYPT_NULL;
  476. HANDLE token = NULL;
  477. //
  478. // First, just call through to wrapper function. If it succeeds, cool.
  479. //
  480. result = ::IISCryptoImportSessionKeyBlob(
  481. &sessionKey,
  482. pSessionKeyBlob,
  483. hProv,
  484. hSignatureKey
  485. );
  486. if( FAILED(result) ) {
  487. HRESULT result2;
  488. //
  489. // Bummer. If the current thread has an impersonation token, then
  490. // temporarily remove it and retry the operation.
  491. //
  492. result2 = GetThreadImpersonationToken( &token );
  493. if( SUCCEEDED(result2) && token != NULL ) {
  494. result2 = SetThreadImpersonationToken( NULL );
  495. if( SUCCEEDED(result2) ) {
  496. result2 = ::IISCryptoImportSessionKeyBlob(
  497. &sessionKey,
  498. pSessionKeyBlob,
  499. hProv,
  500. hSignatureKey
  501. );
  502. if( SUCCEEDED(result2) ) {
  503. result = result2;
  504. }
  505. //
  506. // Restore the original impersonation token.
  507. //
  508. result2 = SetThreadImpersonationToken( token );
  509. DBG_ASSERT( SUCCEEDED(result2) );
  510. }
  511. //
  512. // Close the token handle.
  513. //
  514. DBG_REQUIRE( CloseHandle( token ) );
  515. }
  516. }
  517. if( SUCCEEDED(result) ) {
  518. DBG_ASSERT( sessionKey != NULL );
  519. *phSessionKey = sessionKey;
  520. }
  521. return result;
  522. } // IIS_CRYPTO_BASE::SafeImportSessionKeyBlob
  523. HRESULT
  524. IIS_CRYPTO_BASE::SafeImportSessionKeyBlob2(
  525. OUT HCRYPTKEY * phSessionKey,
  526. IN PIIS_CRYPTO_BLOB pSessionKeyBlob,
  527. IN HCRYPTPROV hProv,
  528. IN LPSTR pszPasswd
  529. )
  530. /*++
  531. Routine Description:
  532. This routine takes the specified session key blob and creates the
  533. corresponding session key, iff the encrypted session key can be
  534. decrypted.
  535. Arguments:
  536. phSessionKey - Receives a pointer to the newly created session key
  537. if successful.
  538. pSessionKeyBlob - Pointer to a key blob created with
  539. IISCryptoExportSessionKeyBlob().
  540. hProv - A handle to a crypto service provider.
  541. pszPasswd - The password to use to encrypt the session key.
  542. Return Value:
  543. HRESULT - Completion status, 0 if successful, !0 otherwise.
  544. --*/
  545. {
  546. HRESULT result;
  547. HCRYPTKEY sessionKey = CRYPT_NULL;
  548. HANDLE token = NULL;
  549. //
  550. // First, just call through to wrapper function. If it succeeds, cool.
  551. //
  552. result = ::IISCryptoImportSessionKeyBlob2(
  553. &sessionKey,
  554. pSessionKeyBlob,
  555. hProv,
  556. pszPasswd
  557. );
  558. if( FAILED(result) ) {
  559. HRESULT result2;
  560. //
  561. // Bummer. If the current thread has an impersonation token, then
  562. // temporarily remove it and retry the operation.
  563. //
  564. result2 = GetThreadImpersonationToken( &token );
  565. if( SUCCEEDED(result2) && token != NULL ) {
  566. result2 = SetThreadImpersonationToken( NULL );
  567. if( SUCCEEDED(result2) ) {
  568. result2 = ::IISCryptoImportSessionKeyBlob2(
  569. &sessionKey,
  570. pSessionKeyBlob,
  571. hProv,
  572. pszPasswd
  573. );
  574. if( SUCCEEDED(result2) ) {
  575. result = result2;
  576. }
  577. //
  578. // Restore the original impersonation token.
  579. //
  580. result2 = SetThreadImpersonationToken( token );
  581. DBG_ASSERT( SUCCEEDED(result2) );
  582. }
  583. //
  584. // Close the token handle.
  585. //
  586. DBG_REQUIRE( CloseHandle( token ) );
  587. }
  588. }
  589. if( SUCCEEDED(result) ) {
  590. DBG_ASSERT( sessionKey != NULL );
  591. *phSessionKey = sessionKey;
  592. }
  593. return result;
  594. } // IIS_CRYPTO_BASE::SafeImportSessionKeyBlob2
  595. HRESULT
  596. IIS_CRYPTO_BASE::SafeExportSessionKeyBlob(
  597. OUT PIIS_CRYPTO_BLOB * ppSessionKeyBlob,
  598. IN HCRYPTPROV hProv,
  599. IN HCRYPTKEY hSessionKey,
  600. IN HCRYPTKEY hKeyExchangeKey
  601. )
  602. /*++
  603. Routine Description:
  604. This routine exports a session key into a secure session key blob.
  605. The blob contains the session key (encrypted with the specified
  606. private key exchange key) and a digital signature (also encrypted).
  607. Arguments:
  608. ppSessionKeyBlob - Will receive a pointer to the newly created
  609. session key blob if successful.
  610. hProv - A handle to a crypto service provider.
  611. hSessionKey - The key to export.
  612. hKeyExchangeKey - The key to use when encrypting the session key.
  613. Return Value:
  614. HRESULT - Completion status, 0 if successful, !0 otherwise.
  615. --*/
  616. {
  617. HRESULT result;
  618. PIIS_CRYPTO_BLOB sessionKeyBlob = NULL;
  619. HANDLE token = NULL;
  620. //
  621. // First, just call through to wrapper function. If it succeeds, cool.
  622. //
  623. result = ::IISCryptoExportSessionKeyBlob(
  624. &sessionKeyBlob,
  625. hProv,
  626. hSessionKey,
  627. hKeyExchangeKey
  628. );
  629. if( FAILED(result) ) {
  630. HRESULT result2;
  631. //
  632. // Bummer. If the current thread has an impersonation token, then
  633. // temporarily remove it and retry the operation.
  634. //
  635. result2 = GetThreadImpersonationToken( &token );
  636. if( SUCCEEDED(result2) && token != NULL ) {
  637. result2 = SetThreadImpersonationToken( NULL );
  638. if( SUCCEEDED(result2) ) {
  639. result2 = ::IISCryptoExportSessionKeyBlob(
  640. &sessionKeyBlob,
  641. hProv,
  642. hSessionKey,
  643. hKeyExchangeKey
  644. );
  645. if( SUCCEEDED(result2) ) {
  646. result = result2;
  647. }
  648. //
  649. // Restore the original impersonation token.
  650. //
  651. result2 = SetThreadImpersonationToken( token );
  652. DBG_ASSERT( SUCCEEDED(result2) );
  653. }
  654. //
  655. // Close the token handle.
  656. //
  657. DBG_REQUIRE( CloseHandle( token ) );
  658. }
  659. }
  660. if( SUCCEEDED(result) ) {
  661. DBG_ASSERT( sessionKeyBlob != NULL );
  662. *ppSessionKeyBlob = sessionKeyBlob;
  663. }
  664. return result;
  665. } // IIS_CRYPTO_BASE::SafeExportSessionKeyBlob
  666. HRESULT
  667. IIS_CRYPTO_BASE::SafeExportSessionKeyBlob2(
  668. OUT PIIS_CRYPTO_BLOB * ppSessionKeyBlob,
  669. IN HCRYPTPROV hProv,
  670. IN HCRYPTKEY hSessionKey,
  671. IN LPSTR pszPasswd
  672. )
  673. /*++
  674. Routine Description:
  675. This routine exports a session key into a secure session key blob.
  676. The blob contains the session key (encrypted with the specified
  677. password) and a digital signature (also encrypted).
  678. Arguments:
  679. ppSessionKeyBlob - Will receive a pointer to the newly created
  680. session key blob if successful.
  681. hProv - A handle to a crypto service provider.
  682. hSessionKey - The key to export.
  683. pszPasswd - The password to use to encrypt the session key.
  684. Return Value:
  685. HRESULT - Completion status, 0 if successful, !0 otherwise.
  686. --*/
  687. {
  688. HRESULT result;
  689. PIIS_CRYPTO_BLOB sessionKeyBlob = NULL;
  690. HANDLE token = NULL;
  691. //
  692. // First, just call through to wrapper function. If it succeeds, cool.
  693. //
  694. result = ::IISCryptoExportSessionKeyBlob2(
  695. &sessionKeyBlob,
  696. hProv,
  697. hSessionKey,
  698. pszPasswd
  699. );
  700. if( FAILED(result) ) {
  701. HRESULT result2;
  702. //
  703. // Bummer. If the current thread has an impersonation token, then
  704. // temporarily remove it and retry the operation.
  705. //
  706. result2 = GetThreadImpersonationToken( &token );
  707. if( SUCCEEDED(result2) && token != NULL ) {
  708. result2 = SetThreadImpersonationToken( NULL );
  709. if( SUCCEEDED(result2) ) {
  710. result2 = ::IISCryptoExportSessionKeyBlob2(
  711. &sessionKeyBlob,
  712. hProv,
  713. hSessionKey,
  714. pszPasswd
  715. );
  716. if( SUCCEEDED(result2) ) {
  717. result = result2;
  718. }
  719. //
  720. // Restore the original impersonation token.
  721. //
  722. result2 = SetThreadImpersonationToken( token );
  723. DBG_ASSERT( SUCCEEDED(result2) );
  724. }
  725. //
  726. // Close the token handle.
  727. //
  728. DBG_REQUIRE( CloseHandle( token ) );
  729. }
  730. }
  731. if( SUCCEEDED(result) ) {
  732. DBG_ASSERT( sessionKeyBlob != NULL );
  733. *ppSessionKeyBlob = sessionKeyBlob;
  734. }
  735. return result;
  736. } // IIS_CRYPTO_BASE::SafeExportSessionKeyBlob
  737. HRESULT
  738. IIS_CRYPTO_BASE::SafeEncryptDataBlob(
  739. OUT PIIS_CRYPTO_BLOB * ppDataBlob,
  740. IN PVOID pBuffer,
  741. IN DWORD dwBufferLength,
  742. IN DWORD dwRegType,
  743. IN HCRYPTPROV hProv,
  744. IN HCRYPTKEY hSessionKey
  745. )
  746. /*++
  747. Routine Description:
  748. This routine encrypts a block of data, resulting in a data blob.
  749. The data blob contains the encrypted data and a digital signature
  750. validating the data.
  751. Arguments:
  752. ppDataBlob - Receives a pointer to the newly created data blob if
  753. successful.
  754. pBuffer - The buffer to encrypt.
  755. dwBufferLength - The length of the buffer.
  756. dwRegType - The REG_* type to associate with this data.
  757. hProv - A handle to a crypto service provider.
  758. hSessionKey - The key used to encrypt the data.
  759. Return Value:
  760. HRESULT - Completion status, 0 if successful, !0 otherwise.
  761. --*/
  762. {
  763. HRESULT result;
  764. PIIS_CRYPTO_BLOB dataBlob = NULL;
  765. HANDLE token = NULL;
  766. //
  767. // First, just call through to wrapper function. If it succeeds, cool.
  768. //
  769. result = ::IISCryptoEncryptDataBlob(
  770. &dataBlob,
  771. pBuffer,
  772. dwBufferLength,
  773. dwRegType,
  774. hProv,
  775. hSessionKey
  776. );
  777. if( FAILED(result) ) {
  778. HRESULT result2;
  779. //
  780. // Bummer. If the current thread has an impersonation token, then
  781. // temporarily remove it and retry the operation.
  782. //
  783. result2 = GetThreadImpersonationToken( &token );
  784. if( SUCCEEDED(result2) && token != NULL ) {
  785. result2 = SetThreadImpersonationToken( NULL );
  786. if( SUCCEEDED(result2) ) {
  787. result2 = ::IISCryptoEncryptDataBlob(
  788. &dataBlob,
  789. pBuffer,
  790. dwBufferLength,
  791. dwRegType,
  792. hProv,
  793. hSessionKey
  794. );
  795. if( SUCCEEDED(result2) ) {
  796. result = result2;
  797. }
  798. //
  799. // Restore the original impersonation token.
  800. //
  801. result2 = SetThreadImpersonationToken( token );
  802. DBG_ASSERT( SUCCEEDED(result2) );
  803. }
  804. //
  805. // Close the token handle.
  806. //
  807. DBG_REQUIRE( CloseHandle( token ) );
  808. }
  809. }
  810. if( SUCCEEDED(result) ) {
  811. DBG_ASSERT( dataBlob != NULL );
  812. *ppDataBlob = dataBlob;
  813. }
  814. return result;
  815. } // IIS_CRYPTO_BASE::SafeEncryptDataBlob
  816. HRESULT
  817. IIS_CRYPTO_BASE::SafeEncryptDataBlob2(
  818. OUT PIIS_CRYPTO_BLOB * ppDataBlob,
  819. IN PVOID pBuffer,
  820. IN DWORD dwBufferLength,
  821. IN DWORD dwRegType,
  822. IN HCRYPTPROV hProv,
  823. IN HCRYPTKEY hSessionKey
  824. )
  825. /*++
  826. Routine Description:
  827. This routine encrypts a block of data, resulting in a data blob.
  828. The data blob contains the encrypted data and a digital signature
  829. validating the data.
  830. Arguments:
  831. ppDataBlob - Receives a pointer to the newly created data blob if
  832. successful.
  833. pBuffer - The buffer to encrypt.
  834. dwBufferLength - The length of the buffer.
  835. dwRegType - The REG_* type to associate with this data.
  836. hProv - A handle to a crypto service provider.
  837. hSessionKey - The key used to encrypt the data.
  838. Return Value:
  839. HRESULT - Completion status, 0 if successful, !0 otherwise.
  840. --*/
  841. {
  842. HRESULT result;
  843. PIIS_CRYPTO_BLOB dataBlob = NULL;
  844. HANDLE token = NULL;
  845. //
  846. // First, just call through to wrapper function. If it succeeds, cool.
  847. //
  848. result = ::IISCryptoEncryptDataBlob2(
  849. &dataBlob,
  850. pBuffer,
  851. dwBufferLength,
  852. dwRegType,
  853. hProv,
  854. hSessionKey
  855. );
  856. if( FAILED(result) ) {
  857. HRESULT result2;
  858. //
  859. // Bummer. If the current thread has an impersonation token, then
  860. // temporarily remove it and retry the operation.
  861. //
  862. result2 = GetThreadImpersonationToken( &token );
  863. if( SUCCEEDED(result2) && token != NULL ) {
  864. result2 = SetThreadImpersonationToken( NULL );
  865. if( SUCCEEDED(result2) ) {
  866. result2 = ::IISCryptoEncryptDataBlob2(
  867. &dataBlob,
  868. pBuffer,
  869. dwBufferLength,
  870. dwRegType,
  871. hProv,
  872. hSessionKey
  873. );
  874. if( SUCCEEDED(result2) ) {
  875. result = result2;
  876. }
  877. //
  878. // Restore the original impersonation token.
  879. //
  880. result2 = SetThreadImpersonationToken( token );
  881. DBG_ASSERT( SUCCEEDED(result2) );
  882. }
  883. //
  884. // Close the token handle.
  885. //
  886. DBG_REQUIRE( CloseHandle( token ) );
  887. }
  888. }
  889. if( SUCCEEDED(result) ) {
  890. DBG_ASSERT( dataBlob != NULL );
  891. *ppDataBlob = dataBlob;
  892. }
  893. return result;
  894. } // IIS_CRYPTO_BASE::SafeEncryptDataBlob2
  895. HRESULT
  896. IIS_CRYPTO_BASE::GetThreadImpersonationToken(
  897. OUT HANDLE * Token
  898. )
  899. /*++
  900. Routine Description:
  901. Gets the impersonation token for the current thread.
  902. Arguments:
  903. Token - Receives a handle to the impersonation token if successful.
  904. If successful, it is the caller's responsibility to CloseHandle()
  905. the token when no longer needed.
  906. Return Value:
  907. HRESULT - 0 if successful, !0 otherwise.
  908. --*/
  909. {
  910. HRESULT result = NO_ERROR;
  911. DBG_ASSERT( Token != NULL );
  912. //
  913. // Open the token.
  914. //
  915. if( !OpenThreadToken(
  916. GetCurrentThread(),
  917. TOKEN_ALL_ACCESS,
  918. TRUE,
  919. Token
  920. ) ) {
  921. DWORD err = GetLastError();
  922. //
  923. // There are a couple of "expected" errors here:
  924. //
  925. // ERROR_NO_TOKEN - The thread has no impersonation token.
  926. // ERROR_CALL_NOT_IMPLEMENTED - We're probably on Win9x.
  927. // ERROR_NOT_SUPPORTED - Ditto.
  928. //
  929. // If OpenThreadToken() failed with any of the above error codes,
  930. // then succeed the call, but return a NULL token handle.
  931. //
  932. if( err != ERROR_NO_TOKEN &&
  933. err != ERROR_CALL_NOT_IMPLEMENTED &&
  934. err != ERROR_NOT_SUPPORTED ) {
  935. result = HRESULT_FROM_WIN32(err);
  936. }
  937. *Token = NULL;
  938. }
  939. return result;
  940. } // IIS_CRYPTO_BASE::GetThreadImpersonationToken
  941. HRESULT
  942. IIS_CRYPTO_BASE::SetThreadImpersonationToken(
  943. IN HANDLE Token
  944. )
  945. /*++
  946. Routine Description:
  947. Sets the impersonation token for the current thread.
  948. Arguments:
  949. Token - A handle to the impersonation token.
  950. Return Value:
  951. HRESULT - 0 if successful, !0 otherwise.
  952. --*/
  953. {
  954. HRESULT result = NO_ERROR;
  955. //
  956. // Set it.
  957. //
  958. if( !SetThreadToken(
  959. NULL,
  960. Token
  961. ) ) {
  962. DWORD err = GetLastError();
  963. result = HRESULT_FROM_WIN32(err);
  964. }
  965. return result;
  966. } // IIS_CRYPTO_BASE::SetThreadImpersonationToken
  967. BOOL
  968. IIS_CRYPTO_BASE::AmIRunningInTheLocalSystemContext(
  969. VOID
  970. )
  971. /*++
  972. Routine Description:
  973. Determines if the current process is running in the local system
  974. security context.
  975. Arguments:
  976. None.
  977. Return Value:
  978. BOOL - TRUE if we're running in the local system context, FALSE
  979. otherwise.
  980. --*/
  981. {
  982. BOOL result;
  983. HANDLE token;
  984. DWORD lengthRequired;
  985. PTOKEN_USER tokenInfo;
  986. PSID systemSid;
  987. SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
  988. //
  989. // Setup local so we know how to cleanup on exit.
  990. //
  991. result = FALSE; // until proven otherwise...
  992. token = NULL;
  993. tokenInfo = NULL;
  994. systemSid = NULL;
  995. //
  996. // Get a handle to the current process token.
  997. //
  998. if( !OpenProcessToken(
  999. GetCurrentProcess(), // ProcessHandle
  1000. TOKEN_READ, // DesiredAccess
  1001. &token // TokenHandle
  1002. ) ) {
  1003. goto cleanup;
  1004. }
  1005. //
  1006. // Determine the length of the token information, then allocate
  1007. // a buffer and read the info.
  1008. //
  1009. if( !GetTokenInformation(
  1010. token, // TokenHandle
  1011. TokenUser, // TokenInformationClass
  1012. NULL, // TokenInformation
  1013. 0, // TokenInformationLength
  1014. &lengthRequired // ReturnLength
  1015. ) ) {
  1016. goto cleanup;
  1017. }
  1018. tokenInfo = (PTOKEN_USER)new char[lengthRequired];
  1019. if( tokenInfo == NULL ) {
  1020. goto cleanup;
  1021. }
  1022. if( !GetTokenInformation(
  1023. token, // TokenHandle
  1024. TokenUser, // TokenInformationClass
  1025. tokenInfo, // TokenInformation
  1026. lengthRequired, // TokenInformationLength
  1027. &lengthRequired // ReturnLength
  1028. ) ) {
  1029. goto cleanup;
  1030. }
  1031. //
  1032. // OK, we have the token. Now build the system SID so we can compare
  1033. // it with the one stored in the token.
  1034. //
  1035. if( !AllocateAndInitializeSid(
  1036. &ntAuthority, // pIdentifierAuthority
  1037. 1, // nSubAuthorityCount
  1038. SECURITY_LOCAL_SYSTEM_RID, // nSubAuthority0
  1039. 0, // nSubAuthority1
  1040. 0, // nSubAuthority2
  1041. 0, // nSubAuthority3
  1042. 0, // nSubAuthority4
  1043. 0, // nSubAuthority5
  1044. 0, // nSubAuthority6
  1045. 0, // nSubAuthority7
  1046. &systemSid // pSid
  1047. ) ) {
  1048. goto cleanup;
  1049. }
  1050. //
  1051. // Now that we have the SID from the token and our hand-built
  1052. // local system SID, we can compare them.
  1053. //
  1054. result = EqualSid(
  1055. tokenInfo->User.Sid, // pSid1
  1056. systemSid // pSid2
  1057. );
  1058. cleanup:
  1059. if( systemSid != NULL ) {
  1060. FreeSid( systemSid );
  1061. }
  1062. if( tokenInfo != NULL ) {
  1063. delete[] tokenInfo;
  1064. }
  1065. if( token != NULL ) {
  1066. CloseHandle( token );
  1067. }
  1068. return result;
  1069. } // IIS_CRYPTO_BASE::AmIRunningInTheLocalSystemContext