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.

409 lines
15 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1995 - 1999
  6. //
  7. // File: revfunc.cpp
  8. //
  9. // Contents: Certificate Revocation Dispatch Functions
  10. //
  11. // Functions: I_CertRevFuncDllMain
  12. // CertVerifyRevocation
  13. //
  14. // History: 12-Dec-96 philh created
  15. // 11-Mar-97 philh changed signature of CertVerifyRevocation
  16. //--------------------------------------------------------------------------
  17. #include "global.hxx"
  18. #include <dbgdef.h>
  19. static HCRYPTOIDFUNCSET hRevFuncSet;
  20. typedef BOOL (WINAPI *PFN_CERT_DLL_VERIFY_REVOCATION)(
  21. IN DWORD dwEncodingType,
  22. IN DWORD dwRevType,
  23. IN DWORD cContext,
  24. IN PVOID rgpvContext[],
  25. IN DWORD dwFlags,
  26. IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
  27. IN OUT PCERT_REVOCATION_STATUS pRevStatus
  28. );
  29. //+-------------------------------------------------------------------------
  30. // Dll initialization
  31. //--------------------------------------------------------------------------
  32. BOOL
  33. WINAPI
  34. I_CertRevFuncDllMain(
  35. HMODULE hModule,
  36. ULONG ulReason,
  37. LPVOID lpReserved)
  38. {
  39. BOOL fRet;
  40. switch (ulReason) {
  41. case DLL_PROCESS_ATTACH:
  42. if (NULL == (hRevFuncSet = CryptInitOIDFunctionSet(
  43. CRYPT_OID_VERIFY_REVOCATION_FUNC,
  44. 0))) // dwFlags
  45. goto CryptInitOIDFunctionSetError;
  46. break;
  47. case DLL_PROCESS_DETACH:
  48. case DLL_THREAD_DETACH:
  49. default:
  50. break;
  51. }
  52. fRet = TRUE;
  53. CommonReturn:
  54. return fRet;
  55. ErrorReturn:
  56. fRet = FALSE;
  57. goto CommonReturn;
  58. TRACE_ERROR(CryptInitOIDFunctionSetError)
  59. }
  60. static inline void ZeroRevStatus(OUT PCERT_REVOCATION_STATUS pRevStatus)
  61. {
  62. DWORD cbSize = pRevStatus->cbSize;
  63. memset(pRevStatus, 0, cbSize);
  64. pRevStatus->cbSize = cbSize;
  65. }
  66. // Remember the first "interesting" error. *pdwError is initialized to
  67. // CRYPT_E_NO_REVOCATION_DLL.
  68. static void UpdateNoRevocationCheckStatus(
  69. IN PCERT_REVOCATION_STATUS pRevStatus,
  70. IN OUT DWORD *pdwError,
  71. IN OUT DWORD *pdwReason,
  72. IN OUT BOOL *pfHasFreshnessTime,
  73. IN OUT DWORD *pdwFreshnessTime
  74. )
  75. {
  76. if (pRevStatus->dwError &&
  77. (*pdwError == (DWORD) CRYPT_E_NO_REVOCATION_DLL ||
  78. *pdwError == (DWORD) CRYPT_E_NO_REVOCATION_CHECK)) {
  79. *pdwError = pRevStatus->dwError;
  80. *pdwReason = pRevStatus->dwReason;
  81. if (pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
  82. dwFreshnessTime)) {
  83. *pfHasFreshnessTime = pRevStatus->fHasFreshnessTime;
  84. *pdwFreshnessTime = pRevStatus->dwFreshnessTime;
  85. }
  86. }
  87. }
  88. static BOOL VerifyDefaultRevocation(
  89. IN DWORD dwEncodingType,
  90. IN DWORD dwRevType,
  91. IN DWORD cContext,
  92. IN PVOID rgpvContext[],
  93. IN DWORD dwFlags,
  94. IN FILETIME *pftEndUrlRetrieval,
  95. IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
  96. IN OUT PCERT_REVOCATION_STATUS pRevStatus
  97. )
  98. {
  99. BOOL fResult;
  100. DWORD dwError = (DWORD) CRYPT_E_NO_REVOCATION_DLL;
  101. DWORD dwReason = 0;
  102. BOOL fHasFreshnessTime = FALSE;
  103. DWORD dwFreshnessTime = 0;
  104. LPWSTR pwszDllList; // _alloca'ed
  105. DWORD cchDllList;
  106. DWORD cchDll;
  107. void *pvFuncAddr;
  108. HCRYPTOIDFUNCADDR hFuncAddr;
  109. // Iterate through the installed default functions.
  110. // Setting pwszDll to NULL searches the installed list. Setting
  111. // hFuncAddr to NULL starts the search at the beginning.
  112. hFuncAddr = NULL;
  113. while (CryptGetDefaultOIDFunctionAddress(
  114. hRevFuncSet,
  115. dwEncodingType,
  116. NULL, // pwszDll
  117. 0, // dwFlags
  118. &pvFuncAddr,
  119. &hFuncAddr)) {
  120. ZeroRevStatus(pRevStatus);
  121. if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
  122. pRevPara->dwUrlRetrievalTimeout =
  123. I_CryptRemainingMilliseconds(pftEndUrlRetrieval);
  124. if (0 == pRevPara->dwUrlRetrievalTimeout)
  125. pRevPara->dwUrlRetrievalTimeout = 1;
  126. }
  127. fResult = ((PFN_CERT_DLL_VERIFY_REVOCATION) pvFuncAddr)(
  128. dwEncodingType,
  129. dwRevType,
  130. cContext,
  131. rgpvContext,
  132. dwFlags,
  133. pRevPara,
  134. pRevStatus);
  135. if (fResult || CRYPT_E_REVOKED == pRevStatus->dwError ||
  136. 0 < pRevStatus->dwIndex) {
  137. // All contexts successfully checked, one of the contexts
  138. // was revoked or successfully able to check at least one
  139. // of the contexts.
  140. CryptFreeOIDFunctionAddress(hFuncAddr, 0);
  141. goto CommonReturn;
  142. } else
  143. // Unable to check revocation for this installed
  144. // function. However, remember any "interesting"
  145. // errors such as, offline.
  146. UpdateNoRevocationCheckStatus(pRevStatus, &dwError, &dwReason,
  147. &fHasFreshnessTime, &dwFreshnessTime);
  148. }
  149. if (!CryptGetDefaultOIDDllList(
  150. hRevFuncSet,
  151. dwEncodingType,
  152. NULL, // pszDllList
  153. &cchDllList)) goto GetDllListError;
  154. __try {
  155. pwszDllList = (LPWSTR) _alloca(cchDllList * sizeof(WCHAR));
  156. } __except(EXCEPTION_EXECUTE_HANDLER) {
  157. goto OutOfMemory;
  158. }
  159. if (!CryptGetDefaultOIDDllList(
  160. hRevFuncSet,
  161. dwEncodingType,
  162. pwszDllList,
  163. &cchDllList)) goto GetDllListError;
  164. for (; 0 != (cchDll = wcslen(pwszDllList)); pwszDllList += cchDll + 1) {
  165. if (CryptGetDefaultOIDFunctionAddress(
  166. hRevFuncSet,
  167. dwEncodingType,
  168. pwszDllList,
  169. 0, // dwFlags
  170. &pvFuncAddr,
  171. &hFuncAddr)) {
  172. ZeroRevStatus(pRevStatus);
  173. if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
  174. pRevPara->dwUrlRetrievalTimeout =
  175. I_CryptRemainingMilliseconds(pftEndUrlRetrieval);
  176. if (0 == pRevPara->dwUrlRetrievalTimeout)
  177. pRevPara->dwUrlRetrievalTimeout = 1;
  178. }
  179. fResult = ((PFN_CERT_DLL_VERIFY_REVOCATION) pvFuncAddr)(
  180. dwEncodingType,
  181. dwRevType,
  182. cContext,
  183. rgpvContext,
  184. dwFlags,
  185. pRevPara,
  186. pRevStatus);
  187. CryptFreeOIDFunctionAddress(hFuncAddr, 0);
  188. if (fResult || CRYPT_E_REVOKED == pRevStatus->dwError ||
  189. 0 < pRevStatus->dwIndex)
  190. // All contexts successfully checked, one of the contexts
  191. // was revoked or successfully able to check at least one
  192. // of the contexts.
  193. goto CommonReturn;
  194. else
  195. // Unable to check revocation for this registered
  196. // function. However, remember any "interesting"
  197. // errors such as, offline.
  198. UpdateNoRevocationCheckStatus(pRevStatus, &dwError, &dwReason,
  199. &fHasFreshnessTime, &dwFreshnessTime);
  200. }
  201. }
  202. goto ErrorReturn;
  203. CommonReturn:
  204. return fResult;
  205. ErrorReturn:
  206. pRevStatus->dwIndex = 0;
  207. pRevStatus->dwError = dwError;
  208. pRevStatus->dwReason = dwReason;
  209. if (pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
  210. dwFreshnessTime)) {
  211. pRevStatus->fHasFreshnessTime = fHasFreshnessTime;
  212. pRevStatus->dwFreshnessTime = dwFreshnessTime;
  213. }
  214. fResult = FALSE;
  215. goto CommonReturn;
  216. TRACE_ERROR(GetDllListError)
  217. TRACE_ERROR(OutOfMemory)
  218. }
  219. //+-------------------------------------------------------------------------
  220. // Verifies the array of contexts for revocation. The dwRevType parameter
  221. // indicates the type of the context data structure passed in rgpvContext.
  222. // Currently only the revocation of certificates is defined.
  223. //
  224. // If the CERT_VERIFY_REV_CHAIN_FLAG flag is set, then, CertVerifyRevocation
  225. // is verifying a chain of certs where, rgpvContext[i + 1] is the issuer
  226. // of rgpvContext[i]. Otherwise, CertVerifyRevocation makes no assumptions
  227. // about the order of the contexts.
  228. //
  229. // To assist in finding the issuer, the pRevPara may optionally be set. See
  230. // the CERT_REVOCATION_PARA data structure for details.
  231. //
  232. // The contexts must contain enough information to allow the
  233. // installable or registered revocation DLLs to find the revocation server. For
  234. // certificates, this information would normally be conveyed in an
  235. // extension such as the IETF's AuthorityInfoAccess extension.
  236. //
  237. // CertVerifyRevocation returns TRUE if all of the contexts were successfully
  238. // checked and none were revoked. Otherwise, returns FALSE and updates the
  239. // returned pRevStatus data structure as follows:
  240. // dwIndex
  241. // Index of the first context that was revoked or unable to
  242. // be checked for revocation
  243. // dwError
  244. // Error status. LastError is also set to this error status.
  245. // dwError can be set to one of the following error codes defined
  246. // in winerror.h:
  247. // ERROR_SUCCESS - good context
  248. // CRYPT_E_REVOKED - context was revoked. dwReason contains the
  249. // reason for revocation
  250. // CRYPT_E_REVOCATION_OFFLINE - unable to connect to the
  251. // revocation server
  252. // CRYPT_E_NOT_IN_REVOCATION_DATABASE - the context to be checked
  253. // was not found in the revocation server's database.
  254. // CRYPT_E_NO_REVOCATION_CHECK - the called revocation function
  255. // wasn't able to do a revocation check on the context
  256. // CRYPT_E_NO_REVOCATION_DLL - no installed or registered Dll was
  257. // found to verify revocation
  258. // dwReason
  259. // The dwReason is currently only set for CRYPT_E_REVOKED and contains
  260. // the reason why the context was revoked. May be one of the following
  261. // CRL reasons defined by the CRL Reason Code extension ("2.5.29.21")
  262. // CRL_REASON_UNSPECIFIED 0
  263. // CRL_REASON_KEY_COMPROMISE 1
  264. // CRL_REASON_CA_COMPROMISE 2
  265. // CRL_REASON_AFFILIATION_CHANGED 3
  266. // CRL_REASON_SUPERSEDED 4
  267. // CRL_REASON_CESSATION_OF_OPERATION 5
  268. // CRL_REASON_CERTIFICATE_HOLD 6
  269. //
  270. // For each entry in rgpvContext, CertVerifyRevocation iterates
  271. // through the CRYPT_OID_VERIFY_REVOCATION_FUNC
  272. // function set's list of installed DEFAULT functions.
  273. // CryptGetDefaultOIDFunctionAddress is called with pwszDll = NULL. If no
  274. // installed functions are found capable of doing the revocation verification,
  275. // CryptVerifyRevocation iterates through CRYPT_OID_VERIFY_REVOCATION_FUNC's
  276. // list of registered DEFAULT Dlls. CryptGetDefaultOIDDllList is called to
  277. // get the list. CryptGetDefaultOIDFunctionAddress is called to load the Dll.
  278. //
  279. // The called functions have the same signature as CertVerifyRevocation. A
  280. // called function returns TRUE if it was able to successfully check all of
  281. // the contexts and none were revoked. Otherwise, the called function returns
  282. // FALSE and updates pRevStatus. dwIndex is set to the index of
  283. // the first context that was found to be revoked or unable to be checked.
  284. // dwError and LastError are updated. For CRYPT_E_REVOKED, dwReason
  285. // is updated. Upon input to the called function, dwIndex, dwError and
  286. // dwReason have been zero'ed. cbSize has been checked to be >=
  287. // sizeof(CERT_REVOCATION_STATUS).
  288. //
  289. // If the called function returns FALSE, and dwError isn't set to
  290. // CRYPT_E_REVOKED, then, CertVerifyRevocation either continues on to the
  291. // next DLL in the list for a returned dwIndex of 0 or for a returned
  292. // dwIndex > 0, restarts the process of finding a verify function by
  293. // advancing the start of the context array to the returned dwIndex and
  294. // decrementing the count of remaining contexts.
  295. //--------------------------------------------------------------------------
  296. BOOL
  297. WINAPI
  298. CertVerifyRevocation(
  299. IN DWORD dwEncodingType,
  300. IN DWORD dwRevType,
  301. IN DWORD cContext,
  302. IN PVOID rgpvContext[],
  303. IN DWORD dwFlags,
  304. IN OPTIONAL PCERT_REVOCATION_PARA pRevPara,
  305. IN OUT PCERT_REVOCATION_STATUS pRevStatus
  306. )
  307. {
  308. BOOL fResult = FALSE;
  309. DWORD dwIndex;
  310. // Following are only used for CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
  311. CERT_REVOCATION_PARA RevPara;
  312. FILETIME ftEndUrlRetrieval;
  313. assert(pRevStatus->cbSize >= STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
  314. dwReason));
  315. if (pRevStatus->cbSize < STRUCT_CBSIZE(CERT_REVOCATION_STATUS,
  316. dwReason))
  317. goto InvalidArg;
  318. if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) {
  319. // RevPara.dwUrlRetrievalTimeout will be updated with the remaining
  320. // timeout
  321. memset(&RevPara, 0, sizeof(RevPara));
  322. if (pRevPara != NULL)
  323. memcpy(&RevPara, pRevPara, min(pRevPara->cbSize, sizeof(RevPara)));
  324. RevPara.cbSize = sizeof(RevPara);
  325. if (0 == RevPara.dwUrlRetrievalTimeout)
  326. dwFlags &= ~CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
  327. else {
  328. FILETIME ftCurrent;
  329. GetSystemTimeAsFileTime(&ftCurrent);
  330. I_CryptIncrementFileTimeByMilliseconds(
  331. &ftCurrent, RevPara.dwUrlRetrievalTimeout, &ftEndUrlRetrieval);
  332. pRevPara = &RevPara;
  333. }
  334. }
  335. dwIndex = 0;
  336. while (dwIndex < cContext) {
  337. fResult = VerifyDefaultRevocation(
  338. dwEncodingType,
  339. dwRevType,
  340. cContext - dwIndex,
  341. &rgpvContext[dwIndex],
  342. dwFlags,
  343. &ftEndUrlRetrieval,
  344. pRevPara,
  345. pRevStatus
  346. );
  347. if (fResult)
  348. // All contexts successfully checked.
  349. break;
  350. else if (CRYPT_E_REVOKED == pRevStatus->dwError ||
  351. 0 == pRevStatus->dwIndex) {
  352. // One of the contexts was revoked or unable to check the
  353. // dwIndex context.
  354. pRevStatus->dwIndex += dwIndex;
  355. SetLastError(pRevStatus->dwError);
  356. break;
  357. } else
  358. // Advance past the checked contexts
  359. dwIndex += pRevStatus->dwIndex;
  360. }
  361. if (dwIndex >= cContext) {
  362. // Able to check all the contexts
  363. fResult = TRUE;
  364. pRevStatus->dwIndex = 0;
  365. pRevStatus->dwError = 0;
  366. pRevStatus->dwReason = 0;
  367. }
  368. CommonReturn:
  369. return fResult;
  370. ErrorReturn:
  371. fResult = FALSE;
  372. goto CommonReturn;
  373. SET_ERROR(InvalidArg, E_INVALIDARG)
  374. }