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