2 * Copyright (C) 2006 Maarten Lankhorst
3 * Copyright 2007 Juan Lang
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.
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.
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
21 #define NONAMELESSUNION
22 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
36 #include "knownfolders.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet
);
43 #define IS_INTOID(x) (((ULONG_PTR)(x) >> 16) == 0)
46 /***********************************************************************
47 * DllRegisterServer (CRYPTNET.@)
49 HRESULT WINAPI
DllRegisterServer(void)
52 CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING
,
53 CRYPT_OID_VERIFY_REVOCATION_FUNC
, 0, L
"cryptnet.dll");
54 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
, "Ldap",
55 L
"cryptnet.dll", "LdapProvOpenStore");
56 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
,
57 CERT_STORE_PROV_LDAP_W
, L
"cryptnet.dll", "LdapProvOpenStore");
61 /***********************************************************************
62 * DllUnregisterServer (CRYPTNET.@)
64 HRESULT WINAPI
DllUnregisterServer(void)
67 CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING
,
68 CRYPT_OID_VERIFY_REVOCATION_FUNC
, L
"cryptnet.dll");
69 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
, "Ldap");
70 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
,
71 CERT_STORE_PROV_LDAP_W
);
75 static const char *url_oid_to_str(LPCSTR oid
)
83 #define _x(oid) case LOWORD(oid): return #oid
84 _x(URL_OID_CERTIFICATE_ISSUER
);
85 _x(URL_OID_CERTIFICATE_CRL_DIST_POINT
);
86 _x(URL_OID_CTL_ISSUER
);
87 _x(URL_OID_CTL_NEXT_UPDATE
);
88 _x(URL_OID_CRL_ISSUER
);
89 _x(URL_OID_CERTIFICATE_FRESHEST_CRL
);
90 _x(URL_OID_CRL_FRESHEST_CRL
);
91 _x(URL_OID_CROSS_CERT_DIST_POINT
);
94 snprintf(buf
, sizeof(buf
), "%d", LOWORD(oid
));
102 typedef BOOL (WINAPI
*UrlDllGetObjectUrlFunc
)(LPCSTR
, LPVOID
, DWORD
,
103 PCRYPT_URL_ARRAY
, DWORD
*, PCRYPT_URL_INFO
, DWORD
*, LPVOID
);
105 static BOOL WINAPI
CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid
,
106 LPVOID pvPara
, DWORD dwFlags
, PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
,
107 PCRYPT_URL_INFO pUrlInfo
, DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
109 PCCERT_CONTEXT cert
= pvPara
;
113 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
114 if (dwFlags
&& !(dwFlags
& CRYPT_GET_URL_FROM_EXTENSION
))
116 SetLastError(CRYPT_E_NOT_FOUND
);
119 if ((ext
= CertFindExtension(szOID_AUTHORITY_INFO_ACCESS
,
120 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
122 CERT_AUTHORITY_INFO_ACCESS
*aia
;
125 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_AUTHORITY_INFO_ACCESS
,
126 ext
->Value
.pbData
, ext
->Value
.cbData
, CRYPT_DECODE_ALLOC_FLAG
, NULL
,
130 DWORD i
, cUrl
, bytesNeeded
= sizeof(CRYPT_URL_ARRAY
);
132 for (i
= 0, cUrl
= 0; i
< aia
->cAccDescr
; i
++)
133 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
,
134 szOID_PKIX_CA_ISSUERS
))
136 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
==
139 if (aia
->rgAccDescr
[i
].AccessLocation
.u
.pwszURL
)
142 bytesNeeded
+= sizeof(LPWSTR
) +
143 (lstrlenW(aia
->rgAccDescr
[i
].AccessLocation
.u
.
144 pwszURL
) + 1) * sizeof(WCHAR
);
148 FIXME("unsupported alt name type %ld\n",
149 aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
);
153 SetLastError(E_INVALIDARG
);
157 *pcbUrlArray
= bytesNeeded
;
158 else if (*pcbUrlArray
< bytesNeeded
)
160 SetLastError(ERROR_MORE_DATA
);
161 *pcbUrlArray
= bytesNeeded
;
168 *pcbUrlArray
= bytesNeeded
;
170 pUrlArray
->rgwszUrl
=
171 (LPWSTR
*)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
));
172 nextUrl
= (LPWSTR
)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
)
173 + cUrl
* sizeof(LPWSTR
));
174 for (i
= 0; i
< aia
->cAccDescr
; i
++)
175 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
,
176 szOID_PKIX_CA_ISSUERS
))
178 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
179 == CERT_ALT_NAME_URL
)
181 if (aia
->rgAccDescr
[i
].AccessLocation
.u
.pwszURL
)
184 aia
->rgAccDescr
[i
].AccessLocation
.u
.pwszURL
);
185 pUrlArray
->rgwszUrl
[pUrlArray
->cUrl
++] =
187 nextUrl
+= (lstrlenW(nextUrl
) + 1);
196 FIXME("url info: stub\n");
198 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
199 else if (*pcbUrlInfo
< sizeof(CRYPT_URL_INFO
))
201 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
202 SetLastError(ERROR_MORE_DATA
);
207 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
208 memset(pUrlInfo
, 0, sizeof(CRYPT_URL_INFO
));
216 SetLastError(CRYPT_E_NOT_FOUND
);
220 static BOOL
CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB
*value
,
221 PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
, PCRYPT_URL_INFO pUrlInfo
,
225 CRL_DIST_POINTS_INFO
*info
;
228 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_CRL_DIST_POINTS
,
229 value
->pbData
, value
->cbData
, CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
);
232 DWORD i
, cUrl
, bytesNeeded
= sizeof(CRYPT_URL_ARRAY
);
234 for (i
= 0, cUrl
= 0; i
< info
->cDistPoint
; i
++)
235 if (info
->rgDistPoint
[i
].DistPointName
.dwDistPointNameChoice
236 == CRL_DIST_POINT_FULL_NAME
)
239 CERT_ALT_NAME_INFO
*name
=
240 &info
->rgDistPoint
[i
].DistPointName
.u
.FullName
;
242 for (j
= 0; j
< name
->cAltEntry
; j
++)
243 if (name
->rgAltEntry
[j
].dwAltNameChoice
==
246 if (name
->rgAltEntry
[j
].u
.pwszURL
)
249 bytesNeeded
+= sizeof(LPWSTR
) +
250 (lstrlenW(name
->rgAltEntry
[j
].u
.pwszURL
) + 1)
257 SetLastError(E_INVALIDARG
);
261 *pcbUrlArray
= bytesNeeded
;
262 else if (*pcbUrlArray
< bytesNeeded
)
264 SetLastError(ERROR_MORE_DATA
);
265 *pcbUrlArray
= bytesNeeded
;
272 *pcbUrlArray
= bytesNeeded
;
274 pUrlArray
->rgwszUrl
=
275 (LPWSTR
*)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
));
276 nextUrl
= (LPWSTR
)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
)
277 + cUrl
* sizeof(LPWSTR
));
278 for (i
= 0; i
< info
->cDistPoint
; i
++)
279 if (info
->rgDistPoint
[i
].DistPointName
.dwDistPointNameChoice
280 == CRL_DIST_POINT_FULL_NAME
)
283 CERT_ALT_NAME_INFO
*name
=
284 &info
->rgDistPoint
[i
].DistPointName
.u
.FullName
;
286 for (j
= 0; j
< name
->cAltEntry
; j
++)
287 if (name
->rgAltEntry
[j
].dwAltNameChoice
==
290 if (name
->rgAltEntry
[j
].u
.pwszURL
)
293 name
->rgAltEntry
[j
].u
.pwszURL
);
294 pUrlArray
->rgwszUrl
[pUrlArray
->cUrl
++] =
297 (lstrlenW(name
->rgAltEntry
[j
].u
.pwszURL
) + 1);
306 FIXME("url info: stub\n");
308 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
309 else if (*pcbUrlInfo
< sizeof(CRYPT_URL_INFO
))
311 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
312 SetLastError(ERROR_MORE_DATA
);
317 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
318 memset(pUrlInfo
, 0, sizeof(CRYPT_URL_INFO
));
327 static BOOL WINAPI
CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid
,
328 LPVOID pvPara
, DWORD dwFlags
, PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
,
329 PCRYPT_URL_INFO pUrlInfo
, DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
331 PCCERT_CONTEXT cert
= pvPara
;
335 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
336 if (dwFlags
&& !(dwFlags
& CRYPT_GET_URL_FROM_EXTENSION
))
338 SetLastError(CRYPT_E_NOT_FOUND
);
341 if ((ext
= CertFindExtension(szOID_CRL_DIST_POINTS
,
342 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
343 ret
= CRYPT_GetUrlFromCRLDistPointsExt(&ext
->Value
, pUrlArray
,
344 pcbUrlArray
, pUrlInfo
, pcbUrlInfo
);
346 SetLastError(CRYPT_E_NOT_FOUND
);
350 /***********************************************************************
351 * CryptGetObjectUrl (CRYPTNET.@)
353 BOOL WINAPI
CryptGetObjectUrl(LPCSTR pszUrlOid
, LPVOID pvPara
, DWORD dwFlags
,
354 PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
, PCRYPT_URL_INFO pUrlInfo
,
355 DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
357 UrlDllGetObjectUrlFunc func
= NULL
;
358 HCRYPTOIDFUNCADDR hFunc
= NULL
;
361 TRACE("(%s, %p, %08lx, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid
),
362 pvPara
, dwFlags
, pUrlArray
, pcbUrlArray
, pUrlInfo
, pcbUrlInfo
, pvReserved
);
364 if (IS_INTOID(pszUrlOid
))
366 switch (LOWORD(pszUrlOid
))
368 case LOWORD(URL_OID_CERTIFICATE_ISSUER
):
369 func
= CRYPT_GetUrlFromCertificateIssuer
;
371 case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT
):
372 func
= CRYPT_GetUrlFromCertificateCRLDistPoint
;
375 FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid
));
376 SetLastError(ERROR_FILE_NOT_FOUND
);
381 static HCRYPTOIDFUNCSET set
= NULL
;
384 set
= CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC
, 0);
385 CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
, pszUrlOid
, 0,
386 (void **)&func
, &hFunc
);
389 ret
= func(pszUrlOid
, pvPara
, dwFlags
, pUrlArray
, pcbUrlArray
,
390 pUrlInfo
, pcbUrlInfo
, pvReserved
);
392 CryptFreeOIDFunctionAddress(hFunc
, 0);
396 /***********************************************************************
397 * CryptRetrieveObjectByUrlA (CRYPTNET.@)
399 BOOL WINAPI
CryptRetrieveObjectByUrlA(LPCSTR pszURL
, LPCSTR pszObjectOid
,
400 DWORD dwRetrievalFlags
, DWORD dwTimeout
, LPVOID
*ppvObject
,
401 HCRYPTASYNC hAsyncRetrieve
, PCRYPT_CREDENTIALS pCredentials
, LPVOID pvVerify
,
402 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
407 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL
),
408 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, ppvObject
,
409 hAsyncRetrieve
, pCredentials
, pvVerify
, pAuxInfo
);
413 SetLastError(ERROR_INVALID_PARAMETER
);
416 len
= MultiByteToWideChar(CP_ACP
, 0, pszURL
, -1, NULL
, 0);
419 LPWSTR url
= CryptMemAlloc(len
* sizeof(WCHAR
));
423 MultiByteToWideChar(CP_ACP
, 0, pszURL
, -1, url
, len
);
424 ret
= CryptRetrieveObjectByUrlW(url
, pszObjectOid
,
425 dwRetrievalFlags
, dwTimeout
, ppvObject
, hAsyncRetrieve
,
426 pCredentials
, pvVerify
, pAuxInfo
);
430 SetLastError(ERROR_OUTOFMEMORY
);
435 static void WINAPI
CRYPT_FreeBlob(LPCSTR pszObjectOid
,
436 PCRYPT_BLOB_ARRAY pObject
, void *pvFreeContext
)
440 for (i
= 0; i
< pObject
->cBlob
; i
++)
441 CryptMemFree(pObject
->rgBlob
[i
].pbData
);
442 CryptMemFree(pObject
->rgBlob
);
445 static BOOL
CRYPT_GetObjectFromFile(HANDLE hFile
, PCRYPT_BLOB_ARRAY pObject
)
450 if ((ret
= GetFileSizeEx(hFile
, &size
)))
454 WARN("file too big\n");
455 SetLastError(ERROR_INVALID_DATA
);
460 CRYPT_DATA_BLOB blob
;
462 blob
.pbData
= CryptMemAlloc(size
.u
.LowPart
);
465 ret
= ReadFile(hFile
, blob
.pbData
, size
.u
.LowPart
, &blob
.cbData
,
469 pObject
->rgBlob
= CryptMemAlloc(sizeof(CRYPT_DATA_BLOB
));
473 memcpy(pObject
->rgBlob
, &blob
, sizeof(CRYPT_DATA_BLOB
));
477 SetLastError(ERROR_OUTOFMEMORY
);
482 CryptMemFree(blob
.pbData
);
486 SetLastError(ERROR_OUTOFMEMORY
);
494 static BOOL
CRYPT_GetObjectFromCache(LPCWSTR pszURL
, PCRYPT_BLOB_ARRAY pObject
,
495 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
498 INTERNET_CACHE_ENTRY_INFOW
*pCacheInfo
= NULL
;
501 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL
), pObject
, pAuxInfo
);
503 RetrieveUrlCacheEntryFileW(pszURL
, NULL
, &size
, 0);
504 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
507 pCacheInfo
= CryptMemAlloc(size
);
510 SetLastError(ERROR_OUTOFMEMORY
);
514 if ((ret
= RetrieveUrlCacheEntryFileW(pszURL
, pCacheInfo
, &size
, 0)))
518 GetSystemTimeAsFileTime(&ft
);
519 if (CompareFileTime(&pCacheInfo
->ExpireTime
, &ft
) >= 0)
521 HANDLE hFile
= CreateFileW(pCacheInfo
->lpszLocalFileName
, GENERIC_READ
,
522 FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
524 if (hFile
!= INVALID_HANDLE_VALUE
)
526 if ((ret
= CRYPT_GetObjectFromFile(hFile
, pObject
)))
528 if (pAuxInfo
&& pAuxInfo
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO
, pLastSyncTime
)
529 && pAuxInfo
->pLastSyncTime
)
531 memcpy(pAuxInfo
->pLastSyncTime
,
532 &pCacheInfo
->LastSyncTime
,
540 DeleteUrlCacheEntryW(pszURL
);
546 DeleteUrlCacheEntryW(pszURL
);
549 UnlockUrlCacheEntryFileW(pszURL
, 0);
551 CryptMemFree(pCacheInfo
);
552 TRACE("returning %d\n", ret
);
556 /* Parses the URL, and sets components' lpszHostName and lpszUrlPath members
557 * to NULL-terminated copies of those portions of the URL (to be freed with
560 static BOOL
CRYPT_CrackUrl(LPCWSTR pszURL
, URL_COMPONENTSW
*components
)
564 TRACE("(%s, %p)\n", debugstr_w(pszURL
), components
);
566 memset(components
, 0, sizeof(*components
));
567 components
->dwStructSize
= sizeof(*components
);
568 components
->lpszHostName
= CryptMemAlloc(INTERNET_MAX_HOST_NAME_LENGTH
* sizeof(WCHAR
));
569 components
->dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
570 if (!components
->lpszHostName
)
572 SetLastError(ERROR_OUTOFMEMORY
);
575 components
->lpszUrlPath
= CryptMemAlloc(INTERNET_MAX_PATH_LENGTH
* sizeof(WCHAR
));
576 components
->dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
577 if (!components
->lpszUrlPath
)
579 CryptMemFree(components
->lpszHostName
);
580 SetLastError(ERROR_OUTOFMEMORY
);
584 ret
= InternetCrackUrlW(pszURL
, 0, ICU_DECODE
, components
);
587 switch (components
->nScheme
)
589 case INTERNET_SCHEME_FTP
:
590 if (!components
->nPort
)
591 components
->nPort
= INTERNET_DEFAULT_FTP_PORT
;
593 case INTERNET_SCHEME_HTTP
:
594 if (!components
->nPort
)
595 components
->nPort
= INTERNET_DEFAULT_HTTP_PORT
;
601 TRACE("returning %d\n", ret
);
612 static struct InetContext
*CRYPT_MakeInetContext(DWORD dwTimeout
)
614 struct InetContext
*context
= CryptMemAlloc(sizeof(struct InetContext
));
618 context
->event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
621 CryptMemFree(context
);
626 context
->timeout
= dwTimeout
;
627 context
->error
= ERROR_SUCCESS
;
633 static BOOL
CRYPT_DownloadObject(DWORD dwRetrievalFlags
, HINTERNET hHttp
,
634 struct InetContext
*context
, PCRYPT_BLOB_ARRAY pObject
,
635 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
637 CRYPT_DATA_BLOB object
= { 0, NULL
};
638 DWORD bytesAvailable
;
642 if ((ret
= InternetQueryDataAvailable(hHttp
, &bytesAvailable
, 0, 0)))
647 object
.pbData
= CryptMemRealloc(object
.pbData
,
648 object
.cbData
+ bytesAvailable
);
650 object
.pbData
= CryptMemAlloc(bytesAvailable
);
653 INTERNET_BUFFERSA buffer
= { sizeof(buffer
), 0 };
655 buffer
.dwBufferLength
= bytesAvailable
;
656 buffer
.lpvBuffer
= object
.pbData
+ object
.cbData
;
657 if (!(ret
= InternetReadFileExA(hHttp
, &buffer
, IRF_NO_WAIT
,
658 (DWORD_PTR
)context
)))
660 if (GetLastError() == ERROR_IO_PENDING
)
662 if (WaitForSingleObject(context
->event
,
663 context
->timeout
) == WAIT_TIMEOUT
)
664 SetLastError(ERROR_TIMEOUT
);
665 else if (context
->error
)
666 SetLastError(context
->error
);
672 object
.cbData
+= buffer
.dwBufferLength
;
676 SetLastError(ERROR_OUTOFMEMORY
);
681 else if (GetLastError() == ERROR_IO_PENDING
)
683 if (WaitForSingleObject(context
->event
, context
->timeout
) ==
685 SetLastError(ERROR_TIMEOUT
);
689 } while (ret
&& bytesAvailable
);
692 pObject
->rgBlob
= CryptMemAlloc(sizeof(CRYPT_DATA_BLOB
));
693 if (!pObject
->rgBlob
)
695 CryptMemFree(object
.pbData
);
696 SetLastError(ERROR_OUTOFMEMORY
);
701 pObject
->rgBlob
[0].cbData
= object
.cbData
;
702 pObject
->rgBlob
[0].pbData
= object
.pbData
;
706 TRACE("returning %d\n", ret
);
710 /* Finds the object specified by pszURL in the cache. If it's not found,
711 * creates a new cache entry for the object and writes the object to it.
712 * Sets the expiration time of the cache entry to expires.
714 static void CRYPT_CacheURL(LPCWSTR pszURL
, const CRYPT_BLOB_ARRAY
*pObject
,
715 DWORD dwRetrievalFlags
, FILETIME expires
)
717 WCHAR cacheFileName
[MAX_PATH
];
719 DWORD size
= 0, entryType
;
722 GetUrlCacheEntryInfoW(pszURL
, NULL
, &size
);
723 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
725 INTERNET_CACHE_ENTRY_INFOW
*info
= CryptMemAlloc(size
);
729 ERR("out of memory\n");
733 if (GetUrlCacheEntryInfoW(pszURL
, info
, &size
))
735 lstrcpyW(cacheFileName
, info
->lpszLocalFileName
);
736 /* Check if the existing cache entry is up to date. If it isn't,
737 * remove the existing cache entry, and create a new one with the
740 GetSystemTimeAsFileTime(&ft
);
741 if (CompareFileTime(&info
->ExpireTime
, &ft
) < 0)
743 DeleteUrlCacheEntryW(pszURL
);
747 info
->ExpireTime
= expires
;
748 SetUrlCacheEntryInfoW(pszURL
, info
, CACHE_ENTRY_EXPTIME_FC
);
756 if (!CreateUrlCacheEntryW(pszURL
, pObject
->rgBlob
[0].cbData
, NULL
, cacheFileName
, 0))
759 hCacheFile
= CreateFileW(cacheFileName
, GENERIC_WRITE
, 0,
760 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
761 if(hCacheFile
== INVALID_HANDLE_VALUE
)
764 WriteFile(hCacheFile
, pObject
->rgBlob
[0].pbData
,
765 pObject
->rgBlob
[0].cbData
, &size
, NULL
);
766 CloseHandle(hCacheFile
);
768 if (!(dwRetrievalFlags
& CRYPT_STICKY_CACHE_RETRIEVAL
))
769 entryType
= NORMAL_CACHE_ENTRY
;
771 entryType
= STICKY_CACHE_ENTRY
;
772 memset(&ft
, 0, sizeof(ft
));
773 CommitUrlCacheEntryW(pszURL
, cacheFileName
, expires
, ft
, entryType
,
774 NULL
, 0, NULL
, NULL
);
777 static void CALLBACK
CRYPT_InetStatusCallback(HINTERNET hInt
,
778 DWORD_PTR dwContext
, DWORD status
, void *statusInfo
, DWORD statusInfoLen
)
780 struct InetContext
*context
= (struct InetContext
*)dwContext
;
781 LPINTERNET_ASYNC_RESULT result
;
785 case INTERNET_STATUS_REQUEST_COMPLETE
:
787 context
->error
= result
->dwError
;
788 SetEvent(context
->event
);
792 static BOOL
CRYPT_Connect(const URL_COMPONENTSW
*components
,
793 struct InetContext
*context
, PCRYPT_CREDENTIALS pCredentials
,
794 HINTERNET
*phInt
, HINTERNET
*phHost
)
798 TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components
->lpszHostName
),
799 components
->nPort
, context
, pCredentials
, phInt
, phInt
);
802 *phInt
= InternetOpenW(NULL
, INTERNET_OPEN_TYPE_PRECONFIG
, NULL
, NULL
,
803 context
? INTERNET_FLAG_ASYNC
: 0);
809 InternetSetStatusCallbackW(*phInt
, CRYPT_InetStatusCallback
);
810 switch (components
->nScheme
)
812 case INTERNET_SCHEME_FTP
:
813 service
= INTERNET_SERVICE_FTP
;
815 case INTERNET_SCHEME_HTTP
:
816 service
= INTERNET_SERVICE_HTTP
;
821 /* FIXME: use pCredentials for username/password */
822 *phHost
= InternetConnectW(*phInt
, components
->lpszHostName
,
823 components
->nPort
, NULL
, NULL
, service
, 0, (DWORD_PTR
)context
);
826 InternetCloseHandle(*phInt
);
835 TRACE("returning %d\n", ret
);
839 static BOOL WINAPI
FTP_RetrieveEncodedObjectW(LPCWSTR pszURL
,
840 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
841 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
842 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
843 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
845 FIXME("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
846 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
847 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
850 pObject
->rgBlob
= NULL
;
851 *ppfnFreeObject
= CRYPT_FreeBlob
;
852 *ppvFreeContext
= NULL
;
856 static BOOL WINAPI
HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL
,
857 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
858 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
859 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
860 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
864 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
865 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
866 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
869 pObject
->rgBlob
= NULL
;
870 *ppfnFreeObject
= CRYPT_FreeBlob
;
871 *ppvFreeContext
= NULL
;
873 if (!(dwRetrievalFlags
& CRYPT_WIRE_ONLY_RETRIEVAL
))
874 ret
= CRYPT_GetObjectFromCache(pszURL
, pObject
, pAuxInfo
);
875 if (!ret
&& (!(dwRetrievalFlags
& CRYPT_CACHE_ONLY_RETRIEVAL
) ||
876 (dwRetrievalFlags
& CRYPT_WIRE_ONLY_RETRIEVAL
)))
878 URL_COMPONENTSW components
;
880 if ((ret
= CRYPT_CrackUrl(pszURL
, &components
)))
882 HINTERNET hInt
, hHost
;
883 struct InetContext
*context
= NULL
;
886 context
= CRYPT_MakeInetContext(dwTimeout
);
887 ret
= CRYPT_Connect(&components
, context
, pCredentials
, &hInt
,
891 static LPCWSTR types
[] =
893 L
"application/x-x509-ca-cert", L
"application/x-x509-email-cert",
894 L
"application/x-x509-server-cert", L
"application/x-x509-user-cert",
895 L
"application/x-pkcs7-certificates", L
"application/pkix-crl",
896 L
"application/x-pkcs7-crl", L
"application/x-pkcs7-signature",
897 L
"application/x-pkcs7-mime", NULL
899 HINTERNET hHttp
= HttpOpenRequestW(hHost
, NULL
,
900 components
.lpszUrlPath
, NULL
, NULL
, types
,
901 INTERNET_FLAG_NO_COOKIES
| INTERNET_FLAG_NO_UI
,
908 InternetSetOptionW(hHttp
,
909 INTERNET_OPTION_RECEIVE_TIMEOUT
, &dwTimeout
,
911 InternetSetOptionW(hHttp
, INTERNET_OPTION_SEND_TIMEOUT
,
912 &dwTimeout
, sizeof(dwTimeout
));
914 ret
= HttpSendRequestExW(hHttp
, NULL
, NULL
, 0,
916 if (!ret
&& GetLastError() == ERROR_IO_PENDING
)
918 if (WaitForSingleObject(context
->event
,
919 context
->timeout
) == WAIT_TIMEOUT
)
920 SetLastError(ERROR_TIMEOUT
);
925 !(ret
= HttpEndRequestW(hHttp
, NULL
, 0, (DWORD_PTR
)context
)) &&
926 GetLastError() == ERROR_IO_PENDING
)
928 if (WaitForSingleObject(context
->event
,
929 context
->timeout
) == WAIT_TIMEOUT
)
930 SetLastError(ERROR_TIMEOUT
);
935 ret
= CRYPT_DownloadObject(dwRetrievalFlags
, hHttp
,
936 context
, pObject
, pAuxInfo
);
937 if (ret
&& !(dwRetrievalFlags
& CRYPT_DONT_CACHE_RESULT
))
941 DWORD len
= sizeof(st
);
943 if (HttpQueryInfoW(hHttp
, HTTP_QUERY_EXPIRES
| HTTP_QUERY_FLAG_SYSTEMTIME
,
944 &st
, &len
, NULL
) && SystemTimeToFileTime(&st
, &ft
))
945 CRYPT_CacheURL(pszURL
, pObject
, dwRetrievalFlags
, ft
);
947 InternetCloseHandle(hHttp
);
949 InternetCloseHandle(hHost
);
950 InternetCloseHandle(hInt
);
954 CloseHandle(context
->event
);
955 CryptMemFree(context
);
957 CryptMemFree(components
.lpszUrlPath
);
958 CryptMemFree(components
.lpszHostName
);
961 TRACE("returning %d\n", ret
);
965 static BOOL WINAPI
File_RetrieveEncodedObjectW(LPCWSTR pszURL
,
966 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
967 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
968 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
969 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
971 URL_COMPONENTSW components
= { sizeof(components
), 0 };
974 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
975 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
976 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
979 pObject
->rgBlob
= NULL
;
980 *ppfnFreeObject
= CRYPT_FreeBlob
;
981 *ppvFreeContext
= NULL
;
983 components
.lpszUrlPath
= CryptMemAlloc(INTERNET_MAX_PATH_LENGTH
* sizeof(WCHAR
));
984 components
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
985 if (!components
.lpszUrlPath
)
987 SetLastError(ERROR_OUTOFMEMORY
);
991 ret
= InternetCrackUrlW(pszURL
, 0, ICU_DECODE
, &components
);
996 /* 3 == lstrlenW(L"c:") + 1 */
997 path
= CryptMemAlloc((components
.dwUrlPathLength
+ 3) * sizeof(WCHAR
));
1002 /* Try to create the file directly - Wine handles / in pathnames */
1003 lstrcpynW(path
, components
.lpszUrlPath
,
1004 components
.dwUrlPathLength
+ 1);
1005 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1006 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1007 if (hFile
== INVALID_HANDLE_VALUE
)
1009 /* Try again on the current drive */
1010 GetCurrentDirectoryW(components
.dwUrlPathLength
, path
);
1013 lstrcpynW(path
+ 2, components
.lpszUrlPath
,
1014 components
.dwUrlPathLength
+ 1);
1015 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1016 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1018 if (hFile
== INVALID_HANDLE_VALUE
)
1020 /* Try again on the Windows drive */
1021 GetWindowsDirectoryW(path
, components
.dwUrlPathLength
);
1024 lstrcpynW(path
+ 2, components
.lpszUrlPath
,
1025 components
.dwUrlPathLength
+ 1);
1026 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1027 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1031 if (hFile
!= INVALID_HANDLE_VALUE
)
1033 if ((ret
= CRYPT_GetObjectFromFile(hFile
, pObject
)))
1035 if (pAuxInfo
&& pAuxInfo
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO
, pLastSyncTime
)
1036 && pAuxInfo
->pLastSyncTime
)
1038 GetFileTime(hFile
, NULL
, NULL
,
1039 pAuxInfo
->pLastSyncTime
);
1050 SetLastError(ERROR_OUTOFMEMORY
);
1054 CryptMemFree(components
.lpszUrlPath
);
1058 typedef BOOL (WINAPI
*SchemeDllRetrieveEncodedObjectW
)(LPCWSTR pwszUrl
,
1059 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
1060 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
1061 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
1062 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
);
1064 static BOOL
CRYPT_GetRetrieveFunction(LPCWSTR pszURL
,
1065 SchemeDllRetrieveEncodedObjectW
*pFunc
, HCRYPTOIDFUNCADDR
*phFunc
)
1067 URL_COMPONENTSW components
= { sizeof(components
), 0 };
1070 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL
), pFunc
, phFunc
);
1074 components
.dwSchemeLength
= 1;
1075 ret
= InternetCrackUrlW(pszURL
, 0, 0, &components
);
1078 /* Microsoft always uses CryptInitOIDFunctionSet/
1079 * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1080 * reason to do so for builtin schemes.
1082 switch (components
.nScheme
)
1084 case INTERNET_SCHEME_FTP
:
1085 *pFunc
= FTP_RetrieveEncodedObjectW
;
1087 case INTERNET_SCHEME_HTTP
:
1088 *pFunc
= HTTP_RetrieveEncodedObjectW
;
1090 case INTERNET_SCHEME_FILE
:
1091 *pFunc
= File_RetrieveEncodedObjectW
;
1095 int len
= WideCharToMultiByte(CP_ACP
, 0, components
.lpszScheme
,
1096 components
.dwSchemeLength
, NULL
, 0, NULL
, NULL
);
1100 LPSTR scheme
= CryptMemAlloc(len
);
1104 static HCRYPTOIDFUNCSET set
= NULL
;
1107 set
= CryptInitOIDFunctionSet(
1108 SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC
, 0);
1109 WideCharToMultiByte(CP_ACP
, 0, components
.lpszScheme
,
1110 components
.dwSchemeLength
, scheme
, len
, NULL
, NULL
);
1111 ret
= CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
,
1112 scheme
, 0, (void **)pFunc
, phFunc
);
1113 CryptMemFree(scheme
);
1117 SetLastError(ERROR_OUTOFMEMORY
);
1126 TRACE("returning %d\n", ret
);
1130 static BOOL WINAPI
CRYPT_CreateBlob(LPCSTR pszObjectOid
,
1131 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1134 CRYPT_BLOB_ARRAY
*context
;
1137 size
= sizeof(CRYPT_BLOB_ARRAY
) + pObject
->cBlob
* sizeof(CRYPT_DATA_BLOB
);
1138 for (i
= 0; i
< pObject
->cBlob
; i
++)
1139 size
+= pObject
->rgBlob
[i
].cbData
;
1140 context
= CryptMemAlloc(size
);
1147 (CRYPT_DATA_BLOB
*)((LPBYTE
)context
+ sizeof(CRYPT_BLOB_ARRAY
));
1149 (LPBYTE
)context
->rgBlob
+ pObject
->cBlob
* sizeof(CRYPT_DATA_BLOB
);
1150 for (i
= 0; i
< pObject
->cBlob
; i
++)
1152 memcpy(nextData
, pObject
->rgBlob
[i
].pbData
,
1153 pObject
->rgBlob
[i
].cbData
);
1154 context
->rgBlob
[i
].pbData
= nextData
;
1155 context
->rgBlob
[i
].cbData
= pObject
->rgBlob
[i
].cbData
;
1156 nextData
+= pObject
->rgBlob
[i
].cbData
;
1159 *ppvContext
= context
;
1165 typedef BOOL (WINAPI
*AddContextToStore
)(HCERTSTORE hCertStore
,
1166 const void *pContext
, DWORD dwAddDisposition
, const void **ppStoreContext
);
1168 static BOOL
decode_base64_blob( const CRYPT_DATA_BLOB
*in
, CRYPT_DATA_BLOB
*out
)
1171 DWORD len
= in
->cbData
;
1173 while (len
&& !in
->pbData
[len
- 1]) len
--;
1174 if (!CryptStringToBinaryA( (char *)in
->pbData
, len
, CRYPT_STRING_BASE64_ANY
,
1175 NULL
, &out
->cbData
, NULL
, NULL
)) return FALSE
;
1177 if (!(out
->pbData
= CryptMemAlloc( out
->cbData
))) return FALSE
;
1178 ret
= CryptStringToBinaryA( (char *)in
->pbData
, len
, CRYPT_STRING_BASE64_ANY
,
1179 out
->pbData
, &out
->cbData
, NULL
, NULL
);
1180 if (!ret
) CryptMemFree( out
->pbData
);
1184 static BOOL
CRYPT_CreateContext(const CRYPT_BLOB_ARRAY
*pObject
,
1185 DWORD dwExpectedContentTypeFlags
, AddContextToStore addFunc
, void **ppvContext
)
1188 CRYPT_DATA_BLOB blob
;
1190 if (!pObject
->cBlob
)
1192 SetLastError(ERROR_INVALID_DATA
);
1196 else if (pObject
->cBlob
== 1)
1198 if (decode_base64_blob(&pObject
->rgBlob
[0], &blob
))
1200 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &blob
,
1201 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
, 0,
1202 NULL
, NULL
, NULL
, NULL
, NULL
, (const void **)ppvContext
);
1203 CryptMemFree(blob
.pbData
);
1207 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &pObject
->rgBlob
[0],
1208 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
, 0,
1209 NULL
, NULL
, NULL
, NULL
, NULL
, (const void **)ppvContext
);
1213 SetLastError(CRYPT_E_NO_MATCH
);
1219 HCERTSTORE store
= CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, 0,
1220 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1225 const void *context
;
1227 for (i
= 0; i
< pObject
->cBlob
; i
++)
1229 if (decode_base64_blob(&pObject
->rgBlob
[i
], &blob
))
1231 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &blob
,
1232 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
,
1233 0, NULL
, NULL
, NULL
, NULL
, NULL
, &context
);
1234 CryptMemFree(blob
.pbData
);
1238 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
,
1239 &pObject
->rgBlob
[i
], dwExpectedContentTypeFlags
,
1240 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, NULL
, NULL
, NULL
,
1245 if (!addFunc(store
, context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1250 SetLastError(CRYPT_E_NO_MATCH
);
1257 *ppvContext
= store
;
1262 static BOOL WINAPI
CRYPT_CreateCert(LPCSTR pszObjectOid
,
1263 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1265 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CERT
,
1266 (AddContextToStore
)CertAddCertificateContextToStore
, ppvContext
);
1269 static BOOL WINAPI
CRYPT_CreateCRL(LPCSTR pszObjectOid
,
1270 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1272 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CRL
,
1273 (AddContextToStore
)CertAddCRLContextToStore
, ppvContext
);
1276 static BOOL WINAPI
CRYPT_CreateCTL(LPCSTR pszObjectOid
,
1277 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1279 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CTL
,
1280 (AddContextToStore
)CertAddCTLContextToStore
, ppvContext
);
1283 static BOOL WINAPI
CRYPT_CreatePKCS7(LPCSTR pszObjectOid
,
1284 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1288 if (!pObject
->cBlob
)
1290 SetLastError(ERROR_INVALID_DATA
);
1294 else if (pObject
->cBlob
== 1)
1295 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &pObject
->rgBlob
[0],
1296 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
1297 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
, CERT_QUERY_FORMAT_FLAG_BINARY
,
1298 0, NULL
, NULL
, NULL
, ppvContext
, NULL
, NULL
);
1301 FIXME("multiple messages unimplemented\n");
1307 static BOOL WINAPI
CRYPT_CreateAny(LPCSTR pszObjectOid
,
1308 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1312 if (!pObject
->cBlob
)
1314 SetLastError(ERROR_INVALID_DATA
);
1320 HCERTSTORE store
= CertOpenStore(CERT_STORE_PROV_COLLECTION
, 0, 0,
1321 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1325 HCERTSTORE memStore
= CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, 0,
1326 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1330 CertAddStoreToCollection(store
, memStore
,
1331 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
, 0);
1332 CertCloseStore(memStore
, 0);
1336 CertCloseStore(store
, 0);
1345 for (i
= 0; i
< pObject
->cBlob
; i
++)
1347 DWORD contentType
, expectedContentTypes
=
1348 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
1349 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
|
1350 CERT_QUERY_CONTENT_FLAG_CERT
|
1351 CERT_QUERY_CONTENT_FLAG_CRL
|
1352 CERT_QUERY_CONTENT_FLAG_CTL
;
1353 HCERTSTORE contextStore
;
1354 const void *context
;
1356 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB
,
1357 &pObject
->rgBlob
[i
], expectedContentTypes
,
1358 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, &contentType
, NULL
,
1359 &contextStore
, NULL
, &context
))
1361 switch (contentType
)
1363 case CERT_QUERY_CONTENT_CERT
:
1364 if (!CertAddCertificateContextToStore(store
,
1365 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1367 CertFreeCertificateContext(context
);
1369 case CERT_QUERY_CONTENT_CRL
:
1370 if (!CertAddCRLContextToStore(store
,
1371 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1373 CertFreeCRLContext(context
);
1375 case CERT_QUERY_CONTENT_CTL
:
1376 if (!CertAddCTLContextToStore(store
,
1377 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1379 CertFreeCTLContext(context
);
1382 CertAddStoreToCollection(store
, contextStore
, 0, 0);
1384 CertCloseStore(contextStore
, 0);
1392 *ppvContext
= store
;
1397 typedef BOOL (WINAPI
*ContextDllCreateObjectContext
)(LPCSTR pszObjectOid
,
1398 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
);
1400 static BOOL
CRYPT_GetCreateFunction(LPCSTR pszObjectOid
,
1401 ContextDllCreateObjectContext
*pFunc
, HCRYPTOIDFUNCADDR
*phFunc
)
1405 TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid
), pFunc
, phFunc
);
1409 if (IS_INTOID(pszObjectOid
))
1411 switch (LOWORD(pszObjectOid
))
1414 *pFunc
= CRYPT_CreateBlob
;
1416 case LOWORD(CONTEXT_OID_CERTIFICATE
):
1417 *pFunc
= CRYPT_CreateCert
;
1419 case LOWORD(CONTEXT_OID_CRL
):
1420 *pFunc
= CRYPT_CreateCRL
;
1422 case LOWORD(CONTEXT_OID_CTL
):
1423 *pFunc
= CRYPT_CreateCTL
;
1425 case LOWORD(CONTEXT_OID_PKCS7
):
1426 *pFunc
= CRYPT_CreatePKCS7
;
1428 case LOWORD(CONTEXT_OID_CAPI2_ANY
):
1429 *pFunc
= CRYPT_CreateAny
;
1435 static HCRYPTOIDFUNCSET set
= NULL
;
1438 set
= CryptInitOIDFunctionSet(
1439 CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC
, 0);
1440 ret
= CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
, pszObjectOid
,
1441 0, (void **)pFunc
, phFunc
);
1443 TRACE("returning %d\n", ret
);
1447 static BOOL
CRYPT_GetExpiration(const void *object
, const char *pszObjectOid
, FILETIME
*expiration
)
1449 if (!IS_INTOID(pszObjectOid
))
1452 switch (LOWORD(pszObjectOid
)) {
1453 case LOWORD(CONTEXT_OID_CERTIFICATE
):
1454 *expiration
= ((const CERT_CONTEXT
*)object
)->pCertInfo
->NotAfter
;
1456 case LOWORD(CONTEXT_OID_CRL
):
1457 *expiration
= ((const CRL_CONTEXT
*)object
)->pCrlInfo
->NextUpdate
;
1459 case LOWORD(CONTEXT_OID_CTL
):
1460 *expiration
= ((const CTL_CONTEXT
*)object
)->pCtlInfo
->NextUpdate
;
1467 /***********************************************************************
1468 * CryptRetrieveObjectByUrlW (CRYPTNET.@)
1470 BOOL WINAPI
CryptRetrieveObjectByUrlW(LPCWSTR pszURL
, LPCSTR pszObjectOid
,
1471 DWORD dwRetrievalFlags
, DWORD dwTimeout
, LPVOID
*ppvObject
,
1472 HCRYPTASYNC hAsyncRetrieve
, PCRYPT_CREDENTIALS pCredentials
, LPVOID pvVerify
,
1473 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
1476 SchemeDllRetrieveEncodedObjectW retrieve
;
1477 ContextDllCreateObjectContext create
;
1478 HCRYPTOIDFUNCADDR hRetrieve
= 0, hCreate
= 0;
1480 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
1481 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, ppvObject
,
1482 hAsyncRetrieve
, pCredentials
, pvVerify
, pAuxInfo
);
1486 SetLastError(ERROR_INVALID_PARAMETER
);
1489 ret
= CRYPT_GetRetrieveFunction(pszURL
, &retrieve
, &hRetrieve
);
1491 ret
= CRYPT_GetCreateFunction(pszObjectOid
, &create
, &hCreate
);
1494 CRYPT_BLOB_ARRAY object
= { 0, NULL
};
1495 PFN_FREE_ENCODED_OBJECT_FUNC freeObject
;
1499 ret
= retrieve(pszURL
, pszObjectOid
, dwRetrievalFlags
, dwTimeout
,
1500 &object
, &freeObject
, &freeContext
, hAsyncRetrieve
, pCredentials
,
1504 ret
= create(pszObjectOid
, dwRetrievalFlags
, &object
, ppvObject
);
1505 if (ret
&& !(dwRetrievalFlags
& CRYPT_DONT_CACHE_RESULT
) &&
1506 CRYPT_GetExpiration(*ppvObject
, pszObjectOid
, &expires
))
1508 CRYPT_CacheURL(pszURL
, &object
, dwRetrievalFlags
, expires
);
1510 freeObject(pszObjectOid
, &object
, freeContext
);
1514 CryptFreeOIDFunctionAddress(hCreate
, 0);
1516 CryptFreeOIDFunctionAddress(hRetrieve
, 0);
1517 TRACE("returning %d\n", ret
);
1521 /* Store successful revocation checks (whether the certificate was revoked or
1522 * not) in an on-disk cache. This is not because of network latency—we already
1523 * have a cache for that—but rather because parsing very large CRLs can take a
1524 * long time (at the time of writing, 20 MB CRLs have been seen in the wild and
1525 * can take several hundred milliseconds) and applications expect chain building
1526 * to be much faster.
1528 * The cache is treated as invalid once we pass the nextUpdate field of the CRL.
1529 * This isn't quite what the field is meant for (it's rather meant to specify a
1530 * later bound for the next time the CRL will be reissued, and doesn't prescribe
1531 * a date by which the CRL is invalid; see RFC 5280 § 5.1.2.5) but it's the way
1532 * it's used in practice.
1534 * The location of the cache roughly matches Windows, but the file name and
1538 static const char revocation_cache_signature
[] = "Wine cached revocation";
1540 #define CACHED_CERT_HASH_SIZE 20
1542 static FILE *open_cached_revocation_file(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1543 const WCHAR
*mode
, int sharing
)
1545 BYTE hash_data
[CACHED_CERT_HASH_SIZE
];
1546 WCHAR path
[MAX_PATH
];
1547 WCHAR
*appdata_path
;
1553 if (FAILED(hr
= SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow
, 0, NULL
, &appdata_path
)))
1555 ERR("Failed to get LocalAppDataLow path, hr %#lx.\n", hr
);
1556 return INVALID_HANDLE_VALUE
;
1559 len
= swprintf(path
, ARRAY_SIZE(path
), L
"%s\\Microsoft\\CryptnetUrlCache\\Content\\", appdata_path
);
1560 CoTaskMemFree(appdata_path
);
1562 if (len
+ CACHED_CERT_HASH_SIZE
* 2 * sizeof(WCHAR
) > ARRAY_SIZE(path
) - 1)
1564 WARN("Hash length exceeds static buffer; not caching.\n");
1565 return INVALID_HANDLE_VALUE
;
1568 CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
);
1569 CryptCreateHash(prov
, CALG_SHA1
, 0, 0, &hash
);
1570 CryptHashData(hash
, cert
->pbCertEncoded
, cert
->cbCertEncoded
, 0);
1571 if (params
&& params
->pIssuerCert
)
1573 CryptHashData(hash
, (BYTE
*)¶ms
->pIssuerCert
->cbCertEncoded
, sizeof(params
->pIssuerCert
->cbCertEncoded
), 0);
1574 CryptHashData(hash
, params
->pIssuerCert
->pbCertEncoded
, params
->pIssuerCert
->cbCertEncoded
, 0);
1579 CryptHashData(hash
, (BYTE
*)&size
, sizeof(size
), 0);
1581 size
= sizeof(hash_data
);
1582 CryptGetHashParam(hash
, HP_HASHVAL
, hash_data
, &size
, 0);
1583 CryptDestroyHash(hash
);
1584 CryptReleaseContext(prov
, 0);
1586 SHCreateDirectoryExW(NULL
, path
, NULL
);
1588 for (i
= 0; i
< CACHED_CERT_HASH_SIZE
; ++i
)
1590 swprintf(path
+ len
, 3, L
"%02x", hash_data
[i
]);
1594 return _wfsopen(path
, mode
, sharing
);
1597 static BOOL
find_cached_revocation_status(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1598 const FILETIME
*time
, CERT_REVOCATION_STATUS
*status
)
1600 char buffer
[sizeof(revocation_cache_signature
)];
1601 FILETIME update_time
;
1605 if (!(file
= open_cached_revocation_file(cert
, params
, L
"rb", _SH_DENYWR
)))
1608 if ((len
= fread(buffer
, 1, sizeof(buffer
), file
)) != sizeof(buffer
)
1609 || memcmp(buffer
, revocation_cache_signature
, len
))
1611 ERR("Invalid cache signature.\n");
1616 if (fread(&update_time
, sizeof(update_time
), 1, file
) != 1)
1618 ERR("Failed to read update time.\n");
1623 if (CompareFileTime(time
, &update_time
) > 0)
1625 TRACE("Cached revocation status is potentially out of date.\n");
1630 if (fread(&status
->dwError
, sizeof(status
->dwError
), 1, file
) != 1)
1632 ERR("Failed to read error code.\n");
1637 if (status
->dwError
== CERT_E_REVOKED
&& fread(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
) != 1)
1639 ERR("Failed to read revocation reason.\n");
1644 TRACE("Using cached status %#lx, reason %#lx.\n", status
->dwError
, status
->dwReason
);
1648 static void cache_revocation_status(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1649 const FILETIME
*time
, const CERT_REVOCATION_STATUS
*status
)
1653 if (!(file
= open_cached_revocation_file(cert
, params
, L
"wb", _SH_DENYRW
)))
1655 fwrite(revocation_cache_signature
, 1, sizeof(revocation_cache_signature
), file
);
1656 fwrite(time
, sizeof(*time
), 1, file
);
1657 fwrite(&status
->dwError
, sizeof(status
->dwError
), 1, file
);
1658 if (status
->dwError
== CERT_E_REVOKED
)
1659 fwrite(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
);
1663 static DWORD
verify_cert_revocation_with_crl_online(const CERT_CONTEXT
*cert
,
1664 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
1666 PCRL_ENTRY entry
= NULL
;
1668 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
1670 return CRYPT_E_REVOKED
;
1672 /* Since the CRL was retrieved for the cert being checked, then it's
1673 * guaranteed to be fresh, and the cert is not revoked. */
1674 return ERROR_SUCCESS
;
1677 /* Try to retrieve a CRL from any one of the specified distribution points. */
1678 static const CRL_CONTEXT
*retrieve_crl_from_dist_points(const CRYPT_URL_ARRAY
*array
,
1679 DWORD verify_flags
, DWORD timeout
)
1681 DWORD retrieve_flags
= 0;
1682 const CRL_CONTEXT
*crl
;
1685 if (verify_flags
& CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION
)
1686 retrieve_flags
|= CRYPT_CACHE_ONLY_RETRIEVAL
;
1688 /* Yes, this is a weird algorithm, but the documentation for
1689 * CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT specifies this, and
1690 * tests seem to bear it out for CertVerifyRevocation() as well. */
1691 if (verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
)
1694 for (i
= 0; i
< array
->cUrl
; ++i
)
1696 if (CryptRetrieveObjectByUrlW(array
->rgwszUrl
[i
], CONTEXT_OID_CRL
, retrieve_flags
,
1697 timeout
, (void **)&crl
, NULL
, NULL
, NULL
, NULL
))
1700 /* We don't check the current time here. This may result in less
1701 * accurate timeouts, but this too seems to be true of Windows. */
1702 if ((verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
) && GetLastError() == ERROR_TIMEOUT
)
1709 static DWORD
verify_cert_revocation_from_dist_points_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
1710 FILETIME
*time
, DWORD flags
, const CERT_REVOCATION_PARA
*params
, CERT_REVOCATION_STATUS
*status
,
1711 FILETIME
*next_update
)
1713 DWORD url_array_size
, error
;
1714 CRYPT_URL_ARRAY
*url_array
;
1715 const CRL_CONTEXT
*crl
;
1718 if (!params
|| !params
->pIssuerCert
)
1720 TRACE("no issuer certificate\n");
1721 return CRYPT_E_REVOCATION_OFFLINE
;
1724 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, NULL
, &url_array_size
, NULL
, NULL
))
1725 return GetLastError();
1727 if (!(url_array
= CryptMemAlloc(url_array_size
)))
1728 return ERROR_OUTOFMEMORY
;
1730 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, url_array
, &url_array_size
, NULL
, NULL
))
1732 CryptMemFree(url_array
);
1733 return GetLastError();
1736 if (params
&& params
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CERT_REVOCATION_PARA
, dwUrlRetrievalTimeout
))
1737 timeout
= params
->dwUrlRetrievalTimeout
;
1739 if (!(crl
= retrieve_crl_from_dist_points(url_array
, flags
, timeout
)))
1741 CryptMemFree(url_array
);
1742 return CRYPT_E_REVOCATION_OFFLINE
;
1745 error
= verify_cert_revocation_with_crl_online(cert
, crl
, time
, status
);
1747 *next_update
= crl
->pCrlInfo
->NextUpdate
;
1749 CertFreeCRLContext(crl
);
1750 CryptMemFree(url_array
);
1754 static void sha1_hash(const BYTE
*data
, DWORD datalen
, BYTE
*buf
, DWORD
*buflen
)
1759 CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
);
1760 CryptCreateHash(prov
, CALG_SHA1
, 0, 0, &hash
);
1761 CryptHashData(hash
, data
, datalen
, 0);
1762 CryptGetHashParam(hash
, HP_HASHVAL
, buf
, buflen
, 0);
1764 CryptDestroyHash(hash
);
1765 CryptReleaseContext(prov
, 0);
1768 static BYTE
*build_ocsp_request(const CERT_CONTEXT
*cert
, const CERT_CONTEXT
*issuer_cert
, DWORD
*ret_size
)
1770 OCSP_REQUEST_ENTRY entry
;
1771 OCSP_REQUEST_INFO request
;
1772 OCSP_SIGNED_REQUEST_INFO request_signed
;
1773 CERT_INFO
*issuer
= issuer_cert
->pCertInfo
;
1774 BYTE issuer_name_hash
[20], issuer_key_hash
[20], *buf
, *ret
;
1775 DWORD size
= 0, hash_len
= sizeof(issuer_name_hash
);
1777 memset(&entry
, 0, sizeof(entry
));
1778 entry
.CertId
.HashAlgorithm
.pszObjId
= (char *)szOID_OIWSEC_sha1
;
1780 sha1_hash(issuer
->Subject
.pbData
, issuer
->Subject
.cbData
, issuer_name_hash
, &hash_len
);
1781 entry
.CertId
.IssuerNameHash
.cbData
= sizeof(issuer_name_hash
);
1782 entry
.CertId
.IssuerNameHash
.pbData
= issuer_name_hash
;
1784 sha1_hash(issuer
->SubjectPublicKeyInfo
.PublicKey
.pbData
, issuer
->SubjectPublicKeyInfo
.PublicKey
.cbData
,
1785 issuer_key_hash
, &hash_len
);
1786 entry
.CertId
.IssuerKeyHash
.cbData
= sizeof(issuer_key_hash
);
1787 entry
.CertId
.IssuerKeyHash
.pbData
= issuer_key_hash
;
1789 entry
.CertId
.SerialNumber
.cbData
= cert
->pCertInfo
->SerialNumber
.cbData
;
1790 entry
.CertId
.SerialNumber
.pbData
= cert
->pCertInfo
->SerialNumber
.pbData
;
1792 request
.dwVersion
= OCSP_REQUEST_V1
;
1793 request
.pRequestorName
= NULL
;
1794 request
.cRequestEntry
= 1;
1795 request
.rgRequestEntry
= &entry
;
1796 request
.cExtension
= 0;
1797 request
.rgExtension
= NULL
;
1798 if (!CryptEncodeObjectEx(X509_ASN_ENCODING
, OCSP_REQUEST
, &request
, CRYPT_ENCODE_ALLOC_FLAG
, NULL
, &buf
, &size
))
1800 ERR("failed to encode request %#lx\n", GetLastError());
1804 request_signed
.ToBeSigned
.pbData
= buf
;
1805 request_signed
.ToBeSigned
.cbData
= size
;
1806 request_signed
.pOptionalSignatureInfo
= NULL
;
1807 if (!CryptEncodeObjectEx(X509_ASN_ENCODING
, OCSP_SIGNED_REQUEST
, &request_signed
, CRYPT_ENCODE_ALLOC_FLAG
, NULL
,
1810 ERR("failed to encode signed request %#lx\n", GetLastError());
1820 static void escape_path(const WCHAR
*src
, DWORD src_len
, WCHAR
*dst
, DWORD
*dst_len
)
1822 static const WCHAR hex
[] = L
"0123456789ABCDEF";
1827 for (i
= 0; i
< src_len
; i
++)
1829 if (src
[i
] == '+' || src
[i
] == '/' || src
[i
] == '=')
1834 ptr
[1] = hex
[(src
[i
] >> 4) & 0xf];
1835 ptr
[2] = hex
[src
[i
] & 0xf];
1840 else if (dst
) *ptr
++ = src
[i
];
1844 static WCHAR
*build_request_path(const BYTE
*data
, DWORD data_size
)
1847 DWORD path_len
, ret_len
;
1849 if (!CryptBinaryToStringW(data
, data_size
, CRYPT_STRING_BASE64
| CRYPT_STRING_NOCRLF
, NULL
, &path_len
)) return NULL
;
1850 if (!(path
= malloc(path_len
* sizeof(WCHAR
)))) return NULL
;
1851 CryptBinaryToStringW(data
, data_size
, CRYPT_STRING_BASE64
| CRYPT_STRING_NOCRLF
, path
, &path_len
);
1853 escape_path(path
, path_len
, NULL
, &ret_len
);
1854 if (!(ret
= malloc((ret_len
+ 2) * sizeof(WCHAR
))))
1859 escape_path(path
, path_len
, ret
+ 1, &ret_len
);
1860 ret
[ret_len
+ 1] = 0;
1867 static WCHAR
*build_request_url(const WCHAR
*base_url
, const BYTE
*data
, DWORD data_size
)
1872 if (!(path
= build_request_path(data
, data_size
))) return NULL
;
1874 InternetCombineUrlW(base_url
, path
, NULL
, &len
, 0);
1875 if (!(ret
= malloc(len
* sizeof(WCHAR
))))
1880 InternetCombineUrlW(base_url
, path
, ret
, &len
, 0);
1885 static DWORD
map_ocsp_status(DWORD status
)
1889 case OCSP_BASIC_GOOD_CERT_STATUS
: return ERROR_SUCCESS
;
1890 case OCSP_BASIC_REVOKED_CERT_STATUS
: return CRYPT_E_REVOKED
;
1891 case OCSP_BASIC_UNKNOWN_CERT_STATUS
: return CRYPT_E_REVOCATION_OFFLINE
;
1893 FIXME("unhandled status %lu\n", status
);
1894 return CRYPT_E_REVOCATION_OFFLINE
;
1898 static BOOL
match_cert_id(const OCSP_CERT_ID
*id
, const CERT_INFO
*cert
, const CERT_INFO
*issuer
)
1901 DWORD hash_len
= sizeof(hash
);
1903 if (!id
->HashAlgorithm
.pszObjId
|| strcmp(id
->HashAlgorithm
.pszObjId
, szOID_OIWSEC_sha1
))
1905 FIXME("hash algorithm %s not supported\n", debugstr_a(id
->HashAlgorithm
.pszObjId
));
1909 sha1_hash(issuer
->Subject
.pbData
, issuer
->Subject
.cbData
, hash
, &hash_len
);
1910 if (id
->IssuerNameHash
.cbData
!= hash_len
) return FALSE
;
1911 if (memcmp(id
->IssuerNameHash
.pbData
, hash
, hash_len
)) return FALSE
;
1913 sha1_hash(issuer
->SubjectPublicKeyInfo
.PublicKey
.pbData
,
1914 issuer
->SubjectPublicKeyInfo
.PublicKey
.cbData
, hash
, &hash_len
);
1915 if (id
->IssuerKeyHash
.cbData
!= hash_len
) return FALSE
;
1916 if (memcmp(id
->IssuerKeyHash
.pbData
, hash
, hash_len
)) return FALSE
;
1918 if (cert
->SerialNumber
.cbData
!= id
->SerialNumber
.cbData
) return FALSE
;
1919 return !memcmp(cert
->SerialNumber
.pbData
, id
->SerialNumber
.pbData
, id
->SerialNumber
.cbData
);
1922 static DWORD
check_ocsp_response_info(const CERT_INFO
*cert
, const CERT_INFO
*issuer
,
1923 const CRYPT_OBJID_BLOB
*blob
, DWORD
*status
, FILETIME
*next_update
)
1925 OCSP_BASIC_RESPONSE_INFO
*info
;
1928 memset(next_update
, 0, sizeof(*next_update
));
1929 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_BASIC_RESPONSE
, blob
->pbData
, blob
->cbData
,
1930 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
)) return GetLastError();
1932 FIXME("check responder id\n");
1933 for (i
= 0; i
< info
->cResponseEntry
; i
++)
1935 OCSP_BASIC_RESPONSE_ENTRY
*entry
= &info
->rgResponseEntry
[i
];
1936 if (match_cert_id(&entry
->CertId
, cert
, issuer
))
1938 *status
= map_ocsp_status(entry
->dwCertStatus
);
1939 *next_update
= entry
->NextUpdate
;
1944 return ERROR_SUCCESS
;
1947 static DWORD
verify_signed_ocsp_response_info(const CERT_INFO
*cert
, const CERT_INFO
*issuer
,
1948 const CRYPT_OBJID_BLOB
*blob
, FILETIME
*next_update
)
1950 OCSP_BASIC_SIGNED_RESPONSE_INFO
*info
;
1951 DWORD size
, error
, status
= CRYPT_E_REVOCATION_OFFLINE
;
1952 CRYPT_ALGORITHM_IDENTIFIER
*alg
;
1953 CRYPT_BIT_BLOB
*sig
;
1954 HCRYPTPROV prov
= 0;
1955 HCRYPTHASH hash
= 0;
1959 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_BASIC_SIGNED_RESPONSE
, blob
->pbData
, blob
->cbData
,
1960 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
)) return GetLastError();
1962 if ((error
= check_ocsp_response_info(cert
, issuer
, &info
->ToBeSigned
, &status
, next_update
))) goto done
;
1964 alg
= &info
->SignatureInfo
.SignatureAlgorithm
;
1965 if (!alg
->pszObjId
|| !(algid
= CertOIDToAlgId(alg
->pszObjId
)))
1967 FIXME("unhandled signature algorithm %s\n", debugstr_a(alg
->pszObjId
));
1968 error
= CRYPT_E_NO_REVOCATION_CHECK
;
1972 if (!CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
)) goto done
;
1973 if (!CryptCreateHash(prov
, algid
, 0, 0, &hash
)) goto done
;
1974 if (!CryptHashData(hash
, info
->ToBeSigned
.pbData
, info
->ToBeSigned
.cbData
, 0)) goto done
;
1976 sig
= &info
->SignatureInfo
.Signature
;
1977 if (!CryptImportPublicKeyInfoEx(prov
, X509_ASN_ENCODING
, (CERT_PUBLIC_KEY_INFO
*)&issuer
->SubjectPublicKeyInfo
,
1980 error
= GetLastError();
1981 TRACE("failed to import public key %#lx\n", error
);
1983 else if (!CryptVerifySignatureW(hash
, sig
->pbData
, sig
->cbData
, key
, NULL
, 0))
1985 error
= GetLastError();
1986 TRACE("failed to verify signature %#lx\n", error
);
1988 else error
= ERROR_SUCCESS
;
1991 CryptDestroyKey(key
);
1992 CryptDestroyHash(hash
);
1993 CryptReleaseContext(prov
, 0);
1995 if (error
) return error
;
1999 static DWORD
handle_ocsp_response(const CERT_INFO
*cert
, const CERT_INFO
*issuer
, const BYTE
*encoded
,
2000 DWORD encoded_size
, FILETIME
*next_update
)
2002 OCSP_RESPONSE_INFO
*info
;
2003 DWORD size
, error
= CRYPT_E_NO_REVOCATION_CHECK
;
2005 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_RESPONSE
, encoded
, encoded_size
, CRYPT_DECODE_ALLOC_FLAG
, NULL
,
2006 &info
, &size
)) return GetLastError();
2008 switch (info
->dwStatus
)
2010 case OCSP_SUCCESSFUL_RESPONSE
:
2011 if (!info
->pszObjId
|| strcmp(info
->pszObjId
, szOID_PKIX_OCSP_BASIC_SIGNED_RESPONSE
))
2013 FIXME("unhandled response type %s\n", debugstr_a(info
->pszObjId
));
2016 error
= verify_signed_ocsp_response_info(cert
, issuer
, &info
->Value
, next_update
);
2020 FIXME("unhandled status %lu\n", info
->dwStatus
);
2028 static DWORD
verify_cert_revocation_with_ocsp(const CERT_CONTEXT
*cert
, const WCHAR
*base_url
,
2029 const CERT_REVOCATION_PARA
*revpara
, FILETIME
*next_update
)
2031 HINTERNET ses
, con
, req
= NULL
;
2032 BYTE
*request_data
= NULL
, *response_data
= NULL
;
2033 DWORD size
, flags
, status
, request_len
, response_len
, count
, ret
= CRYPT_E_REVOCATION_OFFLINE
;
2034 URL_COMPONENTSW comp
;
2037 if (!revpara
|| !revpara
->pIssuerCert
)
2039 TRACE("no issuer certificate\n");
2040 return CRYPT_E_REVOCATION_OFFLINE
;
2042 if (!(request_data
= build_ocsp_request(cert
, revpara
->pIssuerCert
, &request_len
)))
2043 return CRYPT_E_REVOCATION_OFFLINE
;
2045 url
= build_request_url(base_url
, request_data
, request_len
);
2046 LocalFree(request_data
);
2047 if (!url
) return CRYPT_E_REVOCATION_OFFLINE
;
2049 memset(&comp
, 0, sizeof(comp
));
2050 comp
.dwStructSize
= sizeof(comp
);
2051 comp
.dwHostNameLength
= ~0u;
2052 comp
.dwUrlPathLength
= ~0u;
2053 if (!InternetCrackUrlW(url
, 0, 0, &comp
))
2056 return CRYPT_E_REVOCATION_OFFLINE
;
2059 switch (comp
.nScheme
)
2061 case INTERNET_SCHEME_HTTP
:
2064 case INTERNET_SCHEME_HTTPS
:
2065 flags
= INTERNET_FLAG_SECURE
;
2068 FIXME("scheme %u not supported\n", comp
.nScheme
);
2070 return ERROR_NOT_SUPPORTED
;
2073 if (!(ses
= InternetOpenW(L
"CryptoAPI", 0, NULL
, NULL
, 0))) return GetLastError();
2074 comp
.lpszHostName
[comp
.dwHostNameLength
] = 0;
2075 if (!(con
= InternetConnectW(ses
, comp
.lpszHostName
, comp
.nPort
, NULL
, NULL
, INTERNET_SERVICE_HTTP
, 0, 0)))
2078 InternetCloseHandle(ses
);
2079 return GetLastError();
2081 comp
.lpszHostName
[comp
.dwHostNameLength
] = '/';
2082 if (!(req
= HttpOpenRequestW(con
, NULL
, comp
.lpszUrlPath
, NULL
, NULL
, NULL
, flags
, 0)) ||
2083 !HttpSendRequestW(req
, NULL
, 0, NULL
, 0)) goto done
;
2085 size
= sizeof(status
);
2086 if (!HttpQueryInfoW(req
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
, &status
, &size
, NULL
)) goto done
;
2087 if (status
!= HTTP_STATUS_OK
)
2089 WARN("request status %lu\n", status
);
2093 size
= sizeof(response_len
);
2094 if (!HttpQueryInfoW(req
, HTTP_QUERY_FLAG_NUMBER
| HTTP_QUERY_CONTENT_LENGTH
, &response_len
, &size
, 0) ||
2095 !response_len
|| !(response_data
= malloc(response_len
)) ||
2096 !InternetReadFile(req
, response_data
, response_len
, &count
) || count
!= response_len
) goto done
;
2098 ret
= handle_ocsp_response(cert
->pCertInfo
, revpara
->pIssuerCert
->pCertInfo
, response_data
, response_len
,
2103 free(response_data
);
2104 InternetCloseHandle(req
);
2105 InternetCloseHandle(con
);
2106 InternetCloseHandle(ses
);
2110 static DWORD
verify_cert_revocation_from_aia_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
2111 FILETIME
*pTime
, DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
,
2112 FILETIME
*next_update
)
2115 DWORD size
, i
, error
= CRYPT_E_NO_REVOCATION_CHECK
;
2116 CERT_AUTHORITY_INFO_ACCESS
*aia
;
2118 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_AUTHORITY_INFO_ACCESS
, value
->pbData
, value
->cbData
,
2119 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &aia
, &size
);
2120 if (!ret
) return GetLastError();
2122 for (i
= 0; i
< aia
->cAccDescr
; i
++)
2124 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
, szOID_PKIX_OCSP
))
2126 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
== CERT_ALT_NAME_URL
)
2128 const WCHAR
*url
= aia
->rgAccDescr
[i
].AccessLocation
.u
.pwszURL
;
2129 TRACE("OCSP URL = %s\n", debugstr_w(url
));
2130 error
= verify_cert_revocation_with_ocsp(cert
, url
, pRevPara
, next_update
);
2134 FIXME("unsupported AccessLocation type %lu\n", aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
);
2135 error
= ERROR_NOT_SUPPORTED
;
2145 static DWORD
verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert
,
2146 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
2148 PCRL_ENTRY entry
= NULL
;
2151 valid
= CompareFileTime(pTime
, &crl
->pCrlInfo
->ThisUpdate
);
2154 /* If this CRL is not older than the time being verified, there's no
2155 * way to know whether the certificate was revoked.
2157 TRACE("CRL not old enough\n");
2158 return CRYPT_E_REVOCATION_OFFLINE
;
2161 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
2163 return CRYPT_E_REVOKED
;
2165 /* Since the CRL was not retrieved for the cert being checked, there's no
2166 * guarantee it's fresh, so the cert *might* be okay, but it's safer not to
2168 TRACE("certificate not found\n");
2169 return CRYPT_E_REVOCATION_OFFLINE
;
2172 static DWORD
verify_cert_revocation(const CERT_CONTEXT
*cert
, FILETIME
*pTime
,
2173 DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
)
2175 DWORD error
= ERROR_SUCCESS
;
2176 FILETIME next_update
= {0};
2177 PCERT_EXTENSION ext
;
2179 if (find_cached_revocation_status(cert
, pRevPara
, pTime
, pRevStatus
))
2181 if (pRevStatus
->dwError
== ERROR_SUCCESS
|| pRevStatus
->dwError
== CRYPT_E_REVOKED
)
2183 TRACE("Returning cached status.\n");
2184 return pRevStatus
->dwError
;
2188 if ((ext
= CertFindExtension(szOID_AUTHORITY_INFO_ACCESS
, cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
2190 error
= verify_cert_revocation_from_aia_ext(&ext
->Value
, cert
, pTime
, dwFlags
, pRevPara
, pRevStatus
,
2192 TRACE("verify_cert_revocation_from_aia_ext() returned %08lx\n", error
);
2193 if (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
) goto done
;
2195 if ((ext
= CertFindExtension(szOID_CRL_DIST_POINTS
, cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
2197 error
= verify_cert_revocation_from_dist_points_ext(&ext
->Value
, cert
, pTime
, dwFlags
, pRevPara
, pRevStatus
,
2199 TRACE("verify_cert_revocation_from_dist_points_ext() returned %08lx\n", error
);
2200 if (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
) goto done
;
2204 if (pRevPara
&& pRevPara
->hCrlStore
&& pRevPara
->pIssuerCert
)
2206 PCCRL_CONTEXT crl
= NULL
;
2209 /* If the caller told us about the issuer, make sure the issuer
2210 * can sign CRLs before looking for one.
2212 if ((ext
= CertFindExtension(szOID_KEY_USAGE
,
2213 pRevPara
->pIssuerCert
->pCertInfo
->cExtension
,
2214 pRevPara
->pIssuerCert
->pCertInfo
->rgExtension
)))
2216 CRYPT_BIT_BLOB usage
;
2217 DWORD size
= sizeof(usage
);
2219 if (!CryptDecodeObjectEx(cert
->dwCertEncodingType
, X509_BITS
,
2220 ext
->Value
.pbData
, ext
->Value
.cbData
,
2221 CRYPT_DECODE_NOCOPY_FLAG
, NULL
, &usage
, &size
))
2222 canSignCRLs
= FALSE
;
2223 else if (usage
.cbData
> 2)
2225 /* The key usage extension only defines 9 bits => no more
2226 * than 2 bytes are needed to encode all known usages.
2228 canSignCRLs
= FALSE
;
2232 BYTE usageBits
= usage
.pbData
[usage
.cbData
- 1];
2234 canSignCRLs
= usageBits
& CERT_CRL_SIGN_KEY_USAGE
;
2241 /* If the caller was helpful enough to tell us where to find a
2242 * CRL for the cert, look for one and check it.
2244 crl
= CertFindCRLInStore(pRevPara
->hCrlStore
,
2245 cert
->dwCertEncodingType
,
2246 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG
|
2247 CRL_FIND_ISSUED_BY_AKI_FLAG
,
2248 CRL_FIND_ISSUED_BY
, pRevPara
->pIssuerCert
, NULL
);
2252 error
= verify_cert_revocation_with_crl_offline(cert
, crl
, pTime
, pRevStatus
);
2253 CertFreeCRLContext(crl
);
2257 TRACE("no CRL found\n");
2258 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2264 WARN("no CERT_REVOCATION_PARA\n");
2265 else if (!pRevPara
->hCrlStore
)
2266 WARN("no dist points/aia extension and no CRL store\n");
2267 else if (!pRevPara
->pIssuerCert
)
2268 WARN("no dist points/aia extension and no issuer\n");
2269 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2273 if ((next_update
.dwLowDateTime
|| next_update
.dwHighDateTime
)
2274 && (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
))
2276 CERT_REVOCATION_STATUS rev_status
;
2278 memset(&rev_status
, 0, sizeof(rev_status
));
2279 rev_status
.cbSize
= sizeof(rev_status
);
2280 rev_status
.dwError
= error
;
2281 cache_revocation_status(cert
, pRevPara
, &next_update
, &rev_status
);
2286 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
{
2288 PCCERT_CONTEXT pIssuerCert
;
2290 HCERTSTORE
*rgCertStore
;
2291 HCERTSTORE hCrlStore
;
2292 LPFILETIME pftTimeToUse
;
2293 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
;
2295 typedef struct _OLD_CERT_REVOCATION_STATUS
{
2300 } OLD_CERT_REVOCATION_STATUS
;
2302 /***********************************************************************
2303 * CertDllVerifyRevocation (CRYPTNET.@)
2305 BOOL WINAPI
CertDllVerifyRevocation(DWORD dwEncodingType
, DWORD dwRevType
,
2306 DWORD cContext
, PVOID rgpvContext
[], DWORD dwFlags
,
2307 PCERT_REVOCATION_PARA pRevPara
, PCERT_REVOCATION_STATUS pRevStatus
)
2311 LPFILETIME pTime
= NULL
;
2313 TRACE("(%08lx, %ld, %ld, %p, %08lx, %p, %p)\n", dwEncodingType
, dwRevType
,
2314 cContext
, rgpvContext
, dwFlags
, pRevPara
, pRevStatus
);
2316 if (pRevStatus
->cbSize
!= sizeof(OLD_CERT_REVOCATION_STATUS
) &&
2317 pRevStatus
->cbSize
!= sizeof(CERT_REVOCATION_STATUS
))
2319 SetLastError(E_INVALIDARG
);
2324 SetLastError(E_INVALIDARG
);
2327 if (pRevPara
&& pRevPara
->cbSize
>=
2328 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
))
2329 pTime
= pRevPara
->pftTimeToUse
;
2332 GetSystemTimeAsFileTime(&now
);
2335 memset(&pRevStatus
->dwIndex
, 0, pRevStatus
->cbSize
- sizeof(DWORD
));
2336 if (dwRevType
!= CERT_CONTEXT_REVOCATION_TYPE
)
2337 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2340 for (i
= 0; i
< cContext
; i
++)
2342 if ((error
= verify_cert_revocation(rgpvContext
[i
], pTime
, dwFlags
, pRevPara
, pRevStatus
)))
2344 pRevStatus
->dwIndex
= i
;
2351 SetLastError(error
);
2352 pRevStatus
->dwError
= error
;
2354 TRACE("returning %d (%08lx)\n", !error
, error
);