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.

565 lines
12 KiB

  1. /*
  2. Copyright (c) 1997, Microsoft Corporation, all rights reserved
  3. Description:
  4. Smart card helper functions.
  5. History:
  6. 13 Dec 1997: Amanda Matlosz created original version.
  7. 12 May 1998: Vijay Baliga moved things around.
  8. */
  9. #undef UNICODE
  10. #include <winscard.h>
  11. #include <wincrypt.h>
  12. #include <rtutils.h>
  13. VOID
  14. EapTlsTrace(
  15. IN CHAR* Format,
  16. ...
  17. );
  18. typedef
  19. WINSCARDAPI LONG
  20. (WINAPI *GETOPENCARDNAMEA)(
  21. OPENCARDNAMEA*
  22. );
  23. GETOPENCARDNAMEA g_fnGetOpenCardNameA = NULL;
  24. HINSTANCE g_hInstanceScardDlg = NULL;
  25. /*
  26. Returns:
  27. Notes:
  28. */
  29. DWORD
  30. LoadScardDlgDll(
  31. VOID
  32. )
  33. {
  34. DWORD dwErr = NO_ERROR;
  35. if (NULL == g_hInstanceScardDlg)
  36. {
  37. g_hInstanceScardDlg = LoadLibrary("scarddlg.dll");
  38. }
  39. if (NULL == g_hInstanceScardDlg)
  40. {
  41. dwErr = GetLastError();
  42. EapTlsTrace("LoadLibrary(scarddlg.dll) failed and returned 0x%x",
  43. dwErr);
  44. goto LDone;
  45. }
  46. if (NULL == g_fnGetOpenCardNameA)
  47. {
  48. g_fnGetOpenCardNameA = (GETOPENCARDNAMEA)
  49. GetProcAddress(g_hInstanceScardDlg, "GetOpenCardNameA");
  50. }
  51. if (NULL == g_fnGetOpenCardNameA)
  52. {
  53. dwErr = GetLastError();
  54. EapTlsTrace("GetProcAddress(GetOpenCardNameA) failed and returned 0x%x",
  55. dwErr);
  56. goto LDone;
  57. }
  58. LDone:
  59. return(dwErr);
  60. }
  61. /*
  62. Returns:
  63. Notes:
  64. */
  65. VOID
  66. FreeScardDlgDll(
  67. VOID
  68. )
  69. {
  70. if (NULL != g_hInstanceScardDlg)
  71. {
  72. FreeLibrary(g_hInstanceScardDlg);
  73. g_hInstanceScardDlg = NULL;
  74. g_fnGetOpenCardNameA = NULL;
  75. }
  76. }
  77. /*
  78. Returns:
  79. Notes:
  80. */
  81. DWORD
  82. LocalCryptGetProvParamW(
  83. IN HCRYPTPROV hProv,
  84. IN DWORD dwParam,
  85. OUT WCHAR** ppwsz
  86. )
  87. {
  88. CHAR* psz = NULL;
  89. WCHAR* pwszTemp = NULL;
  90. DWORD cb;
  91. int count;
  92. BOOL fSuccess;
  93. DWORD dwErr = NO_ERROR;
  94. fSuccess = CryptGetProvParam(hProv, dwParam, NULL, &cb, 0);
  95. if (!fSuccess)
  96. {
  97. dwErr = GetLastError();
  98. EapTlsTrace("CryptGetProvParam failed and returned 0x%x", dwErr);
  99. goto LDone;
  100. }
  101. psz = (CHAR*)LocalAlloc(LPTR, cb);
  102. if (NULL == psz)
  103. {
  104. dwErr = GetLastError();
  105. EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
  106. goto LDone;
  107. }
  108. fSuccess = CryptGetProvParam(hProv, dwParam, (BYTE*)psz, &cb, 0);
  109. if (!fSuccess)
  110. {
  111. dwErr = GetLastError();
  112. EapTlsTrace("CryptGetProvParam failed and returned 0x%x", dwErr);
  113. goto LDone;
  114. }
  115. count = MultiByteToWideChar(CP_UTF8, 0, psz, -1, NULL, 0);
  116. if (0 == count)
  117. {
  118. dwErr = GetLastError();
  119. EapTlsTrace("MultiByteToWideChar(%s) failed: %d", psz, dwErr);
  120. goto LDone;
  121. }
  122. pwszTemp = (WCHAR*)LocalAlloc(LPTR, count * sizeof(WCHAR));
  123. if (NULL == pwszTemp)
  124. {
  125. dwErr = GetLastError();
  126. EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
  127. goto LDone;
  128. }
  129. count = MultiByteToWideChar(CP_UTF8, 0, psz, -1, pwszTemp, count);
  130. if (0 == count)
  131. {
  132. dwErr = GetLastError();
  133. EapTlsTrace("MultiByteToWideChar(%s) failed: %d", psz, dwErr);
  134. goto LDone;
  135. }
  136. *ppwsz = pwszTemp;
  137. pwszTemp = NULL;
  138. LDone:
  139. LocalFree(pwszTemp);
  140. LocalFree(psz);
  141. return(dwErr);
  142. }
  143. /*
  144. Returns:
  145. Notes:
  146. This internal routine generates a certificate context with (static)
  147. keyprov info suitable for CertStore-based operations.
  148. */
  149. DWORD
  150. BuildCertContext(
  151. IN HCRYPTPROV hProv,
  152. IN BYTE* pbCert,
  153. IN DWORD dwCertLen,
  154. OUT PCCERT_CONTEXT* ppCertContext
  155. )
  156. {
  157. CRYPT_KEY_PROV_INFO KeyProvInfo;
  158. WCHAR* pwszContainerName = NULL;
  159. WCHAR* pwszProviderName = NULL;
  160. BOOL fCertContextCreated = FALSE;
  161. BOOL fSuccess;
  162. DWORD dwErr = NO_ERROR;
  163. if ( (0 == hProv)
  164. || (NULL == pbCert)
  165. || (0 == dwCertLen))
  166. {
  167. dwErr = ERROR_INVALID_PARAMETER;
  168. goto LDone;
  169. }
  170. RTASSERT(NULL != ppCertContext);
  171. // Convert the certificate into a cert context.
  172. *ppCertContext = CertCreateCertificateContext(
  173. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  174. pbCert, dwCertLen);
  175. if (NULL == *ppCertContext)
  176. {
  177. dwErr = GetLastError();
  178. EapTlsTrace("CertCreateCertificateContext failed and returned 0x%x",
  179. dwErr);
  180. goto LDone;
  181. }
  182. fCertContextCreated = TRUE;
  183. // Associate cryptprovider w/ the private key property of this cert
  184. dwErr = LocalCryptGetProvParamW(hProv, PP_CONTAINER, &pwszContainerName);
  185. if (NO_ERROR != dwErr)
  186. {
  187. goto LDone;
  188. }
  189. EapTlsTrace("Container: %ws", pwszContainerName);
  190. dwErr = LocalCryptGetProvParamW(hProv, PP_NAME, &pwszProviderName);
  191. if (NO_ERROR != dwErr)
  192. {
  193. goto LDone;
  194. }
  195. EapTlsTrace("Provider: %ws", pwszProviderName);
  196. // Set the cert context properties to reflect the prov info
  197. KeyProvInfo.pwszContainerName = pwszContainerName;
  198. KeyProvInfo.pwszProvName = pwszProviderName;
  199. KeyProvInfo.dwProvType = PROV_RSA_FULL;
  200. KeyProvInfo.dwFlags = 0;
  201. KeyProvInfo.cProvParam = 0;
  202. KeyProvInfo.rgProvParam = NULL;
  203. KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
  204. fSuccess = CertSetCertificateContextProperty(
  205. *ppCertContext,
  206. CERT_KEY_PROV_INFO_PROP_ID,
  207. 0,
  208. (void *)&KeyProvInfo);
  209. if (!fSuccess)
  210. {
  211. dwErr = GetLastError();
  212. EapTlsTrace("CertSetCertificateContextProperty failed and returned "
  213. "0x%x", dwErr);
  214. goto LDone;
  215. }
  216. LDone:
  217. if (NO_ERROR != dwErr)
  218. {
  219. if (fCertContextCreated)
  220. {
  221. CertFreeCertificateContext(*ppCertContext);
  222. }
  223. *ppCertContext = NULL;
  224. }
  225. LocalFree(pwszContainerName);
  226. LocalFree(pwszProviderName);
  227. return(dwErr);
  228. }
  229. /*
  230. Returns:
  231. Notes:
  232. The "Select Card" common dialog is raised, then the certificate is read
  233. from the card, a certificate context complete with key prov info is
  234. migrated to the cert store and also returned to the caller.
  235. */
  236. DWORD
  237. GetCertFromCard(
  238. OUT PCCERT_CONTEXT* ppCertContext
  239. )
  240. {
  241. CHAR* pszReader = NULL;
  242. CHAR* pszCard = NULL;
  243. DWORD cbReaderOrCard = MAX_PATH;
  244. SCARDCONTEXT hContext = 0;
  245. OPENCARDNAMEA OpenCardName;
  246. CHAR* pszProviderName = NULL;
  247. DWORD cchProvider;
  248. HCRYPTPROV hProv = 0;
  249. HCRYPTKEY hKey = 0;
  250. DWORD cbCertLen;
  251. BYTE* pbCert = NULL;
  252. HCERTSTORE hCertStore = NULL;
  253. BOOL fSuccess;
  254. LONG lErr;
  255. DWORD dwErr = NO_ERROR;
  256. EapTlsTrace("GetCertFromCard");
  257. RTASSERT(NULL != ppCertContext);
  258. dwErr = LoadScardDlgDll();
  259. if (NO_ERROR != dwErr)
  260. {
  261. goto LDone;
  262. }
  263. pszReader = (BYTE*)LocalAlloc(LPTR, cbReaderOrCard);
  264. if (NULL == pszReader)
  265. {
  266. dwErr = GetLastError();
  267. EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
  268. goto LDone;
  269. }
  270. pszCard = (BYTE*)LocalAlloc(LPTR, cbReaderOrCard);
  271. if (NULL == pszCard)
  272. {
  273. dwErr = GetLastError();
  274. EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
  275. goto LDone;
  276. }
  277. lErr = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext);
  278. if (SCARD_S_SUCCESS != lErr)
  279. {
  280. dwErr = lErr;
  281. EapTlsTrace("SCardEstablishContext failed and returned 0x%x", dwErr);
  282. goto LDone;
  283. }
  284. ZeroMemory(&OpenCardName, sizeof(OpenCardName));
  285. OpenCardName.dwStructSize = sizeof(OpenCardName);
  286. OpenCardName.hSCardContext = hContext;
  287. OpenCardName.lpstrCardNames = NULL;
  288. OpenCardName.lpstrRdr = pszReader;
  289. OpenCardName.nMaxRdr = cbReaderOrCard;
  290. OpenCardName.lpstrCard = pszCard;
  291. OpenCardName.nMaxCard = cbReaderOrCard;
  292. OpenCardName.lpstrTitle = "Select smartcard";
  293. OpenCardName.dwFlags = SC_DLG_MINIMAL_UI;
  294. OpenCardName.dwShareMode = 0;
  295. OpenCardName.dwPreferredProtocols = 0;
  296. lErr = g_fnGetOpenCardNameA(&OpenCardName);
  297. if (SCARD_S_SUCCESS != lErr)
  298. {
  299. dwErr = lErr;
  300. EapTlsTrace("GetOpenCardNameA failed and returned 0x%x", dwErr);
  301. goto LDone;
  302. }
  303. EapTlsTrace("Reader: %s, Card: %s", pszReader, pszCard);
  304. RTASSERT(0 == OpenCardName.hCardHandle);
  305. pszProviderName = NULL;
  306. cchProvider = SCARD_AUTOALLOCATE;
  307. lErr = SCardGetCardTypeProviderNameA(hContext, OpenCardName.lpstrCard,
  308. SCARD_PROVIDER_CSP, (CHAR*) &pszProviderName, &cchProvider);
  309. if (SCARD_S_SUCCESS != lErr)
  310. {
  311. dwErr = lErr;
  312. EapTlsTrace("SCardGetCardTypeProviderNameA failed and returned 0x%x",
  313. dwErr);
  314. goto LDone;
  315. }
  316. if (NULL != pszProviderName)
  317. {
  318. EapTlsTrace("Provider: %s", pszProviderName);
  319. }
  320. // Load the CSP
  321. fSuccess = CryptAcquireContext(&hProv, NULL /* default container */,
  322. pszProviderName, PROV_RSA_FULL,
  323. CRYPT_SILENT /* or 0, to show CSP UI as needed */);
  324. if (!fSuccess)
  325. {
  326. dwErr = GetLastError();
  327. EapTlsTrace("CryptAcquireContext failed and returned 0x%x", dwErr);
  328. goto LDone;
  329. }
  330. // Get the key handle.
  331. fSuccess = CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey);
  332. if (!fSuccess)
  333. {
  334. dwErr = GetLastError();
  335. EapTlsTrace("CryptGetUserKey failed and returned 0x%x", dwErr);
  336. goto LDone;
  337. }
  338. // Upload the certificate.
  339. cbCertLen = 0;
  340. fSuccess = CryptGetKeyParam(hKey, KP_CERTIFICATE, NULL, &cbCertLen, 0);
  341. if (!fSuccess)
  342. {
  343. dwErr = GetLastError();
  344. if (ERROR_MORE_DATA != dwErr)
  345. {
  346. EapTlsTrace("CryptGetKeyParam(KP_CERTIFICATE) failed and returned "
  347. "0x%x", dwErr);
  348. goto LDone;
  349. }
  350. dwErr = NO_ERROR;
  351. }
  352. pbCert = (BYTE*)LocalAlloc(LPTR, cbCertLen);
  353. if (NULL == pbCert)
  354. {
  355. dwErr = GetLastError();
  356. EapTlsTrace("LocalAlloc failed and returned %d", dwErr);
  357. goto LDone;
  358. }
  359. fSuccess = CryptGetKeyParam(hKey, KP_CERTIFICATE, pbCert, &cbCertLen, 0);
  360. if (!fSuccess)
  361. {
  362. dwErr = GetLastError();
  363. EapTlsTrace("CryptGetKeyParam(KP_CERTIFICATE) failed and returned "
  364. "0x%x", dwErr);
  365. goto LDone;
  366. }
  367. // Get the cert context...
  368. dwErr = BuildCertContext(hProv, pbCert, cbCertLen, ppCertContext);
  369. if (NO_ERROR != dwErr)
  370. {
  371. goto LDone;
  372. }
  373. // ...and migrate it to the My store
  374. hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, hProv,
  375. CERT_SYSTEM_STORE_CURRENT_USER, "MY");
  376. if (NULL == hCertStore)
  377. {
  378. dwErr = GetLastError();
  379. EapTlsTrace("CertOpenStore failed and returned 0x%x", dwErr);
  380. goto LDone;
  381. }
  382. fSuccess = CertAddCertificateContextToStore(hCertStore, *ppCertContext,
  383. CERT_STORE_ADD_REPLACE_EXISTING, NULL);
  384. if (!fSuccess)
  385. {
  386. // This is OK. Don't return an error.
  387. EapTlsTrace("CertAddCertificateContextToStore failed and returned 0x%x",
  388. GetLastError());
  389. }
  390. LDone:
  391. LocalFree(pszReader);
  392. LocalFree(pszCard);
  393. LocalFree(pbCert);
  394. if (0 != hProv)
  395. {
  396. CryptReleaseContext(hProv, 0);
  397. }
  398. if (NULL != hCertStore)
  399. {
  400. CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
  401. }
  402. if (NULL != pszProviderName)
  403. {
  404. SCardFreeMemory(hContext, pszProviderName);
  405. }
  406. if (0 != hContext)
  407. {
  408. SCardReleaseContext(hContext);
  409. }
  410. if (0 != hKey)
  411. {
  412. CryptDestroyKey(hKey);
  413. }
  414. RTASSERT( (NO_ERROR == dwErr)
  415. || (NULL == *ppCertContext));
  416. if ( (NULL == *ppCertContext)
  417. && (NO_ERROR == dwErr))
  418. {
  419. EapTlsTrace("CertContext is NULL. Returning E_FAIL");
  420. dwErr = E_FAIL;
  421. }
  422. if ( (SCARD_W_CANCELLED_BY_USER == dwErr)
  423. || (SCARD_E_NO_READERS_AVAILABLE == dwErr))
  424. {
  425. dwErr = ERROR_CANCELLED;
  426. }
  427. return(dwErr);
  428. }