Sync with trunk r62529.
[reactos.git] / dll / win32 / cryptnet / cryptnet_main.c
1 /*
2 * Copyright (C) 2006 Maarten Lankhorst
3 * Copyright 2007 Juan Lang
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 *
19 */
20
21 #define WIN32_NO_STATUS
22
23 #include <config.h>
24 //#include "wine/port.h"
25
26 #define NONAMELESSUNION
27 #define NONAMELESSSTRUCT
28 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
29
30 #include <stdio.h>
31 //#include <stdarg.h>
32
33 #include <windef.h>
34 #include <winbase.h>
35 //#include "winnt.h"
36 #include <winnls.h>
37 #include <wininet.h>
38 //#include "objbase.h"
39 #include <wincrypt.h>
40
41 #include <wine/debug.h>
42
43 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet);
44
45 #define IS_INTOID(x) (((ULONG_PTR)(x) >> 16) == 0)
46
47 static const WCHAR cryptNet[] = { 'c','r','y','p','t','n','e','t','.',
48 'd','l','l',0 };
49
50 /***********************************************************************
51 * DllRegisterServer (CRYPTNET.@)
52 */
53 HRESULT WINAPI DllRegisterServer(void)
54 {
55 TRACE("\n");
56 CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING,
57 CRYPT_OID_VERIFY_REVOCATION_FUNC, 0, cryptNet);
58 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap",
59 cryptNet, "LdapProvOpenStore");
60 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
61 CERT_STORE_PROV_LDAP_W, cryptNet, "LdapProvOpenStore");
62 return S_OK;
63 }
64
65 /***********************************************************************
66 * DllUnregisterServer (CRYPTNET.@)
67 */
68 HRESULT WINAPI DllUnregisterServer(void)
69 {
70 TRACE("\n");
71 CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING,
72 CRYPT_OID_VERIFY_REVOCATION_FUNC, cryptNet);
73 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap");
74 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
75 CERT_STORE_PROV_LDAP_W);
76 return S_OK;
77 }
78
79 static const char *url_oid_to_str(LPCSTR oid)
80 {
81 if (IS_INTOID(oid))
82 {
83 static char buf[10];
84
85 switch (LOWORD(oid))
86 {
87 #define _x(oid) case LOWORD(oid): return #oid
88 _x(URL_OID_CERTIFICATE_ISSUER);
89 _x(URL_OID_CERTIFICATE_CRL_DIST_POINT);
90 _x(URL_OID_CTL_ISSUER);
91 _x(URL_OID_CTL_NEXT_UPDATE);
92 _x(URL_OID_CRL_ISSUER);
93 _x(URL_OID_CERTIFICATE_FRESHEST_CRL);
94 _x(URL_OID_CRL_FRESHEST_CRL);
95 _x(URL_OID_CROSS_CERT_DIST_POINT);
96 #undef _x
97 default:
98 snprintf(buf, sizeof(buf), "%d", LOWORD(oid));
99 return buf;
100 }
101 }
102 else
103 return oid;
104 }
105
106 typedef BOOL (WINAPI *UrlDllGetObjectUrlFunc)(LPCSTR, LPVOID, DWORD,
107 PCRYPT_URL_ARRAY, DWORD *, PCRYPT_URL_INFO, DWORD *, LPVOID);
108
109 static BOOL WINAPI CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid,
110 LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
111 PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
112 {
113 PCCERT_CONTEXT cert = pvPara;
114 PCERT_EXTENSION ext;
115 BOOL ret = FALSE;
116
117 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
118 if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
119 {
120 SetLastError(CRYPT_E_NOT_FOUND);
121 return FALSE;
122 }
123 if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
124 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
125 {
126 CERT_AUTHORITY_INFO_ACCESS *aia;
127 DWORD size;
128
129 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
130 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
131 &aia, &size);
132 if (ret)
133 {
134 DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
135
136 for (i = 0, cUrl = 0; i < aia->cAccDescr; i++)
137 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
138 szOID_PKIX_CA_ISSUERS))
139 {
140 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
141 CERT_ALT_NAME_URL)
142 {
143 if (aia->rgAccDescr[i].AccessLocation.u.pwszURL)
144 {
145 cUrl++;
146 bytesNeeded += sizeof(LPWSTR) +
147 (lstrlenW(aia->rgAccDescr[i].AccessLocation.u.
148 pwszURL) + 1) * sizeof(WCHAR);
149 }
150 }
151 else
152 FIXME("unsupported alt name type %d\n",
153 aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
154 }
155 if (!pcbUrlArray)
156 {
157 SetLastError(E_INVALIDARG);
158 ret = FALSE;
159 }
160 else if (!pUrlArray)
161 *pcbUrlArray = bytesNeeded;
162 else if (*pcbUrlArray < bytesNeeded)
163 {
164 SetLastError(ERROR_MORE_DATA);
165 *pcbUrlArray = bytesNeeded;
166 ret = FALSE;
167 }
168 else
169 {
170 LPWSTR nextUrl;
171
172 *pcbUrlArray = bytesNeeded;
173 pUrlArray->cUrl = 0;
174 pUrlArray->rgwszUrl =
175 (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
176 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
177 + cUrl * sizeof(LPWSTR));
178 for (i = 0; i < aia->cAccDescr; i++)
179 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
180 szOID_PKIX_CA_ISSUERS))
181 {
182 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice
183 == CERT_ALT_NAME_URL)
184 {
185 if (aia->rgAccDescr[i].AccessLocation.u.pwszURL)
186 {
187 lstrcpyW(nextUrl,
188 aia->rgAccDescr[i].AccessLocation.u.pwszURL);
189 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
190 nextUrl;
191 nextUrl += (lstrlenW(nextUrl) + 1);
192 }
193 }
194 }
195 }
196 if (ret)
197 {
198 if (pcbUrlInfo)
199 {
200 FIXME("url info: stub\n");
201 if (!pUrlInfo)
202 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
203 else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
204 {
205 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
206 SetLastError(ERROR_MORE_DATA);
207 ret = FALSE;
208 }
209 else
210 {
211 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
212 memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
213 }
214 }
215 }
216 LocalFree(aia);
217 }
218 }
219 else
220 SetLastError(CRYPT_E_NOT_FOUND);
221 return ret;
222 }
223
224 static BOOL CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB *value,
225 PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
226 DWORD *pcbUrlInfo)
227 {
228 BOOL ret;
229 CRL_DIST_POINTS_INFO *info;
230 DWORD size;
231
232 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CRL_DIST_POINTS,
233 value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size);
234 if (ret)
235 {
236 DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
237
238 for (i = 0, cUrl = 0; i < info->cDistPoint; i++)
239 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
240 == CRL_DIST_POINT_FULL_NAME)
241 {
242 DWORD j;
243 CERT_ALT_NAME_INFO *name =
244 &info->rgDistPoint[i].DistPointName.u.FullName;
245
246 for (j = 0; j < name->cAltEntry; j++)
247 if (name->rgAltEntry[j].dwAltNameChoice ==
248 CERT_ALT_NAME_URL)
249 {
250 if (name->rgAltEntry[j].u.pwszURL)
251 {
252 cUrl++;
253 bytesNeeded += sizeof(LPWSTR) +
254 (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1)
255 * sizeof(WCHAR);
256 }
257 }
258 }
259 if (!pcbUrlArray)
260 {
261 SetLastError(E_INVALIDARG);
262 ret = FALSE;
263 }
264 else if (!pUrlArray)
265 *pcbUrlArray = bytesNeeded;
266 else if (*pcbUrlArray < bytesNeeded)
267 {
268 SetLastError(ERROR_MORE_DATA);
269 *pcbUrlArray = bytesNeeded;
270 ret = FALSE;
271 }
272 else
273 {
274 LPWSTR nextUrl;
275
276 *pcbUrlArray = bytesNeeded;
277 pUrlArray->cUrl = 0;
278 pUrlArray->rgwszUrl =
279 (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
280 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
281 + cUrl * sizeof(LPWSTR));
282 for (i = 0; i < info->cDistPoint; i++)
283 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
284 == CRL_DIST_POINT_FULL_NAME)
285 {
286 DWORD j;
287 CERT_ALT_NAME_INFO *name =
288 &info->rgDistPoint[i].DistPointName.u.FullName;
289
290 for (j = 0; j < name->cAltEntry; j++)
291 if (name->rgAltEntry[j].dwAltNameChoice ==
292 CERT_ALT_NAME_URL)
293 {
294 if (name->rgAltEntry[j].u.pwszURL)
295 {
296 lstrcpyW(nextUrl,
297 name->rgAltEntry[j].u.pwszURL);
298 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
299 nextUrl;
300 nextUrl +=
301 (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1);
302 }
303 }
304 }
305 }
306 if (ret)
307 {
308 if (pcbUrlInfo)
309 {
310 FIXME("url info: stub\n");
311 if (!pUrlInfo)
312 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
313 else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
314 {
315 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
316 SetLastError(ERROR_MORE_DATA);
317 ret = FALSE;
318 }
319 else
320 {
321 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
322 memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
323 }
324 }
325 }
326 LocalFree(info);
327 }
328 return ret;
329 }
330
331 static BOOL WINAPI CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid,
332 LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
333 PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
334 {
335 PCCERT_CONTEXT cert = pvPara;
336 PCERT_EXTENSION ext;
337 BOOL ret = FALSE;
338
339 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
340 if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
341 {
342 SetLastError(CRYPT_E_NOT_FOUND);
343 return FALSE;
344 }
345 if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
346 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
347 ret = CRYPT_GetUrlFromCRLDistPointsExt(&ext->Value, pUrlArray,
348 pcbUrlArray, pUrlInfo, pcbUrlInfo);
349 else
350 SetLastError(CRYPT_E_NOT_FOUND);
351 return ret;
352 }
353
354 /***********************************************************************
355 * CryptGetObjectUrl (CRYPTNET.@)
356 */
357 BOOL WINAPI CryptGetObjectUrl(LPCSTR pszUrlOid, LPVOID pvPara, DWORD dwFlags,
358 PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
359 DWORD *pcbUrlInfo, LPVOID pvReserved)
360 {
361 UrlDllGetObjectUrlFunc func = NULL;
362 HCRYPTOIDFUNCADDR hFunc = NULL;
363 BOOL ret = FALSE;
364
365 TRACE("(%s, %p, %08x, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid),
366 pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved);
367
368 if (IS_INTOID(pszUrlOid))
369 {
370 switch (LOWORD(pszUrlOid))
371 {
372 case LOWORD(URL_OID_CERTIFICATE_ISSUER):
373 func = CRYPT_GetUrlFromCertificateIssuer;
374 break;
375 case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT):
376 func = CRYPT_GetUrlFromCertificateCRLDistPoint;
377 break;
378 default:
379 FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid));
380 SetLastError(ERROR_FILE_NOT_FOUND);
381 }
382 }
383 else
384 {
385 static HCRYPTOIDFUNCSET set = NULL;
386
387 if (!set)
388 set = CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC, 0);
389 CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszUrlOid, 0,
390 (void **)&func, &hFunc);
391 }
392 if (func)
393 ret = func(pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray,
394 pUrlInfo, pcbUrlInfo, pvReserved);
395 if (hFunc)
396 CryptFreeOIDFunctionAddress(hFunc, 0);
397 return ret;
398 }
399
400 /***********************************************************************
401 * CryptRetrieveObjectByUrlA (CRYPTNET.@)
402 */
403 BOOL WINAPI CryptRetrieveObjectByUrlA(LPCSTR pszURL, LPCSTR pszObjectOid,
404 DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
405 HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
406 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
407 {
408 BOOL ret = FALSE;
409 int len;
410
411 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL),
412 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
413 hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
414
415 if (!pszURL)
416 {
417 SetLastError(ERROR_INVALID_PARAMETER);
418 return FALSE;
419 }
420 len = MultiByteToWideChar(CP_ACP, 0, pszURL, -1, NULL, 0);
421 if (len)
422 {
423 LPWSTR url = CryptMemAlloc(len * sizeof(WCHAR));
424
425 if (url)
426 {
427 MultiByteToWideChar(CP_ACP, 0, pszURL, -1, url, len);
428 ret = CryptRetrieveObjectByUrlW(url, pszObjectOid,
429 dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve,
430 pCredentials, pvVerify, pAuxInfo);
431 CryptMemFree(url);
432 }
433 else
434 SetLastError(ERROR_OUTOFMEMORY);
435 }
436 return ret;
437 }
438
439 static void WINAPI CRYPT_FreeBlob(LPCSTR pszObjectOid,
440 PCRYPT_BLOB_ARRAY pObject, void *pvFreeContext)
441 {
442 DWORD i;
443
444 for (i = 0; i < pObject->cBlob; i++)
445 CryptMemFree(pObject->rgBlob[i].pbData);
446 CryptMemFree(pObject->rgBlob);
447 }
448
449 static BOOL CRYPT_GetObjectFromFile(HANDLE hFile, PCRYPT_BLOB_ARRAY pObject)
450 {
451 BOOL ret;
452 LARGE_INTEGER size;
453
454 if ((ret = GetFileSizeEx(hFile, &size)))
455 {
456 if (size.u.HighPart)
457 {
458 WARN("file too big\n");
459 SetLastError(ERROR_INVALID_DATA);
460 ret = FALSE;
461 }
462 else
463 {
464 CRYPT_DATA_BLOB blob;
465
466 blob.pbData = CryptMemAlloc(size.u.LowPart);
467 if (blob.pbData)
468 {
469 blob.cbData = size.u.LowPart;
470 ret = ReadFile(hFile, blob.pbData, size.u.LowPart, &blob.cbData,
471 NULL);
472 if (ret)
473 {
474 pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
475 if (pObject->rgBlob)
476 {
477 pObject->cBlob = 1;
478 memcpy(pObject->rgBlob, &blob, sizeof(CRYPT_DATA_BLOB));
479 }
480 else
481 {
482 SetLastError(ERROR_OUTOFMEMORY);
483 ret = FALSE;
484 }
485 }
486 if (!ret)
487 CryptMemFree(blob.pbData);
488 }
489 else
490 {
491 SetLastError(ERROR_OUTOFMEMORY);
492 ret = FALSE;
493 }
494 }
495 }
496 return ret;
497 }
498
499 static BOOL CRYPT_GetObjectFromCache(LPCWSTR pszURL, PCRYPT_BLOB_ARRAY pObject,
500 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
501 {
502 BOOL ret = FALSE;
503 INTERNET_CACHE_ENTRY_INFOW *pCacheInfo = NULL;
504 DWORD size = 0;
505
506 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pObject, pAuxInfo);
507
508 RetrieveUrlCacheEntryFileW(pszURL, NULL, &size, 0);
509 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
510 return FALSE;
511
512 pCacheInfo = CryptMemAlloc(size);
513 if (!pCacheInfo)
514 {
515 SetLastError(ERROR_OUTOFMEMORY);
516 return FALSE;
517 }
518
519 if ((ret = RetrieveUrlCacheEntryFileW(pszURL, pCacheInfo, &size, 0)))
520 {
521 FILETIME ft;
522
523 GetSystemTimeAsFileTime(&ft);
524 if (CompareFileTime(&pCacheInfo->ExpireTime, &ft) >= 0)
525 {
526 HANDLE hFile = CreateFileW(pCacheInfo->lpszLocalFileName, GENERIC_READ,
527 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
528
529 if (hFile != INVALID_HANDLE_VALUE)
530 {
531 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
532 {
533 if (pAuxInfo && pAuxInfo->cbSize >=
534 offsetof(CRYPT_RETRIEVE_AUX_INFO,
535 pLastSyncTime) + sizeof(PFILETIME) &&
536 pAuxInfo->pLastSyncTime)
537 memcpy(pAuxInfo->pLastSyncTime,
538 &pCacheInfo->LastSyncTime,
539 sizeof(FILETIME));
540 }
541 CloseHandle(hFile);
542 }
543 else
544 {
545 DeleteUrlCacheEntryW(pszURL);
546 ret = FALSE;
547 }
548 }
549 else
550 {
551 DeleteUrlCacheEntryW(pszURL);
552 ret = FALSE;
553 }
554 UnlockUrlCacheEntryFileW(pszURL, 0);
555 }
556 CryptMemFree(pCacheInfo);
557 TRACE("returning %d\n", ret);
558 return ret;
559 }
560
561 /* Parses the URL, and sets components' lpszHostName and lpszUrlPath members
562 * to NULL-terminated copies of those portions of the URL (to be freed with
563 * CryptMemFree.)
564 */
565 static BOOL CRYPT_CrackUrl(LPCWSTR pszURL, URL_COMPONENTSW *components)
566 {
567 BOOL ret;
568
569 TRACE("(%s, %p)\n", debugstr_w(pszURL), components);
570
571 memset(components, 0, sizeof(*components));
572 components->dwStructSize = sizeof(*components);
573 components->lpszHostName = CryptMemAlloc(INTERNET_MAX_HOST_NAME_LENGTH * sizeof(WCHAR));
574 components->dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
575 if (!components->lpszHostName)
576 {
577 SetLastError(ERROR_OUTOFMEMORY);
578 return FALSE;
579 }
580 components->lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
581 components->dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
582 if (!components->lpszUrlPath)
583 {
584 CryptMemFree(components->lpszHostName);
585 SetLastError(ERROR_OUTOFMEMORY);
586 return FALSE;
587 }
588
589 ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, components);
590 if (ret)
591 {
592 switch (components->nScheme)
593 {
594 case INTERNET_SCHEME_FTP:
595 if (!components->nPort)
596 components->nPort = INTERNET_DEFAULT_FTP_PORT;
597 break;
598 case INTERNET_SCHEME_HTTP:
599 if (!components->nPort)
600 components->nPort = INTERNET_DEFAULT_HTTP_PORT;
601 break;
602 default:
603 ; /* do nothing */
604 }
605 }
606 TRACE("returning %d\n", ret);
607 return ret;
608 }
609
610 struct InetContext
611 {
612 HANDLE event;
613 DWORD timeout;
614 DWORD error;
615 };
616
617 static struct InetContext *CRYPT_MakeInetContext(DWORD dwTimeout)
618 {
619 struct InetContext *context = CryptMemAlloc(sizeof(struct InetContext));
620
621 if (context)
622 {
623 context->event = CreateEventW(NULL, FALSE, FALSE, NULL);
624 if (!context->event)
625 {
626 CryptMemFree(context);
627 context = NULL;
628 }
629 else
630 {
631 context->timeout = dwTimeout;
632 context->error = ERROR_SUCCESS;
633 }
634 }
635 return context;
636 }
637
638 static BOOL CRYPT_DownloadObject(DWORD dwRetrievalFlags, HINTERNET hHttp,
639 struct InetContext *context, PCRYPT_BLOB_ARRAY pObject,
640 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
641 {
642 CRYPT_DATA_BLOB object = { 0, NULL };
643 DWORD bytesAvailable;
644 BOOL ret;
645
646 do {
647 if ((ret = InternetQueryDataAvailable(hHttp, &bytesAvailable, 0, 0)))
648 {
649 if (bytesAvailable)
650 {
651 if (object.pbData)
652 object.pbData = CryptMemRealloc(object.pbData,
653 object.cbData + bytesAvailable);
654 else
655 object.pbData = CryptMemAlloc(bytesAvailable);
656 if (object.pbData)
657 {
658 INTERNET_BUFFERSA buffer = { sizeof(buffer), 0 };
659
660 buffer.dwBufferLength = bytesAvailable;
661 buffer.lpvBuffer = object.pbData + object.cbData;
662 if (!(ret = InternetReadFileExA(hHttp, &buffer, IRF_NO_WAIT,
663 (DWORD_PTR)context)))
664 {
665 if (GetLastError() == ERROR_IO_PENDING)
666 {
667 if (WaitForSingleObject(context->event,
668 context->timeout) == WAIT_TIMEOUT)
669 SetLastError(ERROR_TIMEOUT);
670 else if (context->error)
671 SetLastError(context->error);
672 else
673 ret = TRUE;
674 }
675 }
676 if (ret)
677 object.cbData += buffer.dwBufferLength;
678 }
679 else
680 {
681 SetLastError(ERROR_OUTOFMEMORY);
682 ret = FALSE;
683 }
684 }
685 }
686 else if (GetLastError() == ERROR_IO_PENDING)
687 {
688 if (WaitForSingleObject(context->event, context->timeout) ==
689 WAIT_TIMEOUT)
690 SetLastError(ERROR_TIMEOUT);
691 else
692 ret = TRUE;
693 }
694 } while (ret && bytesAvailable);
695 if (ret)
696 {
697 pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
698 if (!pObject->rgBlob)
699 {
700 CryptMemFree(object.pbData);
701 SetLastError(ERROR_OUTOFMEMORY);
702 ret = FALSE;
703 }
704 else
705 {
706 pObject->rgBlob[0].cbData = object.cbData;
707 pObject->rgBlob[0].pbData = object.pbData;
708 pObject->cBlob = 1;
709 }
710 }
711 TRACE("returning %d\n", ret);
712 return ret;
713 }
714
715 /* Finds the object specified by pszURL in the cache. If it's not found,
716 * creates a new cache entry for the object and writes the object to it.
717 * Sets the expiration time of the cache entry to expires.
718 */
719 static void CRYPT_CacheURL(LPCWSTR pszURL, const CRYPT_BLOB_ARRAY *pObject,
720 DWORD dwRetrievalFlags, FILETIME expires)
721 {
722 WCHAR cacheFileName[MAX_PATH];
723 HANDLE hCacheFile;
724 DWORD size = 0, entryType;
725 FILETIME ft;
726
727 GetUrlCacheEntryInfoW(pszURL, NULL, &size);
728 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
729 {
730 INTERNET_CACHE_ENTRY_INFOW *info = CryptMemAlloc(size);
731
732 if (!info)
733 {
734 ERR("out of memory\n");
735 return;
736 }
737
738 if (GetUrlCacheEntryInfoW(pszURL, info, &size))
739 {
740 lstrcpyW(cacheFileName, info->lpszLocalFileName);
741 /* Check if the existing cache entry is up to date. If it isn't,
742 * remove the existing cache entry, and create a new one with the
743 * new value.
744 */
745 GetSystemTimeAsFileTime(&ft);
746 if (CompareFileTime(&info->ExpireTime, &ft) < 0)
747 {
748 DeleteUrlCacheEntryW(pszURL);
749 }
750 else
751 {
752 info->ExpireTime = expires;
753 SetUrlCacheEntryInfoW(pszURL, info, CACHE_ENTRY_EXPTIME_FC);
754 CryptMemFree(info);
755 return;
756 }
757 }
758 CryptMemFree(info);
759 }
760
761 if (!CreateUrlCacheEntryW(pszURL, pObject->rgBlob[0].cbData, NULL, cacheFileName, 0))
762 return;
763
764 hCacheFile = CreateFileW(cacheFileName, GENERIC_WRITE, 0,
765 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
766 if(hCacheFile == INVALID_HANDLE_VALUE)
767 return;
768
769 WriteFile(hCacheFile, pObject->rgBlob[0].pbData,
770 pObject->rgBlob[0].cbData, &size, NULL);
771 CloseHandle(hCacheFile);
772
773 if (!(dwRetrievalFlags & CRYPT_STICKY_CACHE_RETRIEVAL))
774 entryType = NORMAL_CACHE_ENTRY;
775 else
776 entryType = STICKY_CACHE_ENTRY;
777 memset(&ft, 0, sizeof(ft));
778 CommitUrlCacheEntryW(pszURL, cacheFileName, expires, ft, entryType,
779 NULL, 0, NULL, NULL);
780 }
781
782 static void CALLBACK CRYPT_InetStatusCallback(HINTERNET hInt,
783 DWORD_PTR dwContext, DWORD status, void *statusInfo, DWORD statusInfoLen)
784 {
785 struct InetContext *context = (struct InetContext *)dwContext;
786 LPINTERNET_ASYNC_RESULT result;
787
788 switch (status)
789 {
790 case INTERNET_STATUS_REQUEST_COMPLETE:
791 result = statusInfo;
792 context->error = result->dwError;
793 SetEvent(context->event);
794 }
795 }
796
797 static BOOL CRYPT_Connect(const URL_COMPONENTSW *components,
798 struct InetContext *context, PCRYPT_CREDENTIALS pCredentials,
799 HINTERNET *phInt, HINTERNET *phHost)
800 {
801 BOOL ret;
802
803 TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components->lpszHostName),
804 components->nPort, context, pCredentials, phInt, phInt);
805
806 *phHost = NULL;
807 *phInt = InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL,
808 context ? INTERNET_FLAG_ASYNC : 0);
809 if (*phInt)
810 {
811 DWORD service;
812
813 if (context)
814 InternetSetStatusCallbackW(*phInt, CRYPT_InetStatusCallback);
815 switch (components->nScheme)
816 {
817 case INTERNET_SCHEME_FTP:
818 service = INTERNET_SERVICE_FTP;
819 break;
820 case INTERNET_SCHEME_HTTP:
821 service = INTERNET_SERVICE_HTTP;
822 break;
823 default:
824 service = 0;
825 }
826 /* FIXME: use pCredentials for username/password */
827 *phHost = InternetConnectW(*phInt, components->lpszHostName,
828 components->nPort, NULL, NULL, service, 0, (DWORD_PTR)context);
829 if (!*phHost)
830 {
831 InternetCloseHandle(*phInt);
832 *phInt = NULL;
833 ret = FALSE;
834 }
835 else
836 ret = TRUE;
837 }
838 else
839 ret = FALSE;
840 TRACE("returning %d\n", ret);
841 return ret;
842 }
843
844 static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
845 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
846 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
847 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
848 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
849 {
850 FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
851 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
852 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
853
854 pObject->cBlob = 0;
855 pObject->rgBlob = NULL;
856 *ppfnFreeObject = CRYPT_FreeBlob;
857 *ppvFreeContext = NULL;
858 return FALSE;
859 }
860
861 static const WCHAR x509cacert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
862 '/','x','-','x','5','0','9','-','c','a','-','c','e','r','t',0 };
863 static const WCHAR x509emailcert[] = { 'a','p','p','l','i','c','a','t','i','o',
864 'n','/','x','-','x','5','0','9','-','e','m','a','i','l','-','c','e','r','t',
865 0 };
866 static const WCHAR x509servercert[] = { 'a','p','p','l','i','c','a','t','i','o',
867 'n','/','x','-','x','5','0','9','-','s','e','r','v','e','r','-','c','e','r',
868 't',0 };
869 static const WCHAR x509usercert[] = { 'a','p','p','l','i','c','a','t','i','o',
870 'n','/','x','-','x','5','0','9','-','u','s','e','r','-','c','e','r','t',0 };
871 static const WCHAR pkcs7cert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
872 '/','x','-','p','k','c','s','7','-','c','e','r','t','i','f','c','a','t','e',
873 's',0 };
874 static const WCHAR pkixCRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
875 '/','p','k','i','x','-','c','r','l',0 };
876 static const WCHAR pkcs7CRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
877 '/','x','-','p','k','c','s','7','-','c','r','l',0 };
878 static const WCHAR pkcs7sig[] = { 'a','p','p','l','i','c','a','t','i','o','n',
879 '/','x','-','p','k','c','s','7','-','s','i','g','n','a','t','u','r','e',0 };
880 static const WCHAR pkcs7mime[] = { 'a','p','p','l','i','c','a','t','i','o','n',
881 '/','x','-','p','k','c','s','7','-','m','i','m','e',0 };
882
883 static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
884 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
885 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
886 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
887 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
888 {
889 BOOL ret = FALSE;
890
891 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
892 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
893 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
894
895 pObject->cBlob = 0;
896 pObject->rgBlob = NULL;
897 *ppfnFreeObject = CRYPT_FreeBlob;
898 *ppvFreeContext = NULL;
899
900 if (!(dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL))
901 ret = CRYPT_GetObjectFromCache(pszURL, pObject, pAuxInfo);
902 if (!ret && (!(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ||
903 (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)))
904 {
905 URL_COMPONENTSW components;
906
907 if ((ret = CRYPT_CrackUrl(pszURL, &components)))
908 {
909 HINTERNET hInt, hHost;
910 struct InetContext *context = NULL;
911
912 if (dwTimeout)
913 context = CRYPT_MakeInetContext(dwTimeout);
914 ret = CRYPT_Connect(&components, context, pCredentials, &hInt,
915 &hHost);
916 if (ret)
917 {
918 static LPCWSTR types[] = { x509cacert, x509emailcert,
919 x509servercert, x509usercert, pkcs7cert, pkixCRL, pkcs7CRL,
920 pkcs7sig, pkcs7mime, NULL };
921 HINTERNET hHttp = HttpOpenRequestW(hHost, NULL,
922 components.lpszUrlPath, NULL, NULL, types,
923 INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
924 (DWORD_PTR)context);
925
926 if (hHttp)
927 {
928 if (dwTimeout)
929 {
930 InternetSetOptionW(hHttp,
931 INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout,
932 sizeof(dwTimeout));
933 InternetSetOptionW(hHttp, INTERNET_OPTION_SEND_TIMEOUT,
934 &dwTimeout, sizeof(dwTimeout));
935 }
936 ret = HttpSendRequestExW(hHttp, NULL, NULL, 0,
937 (DWORD_PTR)context);
938 if (!ret && GetLastError() == ERROR_IO_PENDING)
939 {
940 if (WaitForSingleObject(context->event,
941 context->timeout) == WAIT_TIMEOUT)
942 SetLastError(ERROR_TIMEOUT);
943 else
944 ret = TRUE;
945 }
946 if (ret &&
947 !(ret = HttpEndRequestW(hHttp, NULL, 0, (DWORD_PTR)context)) &&
948 GetLastError() == ERROR_IO_PENDING)
949 {
950 if (WaitForSingleObject(context->event,
951 context->timeout) == WAIT_TIMEOUT)
952 SetLastError(ERROR_TIMEOUT);
953 else
954 ret = TRUE;
955 }
956 if (ret)
957 ret = CRYPT_DownloadObject(dwRetrievalFlags, hHttp,
958 context, pObject, pAuxInfo);
959 if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT))
960 {
961 SYSTEMTIME st;
962 FILETIME ft;
963 DWORD len = sizeof(st);
964
965 if (HttpQueryInfoW(hHttp, HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME,
966 &st, &len, NULL) && SystemTimeToFileTime(&st, &ft))
967 CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags, ft);
968 }
969 InternetCloseHandle(hHttp);
970 }
971 InternetCloseHandle(hHost);
972 InternetCloseHandle(hInt);
973 }
974 if (context)
975 {
976 CloseHandle(context->event);
977 CryptMemFree(context);
978 }
979 CryptMemFree(components.lpszUrlPath);
980 CryptMemFree(components.lpszHostName);
981 }
982 }
983 TRACE("returning %d\n", ret);
984 return ret;
985 }
986
987 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
988 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
989 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
990 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
991 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
992 {
993 URL_COMPONENTSW components = { sizeof(components), 0 };
994 BOOL ret;
995
996 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
997 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
998 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
999
1000 pObject->cBlob = 0;
1001 pObject->rgBlob = NULL;
1002 *ppfnFreeObject = CRYPT_FreeBlob;
1003 *ppvFreeContext = NULL;
1004
1005 components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
1006 components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
1007 if (!components.lpszUrlPath)
1008 {
1009 SetLastError(ERROR_OUTOFMEMORY);
1010 return FALSE;
1011 }
1012
1013 ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
1014 if (ret)
1015 {
1016 LPWSTR path;
1017
1018 /* 3 == lstrlenW(L"c:") + 1 */
1019 path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
1020 if (path)
1021 {
1022 HANDLE hFile;
1023
1024 /* Try to create the file directly - Wine handles / in pathnames */
1025 lstrcpynW(path, components.lpszUrlPath,
1026 components.dwUrlPathLength + 1);
1027 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1028 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1029 if (hFile == INVALID_HANDLE_VALUE)
1030 {
1031 /* Try again on the current drive */
1032 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1033 if (path[1] == ':')
1034 {
1035 lstrcpynW(path + 2, components.lpszUrlPath,
1036 components.dwUrlPathLength + 1);
1037 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1038 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1039 }
1040 if (hFile == INVALID_HANDLE_VALUE)
1041 {
1042 /* Try again on the Windows drive */
1043 GetWindowsDirectoryW(path, components.dwUrlPathLength);
1044 if (path[1] == ':')
1045 {
1046 lstrcpynW(path + 2, components.lpszUrlPath,
1047 components.dwUrlPathLength + 1);
1048 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1049 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1050 }
1051 }
1052 }
1053 if (hFile != INVALID_HANDLE_VALUE)
1054 {
1055 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1056 {
1057 if (pAuxInfo && pAuxInfo->cbSize >=
1058 offsetof(CRYPT_RETRIEVE_AUX_INFO,
1059 pLastSyncTime) + sizeof(PFILETIME) &&
1060 pAuxInfo->pLastSyncTime)
1061 GetFileTime(hFile, NULL, NULL,
1062 pAuxInfo->pLastSyncTime);
1063 }
1064 CloseHandle(hFile);
1065 }
1066 else
1067 ret = FALSE;
1068 CryptMemFree(path);
1069 }
1070 else
1071 {
1072 SetLastError(ERROR_OUTOFMEMORY);
1073 ret = FALSE;
1074 }
1075 }
1076 CryptMemFree(components.lpszUrlPath);
1077 return ret;
1078 }
1079
1080 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1081 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1082 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1083 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1084 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1085
1086 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1087 SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1088 {
1089 URL_COMPONENTSW components = { sizeof(components), 0 };
1090 BOOL ret;
1091
1092 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1093
1094 *pFunc = NULL;
1095 *phFunc = 0;
1096 components.dwSchemeLength = 1;
1097 ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1098 if (ret)
1099 {
1100 /* Microsoft always uses CryptInitOIDFunctionSet/
1101 * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1102 * reason to do so for builtin schemes.
1103 */
1104 switch (components.nScheme)
1105 {
1106 case INTERNET_SCHEME_FTP:
1107 *pFunc = FTP_RetrieveEncodedObjectW;
1108 break;
1109 case INTERNET_SCHEME_HTTP:
1110 *pFunc = HTTP_RetrieveEncodedObjectW;
1111 break;
1112 case INTERNET_SCHEME_FILE:
1113 *pFunc = File_RetrieveEncodedObjectW;
1114 break;
1115 default:
1116 {
1117 int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1118 components.dwSchemeLength, NULL, 0, NULL, NULL);
1119
1120 if (len)
1121 {
1122 LPSTR scheme = CryptMemAlloc(len);
1123
1124 if (scheme)
1125 {
1126 static HCRYPTOIDFUNCSET set = NULL;
1127
1128 if (!set)
1129 set = CryptInitOIDFunctionSet(
1130 SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1131 WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1132 components.dwSchemeLength, scheme, len, NULL, NULL);
1133 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1134 scheme, 0, (void **)pFunc, phFunc);
1135 CryptMemFree(scheme);
1136 }
1137 else
1138 {
1139 SetLastError(ERROR_OUTOFMEMORY);
1140 ret = FALSE;
1141 }
1142 }
1143 else
1144 ret = FALSE;
1145 }
1146 }
1147 }
1148 TRACE("returning %d\n", ret);
1149 return ret;
1150 }
1151
1152 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1153 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1154 {
1155 DWORD size, i;
1156 CRYPT_BLOB_ARRAY *context;
1157 BOOL ret = FALSE;
1158
1159 size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1160 for (i = 0; i < pObject->cBlob; i++)
1161 size += pObject->rgBlob[i].cbData;
1162 context = CryptMemAlloc(size);
1163 if (context)
1164 {
1165 LPBYTE nextData;
1166
1167 context->cBlob = 0;
1168 context->rgBlob =
1169 (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1170 nextData =
1171 (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1172 for (i = 0; i < pObject->cBlob; i++)
1173 {
1174 memcpy(nextData, pObject->rgBlob[i].pbData,
1175 pObject->rgBlob[i].cbData);
1176 context->rgBlob[i].pbData = nextData;
1177 context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1178 nextData += pObject->rgBlob[i].cbData;
1179 context->cBlob++;
1180 }
1181 *ppvContext = context;
1182 ret = TRUE;
1183 }
1184 return ret;
1185 }
1186
1187 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1188 const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1189
1190 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1191 DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1192 {
1193 BOOL ret = TRUE;
1194
1195 if (!pObject->cBlob)
1196 {
1197 SetLastError(ERROR_INVALID_DATA);
1198 *ppvContext = NULL;
1199 ret = FALSE;
1200 }
1201 else if (pObject->cBlob == 1)
1202 {
1203 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1204 dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
1205 NULL, NULL, NULL, NULL, (const void **)ppvContext))
1206 {
1207 SetLastError(CRYPT_E_NO_MATCH);
1208 ret = FALSE;
1209 }
1210 }
1211 else
1212 {
1213 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1214 CERT_STORE_CREATE_NEW_FLAG, NULL);
1215
1216 if (store)
1217 {
1218 DWORD i;
1219 const void *context;
1220
1221 for (i = 0; i < pObject->cBlob; i++)
1222 {
1223 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1224 &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1225 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1226 NULL, &context))
1227 {
1228 if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1229 ret = FALSE;
1230 }
1231 else
1232 {
1233 SetLastError(CRYPT_E_NO_MATCH);
1234 ret = FALSE;
1235 }
1236 }
1237 }
1238 else
1239 ret = FALSE;
1240 *ppvContext = store;
1241 }
1242 return ret;
1243 }
1244
1245 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1246 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1247 {
1248 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1249 (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1250 }
1251
1252 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1253 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1254 {
1255 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1256 (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1257 }
1258
1259 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1260 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1261 {
1262 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1263 (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1264 }
1265
1266 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1267 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1268 {
1269 BOOL ret;
1270
1271 if (!pObject->cBlob)
1272 {
1273 SetLastError(ERROR_INVALID_DATA);
1274 *ppvContext = NULL;
1275 ret = FALSE;
1276 }
1277 else if (pObject->cBlob == 1)
1278 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1279 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1280 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
1281 0, NULL, NULL, NULL, ppvContext, NULL, NULL);
1282 else
1283 {
1284 FIXME("multiple messages unimplemented\n");
1285 ret = FALSE;
1286 }
1287 return ret;
1288 }
1289
1290 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1291 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1292 {
1293 BOOL ret;
1294
1295 if (!pObject->cBlob)
1296 {
1297 SetLastError(ERROR_INVALID_DATA);
1298 *ppvContext = NULL;
1299 ret = FALSE;
1300 }
1301 else
1302 {
1303 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1304 CERT_STORE_CREATE_NEW_FLAG, NULL);
1305
1306 if (store)
1307 {
1308 HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1309 CERT_STORE_CREATE_NEW_FLAG, NULL);
1310
1311 if (memStore)
1312 {
1313 CertAddStoreToCollection(store, memStore,
1314 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1315 CertCloseStore(memStore, 0);
1316 }
1317 else
1318 {
1319 CertCloseStore(store, 0);
1320 store = NULL;
1321 }
1322 }
1323 if (store)
1324 {
1325 DWORD i;
1326
1327 ret = TRUE;
1328 for (i = 0; i < pObject->cBlob; i++)
1329 {
1330 DWORD contentType, expectedContentTypes =
1331 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1332 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
1333 CERT_QUERY_CONTENT_FLAG_CERT |
1334 CERT_QUERY_CONTENT_FLAG_CRL |
1335 CERT_QUERY_CONTENT_FLAG_CTL;
1336 HCERTSTORE contextStore;
1337 const void *context;
1338
1339 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1340 &pObject->rgBlob[i], expectedContentTypes,
1341 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
1342 &contextStore, NULL, &context))
1343 {
1344 switch (contentType)
1345 {
1346 case CERT_QUERY_CONTENT_CERT:
1347 if (!CertAddCertificateContextToStore(store,
1348 context, CERT_STORE_ADD_ALWAYS, NULL))
1349 ret = FALSE;
1350 CertFreeCertificateContext(context);
1351 break;
1352 case CERT_QUERY_CONTENT_CRL:
1353 if (!CertAddCRLContextToStore(store,
1354 context, CERT_STORE_ADD_ALWAYS, NULL))
1355 ret = FALSE;
1356 CertFreeCRLContext(context);
1357 break;
1358 case CERT_QUERY_CONTENT_CTL:
1359 if (!CertAddCTLContextToStore(store,
1360 context, CERT_STORE_ADD_ALWAYS, NULL))
1361 ret = FALSE;
1362 CertFreeCTLContext(context);
1363 break;
1364 default:
1365 CertAddStoreToCollection(store, contextStore, 0, 0);
1366 }
1367 CertCloseStore(contextStore, 0);
1368 }
1369 else
1370 ret = FALSE;
1371 }
1372 }
1373 else
1374 ret = FALSE;
1375 *ppvContext = store;
1376 }
1377 return ret;
1378 }
1379
1380 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1381 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1382
1383 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1384 ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1385 {
1386 BOOL ret = TRUE;
1387
1388 TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1389
1390 *pFunc = NULL;
1391 *phFunc = 0;
1392 if (IS_INTOID(pszObjectOid))
1393 {
1394 switch (LOWORD(pszObjectOid))
1395 {
1396 case 0:
1397 *pFunc = CRYPT_CreateBlob;
1398 break;
1399 case LOWORD(CONTEXT_OID_CERTIFICATE):
1400 *pFunc = CRYPT_CreateCert;
1401 break;
1402 case LOWORD(CONTEXT_OID_CRL):
1403 *pFunc = CRYPT_CreateCRL;
1404 break;
1405 case LOWORD(CONTEXT_OID_CTL):
1406 *pFunc = CRYPT_CreateCTL;
1407 break;
1408 case LOWORD(CONTEXT_OID_PKCS7):
1409 *pFunc = CRYPT_CreatePKCS7;
1410 break;
1411 case LOWORD(CONTEXT_OID_CAPI2_ANY):
1412 *pFunc = CRYPT_CreateAny;
1413 break;
1414 }
1415 }
1416 if (!*pFunc)
1417 {
1418 static HCRYPTOIDFUNCSET set = NULL;
1419
1420 if (!set)
1421 set = CryptInitOIDFunctionSet(
1422 CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
1423 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
1424 0, (void **)pFunc, phFunc);
1425 }
1426 TRACE("returning %d\n", ret);
1427 return ret;
1428 }
1429
1430 typedef BOOL (*get_object_expiration_func)(const void *pvContext,
1431 FILETIME *expiration);
1432
1433 static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration)
1434 {
1435 PCCERT_CONTEXT cert = pvObject;
1436
1437 *expiration = cert->pCertInfo->NotAfter;
1438 return TRUE;
1439 }
1440
1441 static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration)
1442 {
1443 PCCRL_CONTEXT cert = pvObject;
1444
1445 *expiration = cert->pCrlInfo->NextUpdate;
1446 return TRUE;
1447 }
1448
1449 static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration)
1450 {
1451 PCCTL_CONTEXT cert = pvObject;
1452
1453 *expiration = cert->pCtlInfo->NextUpdate;
1454 return TRUE;
1455 }
1456
1457 static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid,
1458 get_object_expiration_func *getExpiration)
1459 {
1460 BOOL ret;
1461
1462 if (IS_INTOID(pszObjectOid))
1463 {
1464 switch (LOWORD(pszObjectOid))
1465 {
1466 case LOWORD(CONTEXT_OID_CERTIFICATE):
1467 *getExpiration = CRYPT_GetExpirationFromCert;
1468 ret = TRUE;
1469 break;
1470 case LOWORD(CONTEXT_OID_CRL):
1471 *getExpiration = CRYPT_GetExpirationFromCRL;
1472 ret = TRUE;
1473 break;
1474 case LOWORD(CONTEXT_OID_CTL):
1475 *getExpiration = CRYPT_GetExpirationFromCTL;
1476 ret = TRUE;
1477 break;
1478 default:
1479 ret = FALSE;
1480 }
1481 }
1482 else
1483 ret = FALSE;
1484 return ret;
1485 }
1486
1487 /***********************************************************************
1488 * CryptRetrieveObjectByUrlW (CRYPTNET.@)
1489 */
1490 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1491 DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1492 HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1493 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1494 {
1495 BOOL ret;
1496 SchemeDllRetrieveEncodedObjectW retrieve;
1497 ContextDllCreateObjectContext create;
1498 HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1499
1500 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1501 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1502 hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1503
1504 if (!pszURL)
1505 {
1506 SetLastError(ERROR_INVALID_PARAMETER);
1507 return FALSE;
1508 }
1509 ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1510 if (ret)
1511 ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1512 if (ret)
1513 {
1514 CRYPT_BLOB_ARRAY object = { 0, NULL };
1515 PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1516 void *freeContext;
1517
1518 ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1519 &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1520 pAuxInfo);
1521 if (ret)
1522 {
1523 get_object_expiration_func getExpiration;
1524
1525 ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1526 if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1527 CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration))
1528 {
1529 FILETIME expires;
1530
1531 if (getExpiration(*ppvObject, &expires))
1532 CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1533 }
1534 freeObject(pszObjectOid, &object, freeContext);
1535 }
1536 }
1537 if (hCreate)
1538 CryptFreeOIDFunctionAddress(hCreate, 0);
1539 if (hRetrieve)
1540 CryptFreeOIDFunctionAddress(hRetrieve, 0);
1541 TRACE("returning %d\n", ret);
1542 return ret;
1543 }
1544
1545 static DWORD verify_cert_revocation_with_crl_online(PCCERT_CONTEXT cert,
1546 PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1547 PCERT_REVOCATION_STATUS pRevStatus)
1548 {
1549 DWORD error;
1550 PCRL_ENTRY entry = NULL;
1551
1552 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1553 if (entry)
1554 {
1555 error = CRYPT_E_REVOKED;
1556 pRevStatus->dwIndex = index;
1557 }
1558 else
1559 {
1560 /* Since the CRL was retrieved for the cert being checked, then it's
1561 * guaranteed to be fresh, and the cert is not revoked.
1562 */
1563 error = ERROR_SUCCESS;
1564 }
1565 return error;
1566 }
1567
1568 static DWORD verify_cert_revocation_from_dist_points_ext(
1569 const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1570 FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara,
1571 PCERT_REVOCATION_STATUS pRevStatus)
1572 {
1573 DWORD error = ERROR_SUCCESS, cbUrlArray;
1574
1575 if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL))
1576 {
1577 CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray);
1578
1579 if (urlArray)
1580 {
1581 DWORD j, retrievalFlags = 0, startTime, endTime, timeout;
1582 BOOL ret;
1583
1584 ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray,
1585 &cbUrlArray, NULL, NULL);
1586 if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1587 retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1588 if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG &&
1589 pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA,
1590 dwUrlRetrievalTimeout) + sizeof(DWORD))
1591 {
1592 startTime = GetTickCount();
1593 endTime = startTime + pRevPara->dwUrlRetrievalTimeout;
1594 timeout = pRevPara->dwUrlRetrievalTimeout;
1595 }
1596 else
1597 endTime = timeout = 0;
1598 if (!ret)
1599 error = GetLastError();
1600 for (j = 0; !error && j < urlArray->cUrl; j++)
1601 {
1602 PCCRL_CONTEXT crl;
1603
1604 ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j],
1605 CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl,
1606 NULL, NULL, NULL, NULL);
1607 if (ret)
1608 {
1609 error = verify_cert_revocation_with_crl_online(cert, crl,
1610 index, pTime, pRevStatus);
1611 if (!error && timeout)
1612 {
1613 DWORD time = GetTickCount();
1614
1615 if ((int)(endTime - time) <= 0)
1616 {
1617 error = ERROR_TIMEOUT;
1618 pRevStatus->dwIndex = index;
1619 }
1620 else
1621 timeout = endTime - time;
1622 }
1623 CertFreeCRLContext(crl);
1624 }
1625 else
1626 error = CRYPT_E_REVOCATION_OFFLINE;
1627 }
1628 CryptMemFree(urlArray);
1629 }
1630 else
1631 {
1632 error = ERROR_OUTOFMEMORY;
1633 pRevStatus->dwIndex = index;
1634 }
1635 }
1636 else
1637 {
1638 error = GetLastError();
1639 pRevStatus->dwIndex = index;
1640 }
1641 return error;
1642 }
1643
1644 static DWORD verify_cert_revocation_from_aia_ext(
1645 const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1646 FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1647 PCERT_REVOCATION_STATUS pRevStatus)
1648 {
1649 BOOL ret;
1650 DWORD error, size;
1651 CERT_AUTHORITY_INFO_ACCESS *aia;
1652
1653 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
1654 value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
1655 if (ret)
1656 {
1657 DWORD i;
1658
1659 for (i = 0; i < aia->cAccDescr; i++)
1660 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
1661 szOID_PKIX_OCSP))
1662 {
1663 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
1664 CERT_ALT_NAME_URL)
1665 FIXME("OCSP URL = %s\n",
1666 debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL));
1667 else
1668 FIXME("unsupported AccessLocation type %d\n",
1669 aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
1670 }
1671 LocalFree(aia);
1672 /* FIXME: lie and pretend OCSP validated the cert */
1673 error = ERROR_SUCCESS;
1674 }
1675 else
1676 error = GetLastError();
1677 return error;
1678 }
1679
1680 static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert,
1681 PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1682 PCERT_REVOCATION_STATUS pRevStatus)
1683 {
1684 DWORD error;
1685 LONG valid;
1686
1687 valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate);
1688 if (valid <= 0)
1689 {
1690 /* If this CRL is not older than the time being verified, there's no
1691 * way to know whether the certificate was revoked.
1692 */
1693 TRACE("CRL not old enough\n");
1694 error = CRYPT_E_REVOCATION_OFFLINE;
1695 }
1696 else
1697 {
1698 PCRL_ENTRY entry = NULL;
1699
1700 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1701 if (entry)
1702 {
1703 error = CRYPT_E_REVOKED;
1704 pRevStatus->dwIndex = index;
1705 }
1706 else
1707 {
1708 /* Since the CRL was not retrieved for the cert being checked,
1709 * there's no guarantee it's fresh, so the cert *might* be okay,
1710 * but it's safer not to guess.
1711 */
1712 TRACE("certificate not found\n");
1713 error = CRYPT_E_REVOCATION_OFFLINE;
1714 }
1715 }
1716 return error;
1717 }
1718
1719 static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index,
1720 FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1721 PCERT_REVOCATION_STATUS pRevStatus)
1722 {
1723 DWORD error = ERROR_SUCCESS;
1724 PCERT_EXTENSION ext;
1725
1726 if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
1727 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1728 error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert,
1729 index, pTime, dwFlags, pRevPara, pRevStatus);
1730 else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
1731 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1732 error = verify_cert_revocation_from_aia_ext(&ext->Value, cert,
1733 index, pTime, dwFlags, pRevPara, pRevStatus);
1734 else
1735 {
1736 if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
1737 {
1738 PCCRL_CONTEXT crl = NULL;
1739 BOOL canSignCRLs;
1740
1741 /* If the caller told us about the issuer, make sure the issuer
1742 * can sign CRLs before looking for one.
1743 */
1744 if ((ext = CertFindExtension(szOID_KEY_USAGE,
1745 pRevPara->pIssuerCert->pCertInfo->cExtension,
1746 pRevPara->pIssuerCert->pCertInfo->rgExtension)))
1747 {
1748 CRYPT_BIT_BLOB usage;
1749 DWORD size = sizeof(usage);
1750
1751 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1752 ext->Value.pbData, ext->Value.cbData,
1753 CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1754 canSignCRLs = FALSE;
1755 else if (usage.cbData > 2)
1756 {
1757 /* The key usage extension only defines 9 bits => no more
1758 * than 2 bytes are needed to encode all known usages.
1759 */
1760 canSignCRLs = FALSE;
1761 }
1762 else
1763 {
1764 BYTE usageBits = usage.pbData[usage.cbData - 1];
1765
1766 canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
1767 }
1768 }
1769 else
1770 canSignCRLs = TRUE;
1771 if (canSignCRLs)
1772 {
1773 /* If the caller was helpful enough to tell us where to find a
1774 * CRL for the cert, look for one and check it.
1775 */
1776 crl = CertFindCRLInStore(pRevPara->hCrlStore,
1777 cert->dwCertEncodingType,
1778 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
1779 CRL_FIND_ISSUED_BY_AKI_FLAG,
1780 CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
1781 }
1782 if (crl)
1783 {
1784 error = verify_cert_revocation_with_crl_offline(cert, crl,
1785 index, pTime, pRevStatus);
1786 CertFreeCRLContext(crl);
1787 }
1788 else
1789 {
1790 TRACE("no CRL found\n");
1791 error = CRYPT_E_NO_REVOCATION_CHECK;
1792 pRevStatus->dwIndex = index;
1793 }
1794 }
1795 else
1796 {
1797 if (!pRevPara)
1798 WARN("no CERT_REVOCATION_PARA\n");
1799 else if (!pRevPara->hCrlStore)
1800 WARN("no dist points/aia extension and no CRL store\n");
1801 else if (!pRevPara->pIssuerCert)
1802 WARN("no dist points/aia extension and no issuer\n");
1803 error = CRYPT_E_NO_REVOCATION_CHECK;
1804 pRevStatus->dwIndex = index;
1805 }
1806 }
1807 return error;
1808 }
1809
1810 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
1811 DWORD cbSize;
1812 PCCERT_CONTEXT pIssuerCert;
1813 DWORD cCertStore;
1814 HCERTSTORE *rgCertStore;
1815 HCERTSTORE hCrlStore;
1816 LPFILETIME pftTimeToUse;
1817 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
1818
1819 typedef struct _OLD_CERT_REVOCATION_STATUS {
1820 DWORD cbSize;
1821 DWORD dwIndex;
1822 DWORD dwError;
1823 DWORD dwReason;
1824 } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
1825
1826 /***********************************************************************
1827 * CertDllVerifyRevocation (CRYPTNET.@)
1828 */
1829 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1830 DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1831 PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1832 {
1833 DWORD error = 0, i;
1834 FILETIME now;
1835 LPFILETIME pTime = NULL;
1836
1837 TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1838 cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1839
1840 if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1841 pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1842 {
1843 SetLastError(E_INVALIDARG);
1844 return FALSE;
1845 }
1846 if (!cContext)
1847 {
1848 SetLastError(E_INVALIDARG);
1849 return FALSE;
1850 }
1851 if (pRevPara && pRevPara->cbSize >=
1852 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
1853 pTime = pRevPara->pftTimeToUse;
1854 if (!pTime)
1855 {
1856 GetSystemTimeAsFileTime(&now);
1857 pTime = &now;
1858 }
1859 memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
1860 if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
1861 error = CRYPT_E_NO_REVOCATION_CHECK;
1862 else
1863 {
1864 for (i = 0; !error && i < cContext; i++)
1865 error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags,
1866 pRevPara, pRevStatus);
1867 }
1868 if (error)
1869 {
1870 SetLastError(error);
1871 pRevStatus->dwError = error;
1872 }
1873 TRACE("returning %d (%08x)\n", !error, error);
1874 return !error;
1875 }