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