secur32: Basic implementation of schannel AcquireCredentialsHandle/FreeCredentialsHandle.
[wine] / dlls / cryptdlg / main.c
1 /*
2  * Copyright 2008 Maarten Lankhorst
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #include "winreg.h"
27 #include "wincrypt.h"
28 #include "wintrust.h"
29 #include "winuser.h"
30 #include "cryptdlg.h"
31 #include "cryptuiapi.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(cryptdlg);
35
36 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
37 {
38     TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
39
40     switch (fdwReason)
41     {
42         case DLL_WINE_PREATTACH:
43             return FALSE;    /* prefer native version */
44         case DLL_PROCESS_ATTACH:
45             DisableThreadLibraryCalls(hinstDLL);
46             break;
47         case DLL_PROCESS_DETACH:
48             break;
49         default:
50             break;
51     }
52     return TRUE;
53 }
54
55 /***********************************************************************
56  *              GetFriendlyNameOfCertA (CRYPTDLG.@)
57  */
58 DWORD WINAPI GetFriendlyNameOfCertA(PCCERT_CONTEXT pccert, LPSTR pchBuffer,
59                              DWORD cchBuffer)
60 {
61     return CertGetNameStringA(pccert, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL,
62      pchBuffer, cchBuffer);
63 }
64
65 /***********************************************************************
66  *              GetFriendlyNameOfCertW (CRYPTDLG.@)
67  */
68 DWORD WINAPI GetFriendlyNameOfCertW(PCCERT_CONTEXT pccert, LPWSTR pchBuffer,
69                              DWORD cchBuffer)
70 {
71     return CertGetNameStringW(pccert, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL,
72      pchBuffer, cchBuffer);
73 }
74
75 /***********************************************************************
76  *              CertTrustInit (CRYPTDLG.@)
77  */
78 HRESULT WINAPI CertTrustInit(CRYPT_PROVIDER_DATA *pProvData)
79 {
80     HRESULT ret = S_FALSE;
81
82     TRACE("(%p)\n", pProvData);
83
84     if (pProvData->padwTrustStepErrors &&
85      !pProvData->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_WVTINIT])
86         ret = S_OK;
87     TRACE("returning %08x\n", ret);
88     return ret;
89 }
90
91 /***********************************************************************
92  *              CertTrustCertPolicy (CRYPTDLG.@)
93  */
94 BOOL WINAPI CertTrustCertPolicy(CRYPT_PROVIDER_DATA *pProvData, DWORD idxSigner, BOOL fCounterSignerChain, DWORD idxCounterSigner)
95 {
96     FIXME("(%p, %d, %s, %d)\n", pProvData, idxSigner, fCounterSignerChain ? "TRUE" : "FALSE", idxCounterSigner);
97     return FALSE;
98 }
99
100 /***********************************************************************
101  *              CertTrustCleanup (CRYPTDLG.@)
102  */
103 HRESULT WINAPI CertTrustCleanup(CRYPT_PROVIDER_DATA *pProvData)
104 {
105     FIXME("(%p)\n", pProvData);
106     return E_NOTIMPL;
107 }
108
109 static BOOL CRYPTDLG_CheckOnlineCRL(void)
110 {
111     static const WCHAR policyFlagsKey[] = { 'S','o','f','t','w','a','r','e',
112      '\\','M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g',
113      'r','a','p','h','y','\\','{','7','8','0','1','e','b','d','0','-','c','f',
114      '4','b','-','1','1','d','0','-','8','5','1','f','-','0','0','6','0','9',
115      '7','9','3','8','7','e','a','}',0 };
116     static const WCHAR policyFlags[] = { 'P','o','l','i','c','y','F','l','a',
117      'g','s',0 };
118     HKEY key;
119     BOOL ret = FALSE;
120
121     if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, policyFlagsKey, 0, KEY_READ, &key))
122     {
123         DWORD type, flags, size = sizeof(flags);
124
125         if (!RegQueryValueExW(key, policyFlags, NULL, &type, (BYTE *)&flags,
126          &size) && type == REG_DWORD)
127         {
128             /* The flag values aren't defined in any header I'm aware of, but
129              * this value is well documented on the net.
130              */
131             if (flags & 0x00010000)
132                 ret = TRUE;
133         }
134         RegCloseKey(key);
135     }
136     return ret;
137 }
138
139 /* Returns TRUE if pCert is not in the Disallowed system store, or FALSE if it
140  * is.
141  */
142 static BOOL CRYPTDLG_IsCertAllowed(PCCERT_CONTEXT pCert)
143 {
144     BOOL ret;
145     BYTE hash[20];
146     DWORD size = sizeof(hash);
147
148     if ((ret = CertGetCertificateContextProperty(pCert,
149      CERT_SIGNATURE_HASH_PROP_ID, hash, &size)))
150     {
151         static const WCHAR disallowedW[] =
152          { 'D','i','s','a','l','l','o','w','e','d',0 };
153         HCERTSTORE disallowed = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
154          X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER, disallowedW);
155
156         if (disallowed)
157         {
158             PCCERT_CONTEXT found = CertFindCertificateInStore(disallowed,
159              X509_ASN_ENCODING, 0, CERT_FIND_SIGNATURE_HASH, hash, NULL);
160
161             if (found)
162             {
163                 ret = FALSE;
164                 CertFreeCertificateContext(found);
165             }
166             CertCloseStore(disallowed, 0);
167         }
168     }
169     return ret;
170 }
171
172 static DWORD CRYPTDLG_TrustStatusToConfidence(DWORD errorStatus)
173 {
174     DWORD confidence = 0;
175
176     confidence = 0;
177     if (!(errorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID))
178         confidence |= CERT_CONFIDENCE_SIG;
179     if (!(errorStatus & CERT_TRUST_IS_NOT_TIME_VALID))
180         confidence |= CERT_CONFIDENCE_TIME;
181     if (!(errorStatus & CERT_TRUST_IS_NOT_TIME_NESTED))
182         confidence |= CERT_CONFIDENCE_TIMENEST;
183     return confidence;
184 }
185
186 static BOOL CRYPTDLG_CopyChain(CRYPT_PROVIDER_DATA *data,
187  PCCERT_CHAIN_CONTEXT chain)
188 {
189     BOOL ret;
190     CRYPT_PROVIDER_SGNR signer;
191     PCERT_SIMPLE_CHAIN simpleChain = chain->rgpChain[0];
192     DWORD i;
193
194     memset(&signer, 0, sizeof(signer));
195     signer.cbStruct = sizeof(signer);
196     ret = data->psPfns->pfnAddSgnr2Chain(data, FALSE, 0, &signer);
197     if (ret)
198     {
199         CRYPT_PROVIDER_SGNR *sgnr = WTHelperGetProvSignerFromChain(data, 0,
200          FALSE, 0);
201
202         if (sgnr)
203         {
204             sgnr->dwError = simpleChain->TrustStatus.dwErrorStatus;
205             sgnr->pChainContext = CertDuplicateCertificateChain(chain);
206         }
207         else
208             ret = FALSE;
209         for (i = 0; ret && i < simpleChain->cElement; i++)
210         {
211             ret = data->psPfns->pfnAddCert2Chain(data, 0, FALSE, 0,
212              simpleChain->rgpElement[i]->pCertContext);
213             if (ret)
214             {
215                 CRYPT_PROVIDER_CERT *cert;
216
217                 if ((cert = WTHelperGetProvCertFromChain(sgnr, i)))
218                 {
219                     CERT_CHAIN_ELEMENT *element = simpleChain->rgpElement[i];
220
221                     cert->dwConfidence = CRYPTDLG_TrustStatusToConfidence(
222                      element->TrustStatus.dwErrorStatus);
223                     cert->dwError = element->TrustStatus.dwErrorStatus;
224                     cert->pChainElement = element;
225                 }
226                 else
227                     ret = FALSE;
228             }
229         }
230     }
231     return ret;
232 }
233
234 static CERT_VERIFY_CERTIFICATE_TRUST *CRYPTDLG_GetVerifyData(
235  CRYPT_PROVIDER_DATA *data)
236 {
237     CERT_VERIFY_CERTIFICATE_TRUST *pCert = NULL;
238
239     /* This should always be true, but just in case the calling function is
240      * called directly:
241      */
242     if (data->pWintrustData->dwUnionChoice == WTD_CHOICE_BLOB &&
243      data->pWintrustData->pBlob && data->pWintrustData->pBlob->cbMemObject ==
244      sizeof(CERT_VERIFY_CERTIFICATE_TRUST) &&
245      data->pWintrustData->pBlob->pbMemObject)
246          pCert = (CERT_VERIFY_CERTIFICATE_TRUST *)
247           data->pWintrustData->pBlob->pbMemObject;
248     return pCert;
249 }
250
251 static HCERTCHAINENGINE CRYPTDLG_MakeEngine(CERT_VERIFY_CERTIFICATE_TRUST *cert)
252 {
253     HCERTCHAINENGINE engine = NULL;
254     HCERTSTORE root = NULL, trust = NULL;
255     DWORD i;
256
257     if (cert->cRootStores)
258     {
259         root = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
260          CERT_STORE_CREATE_NEW_FLAG, NULL);
261         if (root)
262         {
263             for (i = 0; i < cert->cRootStores; i++)
264                 CertAddStoreToCollection(root, cert->rghstoreRoots[i], 0, 0);
265         }
266     }
267     if (cert->cTrustStores)
268     {
269         trust = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
270          CERT_STORE_CREATE_NEW_FLAG, NULL);
271         if (root)
272         {
273             for (i = 0; i < cert->cTrustStores; i++)
274                 CertAddStoreToCollection(trust, cert->rghstoreTrust[i], 0, 0);
275         }
276     }
277     if (cert->cRootStores || cert->cStores || cert->cTrustStores)
278     {
279         CERT_CHAIN_ENGINE_CONFIG config;
280
281         memset(&config, 0, sizeof(config));
282         config.cbSize = sizeof(config);
283         config.hRestrictedRoot = root;
284         config.hRestrictedTrust = trust;
285         config.cAdditionalStore = cert->cStores;
286         config.rghAdditionalStore = cert->rghstoreCAs;
287         config.hRestrictedRoot = root;
288         CertCreateCertificateChainEngine(&config, &engine);
289         CertCloseStore(root, 0);
290         CertCloseStore(trust, 0);
291     }
292     return engine;
293 }
294
295 /***********************************************************************
296  *              CertTrustFinalPolicy (CRYPTDLG.@)
297  */
298 HRESULT WINAPI CertTrustFinalPolicy(CRYPT_PROVIDER_DATA *data)
299 {
300     BOOL ret;
301     DWORD err = S_OK;
302     CERT_VERIFY_CERTIFICATE_TRUST *pCert = CRYPTDLG_GetVerifyData(data);
303
304     TRACE("(%p)\n", data);
305
306     if (data->pWintrustData->dwUIChoice != WTD_UI_NONE)
307         FIXME("unimplemented for UI choice %d\n",
308          data->pWintrustData->dwUIChoice);
309     if (pCert)
310     {
311         DWORD flags = 0;
312         CERT_CHAIN_PARA chainPara;
313         HCERTCHAINENGINE engine;
314
315         memset(&chainPara, 0, sizeof(chainPara));
316         chainPara.cbSize = sizeof(chainPara);
317         if (CRYPTDLG_CheckOnlineCRL())
318             flags |= CERT_CHAIN_REVOCATION_CHECK_END_CERT;
319         engine = CRYPTDLG_MakeEngine(pCert);
320         GetSystemTimeAsFileTime(&data->sftSystemTime);
321         ret = CRYPTDLG_IsCertAllowed(pCert->pccert);
322         if (ret)
323         {
324             PCCERT_CHAIN_CONTEXT chain;
325
326             ret = CertGetCertificateChain(engine, pCert->pccert,
327              &data->sftSystemTime, NULL, &chainPara, flags, NULL, &chain);
328             if (ret)
329             {
330                 if (chain->cChain != 1)
331                 {
332                     FIXME("unimplemented for more than 1 simple chain\n");
333                     err = TRUST_E_SUBJECT_FORM_UNKNOWN;
334                     ret = FALSE;
335                 }
336                 else if ((ret = CRYPTDLG_CopyChain(data, chain)))
337                 {
338                     if (CertVerifyTimeValidity(&data->sftSystemTime,
339                      pCert->pccert->pCertInfo))
340                     {
341                         ret = FALSE;
342                         err = CERT_E_EXPIRED;
343                     }
344                 }
345                 else
346                     err = TRUST_E_SYSTEM_ERROR;
347                 CertFreeCertificateChain(chain);
348             }
349             else
350                 err = TRUST_E_SUBJECT_NOT_TRUSTED;
351         }
352         CertFreeCertificateChainEngine(engine);
353     }
354     else
355     {
356         ret = FALSE;
357         err = TRUST_E_NOSIGNATURE;
358     }
359     /* Oddly, native doesn't set the error in the trust step error location,
360      * probably because this action is more advisory than anything else.
361      * Instead it stores it as the final error, but the function "succeeds" in
362      * any case.
363      */
364     if (!ret)
365         data->dwFinalError = err;
366     TRACE("returning %d (%08x)\n", S_OK, data->dwFinalError);
367     return S_OK;
368 }
369
370 /***********************************************************************
371  *              CertViewPropertiesA (CRYPTDLG.@)
372  */
373 BOOL WINAPI CertViewPropertiesA(CERT_VIEWPROPERTIES_STRUCT_A *info)
374 {
375     CERT_VIEWPROPERTIES_STRUCT_W infoW;
376     LPWSTR title = NULL;
377     BOOL ret;
378
379     TRACE("(%p)\n", info);
380
381     memcpy(&infoW, info, sizeof(infoW));
382     if (info->szTitle)
383     {
384         int len = MultiByteToWideChar(CP_ACP, 0, info->szTitle, -1, NULL, 0);
385
386         title = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
387         if (title)
388         {
389             MultiByteToWideChar(CP_ACP, 0, info->szTitle, -1, title, len);
390             infoW.szTitle = title;
391         }
392         else
393         {
394             ret = FALSE;
395             goto error;
396         }
397     }
398     ret = CertViewPropertiesW(&infoW);
399     HeapFree(GetProcessHeap(), 0, title);
400 error:
401     return ret;
402 }
403
404 /***********************************************************************
405  *              CertViewPropertiesW (CRYPTDLG.@)
406  */
407 BOOL WINAPI CertViewPropertiesW(CERT_VIEWPROPERTIES_STRUCT_W *info)
408 {
409     static GUID cert_action_verify = CERT_CERTIFICATE_ACTION_VERIFY;
410     CERT_VERIFY_CERTIFICATE_TRUST trust;
411     WINTRUST_BLOB_INFO blob;
412     WINTRUST_DATA wtd;
413     LONG err;
414     BOOL ret;
415
416     TRACE("(%p)\n", info);
417
418     memset(&trust, 0, sizeof(trust));
419     trust.cbSize = sizeof(trust);
420     trust.pccert = info->pCertContext;
421     trust.cRootStores = info->cRootStores;
422     trust.rghstoreRoots = info->rghstoreRoots;
423     trust.cStores = info->cStores;
424     trust.rghstoreCAs = info->rghstoreCAs;
425     trust.cTrustStores = info->cTrustStores;
426     trust.rghstoreTrust = info->rghstoreTrust;
427     memset(&blob, 0, sizeof(blob));
428     blob.cbStruct = sizeof(blob);
429     blob.cbMemObject = sizeof(trust);
430     blob.pbMemObject = (BYTE *)&trust;
431     memset(&wtd, 0, sizeof(wtd));
432     wtd.cbStruct = sizeof(wtd);
433     wtd.dwUIChoice = WTD_UI_NONE;
434     wtd.dwUnionChoice = WTD_CHOICE_BLOB;
435     wtd.pBlob = &blob;
436     wtd.dwStateAction = WTD_STATEACTION_VERIFY;
437     err = WinVerifyTrust(NULL, &cert_action_verify, &wtd);
438     if (err == ERROR_SUCCESS)
439     {
440         CRYPTUI_VIEWCERTIFICATE_STRUCTW uiInfo;
441         BOOL propsChanged = FALSE;
442
443         memset(&uiInfo, 0, sizeof(uiInfo));
444         uiInfo.dwSize = sizeof(uiInfo);
445         uiInfo.hwndParent = info->hwndParent;
446         uiInfo.dwFlags =
447          CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_EDITPROPERTIES;
448         uiInfo.szTitle = info->szTitle;
449         uiInfo.pCertContext = info->pCertContext;
450         uiInfo.cPurposes = info->cArrayPurposes;
451         uiInfo.rgszPurposes = (LPCSTR *)info->arrayPurposes;
452         uiInfo.hWVTStateData = wtd.hWVTStateData;
453         uiInfo.fpCryptProviderDataTrustedUsage = TRUE;
454         uiInfo.cPropSheetPages = info->cArrayPropSheetPages;
455         uiInfo.rgPropSheetPages = info->arrayPropSheetPages;
456         uiInfo.nStartPage = info->nStartPage;
457         ret = CryptUIDlgViewCertificateW(&uiInfo, &propsChanged);
458         wtd.dwStateAction = WTD_STATEACTION_CLOSE;
459         WinVerifyTrust(NULL, &cert_action_verify, &wtd);
460     }
461     else
462         ret = FALSE;
463     return ret;
464 }
465
466 /***********************************************************************
467  *              DllRegisterServer (CRYPTDLG.@)
468  */
469 HRESULT WINAPI DllRegisterServer(void)
470 {
471     static WCHAR cryptdlg[] = { 'c','r','y','p','t','d','l','g','.',
472      'd','l','l',0 };
473     static WCHAR wintrust[] = { 'w','i','n','t','r','u','s','t','.',
474      'd','l','l',0 };
475     static WCHAR certTrustInit[] = { 'C','e','r','t','T','r','u','s','t',
476      'I','n','i','t',0 };
477     static WCHAR wintrustCertificateTrust[] = { 'W','i','n','t','r','u','s','t',
478      'C','e','r','t','i','f','i','c','a','t','e','T','r','u','s','t',0 };
479     static WCHAR certTrustCertPolicy[] = { 'C','e','r','t','T','r','u','s','t',
480      'C','e','r','t','P','o','l','i','c','y',0 };
481     static WCHAR certTrustFinalPolicy[] = { 'C','e','r','t','T','r','u','s','t',
482      'F','i','n','a','l','P','o','l','i','c','y',0 };
483     static WCHAR certTrustCleanup[] = { 'C','e','r','t','T','r','u','s','t',
484      'C','l','e','a','n','u','p',0 };
485     CRYPT_REGISTER_ACTIONID reg;
486     GUID guid = CERT_CERTIFICATE_ACTION_VERIFY;
487     HRESULT hr = S_OK;
488
489     memset(&reg, 0, sizeof(reg));
490     reg.cbStruct = sizeof(reg);
491     reg.sInitProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY);
492     reg.sInitProvider.pwszDLLName = cryptdlg;
493     reg.sInitProvider.pwszFunctionName = certTrustInit;
494     reg.sCertificateProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY);
495     reg.sCertificateProvider.pwszDLLName = wintrust;
496     reg.sCertificateProvider.pwszFunctionName = wintrustCertificateTrust;
497     reg.sCertificatePolicyProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY);
498     reg.sCertificatePolicyProvider.pwszDLLName = cryptdlg;
499     reg.sCertificatePolicyProvider.pwszFunctionName = certTrustCertPolicy;
500     reg.sFinalPolicyProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY);
501     reg.sFinalPolicyProvider.pwszDLLName = cryptdlg;
502     reg.sFinalPolicyProvider.pwszFunctionName = certTrustFinalPolicy;
503     reg.sCleanupProvider.cbStruct = sizeof(CRYPT_TRUST_REG_ENTRY);
504     reg.sCleanupProvider.pwszDLLName = cryptdlg;
505     reg.sCleanupProvider.pwszFunctionName = certTrustCleanup;
506     if (!WintrustAddActionID(&guid, WT_ADD_ACTION_ID_RET_RESULT_FLAG, &reg))
507         hr = GetLastError();
508     return hr;
509 }
510
511 /***********************************************************************
512  *              DllUnregisterServer (CRYPTDLG.@)
513  */
514 HRESULT WINAPI DllUnregisterServer(void)
515 {
516     GUID guid = CERT_CERTIFICATE_ACTION_VERIFY;
517
518     WintrustRemoveActionID(&guid);
519     return S_OK;
520 }