wintrust: Use helper function for setting confidence in SoftpubCheckCert.
[wine] / dlls / secur32 / schannel.c
1 /* Copyright (C) 2005 Juan Lang
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16  *
17  * This file implements the schannel provider, or, the SSL/TLS implementations.
18  * FIXME: It should be rather obvious that this file is empty of any
19  * implementation.
20  */
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #ifdef SONAME_LIBGNUTLS
26 #include <gnutls/gnutls.h>
27 #endif
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "sspi.h"
32 #include "schannel.h"
33 #include "secur32_priv.h"
34 #include "wine/debug.h"
35 #include "wine/library.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
38
39 #ifdef SONAME_LIBGNUTLS
40
41 static void *libgnutls_handle;
42 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
43 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
44 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
45 MAKE_FUNCPTR(gnutls_global_deinit);
46 MAKE_FUNCPTR(gnutls_global_init);
47 MAKE_FUNCPTR(gnutls_global_set_log_function);
48 MAKE_FUNCPTR(gnutls_global_set_log_level);
49 #undef MAKE_FUNCPTR
50
51 enum schan_handle_type
52 {
53     SCHAN_HANDLE_CRED,
54     SCHAN_HANDLE_FREE
55 };
56
57 struct schan_handle
58 {
59     void *object;
60     enum schan_handle_type type;
61 };
62
63 struct schan_credentials
64 {
65     ULONG credential_use;
66     gnutls_certificate_credentials credentials;
67 };
68
69 static struct schan_handle *schan_handle_table;
70 static struct schan_handle *schan_free_handles;
71 static SIZE_T schan_handle_table_size;
72 static SIZE_T schan_handle_count;
73
74 static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type)
75 {
76     struct schan_handle *handle;
77
78     if (schan_free_handles)
79     {
80         /* Use a free handle */
81         handle = schan_free_handles;
82         if (handle->type != SCHAN_HANDLE_FREE)
83         {
84             ERR("Handle %d(%p) is in the free list, but has type %#x.\n", (handle-schan_handle_table), handle, handle->type);
85             return -1;
86         }
87         schan_free_handles = (struct schan_handle *)handle->object;
88         handle->object = object;
89         handle->type = type;
90
91         return handle - schan_handle_table;
92     }
93     if (!(schan_handle_count < schan_handle_table_size))
94     {
95         /* Grow the table */
96         SIZE_T new_size = schan_handle_table_size + (schan_handle_table_size >> 1);
97         struct schan_handle *new_table = HeapReAlloc(GetProcessHeap(), 0, schan_handle_table, new_size * sizeof(*schan_handle_table));
98         if (!new_table)
99         {
100             ERR("Failed to grow the handle table\n");
101             return -1;
102         }
103         schan_handle_table = new_table;
104         schan_handle_table_size = new_size;
105     }
106
107     handle = &schan_handle_table[schan_handle_count++];
108     handle->object = object;
109     handle->type = type;
110
111     return handle - schan_handle_table;
112 }
113
114 static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type)
115 {
116     struct schan_handle *handle;
117     void *object;
118
119     if (handle_idx == -1) return NULL;
120     handle = &schan_handle_table[handle_idx];
121     if (handle->type != type)
122     {
123         ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
124         return NULL;
125     }
126
127     object = handle->object;
128     handle->object = schan_free_handles;
129     handle->type = SCHAN_HANDLE_FREE;
130     schan_free_handles = handle;
131
132     return object;
133 }
134
135 static SECURITY_STATUS schan_QueryCredentialsAttributes(
136  PCredHandle phCredential, ULONG ulAttribute, const VOID *pBuffer)
137 {
138     SECURITY_STATUS ret;
139
140     switch (ulAttribute)
141     {
142     case SECPKG_ATTR_SUPPORTED_ALGS:
143         if (pBuffer)
144         {
145             /* FIXME: get from CryptoAPI */
146             FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
147             ret = SEC_E_UNSUPPORTED_FUNCTION;
148         }
149         else
150             ret = SEC_E_INTERNAL_ERROR;
151         break;
152     case SECPKG_ATTR_CIPHER_STRENGTHS:
153         if (pBuffer)
154         {
155             SecPkgCred_CipherStrengths *r = (SecPkgCred_CipherStrengths*)pBuffer;
156
157             /* FIXME: get from CryptoAPI */
158             FIXME("SECPKG_ATTR_CIPHER_STRENGTHS: semi-stub\n");
159             r->dwMinimumCipherStrength = 40;
160             r->dwMaximumCipherStrength = 168;
161             ret = SEC_E_OK;
162         }
163         else
164             ret = SEC_E_INTERNAL_ERROR;
165         break;
166     case SECPKG_ATTR_SUPPORTED_PROTOCOLS:
167         if (pBuffer)
168         {
169             /* FIXME: get from OpenSSL? */
170             FIXME("SECPKG_ATTR_SUPPORTED_PROTOCOLS: stub\n");
171             ret = SEC_E_UNSUPPORTED_FUNCTION;
172         }
173         else
174             ret = SEC_E_INTERNAL_ERROR;
175         break;
176     default:
177         ret = SEC_E_UNSUPPORTED_FUNCTION;
178     }
179     return ret;
180 }
181
182 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesA(
183  PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
184 {
185     SECURITY_STATUS ret;
186
187     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
188
189     switch (ulAttribute)
190     {
191     case SECPKG_CRED_ATTR_NAMES:
192         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
193         ret = SEC_E_UNSUPPORTED_FUNCTION;
194         break;
195     default:
196         ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
197          pBuffer);
198     }
199     return ret;
200 }
201
202 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
203  PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
204 {
205     SECURITY_STATUS ret;
206
207     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
208
209     switch (ulAttribute)
210     {
211     case SECPKG_CRED_ATTR_NAMES:
212         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
213         ret = SEC_E_UNSUPPORTED_FUNCTION;
214         break;
215     default:
216         ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
217          pBuffer);
218     }
219     return ret;
220 }
221
222 static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
223 {
224     SECURITY_STATUS st;
225
226     switch (schanCred->dwVersion)
227     {
228     case SCH_CRED_V3:
229     case SCHANNEL_CRED_VERSION:
230         break;
231     default:
232         return SEC_E_INTERNAL_ERROR;
233     }
234
235     if (schanCred->cCreds == 0)
236         st = SEC_E_NO_CREDENTIALS;
237     else if (schanCred->cCreds > 1)
238         st = SEC_E_UNKNOWN_CREDENTIALS;
239     else
240     {
241         DWORD keySpec;
242         HCRYPTPROV csp;
243         BOOL ret, freeCSP;
244
245         ret = CryptAcquireCertificatePrivateKey(schanCred->paCred[0],
246          0, /* FIXME: what flags to use? */ NULL,
247          &csp, &keySpec, &freeCSP);
248         if (ret)
249         {
250             st = SEC_E_OK;
251             if (freeCSP)
252                 CryptReleaseContext(csp, 0);
253         }
254         else
255             st = SEC_E_UNKNOWN_CREDENTIALS;
256     }
257     return st;
258 }
259
260 static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schanCred,
261  PCredHandle phCredential, PTimeStamp ptsExpiry)
262 {
263     SECURITY_STATUS st = SEC_E_OK;
264
265     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
266
267     if (schanCred)
268     {
269         st = schan_CheckCreds(schanCred);
270         if (st == SEC_E_NO_CREDENTIALS)
271             st = SEC_E_OK;
272     }
273
274     /* For now, the only thing I'm interested in is the direction of the
275      * connection, so just store it.
276      */
277     if (st == SEC_E_OK)
278     {
279         ULONG_PTR handle;
280         struct schan_credentials *creds;
281
282         creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
283         if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
284
285         handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
286         if (handle == -1)
287         {
288             HeapFree(GetProcessHeap(), 0, creds);
289             return SEC_E_INTERNAL_ERROR;
290         }
291
292         creds->credential_use = SECPKG_CRED_OUTBOUND;
293         pgnutls_certificate_allocate_credentials(&creds->credentials);
294
295         phCredential->dwLower = handle;
296         phCredential->dwUpper = 0;
297
298         /* Outbound credentials have no expiry */
299         if (ptsExpiry)
300         {
301             ptsExpiry->LowPart = 0;
302             ptsExpiry->HighPart = 0;
303         }
304     }
305     return st;
306 }
307
308 static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schanCred,
309  PCredHandle phCredential, PTimeStamp ptsExpiry)
310 {
311     SECURITY_STATUS st;
312
313     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
314
315     if (!schanCred) return SEC_E_NO_CREDENTIALS;
316
317     st = schan_CheckCreds(schanCred);
318     if (st == SEC_E_OK)
319     {
320         ULONG_PTR handle;
321         struct schan_credentials *creds;
322
323         creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
324         if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
325         creds->credential_use = SECPKG_CRED_INBOUND;
326
327         handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
328         if (handle == -1)
329         {
330             HeapFree(GetProcessHeap(), 0, creds);
331             return SEC_E_INTERNAL_ERROR;
332         }
333
334         phCredential->dwLower = handle;
335         phCredential->dwUpper = 0;
336
337         /* FIXME: get expiry from cert */
338     }
339     return st;
340 }
341
342 static SECURITY_STATUS schan_AcquireCredentialsHandle(ULONG fCredentialUse,
343  const SCHANNEL_CRED *schanCred, PCredHandle phCredential, PTimeStamp ptsExpiry)
344 {
345     SECURITY_STATUS ret;
346
347     if (fCredentialUse == SECPKG_CRED_OUTBOUND)
348         ret = schan_AcquireClientCredentials(schanCred, phCredential,
349          ptsExpiry);
350     else
351         ret = schan_AcquireServerCredentials(schanCred, phCredential,
352          ptsExpiry);
353     return ret;
354 }
355
356 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleA(
357  SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
358  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
359  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
360 {
361     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
362      debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
363      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
364     return schan_AcquireCredentialsHandle(fCredentialUse,
365      (PSCHANNEL_CRED)pAuthData, phCredential, ptsExpiry);
366 }
367
368 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleW(
369  SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
370  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
371  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
372 {
373     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
374      debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
375      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
376     return schan_AcquireCredentialsHandle(fCredentialUse,
377      (PSCHANNEL_CRED)pAuthData, phCredential, ptsExpiry);
378 }
379
380 static SECURITY_STATUS SEC_ENTRY schan_FreeCredentialsHandle(
381  PCredHandle phCredential)
382 {
383     struct schan_credentials *creds;
384
385     TRACE("phCredential %p\n", phCredential);
386
387     if (!phCredential) return SEC_E_INVALID_HANDLE;
388
389     creds = schan_free_handle(phCredential->dwLower, SCHAN_HANDLE_CRED);
390     if (!creds) return SEC_E_INVALID_HANDLE;
391
392     if (creds->credential_use == SECPKG_CRED_OUTBOUND)
393         pgnutls_certificate_free_credentials(creds->credentials);
394     HeapFree(GetProcessHeap(), 0, creds);
395
396     return SEC_E_OK;
397 }
398
399 /***********************************************************************
400  *              InitializeSecurityContextA
401  */
402 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextA(
403  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
404  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
405  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
406  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
407 {
408     SECURITY_STATUS ret;
409
410     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
411      debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
412      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
413     if(phCredential)
414     {
415         FIXME("stub\n");
416         ret = SEC_E_UNSUPPORTED_FUNCTION;
417     }
418     else
419     {
420         ret = SEC_E_INVALID_HANDLE;
421     }
422     return ret;
423 }
424
425 /***********************************************************************
426  *              InitializeSecurityContextW
427  */
428 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
429  PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
430  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
431  PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext,
432  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
433 {
434     SECURITY_STATUS ret;
435
436     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
437      debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
438      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
439     if (phCredential)
440     {
441         FIXME("stub\n");
442         ret = SEC_E_UNSUPPORTED_FUNCTION;
443     }
444     else
445     {
446         ret = SEC_E_INVALID_HANDLE;
447     }
448     return ret;
449 }
450
451 static void schan_gnutls_log(int level, const char *msg)
452 {
453     TRACE("<%d> %s", level, msg);
454 }
455
456 static const SecurityFunctionTableA schanTableA = {
457     1,
458     NULL, /* EnumerateSecurityPackagesA */
459     schan_QueryCredentialsAttributesA,
460     schan_AcquireCredentialsHandleA,
461     schan_FreeCredentialsHandle,
462     NULL, /* Reserved2 */
463     schan_InitializeSecurityContextA, 
464     NULL, /* AcceptSecurityContext */
465     NULL, /* CompleteAuthToken */
466     NULL, /* DeleteSecurityContext */
467     NULL, /* ApplyControlToken */
468     NULL, /* QueryContextAttributesA */
469     NULL, /* ImpersonateSecurityContext */
470     NULL, /* RevertSecurityContext */
471     NULL, /* MakeSignature */
472     NULL, /* VerifySignature */
473     FreeContextBuffer,
474     NULL, /* QuerySecurityPackageInfoA */
475     NULL, /* Reserved3 */
476     NULL, /* Reserved4 */
477     NULL, /* ExportSecurityContext */
478     NULL, /* ImportSecurityContextA */
479     NULL, /* AddCredentialsA */
480     NULL, /* Reserved8 */
481     NULL, /* QuerySecurityContextToken */
482     NULL, /* EncryptMessage */
483     NULL, /* DecryptMessage */
484     NULL, /* SetContextAttributesA */
485 };
486
487 static const SecurityFunctionTableW schanTableW = {
488     1,
489     NULL, /* EnumerateSecurityPackagesW */
490     schan_QueryCredentialsAttributesW,
491     schan_AcquireCredentialsHandleW,
492     schan_FreeCredentialsHandle,
493     NULL, /* Reserved2 */
494     schan_InitializeSecurityContextW, 
495     NULL, /* AcceptSecurityContext */
496     NULL, /* CompleteAuthToken */
497     NULL, /* DeleteSecurityContext */
498     NULL, /* ApplyControlToken */
499     NULL, /* QueryContextAttributesW */
500     NULL, /* ImpersonateSecurityContext */
501     NULL, /* RevertSecurityContext */
502     NULL, /* MakeSignature */
503     NULL, /* VerifySignature */
504     FreeContextBuffer,
505     NULL, /* QuerySecurityPackageInfoW */
506     NULL, /* Reserved3 */
507     NULL, /* Reserved4 */
508     NULL, /* ExportSecurityContext */
509     NULL, /* ImportSecurityContextW */
510     NULL, /* AddCredentialsW */
511     NULL, /* Reserved8 */
512     NULL, /* QuerySecurityContextToken */
513     NULL, /* EncryptMessage */
514     NULL, /* DecryptMessage */
515     NULL, /* SetContextAttributesW */
516 };
517
518 static const WCHAR schannelComment[] = { 'S','c','h','a','n','n','e','l',' ',
519  'S','e','c','u','r','i','t','y',' ','P','a','c','k','a','g','e',0 };
520 static const WCHAR schannelDllName[] = { 's','c','h','a','n','n','e','l','.','d','l','l',0 };
521
522 void SECUR32_initSchannelSP(void)
523 {
524     SecureProvider *provider;
525
526
527     libgnutls_handle = wine_dlopen(SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0);
528     if (!libgnutls_handle)
529     {
530         WARN("Failed to load libgnutls.\n");
531         return;
532     }
533
534 #define LOAD_FUNCPTR(f) \
535     if (!(p##f = wine_dlsym(libgnutls_handle, #f, NULL, 0))) \
536     { \
537         ERR("Failed to load %s\n", #f); \
538         wine_dlclose(libgnutls_handle, NULL, 0); \
539         libgnutls_handle = NULL; \
540         return; \
541     }
542
543     LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
544     LOAD_FUNCPTR(gnutls_certificate_free_credentials)
545     LOAD_FUNCPTR(gnutls_global_deinit)
546     LOAD_FUNCPTR(gnutls_global_init)
547     LOAD_FUNCPTR(gnutls_global_set_log_function)
548     LOAD_FUNCPTR(gnutls_global_set_log_level)
549 #undef LOAD_FUNCPTR
550
551     provider = SECUR32_addProvider(&schanTableA, &schanTableW, schannelDllName);
552
553     if (provider)
554     {
555         /* This is what Windows reports.  This shouldn't break any applications
556          * even though the functions are missing, because the wrapper will
557          * return SEC_E_UNSUPPORTED_FUNCTION if our function is NULL.
558          */
559         static const long caps =
560          SECPKG_FLAG_INTEGRITY |
561          SECPKG_FLAG_PRIVACY |
562          SECPKG_FLAG_CONNECTION |
563          SECPKG_FLAG_MULTI_REQUIRED |
564          SECPKG_FLAG_EXTENDED_ERROR |
565          SECPKG_FLAG_IMPERSONATION |
566          SECPKG_FLAG_ACCEPT_WIN32_NAME |
567          SECPKG_FLAG_STREAM;
568         static const short version = 1;
569         static const long maxToken = 16384;
570         SEC_WCHAR *uniSPName = (SEC_WCHAR *)UNISP_NAME_W,
571          *schannel = (SEC_WCHAR *)SCHANNEL_NAME_W;
572
573         const SecPkgInfoW info[] = {
574          { caps, version, UNISP_RPC_ID, maxToken, uniSPName, uniSPName },
575          { caps, version, UNISP_RPC_ID, maxToken, schannel,
576           (SEC_WCHAR *)schannelComment },
577         };
578
579         SECUR32_addPackages(provider, sizeof(info) / sizeof(info[0]), NULL,
580          info);
581
582         schan_handle_table = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 64 * sizeof(*schan_handle_table));
583         schan_handle_table_size = 64;
584
585         pgnutls_global_init();
586         if (TRACE_ON(secur32))
587         {
588             pgnutls_global_set_log_level(4);
589             pgnutls_global_set_log_function(schan_gnutls_log);
590         }
591     }
592 }
593
594 void SECUR32_deinitSchannelSP(void)
595 {
596     pgnutls_global_deinit();
597     if (libgnutls_handle) wine_dlclose(libgnutls_handle, NULL, 0);
598 }
599
600 #else /* SONAME_LIBGNUTLS */
601
602 void SECUR32_initSchannelSP(void) {}
603 void SECUR32_deinitSchannelSP(void) {}
604
605 #endif /* SONAME_LIBGNUTLS */