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.

413 lines
12 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2001
  3. Module Name:
  4. pincache.c
  5. Abstract:
  6. Pin Caching Library for Smart Card CSP's
  7. Author:
  8. Dan Griffin
  9. --*/
  10. #include <windows.h>
  11. #include "pincache.h"
  12. #if defined(DBG) || defined(DEBUG)
  13. #define DebugPrint(a) (OutputDebugString(a))
  14. #if TEST_DEBUG
  15. #include <stdio.h>
  16. #define CROW 8
  17. void PCPrintBytes(LPSTR pszHdr, BYTE *pb, DWORD cbSize)
  18. {
  19. ULONG cb, i;
  20. CHAR rgsz[1024];
  21. sprintf(rgsz, "\n %s, %d bytes ::\n", pszHdr, cbSize);
  22. DebugPrint(rgsz);
  23. while (cbSize > 0)
  24. {
  25. // Start every row with an extra space
  26. DebugPrint(" ");
  27. cb = min(CROW, cbSize);
  28. cbSize -= cb;
  29. for (i = 0; i < cb; i++)
  30. sprintf(rgsz + (3*i), " %02x", pb[i]);
  31. DebugPrint(rgsz);
  32. for (i = cb; i < CROW; i++)
  33. DebugPrint(" ");
  34. DebugPrint(" '");
  35. for (i = 0; i < cb; i++)
  36. {
  37. if (pb[i] >= 0x20 && pb[i] <= 0x7f)
  38. sprintf(rgsz+i, "%c", pb[i]);
  39. else
  40. sprintf(rgsz+i, ".");
  41. }
  42. sprintf(rgsz+i, "\n");
  43. DebugPrint(rgsz);
  44. pb += cb;
  45. }
  46. }
  47. BOOL MyGetTokenInformation(
  48. HANDLE TokenHandle,
  49. TOKEN_INFORMATION_CLASS TokenInformationClass,
  50. LPVOID TokenInformation,
  51. DWORD TokenInformationLength,
  52. PDWORD ReturnLength);
  53. #define GetTokenInformation(A, B, C, D, E) MyGetTokenInformation(A, B, C, D, E)
  54. #define TestDebugPrint(a) (OutputDebugString(a))
  55. #else
  56. #define TestDebugPrint(a)
  57. #endif // TEST_DEBUG
  58. #else
  59. #define DebugPrint(a)
  60. #define TestDebugPrint(a)
  61. #endif // DBG || DEBUG
  62. typedef struct _PINCACHEITEM
  63. {
  64. LUID luid;
  65. PBYTE pbPin;
  66. DWORD cbPin;
  67. DWORD dwBadTries;
  68. } PINCACHEITEM, *PPINCACHEITEM;
  69. #define CacheAlloc(X) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, X))
  70. #define CacheFree(X) (HeapFree(GetProcessHeap(), 0, X))
  71. #define INIT_PIN_ATTACK_SLEEP 6000 // milliseconds
  72. #define MAX_PIN_ATTACK_SLEEP 24000 // milliseconds
  73. #define MAX_FREE_BAD_TRIES 3
  74. /**
  75. * Function: PinCacheFlush
  76. */
  77. void WINAPI PinCacheFlush(
  78. IN OUT PINCACHE_HANDLE *phCache)
  79. {
  80. PPINCACHEITEM pCache = (PPINCACHEITEM) *phCache;
  81. if (NULL == pCache)
  82. return;
  83. TestDebugPrint(("PinCacheFlush: deleting cache\n"));
  84. ZeroMemory(pCache->pbPin, pCache->cbPin);
  85. ZeroMemory(pCache, sizeof(PINCACHEITEM));
  86. CacheFree(pCache->pbPin);
  87. CacheFree(pCache);
  88. *phCache = NULL;
  89. }
  90. /**
  91. * Function: PinCacheAdd
  92. */
  93. DWORD WINAPI PinCacheAdd(
  94. IN PINCACHE_HANDLE *phCache,
  95. IN PPINCACHE_PINS pPins,
  96. IN PFN_VERIFYPIN_CALLBACK pfnVerifyPinCallback,
  97. IN PVOID pvCallbackCtx)
  98. {
  99. HANDLE hThreadToken = 0;
  100. TOKEN_STATISTICS stats;
  101. DWORD dwError = ERROR_SUCCESS;
  102. DWORD cb = 0;
  103. PPINCACHEITEM pCache = (PPINCACHEITEM) *phCache;
  104. DWORD cbPinToCache = 0;
  105. PBYTE pbPinToCache = NULL;
  106. BOOL fRefreshPin = FALSE;
  107. DWORD dwSleep = 0;
  108. if (NULL != pCache &&
  109. (pPins->cbCurrentPin != pCache->cbPin ||
  110. 0 != memcmp(pCache->pbPin, pPins->pbCurrentPin, pCache->cbPin)))
  111. {
  112. // The caller hasn't supplied the correct Pin, according to the current
  113. // cache state. Perhaps the user accidently typed the wrong pin, in which
  114. // case the caller's logon LUID should be the same as the cached LUID.
  115. // If the LUID's don't match, this could still be an attack or a legitimate
  116. // attempt from a different logon with a mis-typed pin.
  117. if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  118. {
  119. if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  120. {
  121. dwError = GetLastError();
  122. goto Ret;
  123. }
  124. }
  125. if (! GetTokenInformation(
  126. hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  127. {
  128. dwError = GetLastError();
  129. goto Ret;
  130. }
  131. if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)) &&
  132. ++pCache->dwBadTries > MAX_FREE_BAD_TRIES)
  133. {
  134. // Current caller is a different luid from the cached one,
  135. // and it's happened a few times already, so this call is suspicious.
  136. // Start delaying.
  137. DebugPrint(("PinCacheAdd: error - calling SleepEx(). Currently cached pin doesn't match\n"));
  138. dwSleep = pCache->dwBadTries * INIT_PIN_ATTACK_SLEEP;
  139. if (MAX_PIN_ATTACK_SLEEP < dwSleep)
  140. dwSleep = MAX_PIN_ATTACK_SLEEP;
  141. SleepEx(dwSleep, FALSE);
  142. }
  143. dwError = SCARD_W_WRONG_CHV;
  144. goto Ret;
  145. }
  146. else if (NULL != pCache && 0 != pCache->dwBadTries)
  147. {
  148. pCache->dwBadTries = 0;
  149. }
  150. if (pPins->pbNewPin)
  151. {
  152. fRefreshPin = TRUE;
  153. cbPinToCache = pPins->cbNewPin;
  154. pbPinToCache = pPins->pbNewPin;
  155. }
  156. else
  157. {
  158. cbPinToCache = pPins->cbCurrentPin;
  159. pbPinToCache = pPins->pbCurrentPin;
  160. }
  161. if (fRefreshPin || NULL == pCache)
  162. {
  163. // Check the pin
  164. if (ERROR_SUCCESS != (dwError =
  165. pfnVerifyPinCallback(pPins, pvCallbackCtx)))
  166. {
  167. TestDebugPrint(("PinCacheAdd: pfnVerifyPinCallback failed\n"));
  168. return dwError;
  169. }
  170. }
  171. if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  172. {
  173. if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  174. {
  175. TestDebugPrint(("PinCacheAdd: failed to open thread or process token\n"));
  176. dwError = GetLastError();
  177. goto Ret;
  178. }
  179. }
  180. if (! GetTokenInformation(
  181. hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  182. {
  183. TestDebugPrint(("PinCacheAdd: GetTokenInformation failed\n"));
  184. dwError = GetLastError();
  185. goto Ret;
  186. }
  187. #if TEST_DEBUG
  188. PCPrintBytes("PinCache LUID", (PBYTE) &stats.AuthenticationId, sizeof(LUID));
  189. #endif
  190. // Now the current ID is in stats.AuthenticationId
  191. if (NULL == pCache)
  192. {
  193. TestDebugPrint(("PinCacheAdd: initializing new cache\n"));
  194. // Initialize new cache
  195. if (NULL == (pCache = (PPINCACHEITEM) CacheAlloc(sizeof(PINCACHEITEM))))
  196. {
  197. dwError = ERROR_NOT_ENOUGH_MEMORY;
  198. goto Ret;
  199. }
  200. CopyMemory(&pCache->luid, &stats.AuthenticationId, sizeof(LUID));
  201. *phCache = (PINCACHE_HANDLE) pCache;
  202. fRefreshPin = TRUE;
  203. }
  204. else
  205. {
  206. // Compare ID's
  207. if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
  208. {
  209. // PIN's are the same, so cache the new ID
  210. TestDebugPrint(("PinCacheAdd: same Pin, different Logon as cached values\n"));
  211. CopyMemory(&pCache->luid, &stats.AuthenticationId, sizeof(LUID));
  212. }
  213. }
  214. if (fRefreshPin)
  215. {
  216. if (pCache->pbPin)
  217. CacheFree(pCache->pbPin);
  218. pCache->cbPin = cbPinToCache;
  219. if (NULL == (pCache->pbPin = (PBYTE) CacheAlloc(cbPinToCache)))
  220. {
  221. dwError = ERROR_NOT_ENOUGH_MEMORY;
  222. goto Ret;
  223. }
  224. CopyMemory(pCache->pbPin, pbPinToCache, cbPinToCache);
  225. }
  226. Ret:
  227. if (hThreadToken)
  228. CloseHandle(hThreadToken);
  229. return dwError;
  230. }
  231. /**
  232. * Function: PinCacheQuery
  233. */
  234. DWORD WINAPI PinCacheQuery(
  235. IN PINCACHE_HANDLE hCache,
  236. IN OUT PBYTE pbPin,
  237. IN OUT PDWORD pcbPin)
  238. {
  239. HANDLE hThreadToken = 0;
  240. TOKEN_STATISTICS stats;
  241. DWORD dwError = ERROR_SUCCESS;
  242. DWORD cb = 0;
  243. PPINCACHEITEM pCache = (PPINCACHEITEM) hCache;
  244. if (NULL == pCache)
  245. {
  246. *pcbPin = 0;
  247. return ERROR_EMPTY;
  248. }
  249. if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  250. {
  251. if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  252. {
  253. TestDebugPrint(("PinCacheQuery: failed to open thread or process token\n"));
  254. dwError = GetLastError();
  255. goto Ret;
  256. }
  257. }
  258. if (! GetTokenInformation(
  259. hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  260. {
  261. TestDebugPrint(("PinCacheQuery: GetTokenInformation failed\n"));
  262. dwError = GetLastError();
  263. goto Ret;
  264. }
  265. // Now the current ID is in stats.AuthenticationId
  266. if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
  267. {
  268. // ID's are different, so ignore cache
  269. TestDebugPrint(("PinCacheQuery: different Logon from cached value\n"));
  270. *pcbPin = 0;
  271. goto Ret;
  272. }
  273. // ID's are the same, so return cached PIN
  274. TestDebugPrint(("PinCacheQuery: same Logon as cached value\n"));
  275. if (NULL != pbPin)
  276. {
  277. if (*pcbPin >= pCache->cbPin)
  278. CopyMemory(pbPin, pCache->pbPin, pCache->cbPin);
  279. else
  280. dwError = ERROR_MORE_DATA;
  281. }
  282. *pcbPin = pCache->cbPin;
  283. Ret:
  284. if (hThreadToken)
  285. CloseHandle(hThreadToken);
  286. return dwError;
  287. }
  288. /**
  289. * Function: PinCachePresentPin
  290. */
  291. DWORD WINAPI PinCachePresentPin(
  292. IN PINCACHE_HANDLE hCache,
  293. IN PFN_VERIFYPIN_CALLBACK pfnVerifyPinCallback,
  294. IN PVOID pvCallbackCtx)
  295. {
  296. HANDLE hThreadToken = 0;
  297. TOKEN_STATISTICS stats;
  298. DWORD cb = 0;
  299. DWORD dwError = ERROR_SUCCESS;
  300. PPINCACHEITEM pCache = (PPINCACHEITEM) hCache;
  301. PINCACHE_PINS Pins;
  302. if (NULL == pCache)
  303. return ERROR_EMPTY;
  304. if (! OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hThreadToken))
  305. {
  306. if (! OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThreadToken))
  307. {
  308. TestDebugPrint(("PinCachePresentPin: failed to open thread or process token\n"));
  309. dwError = GetLastError();
  310. goto Ret;
  311. }
  312. }
  313. if (! GetTokenInformation(
  314. hThreadToken, TokenStatistics, &stats, sizeof(stats), &cb))
  315. {
  316. TestDebugPrint(("PinCachePresentPin: GetTokenInformation failed\n"));
  317. dwError = GetLastError();
  318. goto Ret;
  319. }
  320. // Now the current ID is in stats.AuthenticationId
  321. if (0 != memcmp(&stats.AuthenticationId, &pCache->luid, sizeof(LUID)))
  322. {
  323. // ID's are different, so ignore cache
  324. TestDebugPrint(("PinCachePresentPin: different Logon from cached value\n"));
  325. dwError = SCARD_W_CARD_NOT_AUTHENTICATED;
  326. goto Ret;
  327. }
  328. // ID's are the same, so return cached PIN
  329. TestDebugPrint(("PinCachePresentPin: same Logon as cached value\n"));
  330. Pins.cbCurrentPin = pCache->cbPin;
  331. Pins.pbCurrentPin = pCache->pbPin;
  332. Pins.cbNewPin = 0;
  333. Pins.pbNewPin = NULL;
  334. dwError = (*pfnVerifyPinCallback)(&Pins, pvCallbackCtx);
  335. Ret:
  336. if (hThreadToken)
  337. CloseHandle(hThreadToken);
  338. return dwError;
  339. }