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 static FILE *open_cached_revocation_file(const CRYPT_INTEGER_BLOB
*serial
, const WCHAR
*mode
, int sharing
)
1542 WCHAR path
[MAX_PATH
];
1543 WCHAR
*appdata_path
;
1547 if (FAILED(hr
= SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow
, 0, NULL
, &appdata_path
)))
1549 ERR("Failed to get LocalAppDataLow path, hr %#lx.\n", hr
);
1550 return INVALID_HANDLE_VALUE
;
1553 len
= swprintf(path
, ARRAY_SIZE(path
), L
"%s\\Microsoft\\CryptnetUrlCache\\Content\\", appdata_path
);
1554 CoTaskMemFree(appdata_path
);
1556 if (len
+ serial
->cbData
* 2 * sizeof(WCHAR
) > ARRAY_SIZE(path
) - 1)
1558 WARN("Serial length exceeds static buffer; not caching.\n");
1559 return INVALID_HANDLE_VALUE
;
1562 SHCreateDirectoryExW(NULL
, path
, NULL
);
1564 for (i
= 0; i
< serial
->cbData
; ++i
)
1566 swprintf(path
+ len
, 3, L
"%02x", serial
->pbData
[i
]);
1570 return _wfsopen(path
, mode
, sharing
);
1573 static BOOL
find_cached_revocation_status(const CRYPT_INTEGER_BLOB
*serial
,
1574 const FILETIME
*time
, CERT_REVOCATION_STATUS
*status
)
1576 char buffer
[sizeof(revocation_cache_signature
)];
1577 FILETIME update_time
;
1581 if (!(file
= open_cached_revocation_file(serial
, L
"rb", _SH_DENYWR
)))
1584 if ((len
= fread(buffer
, 1, sizeof(buffer
), file
)) != sizeof(buffer
)
1585 || memcmp(buffer
, revocation_cache_signature
, len
))
1587 ERR("Invalid cache signature.\n");
1592 if (fread(&update_time
, sizeof(update_time
), 1, file
) != 1)
1594 ERR("Failed to read update time.\n");
1599 if (CompareFileTime(time
, &update_time
) > 0)
1601 TRACE("Cached revocation status is potentially out of date.\n");
1606 if (fread(&status
->dwError
, sizeof(status
->dwError
), 1, file
) != 1)
1608 ERR("Failed to read error code.\n");
1613 if (status
->dwError
== CERT_E_REVOKED
&& fread(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
) != 1)
1615 ERR("Failed to read revocation reason.\n");
1620 TRACE("Using cached status %#lx, reason %#lx.\n", status
->dwError
, status
->dwReason
);
1624 static void cache_revocation_status(const CRYPT_INTEGER_BLOB
*serial
,
1625 const FILETIME
*time
, const CERT_REVOCATION_STATUS
*status
)
1629 if (!(file
= open_cached_revocation_file(serial
, L
"wb", _SH_DENYRW
)))
1631 fwrite(revocation_cache_signature
, 1, sizeof(revocation_cache_signature
), file
);
1632 fwrite(time
, sizeof(*time
), 1, file
);
1633 fwrite(&status
->dwError
, sizeof(status
->dwError
), 1, file
);
1634 if (status
->dwError
== CERT_E_REVOKED
)
1635 fwrite(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
);
1639 static DWORD
verify_cert_revocation_with_crl_online(const CERT_CONTEXT
*cert
,
1640 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
1642 PCRL_ENTRY entry
= NULL
;
1644 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
1646 return CRYPT_E_REVOKED
;
1648 /* Since the CRL was retrieved for the cert being checked, then it's
1649 * guaranteed to be fresh, and the cert is not revoked. */
1650 return ERROR_SUCCESS
;
1653 /* Try to retrieve a CRL from any one of the specified distribution points. */
1654 static const CRL_CONTEXT
*retrieve_crl_from_dist_points(const CRYPT_URL_ARRAY
*array
,
1655 DWORD verify_flags
, DWORD timeout
)
1657 DWORD retrieve_flags
= 0;
1658 const CRL_CONTEXT
*crl
;
1661 if (verify_flags
& CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION
)
1662 retrieve_flags
|= CRYPT_CACHE_ONLY_RETRIEVAL
;
1664 /* Yes, this is a weird algorithm, but the documentation for
1665 * CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT specifies this, and
1666 * tests seem to bear it out for CertVerifyRevocation() as well. */
1667 if (verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
)
1670 for (i
= 0; i
< array
->cUrl
; ++i
)
1672 if (CryptRetrieveObjectByUrlW(array
->rgwszUrl
[i
], CONTEXT_OID_CRL
, retrieve_flags
,
1673 timeout
, (void **)&crl
, NULL
, NULL
, NULL
, NULL
))
1676 /* We don't check the current time here. This may result in less
1677 * accurate timeouts, but this too seems to be true of Windows. */
1678 if ((verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
) && GetLastError() == ERROR_TIMEOUT
)
1685 static DWORD
verify_cert_revocation_from_dist_points_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
1686 FILETIME
*time
, DWORD flags
, const CERT_REVOCATION_PARA
*params
, CERT_REVOCATION_STATUS
*status
)
1688 DWORD url_array_size
, error
;
1689 CRYPT_URL_ARRAY
*url_array
;
1690 const CRL_CONTEXT
*crl
;
1693 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, NULL
, &url_array_size
, NULL
, NULL
))
1694 return GetLastError();
1696 if (!(url_array
= CryptMemAlloc(url_array_size
)))
1697 return ERROR_OUTOFMEMORY
;
1699 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, url_array
, &url_array_size
, NULL
, NULL
))
1701 CryptMemFree(url_array
);
1702 return GetLastError();
1705 if (params
&& params
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CERT_REVOCATION_PARA
, dwUrlRetrievalTimeout
))
1706 timeout
= params
->dwUrlRetrievalTimeout
;
1708 if (!(crl
= retrieve_crl_from_dist_points(url_array
, flags
, timeout
)))
1710 CryptMemFree(url_array
);
1711 return CRYPT_E_REVOCATION_OFFLINE
;
1714 error
= verify_cert_revocation_with_crl_online(cert
, crl
, time
, status
);
1716 cache_revocation_status(&cert
->pCertInfo
->SerialNumber
, &crl
->pCrlInfo
->NextUpdate
, status
);
1718 CertFreeCRLContext(crl
);
1719 CryptMemFree(url_array
);
1723 static DWORD
verify_cert_revocation_from_aia_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
1724 FILETIME
*pTime
, DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
)
1728 CERT_AUTHORITY_INFO_ACCESS
*aia
;
1730 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_AUTHORITY_INFO_ACCESS
,
1731 value
->pbData
, value
->cbData
, CRYPT_DECODE_ALLOC_FLAG
, NULL
, &aia
, &size
);
1736 for (i
= 0; i
< aia
->cAccDescr
; i
++)
1737 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
,
1740 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
==
1742 FIXME("OCSP URL = %s\n",
1743 debugstr_w(aia
->rgAccDescr
[i
].AccessLocation
.u
.pwszURL
));
1745 FIXME("unsupported AccessLocation type %ld\n",
1746 aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
);
1749 /* FIXME: lie and pretend OCSP validated the cert */
1750 error
= ERROR_SUCCESS
;
1753 error
= GetLastError();
1757 static DWORD
verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert
,
1758 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
1760 PCRL_ENTRY entry
= NULL
;
1763 valid
= CompareFileTime(pTime
, &crl
->pCrlInfo
->ThisUpdate
);
1766 /* If this CRL is not older than the time being verified, there's no
1767 * way to know whether the certificate was revoked.
1769 TRACE("CRL not old enough\n");
1770 return CRYPT_E_REVOCATION_OFFLINE
;
1773 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
1775 return CRYPT_E_REVOKED
;
1777 /* Since the CRL was not retrieved for the cert being checked, there's no
1778 * guarantee it's fresh, so the cert *might* be okay, but it's safer not to
1780 TRACE("certificate not found\n");
1781 return CRYPT_E_REVOCATION_OFFLINE
;
1784 static DWORD
verify_cert_revocation(const CERT_CONTEXT
*cert
, FILETIME
*pTime
,
1785 DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
)
1787 DWORD error
= ERROR_SUCCESS
;
1788 PCERT_EXTENSION ext
;
1790 if (find_cached_revocation_status(&cert
->pCertInfo
->SerialNumber
, pTime
, pRevStatus
))
1791 return pRevStatus
->dwError
;
1793 if ((ext
= CertFindExtension(szOID_CRL_DIST_POINTS
,
1794 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
1796 error
= verify_cert_revocation_from_dist_points_ext(&ext
->Value
, cert
,
1797 pTime
, dwFlags
, pRevPara
, pRevStatus
);
1799 else if ((ext
= CertFindExtension(szOID_AUTHORITY_INFO_ACCESS
,
1800 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
1802 error
= verify_cert_revocation_from_aia_ext(&ext
->Value
, cert
, pTime
,
1803 dwFlags
, pRevPara
, pRevStatus
);
1807 if (pRevPara
&& pRevPara
->hCrlStore
&& pRevPara
->pIssuerCert
)
1809 PCCRL_CONTEXT crl
= NULL
;
1812 /* If the caller told us about the issuer, make sure the issuer
1813 * can sign CRLs before looking for one.
1815 if ((ext
= CertFindExtension(szOID_KEY_USAGE
,
1816 pRevPara
->pIssuerCert
->pCertInfo
->cExtension
,
1817 pRevPara
->pIssuerCert
->pCertInfo
->rgExtension
)))
1819 CRYPT_BIT_BLOB usage
;
1820 DWORD size
= sizeof(usage
);
1822 if (!CryptDecodeObjectEx(cert
->dwCertEncodingType
, X509_BITS
,
1823 ext
->Value
.pbData
, ext
->Value
.cbData
,
1824 CRYPT_DECODE_NOCOPY_FLAG
, NULL
, &usage
, &size
))
1825 canSignCRLs
= FALSE
;
1826 else if (usage
.cbData
> 2)
1828 /* The key usage extension only defines 9 bits => no more
1829 * than 2 bytes are needed to encode all known usages.
1831 canSignCRLs
= FALSE
;
1835 BYTE usageBits
= usage
.pbData
[usage
.cbData
- 1];
1837 canSignCRLs
= usageBits
& CERT_CRL_SIGN_KEY_USAGE
;
1844 /* If the caller was helpful enough to tell us where to find a
1845 * CRL for the cert, look for one and check it.
1847 crl
= CertFindCRLInStore(pRevPara
->hCrlStore
,
1848 cert
->dwCertEncodingType
,
1849 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG
|
1850 CRL_FIND_ISSUED_BY_AKI_FLAG
,
1851 CRL_FIND_ISSUED_BY
, pRevPara
->pIssuerCert
, NULL
);
1855 error
= verify_cert_revocation_with_crl_offline(cert
, crl
, pTime
, pRevStatus
);
1856 CertFreeCRLContext(crl
);
1860 TRACE("no CRL found\n");
1861 error
= CRYPT_E_NO_REVOCATION_CHECK
;
1867 WARN("no CERT_REVOCATION_PARA\n");
1868 else if (!pRevPara
->hCrlStore
)
1869 WARN("no dist points/aia extension and no CRL store\n");
1870 else if (!pRevPara
->pIssuerCert
)
1871 WARN("no dist points/aia extension and no issuer\n");
1872 error
= CRYPT_E_NO_REVOCATION_CHECK
;
1878 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
{
1880 PCCERT_CONTEXT pIssuerCert
;
1882 HCERTSTORE
*rgCertStore
;
1883 HCERTSTORE hCrlStore
;
1884 LPFILETIME pftTimeToUse
;
1885 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
;
1887 typedef struct _OLD_CERT_REVOCATION_STATUS
{
1892 } OLD_CERT_REVOCATION_STATUS
;
1894 /***********************************************************************
1895 * CertDllVerifyRevocation (CRYPTNET.@)
1897 BOOL WINAPI
CertDllVerifyRevocation(DWORD dwEncodingType
, DWORD dwRevType
,
1898 DWORD cContext
, PVOID rgpvContext
[], DWORD dwFlags
,
1899 PCERT_REVOCATION_PARA pRevPara
, PCERT_REVOCATION_STATUS pRevStatus
)
1903 LPFILETIME pTime
= NULL
;
1905 TRACE("(%08lx, %ld, %ld, %p, %08lx, %p, %p)\n", dwEncodingType
, dwRevType
,
1906 cContext
, rgpvContext
, dwFlags
, pRevPara
, pRevStatus
);
1908 if (pRevStatus
->cbSize
!= sizeof(OLD_CERT_REVOCATION_STATUS
) &&
1909 pRevStatus
->cbSize
!= sizeof(CERT_REVOCATION_STATUS
))
1911 SetLastError(E_INVALIDARG
);
1916 SetLastError(E_INVALIDARG
);
1919 if (pRevPara
&& pRevPara
->cbSize
>=
1920 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
))
1921 pTime
= pRevPara
->pftTimeToUse
;
1924 GetSystemTimeAsFileTime(&now
);
1927 memset(&pRevStatus
->dwIndex
, 0, pRevStatus
->cbSize
- sizeof(DWORD
));
1928 if (dwRevType
!= CERT_CONTEXT_REVOCATION_TYPE
)
1929 error
= CRYPT_E_NO_REVOCATION_CHECK
;
1932 for (i
= 0; i
< cContext
; i
++)
1934 if ((error
= verify_cert_revocation(rgpvContext
[i
], pTime
, dwFlags
, pRevPara
, pRevStatus
)))
1936 pRevStatus
->dwIndex
= i
;
1943 SetLastError(error
);
1944 pRevStatus
->dwError
= error
;
1946 TRACE("returning %d (%08lx)\n", !error
, error
);