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.

675 lines
20 KiB

  1. // CardFinder.cpp -- CardFinder class implementation
  2. // (c) Copyright Schlumberger Technology Corp., unpublished work, created
  3. // 1999. This computer program includes Confidential, Proprietary
  4. // Information and is a Trade Secret of Schlumberger Technology Corp. All
  5. // use, disclosure, and/or reproduction is prohibited unless authorized
  6. // in writing. All Rights Reserved.
  7. #if defined(_UNICODE)
  8. #if !defined(UNICODE)
  9. #define UNICODE
  10. #endif //!UNICODE
  11. #endif //_UNICODE
  12. #if defined(UNICODE)
  13. #if !defined(_UNICODE)
  14. #define _UNICODE
  15. #endif //!_UNICODE
  16. #endif //UNICODE
  17. #include "StdAfx.h"
  18. #include <string>
  19. #include <numeric>
  20. #include <Windows.h>
  21. #include <WinUser.h>
  22. #include <scuOsExc.h>
  23. #include <scuCast.h>
  24. #include "PromptUser.h"
  25. #include "CardFinder.h"
  26. #include "CspProfile.h"
  27. #include "StResource.h"
  28. #include "ExceptionContext.h"
  29. #include "Blob.h"
  30. using namespace std;
  31. using namespace scu;
  32. using namespace cci;
  33. using namespace ProviderProfile;
  34. using CardFinder::DialogDisplayMode;
  35. /////////////////////////// LOCAL/HELPER /////////////////////////////////
  36. namespace
  37. {
  38. // Lengths as specified by OPENCARDNAME
  39. size_t const cMaxCardNameLength = 256;
  40. size_t const cMaxReaderNameLength = 256;
  41. // In a preemptive, multi-threaded, environment it's assumed
  42. // access to these scratch buffers does not need to be mutually
  43. // exclusive.
  44. TCHAR CardNamesScratchBuffer[cMaxCardNameLength];
  45. TCHAR ReaderNamesScratchBuffer[cMaxReaderNameLength];
  46. DWORD
  47. AsDialogFlag(DialogDisplayMode ddm)
  48. {
  49. DWORD dwDialogFlag;
  50. switch (ddm)
  51. {
  52. case CardFinder::ddmNever:
  53. dwDialogFlag = SC_DLG_NO_UI;
  54. break;
  55. case CardFinder::ddmIfNecessary:
  56. dwDialogFlag = SC_DLG_MINIMAL_UI;
  57. break;
  58. case CardFinder::ddmAlways:
  59. dwDialogFlag = SC_DLG_FORCE_UI;
  60. break;
  61. default:
  62. throw scu::OsException(E_INVALIDARG);
  63. }
  64. return dwDialogFlag;
  65. }
  66. vector<string> &
  67. CardNameAccumulator(vector<string> &rvs,
  68. CardProfile &rcp)
  69. {
  70. rvs.push_back(rcp.RegistryName());
  71. return rvs;
  72. }
  73. vector<CString>
  74. csCardNameAccumulator(vector<CString> &rvs,
  75. CardProfile &rcp)
  76. {
  77. rvs.push_back(rcp.csRegistryName());
  78. return rvs;
  79. }
  80. } // namespace
  81. /////////////////////////// PUBLIC /////////////////////////////////
  82. // Types
  83. // C'tors/D'tors
  84. CardFinder::CardFinder(DialogDisplayMode ddm,
  85. HWND hwnd,
  86. CString const &rsDialogTitle)
  87. : m_sDialogTitle(rsDialogTitle),
  88. m_ddm(ddm),
  89. m_hwnd(hwnd),
  90. m_apmszSupportedCards(),
  91. m_opcnDlgCtrl(),
  92. #if SLBCSP_USE_SCARDUIDLGSELECTCARD
  93. m_opcnCriteria(),
  94. m_sInsertPrompt(StringResource(IDS_INS_SLB_CRYPTO_CARD).AsCString()),
  95. #endif
  96. m_cspec(),
  97. m_hscardctx()
  98. {
  99. m_hscardctx.Establish();
  100. // TO DO: Since the CCI doesn't provide enough information about
  101. // the cards, the CSP creates its own version for CSP
  102. // registration. Rather than use the CCI's KnownCards routine to
  103. // get the card names, the CSPs version is used until the CCI
  104. // provides enough information.
  105. vector<CardProfile> vcp(CspProfile::Instance().Cards());
  106. m_apmszSupportedCards =
  107. auto_ptr<MultiStringZ>(new MultiStringZ(accumulate(vcp.begin(),
  108. vcp.end(),
  109. vector<CString>(),
  110. csCardNameAccumulator)));
  111. // Fill the Open Card Name Dialog, pvUserData which is set by
  112. // DoFind.
  113. m_opcnDlgCtrl.dwStructSize = sizeof(m_opcnDlgCtrl); // REQUIRED
  114. m_opcnDlgCtrl.hSCardContext = m_hscardctx.AsSCARDCONTEXT(); // REQUIRED
  115. m_opcnDlgCtrl.hwndOwner = m_hwnd; // OPTIONAL
  116. m_opcnDlgCtrl.dwFlags = AsDialogFlag(DisplayMode()); // OPTIONAL -- default is SC_DLG_MINIMAL_UI
  117. m_opcnDlgCtrl.lpstrTitle = (LPCTSTR)m_sDialogTitle; // OPTIONAL
  118. m_opcnDlgCtrl.dwShareMode = SCARD_SHARE_SHARED; // OPTIONAL - if lpfnConnect is NULL, dwShareMode and
  119. m_opcnDlgCtrl.dwPreferredProtocols = SCARD_PROTOCOL_T0; // OPTIONAL dwPreferredProtocols will be used to
  120. // connect to the selected card
  121. m_opcnDlgCtrl.lpstrRdr = ReaderNamesScratchBuffer; // REQUIRED [IN|OUT] Name of selected reader
  122. m_opcnDlgCtrl.nMaxRdr = sizeof ReaderNamesScratchBuffer /
  123. sizeof *ReaderNamesScratchBuffer; // REQUIRED [IN|OUT]
  124. m_opcnDlgCtrl.lpstrCard = CardNamesScratchBuffer; // REQUIRED [IN|OUT] Name of selected card
  125. m_opcnDlgCtrl.nMaxCard = sizeof CardNamesScratchBuffer /
  126. sizeof *CardNamesScratchBuffer; // REQUIRED [IN|OUT]
  127. m_opcnDlgCtrl.dwActiveProtocol = 0; // [OUT] set only if dwShareMode not NULL
  128. m_opcnDlgCtrl.hCardHandle = NULL; // [OUT] set if a card connection was indicated
  129. CheckFn(IsValid);
  130. ConnectFn(Connect);
  131. DisconnectFn(Disconnect);
  132. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  133. m_opcnDlgCtrl.lpstrGroupNames = 0;
  134. m_opcnDlgCtrl.nMaxGroupNames = 0;
  135. m_opcnDlgCtrl.lpstrCardNames = (LPTSTR)m_apmszSupportedCards->csData();
  136. m_opcnDlgCtrl.nMaxCardNames = m_apmszSupportedCards->csLength();
  137. m_opcnDlgCtrl.rgguidInterfaces = 0;
  138. m_opcnDlgCtrl.cguidInterfaces = 0;
  139. #else
  140. m_opcnDlgCtrl.lpstrSearchDesc = (LPCTSTR)m_sInsertPrompt; // OPTIONAL (eg. "Please insert your <brandname> smart card.")
  141. m_opcnDlgCtrl.hIcon = NULL; // OPTIONAL 32x32 icon for your brand insignia
  142. m_opcnDlgCtrl.pOpenCardSearchCriteria = &m_opcnCriteria; // OPTIONAL
  143. m_opcnCriteria.dwStructSize = sizeof(m_opcnCriteria);
  144. m_opcnCriteria.lpstrGroupNames = 0; // OPTIONAL reader groups to include in
  145. m_opcnCriteria.nMaxGroupNames = 0; // search. NULL defaults to
  146. // SCard$DefaultReaders
  147. m_opcnCriteria.rgguidInterfaces = 0; // OPTIONAL requested interfaces
  148. m_opcnCriteria.cguidInterfaces = 0; // supported by card's SSP
  149. m_opcnCriteria.lpstrCardNames = (LPTSTR)m_apmszSupportedCards->csData(); // OPTIONAL requested card names; all cards w/
  150. m_opcnCriteria.nMaxCardNames = m_apmszSupportedCards->csLength(); // matching ATRs will be accepted
  151. m_opcnCriteria.dwShareMode = SCARD_SHARE_SHARED; // OPTIONAL must be set if lpfnCheck is not null
  152. m_opcnCriteria.dwPreferredProtocols = SCARD_PROTOCOL_T0; // OPTIONAL
  153. #endif // !SLBCSP_USE_SCARDUIDLGSELECTCARD
  154. }
  155. CardFinder::~CardFinder()
  156. {}
  157. // Operators
  158. // Operations
  159. Secured<HCardContext>
  160. CardFinder::Find(CSpec const &rcsReader)
  161. {
  162. DoFind(rcsReader);
  163. return CardFound();
  164. }
  165. // Access
  166. DialogDisplayMode
  167. CardFinder::DisplayMode() const
  168. {
  169. return m_ddm;
  170. }
  171. HWND
  172. CardFinder::Window() const
  173. {
  174. return m_hwnd;
  175. }
  176. // Predicates
  177. // Static Variables
  178. /////////////////////////// PROTECTED /////////////////////////////////
  179. // C'tors/D'tors
  180. // Operators
  181. // Operations
  182. void
  183. CardFinder::CardFound(Secured<HCardContext> const &rshcardctx)
  184. {
  185. m_shcardctx = rshcardctx;
  186. }
  187. SCARDHANDLE
  188. CardFinder::DoConnect(string const &rsSelectedReader)
  189. {
  190. SCARDHANDLE hSCard = reinterpret_cast<SCARDHANDLE>(INVALID_HANDLE_VALUE);
  191. // If the reader spec's match...
  192. if (CSpec::Equiv(CardSpec().Reader(), rsSelectedReader))
  193. {
  194. HCardContext hcardctx(rsSelectedReader);
  195. CardFound(Secured<HCardContext>(hcardctx));
  196. }
  197. else
  198. {
  199. CardFound(Secured<HCardContext>(0));
  200. hSCard = 0;
  201. }
  202. return hSCard;
  203. }
  204. void
  205. CardFinder::DoDisconnect()
  206. {
  207. CardFound(Secured<HCardContext>(0));
  208. }
  209. void
  210. CardFinder::DoFind(CSpec const &rcspec)
  211. {
  212. m_cspec = rcspec;
  213. // Bind to the callers call context
  214. UserData(reinterpret_cast<void *>(this));
  215. // Cache to override later
  216. OpenCardNameType opencardname(m_opcnDlgCtrl);
  217. bool fContinue = true;
  218. do
  219. {
  220. DWORD dwStatus(SelectCard(opencardname));
  221. DoProcessSelection(dwStatus, opencardname, fContinue);
  222. } while (fContinue);
  223. OnError();
  224. }
  225. void
  226. CardFinder::DoOnError()
  227. {
  228. scu::Exception const *pexc = Exception();
  229. if (pexc && (ddmNever != DisplayMode()))
  230. {
  231. switch (pexc->Facility())
  232. {
  233. case scu::Exception::fcOS:
  234. {
  235. OsException const *pOsExc =
  236. DownCast<OsException const *>(pexc);
  237. switch (pOsExc->Cause())
  238. {
  239. case SCARD_E_UNSUPPORTED_FEATURE:
  240. YNPrompt(IDS_NOT_CAPI_ENABLED);
  241. ClearException();
  242. break;
  243. case ERROR_INVALID_PARAMETER:
  244. YNPrompt(IDS_READER_NOT_MATCH);
  245. ClearException();
  246. break;
  247. default:
  248. break;
  249. }
  250. }
  251. break;
  252. case scu::Exception::fcCCI:
  253. {
  254. cci::Exception const *pCciExc =
  255. DownCast<cci::Exception const *>(pexc);
  256. if (ccNotPersonalized == pCciExc->Cause())
  257. {
  258. YNPrompt(IDS_CARD_NOT_INIT);
  259. ClearException();
  260. }
  261. }
  262. break;
  263. default:
  264. break;
  265. }
  266. }
  267. }
  268. void
  269. CardFinder::DoProcessSelection(DWORD dwStatus,
  270. OpenCardNameType &ropencardname,
  271. bool &rfContinue)
  272. {
  273. rfContinue = true;
  274. // Handle the error conditions
  275. if (Exception() &&
  276. !((SCARD_E_CANCELLED == dwStatus) ||
  277. (SCARD_W_CANCELLED_BY_USER == dwStatus)))
  278. rfContinue = false;
  279. else
  280. {
  281. WorkaroundOpenCardDefect(ropencardname, dwStatus);
  282. if (SCARD_S_SUCCESS != dwStatus)
  283. {
  284. // Translate the cancellation error as needed.
  285. if ((SCARD_E_CANCELLED == dwStatus) ||
  286. (SCARD_W_CANCELLED_BY_USER == dwStatus))
  287. {
  288. if (ddmNever == DisplayMode())
  289. {
  290. if ((SCARD_E_CANCELLED == dwStatus) &&
  291. !CardFound())
  292. // SCARD_E_NO_SMARTCARD is returned
  293. // because the Smart Card Dialog will
  294. // return SCARD_E_CANCELLED when the GUI
  295. // is not allowed and there is no smart
  296. // card in the reader, so the error is
  297. // translated here.
  298. dwStatus = SCARD_E_NO_SMARTCARD;
  299. else
  300. // NTE_BAD_KEYSET is returned because some version
  301. // of a Microsoft application would go into an
  302. // infinite loop when ERROR_CANCELLED was returned
  303. // under these conditions. Doug Barlow at
  304. // Microsoft noticed the behaviour and implemented
  305. // the workaround. It's unclear what that
  306. // application was and if the workaround remains
  307. // necessary.
  308. dwStatus = NTE_BAD_KEYSET; // how can this happen?
  309. }
  310. else
  311. dwStatus = ERROR_CANCELLED;
  312. }
  313. Exception(auto_ptr<scu::Exception const>(scu::OsException(dwStatus).Clone()));
  314. rfContinue = false;
  315. }
  316. else
  317. rfContinue = false;
  318. }
  319. if (SC_DLG_MINIMAL_UI == ropencardname.dwFlags)
  320. ropencardname.dwFlags = SC_DLG_FORCE_UI;
  321. }
  322. void
  323. CardFinder::YNPrompt(UINT uID) const
  324. {
  325. int iResponse = PromptUser(Window(), uID, MB_YESNO | MB_ICONWARNING);
  326. switch (iResponse)
  327. {
  328. case IDABORT: // fall-through intentional
  329. case IDCANCEL:
  330. case IDNO:
  331. throw scu::OsException(ERROR_CANCELLED);
  332. break;
  333. case IDOK:
  334. case IDYES:
  335. case IDRETRY:
  336. break;
  337. case 0:
  338. throw scu::OsException(GetLastError());
  339. break;
  340. default:
  341. throw scu::OsException(ERROR_INTERNAL_ERROR);
  342. break;
  343. }
  344. }
  345. // Access
  346. CSpec const &
  347. CardFinder::CardSpec() const
  348. {
  349. return m_cspec;
  350. }
  351. Secured<HCardContext>
  352. CardFinder::CardFound() const
  353. {
  354. return m_shcardctx;
  355. }
  356. // Predicates
  357. bool
  358. CardFinder::DoIsValid()
  359. {
  360. Secured<HCardContext> shcardctx(CardFound());
  361. if (shcardctx &&
  362. !shcardctx->Card()->IsCAPIEnabled())
  363. throw scu::OsException(SCARD_E_UNSUPPORTED_FEATURE);
  364. return (shcardctx != 0);
  365. }
  366. // Static Variables
  367. /////////////////////////// PRIVATE /////////////////////////////////
  368. // C'tors/D'tors
  369. // Operators
  370. // Operations
  371. void
  372. CardFinder::CheckFn(LPOCNCHKPROC lpfnCheck)
  373. {
  374. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  375. m_opcnDlgCtrl.lpfnCheck = lpfnCheck;
  376. #else
  377. m_opcnCriteria.lpfnCheck = lpfnCheck;
  378. #endif
  379. }
  380. SCARDHANDLE __stdcall
  381. CardFinder::Connect(SCARDCONTEXT scardctx,
  382. LPTSTR szReader,
  383. LPTSTR mszCards,
  384. LPVOID lpvUserData)
  385. {
  386. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  387. CardFinder *pfinder =
  388. reinterpret_cast<CardFinder *>(lpvUserData);
  389. SCARDHANDLE hResult = reinterpret_cast<SCARDHANDLE>(INVALID_HANDLE_VALUE);
  390. EXCCTX_TRY
  391. {
  392. // Starting fresh, clear any earler exception
  393. pfinder->ClearException();
  394. string sSelectedReader(StringResource::AsciiFromUnicode(szReader));
  395. hResult =
  396. pfinder->DoConnect(sSelectedReader);
  397. }
  398. EXCCTX_CATCH(pfinder, false);
  399. return hResult;
  400. }
  401. void
  402. CardFinder::ConnectFn(LPOCNCONNPROC lpfnConnect)
  403. {
  404. m_opcnDlgCtrl.lpfnConnect = lpfnConnect;
  405. #if SLBCSP_USE_SCARDUIDLGSELECTCARD
  406. m_opcnCriteria.lpfnConnect = m_opcnDlgCtrl.lpfnConnect;
  407. #endif
  408. }
  409. void __stdcall
  410. CardFinder::Disconnect(SCARDCONTEXT scardctx,
  411. SCARDHANDLE hSCard,
  412. LPVOID lpvUserData)
  413. {
  414. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  415. CardFinder *pfinder =
  416. reinterpret_cast<CardFinder *>(lpvUserData);
  417. EXCCTX_TRY
  418. {
  419. pfinder->DoDisconnect();
  420. }
  421. EXCCTX_CATCH(pfinder, false);
  422. }
  423. void
  424. CardFinder::DisconnectFn(LPOCNDSCPROC lpfnDisconnect)
  425. {
  426. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  427. m_opcnDlgCtrl.lpfnDisconnect = lpfnDisconnect;
  428. #else
  429. m_opcnCriteria.lpfnDisconnect = lpfnDisconnect;
  430. #endif
  431. }
  432. void
  433. CardFinder::OnError()
  434. {
  435. if (Exception())
  436. {
  437. DoOnError();
  438. PropagateException();
  439. }
  440. }
  441. DWORD
  442. CardFinder::SelectCard(OpenCardNameType &ropcn)
  443. {
  444. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  445. return GetOpenCardName(&ropcn);
  446. #else
  447. return SCardUIDlgSelectCard(&ropcn);
  448. #endif
  449. }
  450. void
  451. CardFinder::UserData(void *pvUserData)
  452. {
  453. m_opcnDlgCtrl.pvUserData = pvUserData;
  454. #if SLBCSP_USE_SCARDUIDLGSELECTCARD
  455. m_opcnCriteria.pvUserData = m_opcnDlgCtrl.pvUserData;
  456. #endif
  457. }
  458. void
  459. CardFinder::WorkaroundOpenCardDefect(OpenCardNameType const &ropcnDlgCtrl,
  460. DWORD &rdwStatus)
  461. {
  462. // On systems using Smart Card Kit v1.0 and prior (in other words
  463. // systems prior to Windows 2000/NT 5.0), MS' GetOpenCardName
  464. // (scarddlg.dll) has a defect that manifests when the check
  465. // routine always returns FALSE. In this case, the common dialog
  466. // will call connect routine one additional time without calling
  467. // check or disconnect routine. Therefore upon return, it appears
  468. // a card match was found when there really wasn't. The
  469. // workaround is to make additional calls to the check routine
  470. // after the call to GetOpenCardName. If there isn't a match,
  471. // then the card is invalid and should act as if the card was not
  472. // connected. Fortunately, this workaround behaves correctly on
  473. // the good scarddlg.dll as well (post Smart Card Kit v1.0).
  474. if (SCARD_S_SUCCESS == rdwStatus)
  475. {
  476. try
  477. {
  478. LPOCNCHKPROC lpfnCheck = CheckFn();
  479. LPOCNDSCPROC lpfnDisconnect = DisconnectFn();
  480. if (CardFound() &&
  481. !lpfnCheck(ropcnDlgCtrl.hSCardContext, 0, this))
  482. lpfnDisconnect(ropcnDlgCtrl.hSCardContext, 0, this);
  483. if (!CardFound() &&
  484. (SC_DLG_MINIMAL_UI == ropcnDlgCtrl.dwFlags))
  485. {
  486. // A card didn't matched and the user wasn't actually
  487. // prompted, so force the smart card dialog to prompt
  488. // the user to select a card.
  489. lpfnDisconnect(ropcnDlgCtrl.hSCardContext, 0, this);
  490. OpenCardNameType opencardname = ropcnDlgCtrl;
  491. opencardname.dwFlags = SC_DLG_FORCE_UI;
  492. rdwStatus = SelectCard(opencardname);
  493. if ((SCARD_S_SUCCESS == rdwStatus) &&
  494. !Exception() &&
  495. !lpfnCheck(opencardname.hSCardContext, 0, this))
  496. lpfnDisconnect(opencardname.hSCardContext, 0, this);
  497. }
  498. }
  499. catch (...)
  500. {
  501. // propagate the exception here if one didn't occur in
  502. // one of the callback routines.
  503. if (!Exception())
  504. throw;
  505. }
  506. OnError();
  507. }
  508. }
  509. // Access
  510. LPOCNCHKPROC
  511. CardFinder::CheckFn() const
  512. {
  513. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  514. return m_opcnDlgCtrl.lpfnCheck;
  515. #else
  516. return m_opcnCriteria.lpfnCheck;
  517. #endif
  518. }
  519. LPOCNDSCPROC
  520. CardFinder::DisconnectFn() const
  521. {
  522. #if !SLBCSP_USE_SCARDUIDLGSELECTCARD
  523. return m_opcnDlgCtrl.lpfnDisconnect;
  524. #else
  525. return m_opcnCriteria.lpfnDisconnect;
  526. #endif
  527. }
  528. // Predicates
  529. BOOL __stdcall
  530. CardFinder::IsValid(SCARDCONTEXT scardctx,
  531. SCARDHANDLE hSCard,
  532. LPVOID lpvUserData)
  533. {
  534. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  535. CardFinder *pfinder =
  536. reinterpret_cast<CardFinder *>(lpvUserData);
  537. bool fResult = false;
  538. EXCCTX_TRY
  539. {
  540. fResult = pfinder->DoIsValid();
  541. }
  542. // Throwing the callback exception is optional because the
  543. // Microsoft Smart Card Dialog sensitive to throwing from the
  544. // IsValid callback, particularly when multiple readers are
  545. // connected.
  546. EXCCTX_CATCH(pfinder, false);
  547. return fResult
  548. ? TRUE
  549. : FALSE;
  550. }
  551. // Static Variables