Sync with trunk r58740.
[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 DWORD len = sizeof(st);
963
964 if (HttpQueryInfoW(hHttp,
965 HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME, &st,
966 &len, NULL))
967 {
968 FILETIME ft;
969
970 SystemTimeToFileTime(&st, &ft);
971 CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags,
972 ft);
973 }
974 }
975 InternetCloseHandle(hHttp);
976 }
977 InternetCloseHandle(hHost);
978 InternetCloseHandle(hInt);
979 }
980 if (context)
981 {
982 CloseHandle(context->event);
983 CryptMemFree(context);
984 }
985 CryptMemFree(components.lpszUrlPath);
986 CryptMemFree(components.lpszHostName);
987 }
988 }
989 TRACE("returning %d\n", ret);
990 return ret;
991 }
992
993 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
994 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
995 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
996 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
997 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
998 {
999 URL_COMPONENTSW components = { sizeof(components), 0 };
1000 BOOL ret;
1001
1002 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1003 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
1004 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
1005
1006 pObject->cBlob = 0;
1007 pObject->rgBlob = NULL;
1008 *ppfnFreeObject = CRYPT_FreeBlob;
1009 *ppvFreeContext = NULL;
1010
1011 components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
1012 components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
1013 if (!components.lpszUrlPath)
1014 {
1015 SetLastError(ERROR_OUTOFMEMORY);
1016 return FALSE;
1017 }
1018
1019 ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
1020 if (ret)
1021 {
1022 LPWSTR path;
1023
1024 /* 3 == lstrlenW(L"c:") + 1 */
1025 path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
1026 if (path)
1027 {
1028 HANDLE hFile;
1029
1030 /* Try to create the file directly - Wine handles / in pathnames */
1031 lstrcpynW(path, components.lpszUrlPath,
1032 components.dwUrlPathLength + 1);
1033 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1034 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1035 if (hFile == INVALID_HANDLE_VALUE)
1036 {
1037 /* Try again on the current drive */
1038 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1039 if (path[1] == ':')
1040 {
1041 lstrcpynW(path + 2, components.lpszUrlPath,
1042 components.dwUrlPathLength + 1);
1043 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1044 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1045 }
1046 if (hFile == INVALID_HANDLE_VALUE)
1047 {
1048 /* Try again on the Windows drive */
1049 GetWindowsDirectoryW(path, components.dwUrlPathLength);
1050 if (path[1] == ':')
1051 {
1052 lstrcpynW(path + 2, components.lpszUrlPath,
1053 components.dwUrlPathLength + 1);
1054 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1055 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1056 }
1057 }
1058 }
1059 if (hFile != INVALID_HANDLE_VALUE)
1060 {
1061 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1062 {
1063 if (pAuxInfo && pAuxInfo->cbSize >=
1064 offsetof(CRYPT_RETRIEVE_AUX_INFO,
1065 pLastSyncTime) + sizeof(PFILETIME) &&
1066 pAuxInfo->pLastSyncTime)
1067 GetFileTime(hFile, NULL, NULL,
1068 pAuxInfo->pLastSyncTime);
1069 }
1070 CloseHandle(hFile);
1071 }
1072 else
1073 ret = FALSE;
1074 CryptMemFree(path);
1075 }
1076 else
1077 {
1078 SetLastError(ERROR_OUTOFMEMORY);
1079 ret = FALSE;
1080 }
1081 }
1082 CryptMemFree(components.lpszUrlPath);
1083 return ret;
1084 }
1085
1086 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1087 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1088 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1089 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1090 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1091
1092 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1093 SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1094 {
1095 URL_COMPONENTSW components = { sizeof(components), 0 };
1096 BOOL ret;
1097
1098 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1099
1100 *pFunc = NULL;
1101 *phFunc = 0;
1102 components.dwSchemeLength = 1;
1103 ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1104 if (ret)
1105 {
1106 /* Microsoft always uses CryptInitOIDFunctionSet/
1107 * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1108 * reason to do so for builtin schemes.
1109 */
1110 switch (components.nScheme)
1111 {
1112 case INTERNET_SCHEME_FTP:
1113 *pFunc = FTP_RetrieveEncodedObjectW;
1114 break;
1115 case INTERNET_SCHEME_HTTP:
1116 *pFunc = HTTP_RetrieveEncodedObjectW;
1117 break;
1118 case INTERNET_SCHEME_FILE:
1119 *pFunc = File_RetrieveEncodedObjectW;
1120 break;
1121 default:
1122 {
1123 int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1124 components.dwSchemeLength, NULL, 0, NULL, NULL);
1125
1126 if (len)
1127 {
1128 LPSTR scheme = CryptMemAlloc(len);
1129
1130 if (scheme)
1131 {
1132 static HCRYPTOIDFUNCSET set = NULL;
1133
1134 if (!set)
1135 set = CryptInitOIDFunctionSet(
1136 SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1137 WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1138 components.dwSchemeLength, scheme, len, NULL, NULL);
1139 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1140 scheme, 0, (void **)pFunc, phFunc);
1141 CryptMemFree(scheme);
1142 }
1143 else
1144 {
1145 SetLastError(ERROR_OUTOFMEMORY);
1146 ret = FALSE;
1147 }
1148 }
1149 else
1150 ret = FALSE;
1151 }
1152 }
1153 }
1154 TRACE("returning %d\n", ret);
1155 return ret;
1156 }
1157
1158 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1159 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1160 {
1161 DWORD size, i;
1162 CRYPT_BLOB_ARRAY *context;
1163 BOOL ret = FALSE;
1164
1165 size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1166 for (i = 0; i < pObject->cBlob; i++)
1167 size += pObject->rgBlob[i].cbData;
1168 context = CryptMemAlloc(size);
1169 if (context)
1170 {
1171 LPBYTE nextData;
1172
1173 context->cBlob = 0;
1174 context->rgBlob =
1175 (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1176 nextData =
1177 (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1178 for (i = 0; i < pObject->cBlob; i++)
1179 {
1180 memcpy(nextData, pObject->rgBlob[i].pbData,
1181 pObject->rgBlob[i].cbData);
1182 context->rgBlob[i].pbData = nextData;
1183 context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1184 nextData += pObject->rgBlob[i].cbData;
1185 context->cBlob++;
1186 }
1187 *ppvContext = context;
1188 ret = TRUE;
1189 }
1190 return ret;
1191 }
1192
1193 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1194 const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1195
1196 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1197 DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1198 {
1199 BOOL ret = TRUE;
1200
1201 if (!pObject->cBlob)
1202 {
1203 SetLastError(ERROR_INVALID_DATA);
1204 *ppvContext = NULL;
1205 ret = FALSE;
1206 }
1207 else if (pObject->cBlob == 1)
1208 {
1209 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1210 dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
1211 NULL, NULL, NULL, NULL, (const void **)ppvContext))
1212 {
1213 SetLastError(CRYPT_E_NO_MATCH);
1214 ret = FALSE;
1215 }
1216 }
1217 else
1218 {
1219 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1220 CERT_STORE_CREATE_NEW_FLAG, NULL);
1221
1222 if (store)
1223 {
1224 DWORD i;
1225 const void *context;
1226
1227 for (i = 0; i < pObject->cBlob; i++)
1228 {
1229 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1230 &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1231 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1232 NULL, &context))
1233 {
1234 if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1235 ret = FALSE;
1236 }
1237 else
1238 {
1239 SetLastError(CRYPT_E_NO_MATCH);
1240 ret = FALSE;
1241 }
1242 }
1243 }
1244 else
1245 ret = FALSE;
1246 *ppvContext = store;
1247 }
1248 return ret;
1249 }
1250
1251 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1252 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1253 {
1254 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1255 (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1256 }
1257
1258 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1259 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1260 {
1261 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1262 (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1263 }
1264
1265 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1266 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1267 {
1268 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1269 (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1270 }
1271
1272 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1273 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1274 {
1275 BOOL ret;
1276
1277 if (!pObject->cBlob)
1278 {
1279 SetLastError(ERROR_INVALID_DATA);
1280 *ppvContext = NULL;
1281 ret = FALSE;
1282 }
1283 else if (pObject->cBlob == 1)
1284 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1285 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1286 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
1287 0, NULL, NULL, NULL, ppvContext, NULL, NULL);
1288 else
1289 {
1290 FIXME("multiple messages unimplemented\n");
1291 ret = FALSE;
1292 }
1293 return ret;
1294 }
1295
1296 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1297 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1298 {
1299 BOOL ret;
1300
1301 if (!pObject->cBlob)
1302 {
1303 SetLastError(ERROR_INVALID_DATA);
1304 *ppvContext = NULL;
1305 ret = FALSE;
1306 }
1307 else
1308 {
1309 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1310 CERT_STORE_CREATE_NEW_FLAG, NULL);
1311
1312 if (store)
1313 {
1314 HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1315 CERT_STORE_CREATE_NEW_FLAG, NULL);
1316
1317 if (memStore)
1318 {
1319 CertAddStoreToCollection(store, memStore,
1320 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1321 CertCloseStore(memStore, 0);
1322 }
1323 else
1324 {
1325 CertCloseStore(store, 0);
1326 store = NULL;
1327 }
1328 }
1329 if (store)
1330 {
1331 DWORD i;
1332
1333 ret = TRUE;
1334 for (i = 0; i < pObject->cBlob; i++)
1335 {
1336 DWORD contentType, expectedContentTypes =
1337 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1338 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
1339 CERT_QUERY_CONTENT_FLAG_CERT |
1340 CERT_QUERY_CONTENT_FLAG_CRL |
1341 CERT_QUERY_CONTENT_FLAG_CTL;
1342 HCERTSTORE contextStore;
1343 const void *context;
1344
1345 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1346 &pObject->rgBlob[i], expectedContentTypes,
1347 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
1348 &contextStore, NULL, &context))
1349 {
1350 switch (contentType)
1351 {
1352 case CERT_QUERY_CONTENT_CERT:
1353 if (!CertAddCertificateContextToStore(store,
1354 context, CERT_STORE_ADD_ALWAYS, NULL))
1355 ret = FALSE;
1356 CertFreeCertificateContext(context);
1357 break;
1358 case CERT_QUERY_CONTENT_CRL:
1359 if (!CertAddCRLContextToStore(store,
1360 context, CERT_STORE_ADD_ALWAYS, NULL))
1361 ret = FALSE;
1362 CertFreeCRLContext(context);
1363 break;
1364 case CERT_QUERY_CONTENT_CTL:
1365 if (!CertAddCTLContextToStore(store,
1366 context, CERT_STORE_ADD_ALWAYS, NULL))
1367 ret = FALSE;
1368 CertFreeCTLContext(context);
1369 break;
1370 default:
1371 CertAddStoreToCollection(store, contextStore, 0, 0);
1372 }
1373 CertCloseStore(contextStore, 0);
1374 }
1375 else
1376 ret = FALSE;
1377 }
1378 }
1379 else
1380 ret = FALSE;
1381 *ppvContext = store;
1382 }
1383 return ret;
1384 }
1385
1386 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1387 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1388
1389 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1390 ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1391 {
1392 BOOL ret = TRUE;
1393
1394 TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1395
1396 *pFunc = NULL;
1397 *phFunc = 0;
1398 if (IS_INTOID(pszObjectOid))
1399 {
1400 switch (LOWORD(pszObjectOid))
1401 {
1402 case 0:
1403 *pFunc = CRYPT_CreateBlob;
1404 break;
1405 case LOWORD(CONTEXT_OID_CERTIFICATE):
1406 *pFunc = CRYPT_CreateCert;
1407 break;
1408 case LOWORD(CONTEXT_OID_CRL):
1409 *pFunc = CRYPT_CreateCRL;
1410 break;
1411 case LOWORD(CONTEXT_OID_CTL):
1412 *pFunc = CRYPT_CreateCTL;
1413 break;
1414 case LOWORD(CONTEXT_OID_PKCS7):
1415 *pFunc = CRYPT_CreatePKCS7;
1416 break;
1417 case LOWORD(CONTEXT_OID_CAPI2_ANY):
1418 *pFunc = CRYPT_CreateAny;
1419 break;
1420 }
1421 }
1422 if (!*pFunc)
1423 {
1424 static HCRYPTOIDFUNCSET set = NULL;
1425
1426 if (!set)
1427 set = CryptInitOIDFunctionSet(
1428 CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
1429 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
1430 0, (void **)pFunc, phFunc);
1431 }
1432 TRACE("returning %d\n", ret);
1433 return ret;
1434 }
1435
1436 typedef BOOL (*get_object_expiration_func)(const void *pvContext,
1437 FILETIME *expiration);
1438
1439 static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration)
1440 {
1441 PCCERT_CONTEXT cert = pvObject;
1442
1443 *expiration = cert->pCertInfo->NotAfter;
1444 return TRUE;
1445 }
1446
1447 static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration)
1448 {
1449 PCCRL_CONTEXT cert = pvObject;
1450
1451 *expiration = cert->pCrlInfo->NextUpdate;
1452 return TRUE;
1453 }
1454
1455 static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration)
1456 {
1457 PCCTL_CONTEXT cert = pvObject;
1458
1459 *expiration = cert->pCtlInfo->NextUpdate;
1460 return TRUE;
1461 }
1462
1463 static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid,
1464 get_object_expiration_func *getExpiration)
1465 {
1466 BOOL ret;
1467
1468 if (IS_INTOID(pszObjectOid))
1469 {
1470 switch (LOWORD(pszObjectOid))
1471 {
1472 case LOWORD(CONTEXT_OID_CERTIFICATE):
1473 *getExpiration = CRYPT_GetExpirationFromCert;
1474 ret = TRUE;
1475 break;
1476 case LOWORD(CONTEXT_OID_CRL):
1477 *getExpiration = CRYPT_GetExpirationFromCRL;
1478 ret = TRUE;
1479 break;
1480 case LOWORD(CONTEXT_OID_CTL):
1481 *getExpiration = CRYPT_GetExpirationFromCTL;
1482 ret = TRUE;
1483 break;
1484 default:
1485 ret = FALSE;
1486 }
1487 }
1488 else
1489 ret = FALSE;
1490 return ret;
1491 }
1492
1493 /***********************************************************************
1494 * CryptRetrieveObjectByUrlW (CRYPTNET.@)
1495 */
1496 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1497 DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1498 HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1499 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1500 {
1501 BOOL ret;
1502 SchemeDllRetrieveEncodedObjectW retrieve;
1503 ContextDllCreateObjectContext create;
1504 HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1505
1506 TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1507 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1508 hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1509
1510 if (!pszURL)
1511 {
1512 SetLastError(ERROR_INVALID_PARAMETER);
1513 return FALSE;
1514 }
1515 ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1516 if (ret)
1517 ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1518 if (ret)
1519 {
1520 CRYPT_BLOB_ARRAY object = { 0, NULL };
1521 PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1522 void *freeContext;
1523
1524 ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1525 &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1526 pAuxInfo);
1527 if (ret)
1528 {
1529 get_object_expiration_func getExpiration;
1530
1531 ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1532 if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1533 CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration))
1534 {
1535 FILETIME expires;
1536
1537 if (getExpiration(*ppvObject, &expires))
1538 CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1539 }
1540 freeObject(pszObjectOid, &object, freeContext);
1541 }
1542 }
1543 if (hCreate)
1544 CryptFreeOIDFunctionAddress(hCreate, 0);
1545 if (hRetrieve)
1546 CryptFreeOIDFunctionAddress(hRetrieve, 0);
1547 TRACE("returning %d\n", ret);
1548 return ret;
1549 }
1550
1551 static DWORD verify_cert_revocation_with_crl_online(PCCERT_CONTEXT cert,
1552 PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1553 PCERT_REVOCATION_STATUS pRevStatus)
1554 {
1555 DWORD error;
1556 PCRL_ENTRY entry = NULL;
1557
1558 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1559 if (entry)
1560 {
1561 error = CRYPT_E_REVOKED;
1562 pRevStatus->dwIndex = index;
1563 }
1564 else
1565 {
1566 /* Since the CRL was retrieved for the cert being checked, then it's
1567 * guaranteed to be fresh, and the cert is not revoked.
1568 */
1569 error = ERROR_SUCCESS;
1570 }
1571 return error;
1572 }
1573
1574 static DWORD verify_cert_revocation_from_dist_points_ext(
1575 const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1576 FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara,
1577 PCERT_REVOCATION_STATUS pRevStatus)
1578 {
1579 DWORD error = ERROR_SUCCESS, cbUrlArray;
1580
1581 if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL))
1582 {
1583 CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray);
1584
1585 if (urlArray)
1586 {
1587 DWORD j, retrievalFlags = 0, startTime, endTime, timeout;
1588 BOOL ret;
1589
1590 ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray,
1591 &cbUrlArray, NULL, NULL);
1592 if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1593 retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1594 if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG &&
1595 pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA,
1596 dwUrlRetrievalTimeout) + sizeof(DWORD))
1597 {
1598 startTime = GetTickCount();
1599 endTime = startTime + pRevPara->dwUrlRetrievalTimeout;
1600 timeout = pRevPara->dwUrlRetrievalTimeout;
1601 }
1602 else
1603 endTime = timeout = 0;
1604 if (!ret)
1605 error = GetLastError();
1606 for (j = 0; !error && j < urlArray->cUrl; j++)
1607 {
1608 PCCRL_CONTEXT crl;
1609
1610 ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j],
1611 CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl,
1612 NULL, NULL, NULL, NULL);
1613 if (ret)
1614 {
1615 error = verify_cert_revocation_with_crl_online(cert, crl,
1616 index, pTime, pRevStatus);
1617 if (!error && timeout)
1618 {
1619 DWORD time = GetTickCount();
1620
1621 if ((int)(endTime - time) <= 0)
1622 {
1623 error = ERROR_TIMEOUT;
1624 pRevStatus->dwIndex = index;
1625 }
1626 else
1627 timeout = endTime - time;
1628 }
1629 CertFreeCRLContext(crl);
1630 }
1631 else
1632 error = CRYPT_E_REVOCATION_OFFLINE;
1633 }
1634 CryptMemFree(urlArray);
1635 }
1636 else
1637 {
1638 error = ERROR_OUTOFMEMORY;
1639 pRevStatus->dwIndex = index;
1640 }
1641 }
1642 else
1643 {
1644 error = GetLastError();
1645 pRevStatus->dwIndex = index;
1646 }
1647 return error;
1648 }
1649
1650 static DWORD verify_cert_revocation_from_aia_ext(
1651 const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1652 FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1653 PCERT_REVOCATION_STATUS pRevStatus)
1654 {
1655 BOOL ret;
1656 DWORD error, size;
1657 CERT_AUTHORITY_INFO_ACCESS *aia;
1658
1659 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
1660 value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
1661 if (ret)
1662 {
1663 DWORD i;
1664
1665 for (i = 0; i < aia->cAccDescr; i++)
1666 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
1667 szOID_PKIX_OCSP))
1668 {
1669 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
1670 CERT_ALT_NAME_URL)
1671 FIXME("OCSP URL = %s\n",
1672 debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL));
1673 else
1674 FIXME("unsupported AccessLocation type %d\n",
1675 aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
1676 }
1677 LocalFree(aia);
1678 /* FIXME: lie and pretend OCSP validated the cert */
1679 error = ERROR_SUCCESS;
1680 }
1681 else
1682 error = GetLastError();
1683 return error;
1684 }
1685
1686 static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert,
1687 PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1688 PCERT_REVOCATION_STATUS pRevStatus)
1689 {
1690 DWORD error;
1691 LONG valid;
1692
1693 valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate);
1694 if (valid <= 0)
1695 {
1696 /* If this CRL is not older than the time being verified, there's no
1697 * way to know whether the certificate was revoked.
1698 */
1699 TRACE("CRL not old enough\n");
1700 error = CRYPT_E_REVOCATION_OFFLINE;
1701 }
1702 else
1703 {
1704 PCRL_ENTRY entry = NULL;
1705
1706 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1707 if (entry)
1708 {
1709 error = CRYPT_E_REVOKED;
1710 pRevStatus->dwIndex = index;
1711 }
1712 else
1713 {
1714 /* Since the CRL was not retrieved for the cert being checked,
1715 * there's no guarantee it's fresh, so the cert *might* be okay,
1716 * but it's safer not to guess.
1717 */
1718 TRACE("certificate not found\n");
1719 error = CRYPT_E_REVOCATION_OFFLINE;
1720 }
1721 }
1722 return error;
1723 }
1724
1725 static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index,
1726 FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1727 PCERT_REVOCATION_STATUS pRevStatus)
1728 {
1729 DWORD error = ERROR_SUCCESS;
1730 PCERT_EXTENSION ext;
1731
1732 if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
1733 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1734 error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert,
1735 index, pTime, dwFlags, pRevPara, pRevStatus);
1736 else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
1737 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1738 error = verify_cert_revocation_from_aia_ext(&ext->Value, cert,
1739 index, pTime, dwFlags, pRevPara, pRevStatus);
1740 else
1741 {
1742 if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
1743 {
1744 PCCRL_CONTEXT crl = NULL;
1745 BOOL canSignCRLs;
1746
1747 /* If the caller told us about the issuer, make sure the issuer
1748 * can sign CRLs before looking for one.
1749 */
1750 if ((ext = CertFindExtension(szOID_KEY_USAGE,
1751 pRevPara->pIssuerCert->pCertInfo->cExtension,
1752 pRevPara->pIssuerCert->pCertInfo->rgExtension)))
1753 {
1754 CRYPT_BIT_BLOB usage;
1755 DWORD size = sizeof(usage);
1756
1757 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1758 ext->Value.pbData, ext->Value.cbData,
1759 CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1760 canSignCRLs = FALSE;
1761 else if (usage.cbData > 2)
1762 {
1763 /* The key usage extension only defines 9 bits => no more
1764 * than 2 bytes are needed to encode all known usages.
1765 */
1766 canSignCRLs = FALSE;
1767 }
1768 else
1769 {
1770 BYTE usageBits = usage.pbData[usage.cbData - 1];
1771
1772 canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
1773 }
1774 }
1775 else
1776 canSignCRLs = TRUE;
1777 if (canSignCRLs)
1778 {
1779 /* If the caller was helpful enough to tell us where to find a
1780 * CRL for the cert, look for one and check it.
1781 */
1782 crl = CertFindCRLInStore(pRevPara->hCrlStore,
1783 cert->dwCertEncodingType,
1784 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
1785 CRL_FIND_ISSUED_BY_AKI_FLAG,
1786 CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
1787 }
1788 if (crl)
1789 {
1790 error = verify_cert_revocation_with_crl_offline(cert, crl,
1791 index, pTime, pRevStatus);
1792 CertFreeCRLContext(crl);
1793 }
1794 else
1795 {
1796 TRACE("no CRL found\n");
1797 error = CRYPT_E_NO_REVOCATION_CHECK;
1798 pRevStatus->dwIndex = index;
1799 }
1800 }
1801 else
1802 {
1803 if (!pRevPara)
1804 WARN("no CERT_REVOCATION_PARA\n");
1805 else if (!pRevPara->hCrlStore)
1806 WARN("no dist points/aia extension and no CRL store\n");
1807 else if (!pRevPara->pIssuerCert)
1808 WARN("no dist points/aia extension and no issuer\n");
1809 error = CRYPT_E_NO_REVOCATION_CHECK;
1810 pRevStatus->dwIndex = index;
1811 }
1812 }
1813 return error;
1814 }
1815
1816 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
1817 DWORD cbSize;
1818 PCCERT_CONTEXT pIssuerCert;
1819 DWORD cCertStore;
1820 HCERTSTORE *rgCertStore;
1821 HCERTSTORE hCrlStore;
1822 LPFILETIME pftTimeToUse;
1823 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
1824
1825 typedef struct _OLD_CERT_REVOCATION_STATUS {
1826 DWORD cbSize;
1827 DWORD dwIndex;
1828 DWORD dwError;
1829 DWORD dwReason;
1830 } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
1831
1832 /***********************************************************************
1833 * CertDllVerifyRevocation (CRYPTNET.@)
1834 */
1835 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1836 DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1837 PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1838 {
1839 DWORD error = 0, i;
1840 FILETIME now;
1841 LPFILETIME pTime = NULL;
1842
1843 TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1844 cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1845
1846 if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1847 pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1848 {
1849 SetLastError(E_INVALIDARG);
1850 return FALSE;
1851 }
1852 if (!cContext)
1853 {
1854 SetLastError(E_INVALIDARG);
1855 return FALSE;
1856 }
1857 if (pRevPara && pRevPara->cbSize >=
1858 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
1859 pTime = pRevPara->pftTimeToUse;
1860 if (!pTime)
1861 {
1862 GetSystemTimeAsFileTime(&now);
1863 pTime = &now;
1864 }
1865 memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
1866 if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
1867 error = CRYPT_E_NO_REVOCATION_CHECK;
1868 else
1869 {
1870 for (i = 0; !error && i < cContext; i++)
1871 error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags,
1872 pRevPara, pRevStatus);
1873 }
1874 if (error)
1875 {
1876 SetLastError(error);
1877 pRevStatus->dwError = error;
1878 }
1879 TRACE("returning %d (%08x)\n", !error, error);
1880 return !error;
1881 }