/src/FreeRDP/winpr/libwinpr/sspi/Negotiate/negotiate.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /**  | 
2  |  |  * WinPR: Windows Portable Runtime  | 
3  |  |  * Negotiate Security Package  | 
4  |  |  *  | 
5  |  |  * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>  | 
6  |  |  * Copyright 2017 Dorian Ducournau <dorian.ducournau@gmail.com>  | 
7  |  |  *  | 
8  |  |  * Licensed under the Apache License, Version 2.0 (the "License");  | 
9  |  |  * you may not use this file except in compliance with the License.  | 
10  |  |  * You may obtain a copy of the License at  | 
11  |  |  *  | 
12  |  |  *     http://www.apache.org/licenses/LICENSE-2.0  | 
13  |  |  *  | 
14  |  |  * Unless required by applicable law or agreed to in writing, software  | 
15  |  |  * distributed under the License is distributed on an "AS IS" BASIS,  | 
16  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
17  |  |  * See the License for the specific language governing permissions and  | 
18  |  |  * limitations under the License.  | 
19  |  |  */  | 
20  |  |  | 
21  |  | #include <winpr/config.h>  | 
22  |  |  | 
23  |  | #include <winpr/crt.h>  | 
24  |  | #include <winpr/wtypes.h>  | 
25  |  | #include <winpr/assert.h>  | 
26  |  | #include <winpr/sspi.h>  | 
27  |  | #include <winpr/tchar.h>  | 
28  |  | #include <winpr/registry.h>  | 
29  |  | #include <winpr/build-config.h>  | 
30  |  | #include <winpr/asn1.h>  | 
31  |  |  | 
32  |  | #include "negotiate.h"  | 
33  |  |  | 
34  |  | #include "../NTLM/ntlm.h"  | 
35  |  | #include "../NTLM/ntlm_export.h"  | 
36  |  | #include "../Kerberos/kerberos.h"  | 
37  |  | #include "../sspi.h"  | 
38  |  | #include "../../log.h"  | 
39  |  | #define TAG WINPR_TAG("negotiate") | 
40  |  |  | 
41  |  | static const char NEGO_REG_KEY[] =  | 
42  |  |     "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\SSPI\\Negotiate";  | 
43  |  |  | 
44  |  | typedef struct  | 
45  |  | { | 
46  |  |   const TCHAR* name;  | 
47  |  |   const SecurityFunctionTableA* table;  | 
48  |  |   const SecurityFunctionTableW* table_w;  | 
49  |  | } SecPkg;  | 
50  |  |  | 
51  |  | struct Mech_st  | 
52  |  | { | 
53  |  |   const WinPrAsn1_OID* oid;  | 
54  |  |   const SecPkg* pkg;  | 
55  |  |   const UINT flags;  | 
56  |  |   const BOOL preferred;  | 
57  |  | };  | 
58  |  |  | 
59  |  | typedef struct  | 
60  |  | { | 
61  |  |   const Mech* mech;  | 
62  |  |   CredHandle cred;  | 
63  |  |   BOOL valid;  | 
64  |  | } MechCred;  | 
65  |  |  | 
66  |  | const SecPkgInfoA NEGOTIATE_SecPkgInfoA = { | 
67  |  |   0x00083BB3,                    /* fCapabilities */  | 
68  |  |   1,                             /* wVersion */  | 
69  |  |   0x0009,                        /* wRPCID */  | 
70  |  |   0x00002FE0,                    /* cbMaxToken */  | 
71  |  |   "Negotiate",                   /* Name */  | 
72  |  |   "Microsoft Package Negotiator" /* Comment */  | 
73  |  | };  | 
74  |  |  | 
75  |  | static WCHAR NEGOTIATE_SecPkgInfoW_NameBuffer[32] = { 0 }; | 
76  |  | static WCHAR NEGOTIATE_SecPkgInfoW_CommentBuffer[32] = { 0 }; | 
77  |  |  | 
78  |  | const SecPkgInfoW NEGOTIATE_SecPkgInfoW = { | 
79  |  |   0x00083BB3,                         /* fCapabilities */  | 
80  |  |   1,                                  /* wVersion */  | 
81  |  |   0x0009,                             /* wRPCID */  | 
82  |  |   0x00002FE0,                         /* cbMaxToken */  | 
83  |  |   NEGOTIATE_SecPkgInfoW_NameBuffer,   /* Name */  | 
84  |  |   NEGOTIATE_SecPkgInfoW_CommentBuffer /* Comment */  | 
85  |  | };  | 
86  |  |  | 
87  |  | static const WinPrAsn1_OID spnego_OID = { 6, (BYTE*)"\x2b\x06\x01\x05\x05\x02" }; | 
88  |  | static const WinPrAsn1_OID kerberos_u2u_OID = { 10, | 
89  |  |                                               (BYTE*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03" };  | 
90  |  | static const WinPrAsn1_OID kerberos_OID = { 9, (BYTE*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; | 
91  |  | static const WinPrAsn1_OID kerberos_wrong_OID = { 9, | 
92  |  |                                                 (BYTE*)"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02" };  | 
93  |  | static const WinPrAsn1_OID ntlm_OID = { 10, (BYTE*)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }; | 
94  |  |  | 
95  |  | static const WinPrAsn1_OID negoex_OID = { 10, (BYTE*)"\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e" }; | 
96  |  |  | 
97  |  | #ifdef WITH_KRB5  | 
98  |  | static const SecPkg SecPkgTable[] = { | 
99  |  |   { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW }, | 
100  |  |   { KERBEROS_SSP_NAME, &KERBEROS_SecurityFunctionTableA, &KERBEROS_SecurityFunctionTableW }, | 
101  |  |   { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, &NTLM_SecurityFunctionTableW } | 
102  |  | };  | 
103  |  |  | 
104  |  | static const Mech MechTable[] = { | 
105  |  |   { &kerberos_u2u_OID, &SecPkgTable[0], ISC_REQ_INTEGRITY | ISC_REQ_USE_SESSION_KEY, TRUE }, | 
106  |  |   { &kerberos_OID, &SecPkgTable[1], ISC_REQ_INTEGRITY, TRUE }, | 
107  |  |   { &ntlm_OID, &SecPkgTable[2], 0, FALSE }, | 
108  |  | };  | 
109  |  | #else  | 
110  |  | static const SecPkg SecPkgTable[] = { { NTLM_SSP_NAME, &NTLM_SecurityFunctionTableA, | 
111  |  |                                       &NTLM_SecurityFunctionTableW } };  | 
112  |  |  | 
113  |  | static const Mech MechTable[] = { | 
114  |  |   { &ntlm_OID, &SecPkgTable[0], 0, FALSE }, | 
115  |  | };  | 
116  |  | #endif  | 
117  |  |  | 
118  |  | static const size_t MECH_COUNT = sizeof(MechTable) / sizeof(Mech);  | 
119  |  |  | 
120  |  | enum NegState  | 
121  |  | { | 
122  |  |   NOSTATE = -1,  | 
123  |  |   ACCEPT_COMPLETED,  | 
124  |  |   ACCEPT_INCOMPLETE,  | 
125  |  |   REJECT,  | 
126  |  |   REQUEST_MIC  | 
127  |  | };  | 
128  |  |  | 
129  |  | typedef struct  | 
130  |  | { | 
131  |  |   enum NegState negState;  | 
132  |  |   BOOL init;  | 
133  |  |   WinPrAsn1_OID supportedMech;  | 
134  |  |   SecBuffer mechTypes;  | 
135  |  |   SecBuffer mechToken;  | 
136  |  |   SecBuffer mic;  | 
137  |  | } NegToken;  | 
138  |  |  | 
139  |  | static const NegToken empty_neg_token = { NOSTATE,        FALSE,          { 0, NULL }, | 
140  |  |                                         { 0, 0, NULL }, { 0, 0, NULL }, { 0, 0, NULL } }; | 
141  |  |  | 
142  |  | static NEGOTIATE_CONTEXT* negotiate_ContextNew(NEGOTIATE_CONTEXT* init_context)  | 
143  | 0  | { | 
144  | 0  |   NEGOTIATE_CONTEXT* context = NULL;  | 
145  |  | 
  | 
146  | 0  |   WINPR_ASSERT(init_context);  | 
147  |  |  | 
148  | 0  |   context = calloc(1, sizeof(NEGOTIATE_CONTEXT));  | 
149  | 0  |   if (!context)  | 
150  | 0  |     return NULL;  | 
151  |  |  | 
152  | 0  |   if (init_context->spnego)  | 
153  | 0  |   { | 
154  | 0  |     init_context->mechTypes.pvBuffer = malloc(init_context->mechTypes.cbBuffer);  | 
155  | 0  |     if (!init_context->mechTypes.pvBuffer)  | 
156  | 0  |     { | 
157  | 0  |       free(context);  | 
158  | 0  |       return NULL;  | 
159  | 0  |     }  | 
160  | 0  |   }  | 
161  |  |  | 
162  | 0  |   *context = *init_context;  | 
163  |  | 
  | 
164  | 0  |   return context;  | 
165  | 0  | }  | 
166  |  |  | 
167  |  | static void negotiate_ContextFree(NEGOTIATE_CONTEXT* context)  | 
168  | 0  | { | 
169  | 0  |   WINPR_ASSERT(context);  | 
170  |  |  | 
171  | 0  |   if (context->mechTypes.pvBuffer)  | 
172  | 0  |     free(context->mechTypes.pvBuffer);  | 
173  | 0  |   free(context);  | 
174  | 0  | }  | 
175  |  |  | 
176  |  | static const char* negotiate_mech_name(const WinPrAsn1_OID* oid)  | 
177  | 0  | { | 
178  | 0  |   if (sspi_gss_oid_compare(oid, &spnego_OID))  | 
179  | 0  |     return "SPNEGO (1.3.6.1.5.5.2)";  | 
180  | 0  |   else if (sspi_gss_oid_compare(oid, &kerberos_u2u_OID))  | 
181  | 0  |     return "Kerberos user to user (1.2.840.113554.1.2.2.3)";  | 
182  | 0  |   else if (sspi_gss_oid_compare(oid, &kerberos_OID))  | 
183  | 0  |     return "Kerberos (1.2.840.113554.1.2.2)";  | 
184  | 0  |   else if (sspi_gss_oid_compare(oid, &kerberos_wrong_OID))  | 
185  | 0  |     return "Kerberos [wrong OID] (1.2.840.48018.1.2.2)";  | 
186  | 0  |   else if (sspi_gss_oid_compare(oid, &ntlm_OID))  | 
187  | 0  |     return "NTLM (1.3.6.1.4.1.311.2.2.10)";  | 
188  | 0  |   else if (sspi_gss_oid_compare(oid, &negoex_OID))  | 
189  | 0  |     return "NegoEx (1.3.6.1.4.1.311.2.2.30)";  | 
190  | 0  |   else  | 
191  | 0  |     return "Unknown mechanism";  | 
192  | 0  | }  | 
193  |  |  | 
194  |  | static const Mech* negotiate_GetMechByOID(const WinPrAsn1_OID* oid)  | 
195  | 0  | { | 
196  | 0  |   WINPR_ASSERT(oid);  | 
197  |  |  | 
198  | 0  |   WinPrAsn1_OID testOid = *oid;  | 
199  |  | 
  | 
200  | 0  |   if (sspi_gss_oid_compare(&testOid, &kerberos_wrong_OID))  | 
201  | 0  |   { | 
202  | 0  |     testOid.len = kerberos_OID.len;  | 
203  | 0  |     testOid.data = kerberos_OID.data;  | 
204  | 0  |   }  | 
205  |  | 
  | 
206  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
207  | 0  |   { | 
208  | 0  |     if (sspi_gss_oid_compare(&testOid, MechTable[i].oid))  | 
209  | 0  |       return &MechTable[i];  | 
210  | 0  |   }  | 
211  | 0  |   return NULL;  | 
212  | 0  | }  | 
213  |  |  | 
214  |  | static PSecHandle negotiate_FindCredential(MechCred* creds, const Mech* mech)  | 
215  | 0  | { | 
216  | 0  |   WINPR_ASSERT(creds);  | 
217  |  |  | 
218  | 0  |   if (!mech)  | 
219  | 0  |     return NULL;  | 
220  |  |  | 
221  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
222  | 0  |   { | 
223  | 0  |     MechCred* cred = &creds[i];  | 
224  |  | 
  | 
225  | 0  |     if (cred->mech == mech)  | 
226  | 0  |     { | 
227  | 0  |       if (cred->valid)  | 
228  | 0  |         return &cred->cred;  | 
229  | 0  |       return NULL;  | 
230  | 0  |     }  | 
231  | 0  |   }  | 
232  |  |  | 
233  | 0  |   return NULL;  | 
234  | 0  | }  | 
235  |  |  | 
236  |  | static BOOL negotiate_get_dword(HKEY hKey, const char* subkey, DWORD* pdwValue)  | 
237  | 0  | { | 
238  | 0  |   DWORD dwValue = 0;  | 
239  | 0  |   DWORD dwType = 0;  | 
240  | 0  |   DWORD dwSize = sizeof(dwValue);  | 
241  | 0  |   LONG rc = RegQueryValueExA(hKey, subkey, NULL, &dwType, (BYTE*)&dwValue, &dwSize);  | 
242  |  | 
  | 
243  | 0  |   if (rc != ERROR_SUCCESS)  | 
244  | 0  |     return FALSE;  | 
245  | 0  |   if (dwType != REG_DWORD)  | 
246  | 0  |     return FALSE;  | 
247  |  |  | 
248  | 0  |   *pdwValue = dwValue;  | 
249  | 0  |   return TRUE;  | 
250  | 0  | }  | 
251  |  |  | 
252  |  | static BOOL negotiate_get_config_from_auth_package_list(void* pAuthData, BOOL* kerberos, BOOL* ntlm)  | 
253  | 0  | { | 
254  | 0  |   char* tok_ctx = NULL;  | 
255  | 0  |   char* tok_ptr = NULL;  | 
256  | 0  |   char* PackageList = NULL;  | 
257  |  | 
  | 
258  | 0  |   if (!sspi_CopyAuthPackageListA((const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData, &PackageList))  | 
259  | 0  |     return FALSE;  | 
260  |  |  | 
261  | 0  |   tok_ptr = strtok_s(PackageList, ",", &tok_ctx);  | 
262  |  | 
  | 
263  | 0  |   while (tok_ptr)  | 
264  | 0  |   { | 
265  | 0  |     char* PackageName = tok_ptr;  | 
266  | 0  |     BOOL PackageInclude = TRUE;  | 
267  |  | 
  | 
268  | 0  |     if (PackageName[0] == '!')  | 
269  | 0  |     { | 
270  | 0  |       PackageName = &PackageName[1];  | 
271  | 0  |       PackageInclude = FALSE;  | 
272  | 0  |     }  | 
273  |  | 
  | 
274  | 0  |     if (!_stricmp(PackageName, "ntlm"))  | 
275  | 0  |     { | 
276  | 0  |       *ntlm = PackageInclude;  | 
277  | 0  |     }  | 
278  | 0  |     else if (!_stricmp(PackageName, "kerberos"))  | 
279  | 0  |     { | 
280  | 0  |       *kerberos = PackageInclude;  | 
281  | 0  |     }  | 
282  | 0  |     else  | 
283  | 0  |     { | 
284  | 0  |       WLog_WARN(TAG, "Unknown authentication package name: %s", PackageName);  | 
285  | 0  |     }  | 
286  |  | 
  | 
287  | 0  |     tok_ptr = strtok_s(NULL, ",", &tok_ctx);  | 
288  | 0  |   }  | 
289  |  | 
  | 
290  | 0  |   free(PackageList);  | 
291  | 0  |   return TRUE;  | 
292  | 0  | }  | 
293  |  |  | 
294  |  | static BOOL negotiate_get_config(void* pAuthData, BOOL* kerberos, BOOL* ntlm)  | 
295  | 0  | { | 
296  | 0  |   HKEY hKey = NULL;  | 
297  | 0  |   LONG rc = 0;  | 
298  |  | 
  | 
299  | 0  |   WINPR_ASSERT(kerberos);  | 
300  | 0  |   WINPR_ASSERT(ntlm);  | 
301  |  |  | 
302  | 0  | #if !defined(WITH_KRB5_NO_NTLM_FALLBACK)  | 
303  | 0  |   *ntlm = TRUE;  | 
304  |  | #else  | 
305  |  |   *ntlm = FALSE;  | 
306  |  | #endif  | 
307  | 0  |   *kerberos = TRUE;  | 
308  |  | 
  | 
309  | 0  |   if (negotiate_get_config_from_auth_package_list(pAuthData, kerberos, ntlm))  | 
310  | 0  |   { | 
311  | 0  |     return TRUE; // use explicit authentication package list  | 
312  | 0  |   }  | 
313  |  |  | 
314  | 0  |   rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, NEGO_REG_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);  | 
315  | 0  |   if (rc == ERROR_SUCCESS)  | 
316  | 0  |   { | 
317  | 0  |     DWORD dwValue = 0;  | 
318  |  | 
  | 
319  | 0  |     if (negotiate_get_dword(hKey, "kerberos", &dwValue))  | 
320  | 0  |       *kerberos = (dwValue != 0) ? TRUE : FALSE;  | 
321  |  | 
  | 
322  | 0  | #if !defined(WITH_KRB5_NO_NTLM_FALLBACK)  | 
323  | 0  |     if (negotiate_get_dword(hKey, "ntlm", &dwValue))  | 
324  | 0  |       *ntlm = (dwValue != 0) ? TRUE : FALSE;  | 
325  | 0  | #endif  | 
326  |  | 
  | 
327  | 0  |     RegCloseKey(hKey);  | 
328  | 0  |   }  | 
329  |  | 
  | 
330  | 0  |   return TRUE;  | 
331  | 0  | }  | 
332  |  |  | 
333  |  | static BOOL negotiate_write_neg_token(PSecBuffer output_buffer, NegToken* token)  | 
334  | 0  | { | 
335  | 0  |   WINPR_ASSERT(output_buffer);  | 
336  | 0  |   WINPR_ASSERT(token);  | 
337  |  |  | 
338  | 0  |   BOOL ret = FALSE;  | 
339  | 0  |   WinPrAsn1Encoder* enc = NULL;  | 
340  | 0  |   WinPrAsn1_MemoryChunk mechTypes = { token->mechTypes.cbBuffer, token->mechTypes.pvBuffer }; | 
341  | 0  |   WinPrAsn1_OctetString mechToken = { token->mechToken.cbBuffer, token->mechToken.pvBuffer }; | 
342  | 0  |   WinPrAsn1_OctetString mechListMic = { token->mic.cbBuffer, token->mic.pvBuffer }; | 
343  | 0  |   wStream s;  | 
344  | 0  |   size_t len = 0;  | 
345  |  | 
  | 
346  | 0  |   enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);  | 
347  | 0  |   if (!enc)  | 
348  | 0  |     return FALSE;  | 
349  |  |  | 
350  |  |   /* For NegTokenInit wrap in an initialContextToken */  | 
351  | 0  |   if (token->init)  | 
352  | 0  |   { | 
353  |  |     /* InitialContextToken [APPLICATION 0] IMPLICIT SEQUENCE */  | 
354  | 0  |     if (!WinPrAsn1EncAppContainer(enc, 0))  | 
355  | 0  |       goto cleanup;  | 
356  |  |  | 
357  |  |     /* thisMech MechType OID */  | 
358  | 0  |     if (!WinPrAsn1EncOID(enc, &spnego_OID))  | 
359  | 0  |       goto cleanup;  | 
360  | 0  |   }  | 
361  |  |  | 
362  |  |   /* innerContextToken [0] NegTokenInit or [1] NegTokenResp */  | 
363  | 0  |   if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))  | 
364  | 0  |     goto cleanup;  | 
365  |  |  | 
366  | 0  |   WLog_DBG(TAG, token->init ? "Writing negTokenInit..." : "Writing negTokenResp...");  | 
367  |  |  | 
368  |  |   /* mechTypes [0] MechTypeList (mechTypes already contains the SEQUENCE tag) */  | 
369  | 0  |   if (token->init)  | 
370  | 0  |   { | 
371  | 0  |     if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))  | 
372  | 0  |       goto cleanup;  | 
373  | 0  |     WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);  | 
374  | 0  |   }  | 
375  |  |   /* negState [0] ENUMERATED */  | 
376  | 0  |   else if (token->negState != NOSTATE)  | 
377  | 0  |   { | 
378  | 0  |     if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))  | 
379  | 0  |       goto cleanup;  | 
380  | 0  |     WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);  | 
381  | 0  |   }  | 
382  |  |  | 
383  |  |   /* supportedMech [1] OID */  | 
384  | 0  |   if (token->supportedMech.len)  | 
385  | 0  |   { | 
386  | 0  |     if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))  | 
387  | 0  |       goto cleanup;  | 
388  | 0  |     WLog_DBG(TAG, "\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));  | 
389  | 0  |   }  | 
390  |  |  | 
391  |  |   /* mechToken [2] OCTET STRING */  | 
392  | 0  |   if (token->mechToken.cbBuffer)  | 
393  | 0  |   { | 
394  | 0  |     if (WinPrAsn1EncContextualOctetString(enc, 2, &mechToken) == 0)  | 
395  | 0  |       goto cleanup;  | 
396  | 0  |     WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);  | 
397  | 0  |   }  | 
398  |  |  | 
399  |  |   /* mechListMIC [3] OCTET STRING */  | 
400  | 0  |   if (token->mic.cbBuffer)  | 
401  | 0  |   { | 
402  | 0  |     if (WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic) == 0)  | 
403  | 0  |       goto cleanup;  | 
404  | 0  |     WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);  | 
405  | 0  |   }  | 
406  |  |  | 
407  |  |   /* NegTokenInit or NegTokenResp */  | 
408  | 0  |   if (!WinPrAsn1EncEndContainer(enc))  | 
409  | 0  |     goto cleanup;  | 
410  |  |  | 
411  | 0  |   if (token->init)  | 
412  | 0  |   { | 
413  |  |     /* initialContextToken */  | 
414  | 0  |     if (!WinPrAsn1EncEndContainer(enc))  | 
415  | 0  |       goto cleanup;  | 
416  | 0  |   }  | 
417  |  |  | 
418  | 0  |   if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)  | 
419  | 0  |     goto cleanup;  | 
420  |  |  | 
421  | 0  |   Stream_StaticInit(&s, output_buffer->pvBuffer, len);  | 
422  |  | 
  | 
423  | 0  |   if (WinPrAsn1EncToStream(enc, &s))  | 
424  | 0  |   { | 
425  | 0  |     output_buffer->cbBuffer = len;  | 
426  | 0  |     ret = TRUE;  | 
427  | 0  |   }  | 
428  |  | 
  | 
429  | 0  | cleanup:  | 
430  | 0  |   WinPrAsn1Encoder_Free(&enc);  | 
431  | 0  |   return ret;  | 
432  | 0  | }  | 
433  |  |  | 
434  |  | static BOOL negotiate_read_neg_token(PSecBuffer input, NegToken* token)  | 
435  | 0  | { | 
436  | 0  |   WinPrAsn1Decoder dec;  | 
437  | 0  |   WinPrAsn1Decoder dec2;  | 
438  | 0  |   WinPrAsn1_OID oid;  | 
439  | 0  |   WinPrAsn1_tagId contextual = 0;  | 
440  | 0  |   WinPrAsn1_tag tag = 0;  | 
441  | 0  |   size_t len = 0;  | 
442  | 0  |   WinPrAsn1_OctetString octet_string;  | 
443  | 0  |   BOOL err = 0;  | 
444  |  | 
  | 
445  | 0  |   WINPR_ASSERT(input);  | 
446  | 0  |   WINPR_ASSERT(token);  | 
447  |  |  | 
448  | 0  |   WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);  | 
449  |  | 
  | 
450  | 0  |   if (!WinPrAsn1DecPeekTag(&dec, &tag))  | 
451  | 0  |     return FALSE;  | 
452  |  |  | 
453  | 0  |   if (tag == 0x60)  | 
454  | 0  |   { | 
455  |  |     /* initialContextToken [APPLICATION 0] */  | 
456  | 0  |     if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)  | 
457  | 0  |       return FALSE;  | 
458  | 0  |     dec = dec2;  | 
459  |  |  | 
460  |  |     /* thisMech OID */  | 
461  | 0  |     if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))  | 
462  | 0  |       return FALSE;  | 
463  |  |  | 
464  | 0  |     if (!sspi_gss_oid_compare(&spnego_OID, &oid))  | 
465  | 0  |       return FALSE;  | 
466  |  |  | 
467  |  |     /* [0] NegTokenInit */  | 
468  | 0  |     if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))  | 
469  | 0  |       return FALSE;  | 
470  |  |  | 
471  | 0  |     token->init = TRUE;  | 
472  | 0  |   }  | 
473  |  |   /* [1] NegTokenResp */  | 
474  | 0  |   else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))  | 
475  | 0  |     return FALSE;  | 
476  | 0  |   dec = dec2;  | 
477  |  | 
  | 
478  | 0  |   WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp...");  | 
479  |  |  | 
480  |  |   /* Read NegTokenResp sequence members */  | 
481  | 0  |   do  | 
482  | 0  |   { | 
483  | 0  |     if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))  | 
484  | 0  |       return FALSE;  | 
485  |  |  | 
486  | 0  |     switch (contextual)  | 
487  | 0  |     { | 
488  | 0  |       case 0:  | 
489  | 0  |         if (token->init)  | 
490  | 0  |         { | 
491  |  |           /* mechTypes [0] MechTypeList */  | 
492  | 0  |           wStream s = WinPrAsn1DecGetStream(&dec2);  | 
493  | 0  |           token->mechTypes.BufferType = SECBUFFER_TOKEN;  | 
494  | 0  |           token->mechTypes.cbBuffer = Stream_Length(&s);  | 
495  | 0  |           token->mechTypes.pvBuffer = Stream_Buffer(&s);  | 
496  | 0  |           WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);  | 
497  | 0  |         }  | 
498  | 0  |         else  | 
499  | 0  |         { | 
500  |  |           /* negState [0] ENUMERATED */  | 
501  | 0  |           WinPrAsn1_ENUMERATED rd = 0;  | 
502  | 0  |           if (!WinPrAsn1DecReadEnumerated(&dec2, &rd))  | 
503  | 0  |             return FALSE;  | 
504  | 0  |           token->negState = rd;  | 
505  | 0  |           WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);  | 
506  | 0  |         }  | 
507  | 0  |         break;  | 
508  | 0  |       case 1:  | 
509  | 0  |         if (token->init)  | 
510  | 0  |         { | 
511  |  |           /* reqFlags [1] ContextFlags BIT STRING (ignored) */  | 
512  | 0  |           if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))  | 
513  | 0  |             return FALSE;  | 
514  | 0  |           WLog_DBG(TAG, "\treqFlags [1] (%li bytes)", len);  | 
515  | 0  |         }  | 
516  | 0  |         else  | 
517  | 0  |         { | 
518  |  |           /* supportedMech [1] MechType */  | 
519  | 0  |           if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))  | 
520  | 0  |             return FALSE;  | 
521  | 0  |           WLog_DBG(TAG, "\tsupportedMech [1] (%s)",  | 
522  | 0  |                    negotiate_mech_name(&token->supportedMech));  | 
523  | 0  |         }  | 
524  | 0  |         break;  | 
525  | 0  |       case 2:  | 
526  |  |         /* mechToken [2] OCTET STRING */  | 
527  | 0  |         if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))  | 
528  | 0  |           return FALSE;  | 
529  | 0  |         token->mechToken.cbBuffer = octet_string.len;  | 
530  | 0  |         token->mechToken.pvBuffer = octet_string.data;  | 
531  | 0  |         token->mechToken.BufferType = SECBUFFER_TOKEN;  | 
532  | 0  |         WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", octet_string.len);  | 
533  | 0  |         break;  | 
534  | 0  |       case 3:  | 
535  |  |         /* mechListMic [3] OCTET STRING */  | 
536  | 0  |         if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))  | 
537  | 0  |           return FALSE;  | 
538  | 0  |         token->mic.cbBuffer = octet_string.len;  | 
539  | 0  |         token->mic.pvBuffer = octet_string.data;  | 
540  | 0  |         token->mic.BufferType = SECBUFFER_TOKEN;  | 
541  | 0  |         WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", octet_string.len);  | 
542  | 0  |         break;  | 
543  | 0  |       default:  | 
544  | 0  |         WLog_ERR(TAG, "unknown contextual item %d", contextual);  | 
545  | 0  |         return FALSE;  | 
546  | 0  |     }  | 
547  | 0  |   } while (WinPrAsn1DecPeekTag(&dec, &tag));  | 
548  |  |  | 
549  | 0  |   return TRUE;  | 
550  | 0  | }  | 
551  |  |  | 
552  |  | static SECURITY_STATUS negotiate_mic_exchange(NEGOTIATE_CONTEXT* context, NegToken* input_token,  | 
553  |  |                                               NegToken* output_token, PSecBuffer output_buffer)  | 
554  | 0  | { | 
555  | 0  |   SecBuffer mic_buffers[2] = { 0 }; | 
556  | 0  |   SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers }; | 
557  | 0  |   SECURITY_STATUS status = 0;  | 
558  |  | 
  | 
559  | 0  |   WINPR_ASSERT(context);  | 
560  | 0  |   WINPR_ASSERT(input_token);  | 
561  | 0  |   WINPR_ASSERT(output_token);  | 
562  | 0  |   WINPR_ASSERT(context->mech);  | 
563  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
564  |  |  | 
565  | 0  |   const SecurityFunctionTableA* table = context->mech->pkg->table;  | 
566  | 0  |   WINPR_ASSERT(table);  | 
567  |  |  | 
568  | 0  |   mic_buffers[0] = context->mechTypes;  | 
569  |  |  | 
570  |  |   /* Verify MIC if we received one */  | 
571  | 0  |   if (input_token->mic.cbBuffer > 0)  | 
572  | 0  |   { | 
573  | 0  |     mic_buffers[1] = input_token->mic;  | 
574  |  | 
  | 
575  | 0  |     status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, 0);  | 
576  | 0  |     if (status != SEC_E_OK)  | 
577  | 0  |       return status;  | 
578  |  |  | 
579  | 0  |     output_token->negState = ACCEPT_COMPLETED;  | 
580  | 0  |   }  | 
581  |  |  | 
582  |  |   /* If peer expects a MIC then generate it */  | 
583  | 0  |   if (input_token->negState != ACCEPT_COMPLETED)  | 
584  | 0  |   { | 
585  |  |     /* Store the mic token after the mech token in the output buffer */  | 
586  | 0  |     output_token->mic.BufferType = SECBUFFER_TOKEN;  | 
587  | 0  |     if (output_buffer)  | 
588  | 0  |     { | 
589  | 0  |       output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;  | 
590  | 0  |       output_token->mic.pvBuffer =  | 
591  | 0  |           (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;  | 
592  | 0  |     }  | 
593  | 0  |     mic_buffers[1] = output_token->mic;  | 
594  |  | 
  | 
595  | 0  |     status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);  | 
596  | 0  |     if (status != SEC_E_OK)  | 
597  | 0  |       return status;  | 
598  |  |  | 
599  | 0  |     output_token->mic = mic_buffers[1];  | 
600  | 0  |   }  | 
601  |  |  | 
602  |  |   /* When using NTLM cipher states need to be reset after mic exchange */  | 
603  | 0  |   const TCHAR* name = sspi_SecureHandleGetUpperPointer(&context->sub_context);  | 
604  | 0  |   if (!name)  | 
605  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
606  |  |  | 
607  | 0  |   if (_tcscmp(name, NTLM_SSP_NAME) == 0)  | 
608  | 0  |   { | 
609  | 0  |     if (!ntlm_reset_cipher_state(&context->sub_context))  | 
610  | 0  |       return SEC_E_INTERNAL_ERROR;  | 
611  | 0  |   }  | 
612  |  |  | 
613  | 0  |   return SEC_E_OK;  | 
614  | 0  | }  | 
615  |  |  | 
616  |  | static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(  | 
617  |  |     PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,  | 
618  |  |     ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,  | 
619  |  |     PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)  | 
620  | 0  | { | 
621  | 0  |   NEGOTIATE_CONTEXT* context = NULL;  | 
622  | 0  |   NEGOTIATE_CONTEXT init_context = { 0 }; | 
623  | 0  |   MechCred* creds = NULL;  | 
624  | 0  |   PCtxtHandle sub_context = NULL;  | 
625  | 0  |   PCredHandle sub_cred = NULL;  | 
626  | 0  |   NegToken input_token = empty_neg_token;  | 
627  | 0  |   NegToken output_token = empty_neg_token;  | 
628  | 0  |   PSecBuffer input_buffer = NULL;  | 
629  | 0  |   PSecBuffer output_buffer = NULL;  | 
630  | 0  |   PSecBuffer bindings_buffer = NULL;  | 
631  | 0  |   SecBuffer mech_input_buffers[2] = { 0 }; | 
632  | 0  |   SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers }; | 
633  | 0  |   SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken }; | 
634  | 0  |   SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;  | 
635  | 0  |   SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR;  | 
636  | 0  |   WinPrAsn1Encoder* enc = NULL;  | 
637  | 0  |   wStream s;  | 
638  | 0  |   const Mech* mech = NULL;  | 
639  |  | 
  | 
640  | 0  |   if (!phCredential || !SecIsValidHandle(phCredential))  | 
641  | 0  |     return SEC_E_NO_CREDENTIALS;  | 
642  |  |  | 
643  | 0  |   creds = sspi_SecureHandleGetLowerPointer(phCredential);  | 
644  |  |  | 
645  |  |   /* behave like windows SSPIs that don't want empty context */  | 
646  | 0  |   if (phContext && !phContext->dwLower && !phContext->dwUpper)  | 
647  | 0  |     return SEC_E_INVALID_HANDLE;  | 
648  |  |  | 
649  | 0  |   context = sspi_SecureHandleGetLowerPointer(phContext);  | 
650  |  | 
  | 
651  | 0  |   if (pInput)  | 
652  | 0  |   { | 
653  | 0  |     input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);  | 
654  | 0  |     bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);  | 
655  | 0  |   }  | 
656  | 0  |   if (pOutput)  | 
657  | 0  |     output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);  | 
658  |  | 
  | 
659  | 0  |   if (!context)  | 
660  | 0  |   { | 
661  | 0  |     enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);  | 
662  | 0  |     if (!enc)  | 
663  | 0  |       return SEC_E_INSUFFICIENT_MEMORY;  | 
664  |  |  | 
665  | 0  |     if (!WinPrAsn1EncSeqContainer(enc))  | 
666  | 0  |       goto cleanup;  | 
667  |  |  | 
668  | 0  |     for (size_t i = 0; i < MECH_COUNT; i++)  | 
669  | 0  |     { | 
670  | 0  |       MechCred* cred = &creds[i];  | 
671  | 0  |       const SecPkg* pkg = MechTable[i].pkg;  | 
672  | 0  |       WINPR_ASSERT(pkg);  | 
673  | 0  |       WINPR_ASSERT(pkg->table_w);  | 
674  |  |  | 
675  | 0  |       if (!cred->valid)  | 
676  | 0  |         continue;  | 
677  |  |  | 
678  |  |       /* Send an optimistic token for the first valid mechanism */  | 
679  | 0  |       if (!init_context.mech)  | 
680  | 0  |       { | 
681  |  |         /* Use the output buffer to store the optimistic token */  | 
682  | 0  |         if (!output_buffer)  | 
683  | 0  |           goto cleanup;  | 
684  |  |  | 
685  | 0  |         CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));  | 
686  |  | 
  | 
687  | 0  |         if (bindings_buffer)  | 
688  | 0  |           mech_input_buffers[0] = *bindings_buffer;  | 
689  |  | 
  | 
690  | 0  |         WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW);  | 
691  | 0  |         sub_status = pkg->table_w->InitializeSecurityContextW(  | 
692  | 0  |             &cred->cred, NULL, pszTargetName, fContextReq | cred->mech->flags, Reserved1,  | 
693  | 0  |             TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output,  | 
694  | 0  |             pfContextAttr, ptsExpiry);  | 
695  |  |  | 
696  |  |         /* If the mechanism failed we can't use it; skip */  | 
697  | 0  |         if (IsSecurityStatusError(sub_status))  | 
698  | 0  |         { | 
699  | 0  |           if (SecIsValidHandle(&init_context.sub_context))  | 
700  | 0  |           { | 
701  | 0  |             WINPR_ASSERT(pkg->table_w->DeleteSecurityContext);  | 
702  | 0  |             pkg->table_w->DeleteSecurityContext(&init_context.sub_context);  | 
703  | 0  |           }  | 
704  | 0  |           cred->valid = FALSE;  | 
705  | 0  |           continue;  | 
706  | 0  |         }  | 
707  |  |  | 
708  | 0  |         init_context.mech = cred->mech;  | 
709  | 0  |       }  | 
710  |  |  | 
711  | 0  |       if (!WinPrAsn1EncOID(enc, cred->mech->oid))  | 
712  | 0  |         goto cleanup;  | 
713  | 0  |       WLog_DBG(TAG, "Available mechanism: %s", negotiate_mech_name(cred->mech->oid));  | 
714  | 0  |     }  | 
715  |  |  | 
716  |  |     /* No usable mechanisms were found */  | 
717  | 0  |     if (!init_context.mech)  | 
718  | 0  |       goto cleanup;  | 
719  |  |  | 
720  |  |     /* If the only available mech is NTLM use it directly otherwise use spnego */  | 
721  | 0  |     if (init_context.mech->oid == &ntlm_OID)  | 
722  | 0  |     { | 
723  | 0  |       init_context.spnego = FALSE;  | 
724  | 0  |       output_buffer->cbBuffer = output_token.mechToken.cbBuffer;  | 
725  | 0  |       WLog_DBG(TAG, "Using direct NTLM");  | 
726  | 0  |     }  | 
727  | 0  |     else  | 
728  | 0  |     { | 
729  | 0  |       init_context.spnego = TRUE;  | 
730  | 0  |       init_context.mechTypes.BufferType = SECBUFFER_DATA;  | 
731  | 0  |       init_context.mechTypes.cbBuffer = WinPrAsn1EncEndContainer(enc);  | 
732  | 0  |     }  | 
733  |  |  | 
734  |  |     /* Allocate memory for the new context */  | 
735  | 0  |     context = negotiate_ContextNew(&init_context);  | 
736  | 0  |     if (!context)  | 
737  | 0  |     { | 
738  | 0  |       init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);  | 
739  | 0  |       WinPrAsn1Encoder_Free(&enc);  | 
740  | 0  |       return SEC_E_INSUFFICIENT_MEMORY;  | 
741  | 0  |     }  | 
742  |  |  | 
743  | 0  |     sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);  | 
744  | 0  |     sspi_SecureHandleSetLowerPointer(phNewContext, context);  | 
745  |  | 
  | 
746  | 0  |     if (!context->spnego)  | 
747  | 0  |     { | 
748  | 0  |       status = sub_status;  | 
749  | 0  |       goto cleanup;  | 
750  | 0  |     }  | 
751  |  |  | 
752  |  |     /* Write mechTypesList */  | 
753  | 0  |     Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer);  | 
754  | 0  |     if (!WinPrAsn1EncToStream(enc, &s))  | 
755  | 0  |       goto cleanup;  | 
756  |  |  | 
757  | 0  |     output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;  | 
758  | 0  |     output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;  | 
759  | 0  |     output_token.init = TRUE;  | 
760  |  | 
  | 
761  | 0  |     if (sub_status == SEC_E_OK)  | 
762  | 0  |       context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC;  | 
763  | 0  |   }  | 
764  | 0  |   else  | 
765  | 0  |   { | 
766  | 0  |     if (!input_buffer)  | 
767  | 0  |       return SEC_E_INVALID_TOKEN;  | 
768  |  |  | 
769  | 0  |     sub_context = &context->sub_context;  | 
770  | 0  |     sub_cred = negotiate_FindCredential(creds, context->mech);  | 
771  |  | 
  | 
772  | 0  |     if (!context->spnego)  | 
773  | 0  |     { | 
774  | 0  |       return context->mech->pkg->table_w->InitializeSecurityContextW(  | 
775  | 0  |           sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,  | 
776  | 0  |           TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);  | 
777  | 0  |     }  | 
778  |  |  | 
779  | 0  |     if (!negotiate_read_neg_token(input_buffer, &input_token))  | 
780  | 0  |       return SEC_E_INVALID_TOKEN;  | 
781  |  |  | 
782  |  |     /* On first response check if the server doesn't like out prefered mech */  | 
783  | 0  |     if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len &&  | 
784  | 0  |         !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid))  | 
785  | 0  |     { | 
786  | 0  |       mech = negotiate_GetMechByOID(&input_token.supportedMech);  | 
787  | 0  |       if (!mech)  | 
788  | 0  |         return SEC_E_INVALID_TOKEN;  | 
789  |  |  | 
790  |  |       /* Make sure the specified mech is supported and get the appropriate credential */  | 
791  | 0  |       sub_cred = negotiate_FindCredential(creds, mech);  | 
792  | 0  |       if (!sub_cred)  | 
793  | 0  |         return SEC_E_INVALID_TOKEN;  | 
794  |  |  | 
795  |  |       /* Clean up the optimistic mech */  | 
796  | 0  |       context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);  | 
797  | 0  |       sub_context = NULL;  | 
798  |  | 
  | 
799  | 0  |       context->mech = mech;  | 
800  | 0  |       context->mic = TRUE;  | 
801  | 0  |     }  | 
802  |  |  | 
803  |  |     /* Check neg_state (required on first response) */  | 
804  | 0  |     if (context->state < NEGOTIATE_STATE_NEGORESP)  | 
805  | 0  |     { | 
806  | 0  |       switch (input_token.negState)  | 
807  | 0  |       { | 
808  | 0  |         case NOSTATE:  | 
809  | 0  |           return SEC_E_INVALID_TOKEN;  | 
810  | 0  |         case REJECT:  | 
811  | 0  |           return SEC_E_LOGON_DENIED;  | 
812  | 0  |         case REQUEST_MIC:  | 
813  | 0  |           context->mic = TRUE;  | 
814  |  |           /* fallthrough */  | 
815  | 0  |           WINPR_FALLTHROUGH  | 
816  | 0  |         case ACCEPT_INCOMPLETE:  | 
817  | 0  |           context->state = NEGOTIATE_STATE_NEGORESP;  | 
818  | 0  |           break;  | 
819  | 0  |         case ACCEPT_COMPLETED:  | 
820  | 0  |           if (context->state == NEGOTIATE_STATE_INITIAL)  | 
821  | 0  |             context->state = NEGOTIATE_STATE_NEGORESP;  | 
822  | 0  |           else  | 
823  | 0  |             context->state = NEGOTIATE_STATE_FINAL;  | 
824  | 0  |           break;  | 
825  | 0  |       }  | 
826  |  |  | 
827  | 0  |       WLog_DBG(TAG, "Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));  | 
828  | 0  |     }  | 
829  |  |  | 
830  | 0  |     if (context->state == NEGOTIATE_STATE_NEGORESP)  | 
831  | 0  |     { | 
832  |  |       /* Store the mech token in the output buffer */  | 
833  | 0  |       if (!output_buffer)  | 
834  | 0  |         goto cleanup;  | 
835  | 0  |       CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));  | 
836  |  | 
  | 
837  | 0  |       mech_input_buffers[0] = input_token.mechToken;  | 
838  | 0  |       if (bindings_buffer)  | 
839  | 0  |         mech_input_buffers[1] = *bindings_buffer;  | 
840  |  | 
  | 
841  | 0  |       status = context->mech->pkg->table_w->InitializeSecurityContextW(  | 
842  | 0  |           sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,  | 
843  | 0  |           TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : NULL, Reserved2,  | 
844  | 0  |           &context->sub_context, &mech_output, pfContextAttr, ptsExpiry);  | 
845  |  | 
  | 
846  | 0  |       if (IsSecurityStatusError(status))  | 
847  | 0  |         return status;  | 
848  | 0  |     }  | 
849  |  |  | 
850  | 0  |     if (status == SEC_E_OK)  | 
851  | 0  |     { | 
852  | 0  |       if (output_token.mechToken.cbBuffer > 0)  | 
853  | 0  |         context->state = NEGOTIATE_STATE_MIC;  | 
854  | 0  |       else  | 
855  | 0  |         context->state = NEGOTIATE_STATE_FINAL;  | 
856  | 0  |     }  | 
857  |  |  | 
858  |  |     /* Check if the acceptor sent its final token without a mic */  | 
859  | 0  |     if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)  | 
860  | 0  |     { | 
861  | 0  |       if (context->mic || input_token.negState != ACCEPT_COMPLETED)  | 
862  | 0  |         return SEC_E_INVALID_TOKEN;  | 
863  |  |  | 
864  | 0  |       if (output_buffer)  | 
865  | 0  |         output_buffer->cbBuffer = 0;  | 
866  | 0  |       return SEC_E_OK;  | 
867  | 0  |     }  | 
868  |  |  | 
869  | 0  |     if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||  | 
870  | 0  |         context->state == NEGOTIATE_STATE_FINAL)  | 
871  | 0  |     { | 
872  | 0  |       status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);  | 
873  | 0  |       if (status != SEC_E_OK)  | 
874  | 0  |         return status;  | 
875  | 0  |     }  | 
876  | 0  |   }  | 
877  |  |  | 
878  | 0  |   if (input_token.negState == ACCEPT_COMPLETED)  | 
879  | 0  |   { | 
880  | 0  |     if (output_buffer)  | 
881  | 0  |       output_buffer->cbBuffer = 0;  | 
882  | 0  |     return SEC_E_OK;  | 
883  | 0  |   }  | 
884  |  |  | 
885  | 0  |   if (output_token.negState == ACCEPT_COMPLETED)  | 
886  | 0  |     status = SEC_E_OK;  | 
887  | 0  |   else  | 
888  | 0  |     status = SEC_I_CONTINUE_NEEDED;  | 
889  |  | 
  | 
890  | 0  |   if (!negotiate_write_neg_token(output_buffer, &output_token))  | 
891  | 0  |     status = SEC_E_INTERNAL_ERROR;  | 
892  |  | 
  | 
893  | 0  | cleanup:  | 
894  | 0  |   WinPrAsn1Encoder_Free(&enc);  | 
895  | 0  |   return status;  | 
896  | 0  | }  | 
897  |  |  | 
898  |  | static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(  | 
899  |  |     PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,  | 
900  |  |     ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,  | 
901  |  |     PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)  | 
902  | 0  | { | 
903  | 0  |   SECURITY_STATUS status = 0;  | 
904  | 0  |   SEC_WCHAR* pszTargetNameW = NULL;  | 
905  |  | 
  | 
906  | 0  |   if (pszTargetName)  | 
907  | 0  |   { | 
908  | 0  |     pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);  | 
909  | 0  |     if (!pszTargetNameW)  | 
910  | 0  |       return SEC_E_INTERNAL_ERROR;  | 
911  | 0  |   }  | 
912  |  |  | 
913  | 0  |   status = negotiate_InitializeSecurityContextW(  | 
914  | 0  |       phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,  | 
915  | 0  |       Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);  | 
916  | 0  |   free(pszTargetNameW);  | 
917  | 0  |   return status;  | 
918  | 0  | }  | 
919  |  |  | 
920  |  | static const Mech* guessMech(PSecBuffer input_buffer, BOOL* spNego, WinPrAsn1_OID* oid)  | 
921  | 0  | { | 
922  | 0  |   WinPrAsn1Decoder decoder;  | 
923  | 0  |   WinPrAsn1Decoder appDecoder;  | 
924  | 0  |   WinPrAsn1_tagId tag = 0;  | 
925  |  | 
  | 
926  | 0  |   *spNego = FALSE;  | 
927  |  |  | 
928  |  |   /* Check for NTLM token */  | 
929  | 0  |   if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, "NTLMSSP", 8) == 0)  | 
930  | 0  |   { | 
931  | 0  |     *oid = ntlm_OID;  | 
932  | 0  |     return negotiate_GetMechByOID(&ntlm_OID);  | 
933  | 0  |   }  | 
934  |  |  | 
935  |  |   /* Read initialContextToken or raw Kerberos token */  | 
936  | 0  |   WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer,  | 
937  | 0  |                            input_buffer->cbBuffer);  | 
938  |  | 
  | 
939  | 0  |   if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0)  | 
940  | 0  |     return NULL;  | 
941  |  |  | 
942  | 0  |   if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE))  | 
943  | 0  |     return NULL;  | 
944  |  |  | 
945  | 0  |   if (sspi_gss_oid_compare(oid, &spnego_OID))  | 
946  | 0  |   { | 
947  | 0  |     *spNego = TRUE;  | 
948  | 0  |     return NULL;  | 
949  | 0  |   }  | 
950  |  |  | 
951  | 0  |   return negotiate_GetMechByOID(oid);  | 
952  | 0  | }  | 
953  |  |  | 
954  |  | static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(  | 
955  |  |     PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,  | 
956  |  |     ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr,  | 
957  |  |     PTimeStamp ptsTimeStamp)  | 
958  | 0  | { | 
959  | 0  |   NEGOTIATE_CONTEXT* context = NULL;  | 
960  | 0  |   NEGOTIATE_CONTEXT init_context = { 0 }; | 
961  | 0  |   MechCred* creds = NULL;  | 
962  | 0  |   PCredHandle sub_cred = NULL;  | 
963  | 0  |   NegToken input_token = empty_neg_token;  | 
964  | 0  |   NegToken output_token = empty_neg_token;  | 
965  | 0  |   PSecBuffer input_buffer = NULL;  | 
966  | 0  |   PSecBuffer output_buffer = NULL;  | 
967  | 0  |   SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken }; | 
968  | 0  |   SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken }; | 
969  | 0  |   SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;  | 
970  | 0  |   WinPrAsn1Decoder dec;  | 
971  | 0  |   WinPrAsn1Decoder dec2;  | 
972  | 0  |   WinPrAsn1_tagId tag = 0;  | 
973  | 0  |   WinPrAsn1_OID oid = { 0 }; | 
974  | 0  |   const Mech* first_mech = NULL;  | 
975  |  | 
  | 
976  | 0  |   if (!phCredential || !SecIsValidHandle(phCredential))  | 
977  | 0  |     return SEC_E_NO_CREDENTIALS;  | 
978  |  |  | 
979  | 0  |   creds = sspi_SecureHandleGetLowerPointer(phCredential);  | 
980  |  | 
  | 
981  | 0  |   if (!pInput)  | 
982  | 0  |     return SEC_E_INVALID_TOKEN;  | 
983  |  |  | 
984  |  |   /* behave like windows SSPIs that don't want empty context */  | 
985  | 0  |   if (phContext && !phContext->dwLower && !phContext->dwUpper)  | 
986  | 0  |     return SEC_E_INVALID_HANDLE;  | 
987  |  |  | 
988  | 0  |   context = sspi_SecureHandleGetLowerPointer(phContext);  | 
989  |  | 
  | 
990  | 0  |   input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);  | 
991  | 0  |   if (pOutput)  | 
992  | 0  |     output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);  | 
993  |  | 
  | 
994  | 0  |   if (!context)  | 
995  | 0  |   { | 
996  | 0  |     init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);  | 
997  | 0  |     if (!init_context.mech && !init_context.spnego)  | 
998  | 0  |       return SEC_E_INVALID_TOKEN;  | 
999  |  |  | 
1000  | 0  |     WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid));  | 
1001  |  | 
  | 
1002  | 0  |     if (init_context.spnego)  | 
1003  | 0  |     { | 
1004  |  |       /* Process spnego token */  | 
1005  | 0  |       if (!negotiate_read_neg_token(input_buffer, &input_token))  | 
1006  | 0  |         return SEC_E_INVALID_TOKEN;  | 
1007  |  |  | 
1008  |  |       /* First token must be negoTokenInit and must contain a mechList */  | 
1009  | 0  |       if (!input_token.init || input_token.mechTypes.cbBuffer == 0)  | 
1010  | 0  |         return SEC_E_INVALID_TOKEN;  | 
1011  |  |  | 
1012  | 0  |       init_context.mechTypes.BufferType = SECBUFFER_DATA;  | 
1013  | 0  |       init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;  | 
1014  |  |  | 
1015  |  |       /* Prepare to read mechList */  | 
1016  | 0  |       WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,  | 
1017  | 0  |                                input_token.mechTypes.cbBuffer);  | 
1018  |  | 
  | 
1019  | 0  |       if (!WinPrAsn1DecReadSequence(&dec, &dec2))  | 
1020  | 0  |         return SEC_E_INVALID_TOKEN;  | 
1021  | 0  |       dec = dec2;  | 
1022  |  |  | 
1023  |  |       /* If an optimistic token was provided pass it into the first mech */  | 
1024  | 0  |       if (input_token.mechToken.cbBuffer)  | 
1025  | 0  |       { | 
1026  | 0  |         if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))  | 
1027  | 0  |           return SEC_E_INVALID_TOKEN;  | 
1028  |  |  | 
1029  | 0  |         init_context.mech = negotiate_GetMechByOID(&oid);  | 
1030  |  | 
  | 
1031  | 0  |         if (init_context.mech)  | 
1032  | 0  |         { | 
1033  | 0  |           if (output_buffer)  | 
1034  | 0  |             output_token.mechToken = *output_buffer;  | 
1035  | 0  |           WLog_DBG(TAG, "Requested mechanism: %s",  | 
1036  | 0  |                    negotiate_mech_name(init_context.mech->oid));  | 
1037  | 0  |         }  | 
1038  | 0  |       }  | 
1039  | 0  |     }  | 
1040  |  |  | 
1041  | 0  |     if (init_context.mech)  | 
1042  | 0  |     { | 
1043  | 0  |       sub_cred = negotiate_FindCredential(creds, init_context.mech);  | 
1044  |  | 
  | 
1045  | 0  |       status = init_context.mech->pkg->table->AcceptSecurityContext(  | 
1046  | 0  |           sub_cred, NULL, init_context.spnego ? &mech_input : pInput, fContextReq,  | 
1047  | 0  |           TargetDataRep, &init_context.sub_context,  | 
1048  | 0  |           init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);  | 
1049  | 0  |     }  | 
1050  |  | 
  | 
1051  | 0  |     if (IsSecurityStatusError(status))  | 
1052  | 0  |     { | 
1053  | 0  |       if (!init_context.spnego)  | 
1054  | 0  |         return status;  | 
1055  |  |  | 
1056  | 0  |       init_context.mic = TRUE;  | 
1057  | 0  |       first_mech = init_context.mech;  | 
1058  | 0  |       init_context.mech = NULL;  | 
1059  | 0  |       output_token.mechToken.cbBuffer = 0;  | 
1060  | 0  |     }  | 
1061  |  |  | 
1062  | 0  |     while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))  | 
1063  | 0  |     { | 
1064  |  |       /* Read each mechanism */  | 
1065  | 0  |       if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))  | 
1066  | 0  |         return SEC_E_INVALID_TOKEN;  | 
1067  |  |  | 
1068  | 0  |       init_context.mech = negotiate_GetMechByOID(&oid);  | 
1069  | 0  |       WLog_DBG(TAG, "Requested mechanism: %s", negotiate_mech_name(&oid));  | 
1070  |  |  | 
1071  |  |       /* Microsoft may send two versions of the kerberos OID */  | 
1072  | 0  |       if (init_context.mech == first_mech)  | 
1073  | 0  |         init_context.mech = NULL;  | 
1074  |  | 
  | 
1075  | 0  |       if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))  | 
1076  | 0  |         init_context.mech = NULL;  | 
1077  | 0  |     }  | 
1078  |  |  | 
1079  | 0  |     if (!init_context.mech)  | 
1080  | 0  |       return SEC_E_INTERNAL_ERROR;  | 
1081  |  |  | 
1082  | 0  |     context = negotiate_ContextNew(&init_context);  | 
1083  | 0  |     if (!context)  | 
1084  | 0  |     { | 
1085  | 0  |       if (!IsSecurityStatusError(status))  | 
1086  | 0  |         init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);  | 
1087  | 0  |       return SEC_E_INSUFFICIENT_MEMORY;  | 
1088  | 0  |     }  | 
1089  |  |  | 
1090  | 0  |     sspi_SecureHandleSetUpperPointer(phNewContext, NEGO_SSP_NAME);  | 
1091  | 0  |     sspi_SecureHandleSetLowerPointer(phNewContext, context);  | 
1092  |  | 
  | 
1093  | 0  |     if (!init_context.spnego)  | 
1094  | 0  |       return status;  | 
1095  |  |  | 
1096  | 0  |     CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,  | 
1097  | 0  |                input_token.mechTypes.cbBuffer);  | 
1098  |  | 
  | 
1099  | 0  |     if (!context->mech->preferred)  | 
1100  | 0  |     { | 
1101  | 0  |       output_token.negState = REQUEST_MIC;  | 
1102  | 0  |       context->mic = TRUE;  | 
1103  | 0  |     }  | 
1104  | 0  |     else  | 
1105  | 0  |     { | 
1106  | 0  |       output_token.negState = ACCEPT_INCOMPLETE;  | 
1107  | 0  |     }  | 
1108  |  | 
  | 
1109  | 0  |     if (status == SEC_E_OK)  | 
1110  | 0  |       context->state = NEGOTIATE_STATE_FINAL;  | 
1111  | 0  |     else  | 
1112  | 0  |       context->state = NEGOTIATE_STATE_NEGORESP;  | 
1113  |  | 
  | 
1114  | 0  |     output_token.supportedMech = oid;  | 
1115  | 0  |     WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));  | 
1116  | 0  |   }  | 
1117  | 0  |   else  | 
1118  | 0  |   { | 
1119  | 0  |     sub_cred = negotiate_FindCredential(creds, context->mech);  | 
1120  | 0  |     if (!sub_cred)  | 
1121  | 0  |       return SEC_E_NO_CREDENTIALS;  | 
1122  |  |  | 
1123  | 0  |     if (!context->spnego)  | 
1124  | 0  |     { | 
1125  | 0  |       return context->mech->pkg->table->AcceptSecurityContext(  | 
1126  | 0  |           sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,  | 
1127  | 0  |           &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);  | 
1128  | 0  |     }  | 
1129  |  |  | 
1130  | 0  |     if (!negotiate_read_neg_token(input_buffer, &input_token))  | 
1131  | 0  |       return SEC_E_INVALID_TOKEN;  | 
1132  |  |  | 
1133  |  |     /* Process the mechanism token */  | 
1134  | 0  |     if (input_token.mechToken.cbBuffer > 0)  | 
1135  | 0  |     { | 
1136  | 0  |       if (context->state != NEGOTIATE_STATE_NEGORESP)  | 
1137  | 0  |         return SEC_E_INVALID_TOKEN;  | 
1138  |  |  | 
1139  |  |       /* Use the output buffer to store the optimistic token */  | 
1140  | 0  |       if (output_buffer)  | 
1141  | 0  |         CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));  | 
1142  |  | 
  | 
1143  | 0  |       status = context->mech->pkg->table->AcceptSecurityContext(  | 
1144  | 0  |           sub_cred, &context->sub_context, &mech_input, fContextReq | context->mech->flags,  | 
1145  | 0  |           TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);  | 
1146  |  | 
  | 
1147  | 0  |       if (IsSecurityStatusError(status))  | 
1148  | 0  |         return status;  | 
1149  |  |  | 
1150  | 0  |       if (status == SEC_E_OK)  | 
1151  | 0  |         context->state = NEGOTIATE_STATE_FINAL;  | 
1152  | 0  |     }  | 
1153  | 0  |     else if (context->state == NEGOTIATE_STATE_NEGORESP)  | 
1154  | 0  |       return SEC_E_INVALID_TOKEN;  | 
1155  | 0  |   }  | 
1156  |  |  | 
1157  | 0  |   if (context->state == NEGOTIATE_STATE_FINAL)  | 
1158  | 0  |   { | 
1159  |  |     /* Check if initiator sent the last mech token without a mic and a mic was required */  | 
1160  | 0  |     if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)  | 
1161  | 0  |       return SEC_E_INVALID_TOKEN;  | 
1162  |  |  | 
1163  | 0  |     if (context->mic || input_token.mic.cbBuffer > 0)  | 
1164  | 0  |     { | 
1165  | 0  |       status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);  | 
1166  | 0  |       if (status != SEC_E_OK)  | 
1167  | 0  |         return status;  | 
1168  | 0  |     }  | 
1169  | 0  |     else  | 
1170  | 0  |       output_token.negState = ACCEPT_COMPLETED;  | 
1171  | 0  |   }  | 
1172  |  |  | 
1173  | 0  |   if (input_token.negState == ACCEPT_COMPLETED)  | 
1174  | 0  |   { | 
1175  | 0  |     if (output_buffer)  | 
1176  | 0  |       output_buffer->cbBuffer = 0;  | 
1177  | 0  |     return SEC_E_OK;  | 
1178  | 0  |   }  | 
1179  |  |  | 
1180  | 0  |   if (output_token.negState == ACCEPT_COMPLETED)  | 
1181  | 0  |     status = SEC_E_OK;  | 
1182  | 0  |   else  | 
1183  | 0  |     status = SEC_I_CONTINUE_NEEDED;  | 
1184  |  | 
  | 
1185  | 0  |   if (!negotiate_write_neg_token(output_buffer, &output_token))  | 
1186  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
1187  |  |  | 
1188  | 0  |   return status;  | 
1189  | 0  | }  | 
1190  |  |  | 
1191  |  | static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(PCtxtHandle phContext,  | 
1192  |  |                                                              PSecBufferDesc pToken)  | 
1193  | 0  | { | 
1194  | 0  |   NEGOTIATE_CONTEXT* context = NULL;  | 
1195  | 0  |   SECURITY_STATUS status = SEC_E_OK;  | 
1196  | 0  |   context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1197  |  | 
  | 
1198  | 0  |   if (!context)  | 
1199  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1200  |  |  | 
1201  | 0  |   WINPR_ASSERT(context->mech);  | 
1202  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1203  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1204  | 0  |   if (context->mech->pkg->table->CompleteAuthToken)  | 
1205  | 0  |     status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);  | 
1206  |  | 
  | 
1207  | 0  |   return status;  | 
1208  | 0  | }  | 
1209  |  |  | 
1210  |  | static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(PCtxtHandle phContext)  | 
1211  | 0  | { | 
1212  | 0  |   NEGOTIATE_CONTEXT* context = NULL;  | 
1213  | 0  |   SECURITY_STATUS status = SEC_E_OK;  | 
1214  | 0  |   context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1215  | 0  |   const SecPkg* pkg = NULL;  | 
1216  |  | 
  | 
1217  | 0  |   if (!context)  | 
1218  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1219  |  |  | 
1220  | 0  |   WINPR_ASSERT(context->mech);  | 
1221  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1222  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1223  | 0  |   pkg = context->mech->pkg;  | 
1224  |  | 
  | 
1225  | 0  |   if (pkg->table->DeleteSecurityContext)  | 
1226  | 0  |     status = pkg->table->DeleteSecurityContext(&context->sub_context);  | 
1227  |  | 
  | 
1228  | 0  |   negotiate_ContextFree(context);  | 
1229  | 0  |   return status;  | 
1230  | 0  | }  | 
1231  |  |  | 
1232  |  | static SECURITY_STATUS SEC_ENTRY negotiate_ImpersonateSecurityContext(PCtxtHandle phContext)  | 
1233  | 0  | { | 
1234  | 0  |   return SEC_E_OK;  | 
1235  | 0  | }  | 
1236  |  |  | 
1237  |  | static SECURITY_STATUS SEC_ENTRY negotiate_RevertSecurityContext(PCtxtHandle phContext)  | 
1238  | 0  | { | 
1239  | 0  |   return SEC_E_OK;  | 
1240  | 0  | }  | 
1241  |  |  | 
1242  |  | static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(PCtxtHandle phContext,  | 
1243  |  |                                                                    ULONG ulAttribute, void* pBuffer)  | 
1244  | 0  | { | 
1245  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1246  |  | 
  | 
1247  | 0  |   if (!context)  | 
1248  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1249  |  |  | 
1250  | 0  |   WINPR_ASSERT(context->mech);  | 
1251  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1252  | 0  |   WINPR_ASSERT(context->mech->pkg->table_w);  | 
1253  | 0  |   if (context->mech->pkg->table_w->QueryContextAttributesW)  | 
1254  | 0  |     return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,  | 
1255  | 0  |                                                                 ulAttribute, pBuffer);  | 
1256  |  |  | 
1257  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1258  | 0  | }  | 
1259  |  |  | 
1260  |  | static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(PCtxtHandle phContext,  | 
1261  |  |                                                                    ULONG ulAttribute, void* pBuffer)  | 
1262  | 0  | { | 
1263  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1264  |  | 
  | 
1265  | 0  |   if (!context)  | 
1266  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1267  |  |  | 
1268  | 0  |   WINPR_ASSERT(context->mech);  | 
1269  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1270  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1271  | 0  |   if (context->mech->pkg->table->QueryContextAttributesA)  | 
1272  | 0  |     return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,  | 
1273  | 0  |                                                               ulAttribute, pBuffer);  | 
1274  |  |  | 
1275  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1276  | 0  | }  | 
1277  |  |  | 
1278  |  | static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(PCtxtHandle phContext,  | 
1279  |  |                                                                  ULONG ulAttribute, void* pBuffer,  | 
1280  |  |                                                                  ULONG cbBuffer)  | 
1281  | 0  | { | 
1282  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1283  |  | 
  | 
1284  | 0  |   if (!context)  | 
1285  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1286  |  |  | 
1287  | 0  |   WINPR_ASSERT(context->mech);  | 
1288  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1289  | 0  |   WINPR_ASSERT(context->mech->pkg->table_w);  | 
1290  | 0  |   if (context->mech->pkg->table_w->SetContextAttributesW)  | 
1291  | 0  |     return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,  | 
1292  | 0  |                                                               ulAttribute, pBuffer, cbBuffer);  | 
1293  |  |  | 
1294  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1295  | 0  | }  | 
1296  |  |  | 
1297  |  | static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(PCtxtHandle phContext,  | 
1298  |  |                                                                  ULONG ulAttribute, void* pBuffer,  | 
1299  |  |                                                                  ULONG cbBuffer)  | 
1300  | 0  | { | 
1301  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1302  |  | 
  | 
1303  | 0  |   if (!context)  | 
1304  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1305  |  |  | 
1306  | 0  |   WINPR_ASSERT(context->mech);  | 
1307  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1308  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1309  | 0  |   if (context->mech->pkg->table->SetContextAttributesA)  | 
1310  | 0  |     return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,  | 
1311  | 0  |                                                             pBuffer, cbBuffer);  | 
1312  |  |  | 
1313  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1314  | 0  | }  | 
1315  |  |  | 
1316  |  | static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(PCredHandle phCredential,  | 
1317  |  |                                                                      ULONG ulAttribute,  | 
1318  |  |                                                                      void* pBuffer, ULONG cbBuffer)  | 
1319  | 0  | { | 
1320  | 0  |   MechCred* creds = NULL;  | 
1321  | 0  |   BOOL success = FALSE;  | 
1322  | 0  |   SECURITY_STATUS secStatus = 0;  | 
1323  |  | 
  | 
1324  | 0  |   creds = sspi_SecureHandleGetLowerPointer(phCredential);  | 
1325  |  | 
  | 
1326  | 0  |   if (!creds)  | 
1327  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1328  |  |  | 
1329  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
1330  | 0  |   { | 
1331  | 0  |     MechCred* cred = &creds[i];  | 
1332  |  | 
  | 
1333  | 0  |     WINPR_ASSERT(cred->mech);  | 
1334  | 0  |     WINPR_ASSERT(cred->mech->pkg);  | 
1335  | 0  |     WINPR_ASSERT(cred->mech->pkg->table);  | 
1336  | 0  |     WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW);  | 
1337  | 0  |     secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(&cred->cred, ulAttribute,  | 
1338  | 0  |                                                                     pBuffer, cbBuffer);  | 
1339  |  | 
  | 
1340  | 0  |     if (secStatus == SEC_E_OK)  | 
1341  | 0  |     { | 
1342  | 0  |       success = TRUE;  | 
1343  | 0  |     }  | 
1344  | 0  |   }  | 
1345  |  |  | 
1346  |  |   // return success if at least one submodule accepts the credential attribute  | 
1347  | 0  |   return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);  | 
1348  | 0  | }  | 
1349  |  |  | 
1350  |  | static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(PCredHandle phCredential,  | 
1351  |  |                                                                      ULONG ulAttribute,  | 
1352  |  |                                                                      void* pBuffer, ULONG cbBuffer)  | 
1353  | 0  | { | 
1354  | 0  |   MechCred* creds = NULL;  | 
1355  | 0  |   BOOL success = FALSE;  | 
1356  | 0  |   SECURITY_STATUS secStatus = 0;  | 
1357  |  | 
  | 
1358  | 0  |   creds = sspi_SecureHandleGetLowerPointer(phCredential);  | 
1359  |  | 
  | 
1360  | 0  |   if (!creds)  | 
1361  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1362  |  |  | 
1363  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
1364  | 0  |   { | 
1365  | 0  |     MechCred* cred = &creds[i];  | 
1366  |  | 
  | 
1367  | 0  |     if (!cred->valid)  | 
1368  | 0  |       continue;  | 
1369  |  |  | 
1370  | 0  |     WINPR_ASSERT(cred->mech);  | 
1371  | 0  |     WINPR_ASSERT(cred->mech->pkg);  | 
1372  | 0  |     WINPR_ASSERT(cred->mech->pkg->table);  | 
1373  | 0  |     WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA);  | 
1374  | 0  |     secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(&cred->cred, ulAttribute,  | 
1375  | 0  |                                                                   pBuffer, cbBuffer);  | 
1376  |  | 
  | 
1377  | 0  |     if (secStatus == SEC_E_OK)  | 
1378  | 0  |     { | 
1379  | 0  |       success = TRUE;  | 
1380  | 0  |     }  | 
1381  | 0  |   }  | 
1382  |  |  | 
1383  |  |   // return success if at least one submodule accepts the credential attribute  | 
1384  | 0  |   return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);  | 
1385  | 0  | }  | 
1386  |  |  | 
1387  |  | static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(  | 
1388  |  |     SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,  | 
1389  |  |     void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,  | 
1390  |  |     PTimeStamp ptsExpiry)  | 
1391  | 0  | { | 
1392  | 0  |   BOOL kerberos = 0;  | 
1393  | 0  |   BOOL ntlm = 0;  | 
1394  |  | 
  | 
1395  | 0  |   if (!negotiate_get_config(pAuthData, &kerberos, &ntlm))  | 
1396  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
1397  |  |  | 
1398  | 0  |   MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));  | 
1399  |  | 
  | 
1400  | 0  |   if (!creds)  | 
1401  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
1402  |  |  | 
1403  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
1404  | 0  |   { | 
1405  | 0  |     MechCred* cred = &creds[i];  | 
1406  | 0  |     const SecPkg* pkg = MechTable[i].pkg;  | 
1407  | 0  |     cred->mech = &MechTable[i];  | 
1408  |  | 
  | 
1409  | 0  |     if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0)  | 
1410  | 0  |       continue;  | 
1411  | 0  |     if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0)  | 
1412  | 0  |       continue;  | 
1413  |  |  | 
1414  | 0  |     WINPR_ASSERT(pkg->table_w);  | 
1415  | 0  |     WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);  | 
1416  | 0  |     if (pkg->table_w->AcquireCredentialsHandleW(  | 
1417  | 0  |             pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,  | 
1418  | 0  |             pvGetKeyArgument, &cred->cred, ptsExpiry) != SEC_E_OK)  | 
1419  | 0  |       continue;  | 
1420  |  |  | 
1421  | 0  |     cred->valid = TRUE;  | 
1422  | 0  |   }  | 
1423  |  |  | 
1424  | 0  |   sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);  | 
1425  | 0  |   sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME);  | 
1426  | 0  |   return SEC_E_OK;  | 
1427  | 0  | }  | 
1428  |  |  | 
1429  |  | static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(  | 
1430  |  |     SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,  | 
1431  |  |     void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,  | 
1432  |  |     PTimeStamp ptsExpiry)  | 
1433  | 0  | { | 
1434  | 0  |   BOOL kerberos = 0;  | 
1435  | 0  |   BOOL ntlm = 0;  | 
1436  |  | 
  | 
1437  | 0  |   if (!negotiate_get_config(pAuthData, &kerberos, &ntlm))  | 
1438  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
1439  |  |  | 
1440  | 0  |   MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));  | 
1441  |  | 
  | 
1442  | 0  |   if (!creds)  | 
1443  | 0  |     return SEC_E_INTERNAL_ERROR;  | 
1444  |  |  | 
1445  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
1446  | 0  |   { | 
1447  | 0  |     const SecPkg* pkg = MechTable[i].pkg;  | 
1448  | 0  |     MechCred* cred = &creds[i];  | 
1449  |  | 
  | 
1450  | 0  |     cred->mech = &MechTable[i];  | 
1451  |  | 
  | 
1452  | 0  |     if (!kerberos && _tcscmp(pkg->name, KERBEROS_SSP_NAME) == 0)  | 
1453  | 0  |       continue;  | 
1454  | 0  |     if (!ntlm && _tcscmp(SecPkgTable[i].name, NTLM_SSP_NAME) == 0)  | 
1455  | 0  |       continue;  | 
1456  |  |  | 
1457  | 0  |     WINPR_ASSERT(pkg->table);  | 
1458  | 0  |     WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);  | 
1459  | 0  |     if (pkg->table->AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse,  | 
1460  | 0  |                                               pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,  | 
1461  | 0  |                                               &cred->cred, ptsExpiry) != SEC_E_OK)  | 
1462  | 0  |       continue;  | 
1463  |  |  | 
1464  | 0  |     cred->valid = TRUE;  | 
1465  | 0  |   }  | 
1466  |  |  | 
1467  | 0  |   sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);  | 
1468  | 0  |   sspi_SecureHandleSetUpperPointer(phCredential, (void*)NEGO_SSP_NAME);  | 
1469  | 0  |   return SEC_E_OK;  | 
1470  | 0  | }  | 
1471  |  |  | 
1472  |  | static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(PCredHandle phCredential,  | 
1473  |  |                                                                        ULONG ulAttribute,  | 
1474  |  |                                                                        void* pBuffer)  | 
1475  | 0  | { | 
1476  | 0  |   WLog_ERR(TAG, "TODO: Implement");  | 
1477  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1478  | 0  | }  | 
1479  |  |  | 
1480  |  | static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(PCredHandle phCredential,  | 
1481  |  |                                                                        ULONG ulAttribute,  | 
1482  |  |                                                                        void* pBuffer)  | 
1483  | 0  | { | 
1484  | 0  |   WLog_ERR(TAG, "TODO: Implement");  | 
1485  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1486  | 0  | }  | 
1487  |  |  | 
1488  |  | static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(PCredHandle phCredential)  | 
1489  | 0  | { | 
1490  | 0  |   MechCred* creds = NULL;  | 
1491  |  | 
  | 
1492  | 0  |   creds = sspi_SecureHandleGetLowerPointer(phCredential);  | 
1493  | 0  |   if (!creds)  | 
1494  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1495  |  |  | 
1496  | 0  |   for (size_t i = 0; i < MECH_COUNT; i++)  | 
1497  | 0  |   { | 
1498  | 0  |     MechCred* cred = &creds[i];  | 
1499  |  | 
  | 
1500  | 0  |     WINPR_ASSERT(cred->mech);  | 
1501  | 0  |     WINPR_ASSERT(cred->mech->pkg);  | 
1502  | 0  |     WINPR_ASSERT(cred->mech->pkg->table);  | 
1503  | 0  |     WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);  | 
1504  | 0  |     cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);  | 
1505  | 0  |   }  | 
1506  | 0  |   free(creds);  | 
1507  |  | 
  | 
1508  | 0  |   return SEC_E_OK;  | 
1509  | 0  | }  | 
1510  |  |  | 
1511  |  | static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,  | 
1512  |  |                                                           PSecBufferDesc pMessage,  | 
1513  |  |                                                           ULONG MessageSeqNo)  | 
1514  | 0  | { | 
1515  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1516  |  | 
  | 
1517  | 0  |   if (!context)  | 
1518  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1519  |  |  | 
1520  | 0  |   if (context->mic)  | 
1521  | 0  |     MessageSeqNo++;  | 
1522  |  | 
  | 
1523  | 0  |   WINPR_ASSERT(context->mech);  | 
1524  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1525  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1526  | 0  |   if (context->mech->pkg->table->EncryptMessage)  | 
1527  | 0  |     return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,  | 
1528  | 0  |                                                      MessageSeqNo);  | 
1529  |  |  | 
1530  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1531  | 0  | }  | 
1532  |  |  | 
1533  |  | static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(PCtxtHandle phContext,  | 
1534  |  |                                                           PSecBufferDesc pMessage,  | 
1535  |  |                                                           ULONG MessageSeqNo, ULONG* pfQOP)  | 
1536  | 0  | { | 
1537  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1538  |  | 
  | 
1539  | 0  |   if (!context)  | 
1540  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1541  |  |  | 
1542  | 0  |   if (context->mic)  | 
1543  | 0  |     MessageSeqNo++;  | 
1544  |  | 
  | 
1545  | 0  |   WINPR_ASSERT(context->mech);  | 
1546  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1547  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1548  | 0  |   if (context->mech->pkg->table->DecryptMessage)  | 
1549  | 0  |     return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,  | 
1550  | 0  |                                                      MessageSeqNo, pfQOP);  | 
1551  |  |  | 
1552  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1553  | 0  | }  | 
1554  |  |  | 
1555  |  | static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(PCtxtHandle phContext, ULONG fQOP,  | 
1556  |  |                                                          PSecBufferDesc pMessage,  | 
1557  |  |                                                          ULONG MessageSeqNo)  | 
1558  | 0  | { | 
1559  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1560  |  | 
  | 
1561  | 0  |   if (!context)  | 
1562  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1563  |  |  | 
1564  | 0  |   if (context->mic)  | 
1565  | 0  |     MessageSeqNo++;  | 
1566  |  | 
  | 
1567  | 0  |   WINPR_ASSERT(context->mech);  | 
1568  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1569  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1570  | 0  |   if (context->mech->pkg->table->MakeSignature)  | 
1571  | 0  |     return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,  | 
1572  | 0  |                                                     MessageSeqNo);  | 
1573  |  |  | 
1574  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1575  | 0  | }  | 
1576  |  |  | 
1577  |  | static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(PCtxtHandle phContext,  | 
1578  |  |                                                            PSecBufferDesc pMessage,  | 
1579  |  |                                                            ULONG MessageSeqNo, ULONG* pfQOP)  | 
1580  | 0  | { | 
1581  | 0  |   NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);  | 
1582  |  | 
  | 
1583  | 0  |   if (!context)  | 
1584  | 0  |     return SEC_E_INVALID_HANDLE;  | 
1585  |  |  | 
1586  | 0  |   if (context->mic)  | 
1587  | 0  |     MessageSeqNo++;  | 
1588  |  | 
  | 
1589  | 0  |   WINPR_ASSERT(context->mech);  | 
1590  | 0  |   WINPR_ASSERT(context->mech->pkg);  | 
1591  | 0  |   WINPR_ASSERT(context->mech->pkg->table);  | 
1592  | 0  |   if (context->mech->pkg->table->VerifySignature)  | 
1593  | 0  |     return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,  | 
1594  | 0  |                                                       MessageSeqNo, pfQOP);  | 
1595  |  |  | 
1596  | 0  |   return SEC_E_UNSUPPORTED_FUNCTION;  | 
1597  | 0  | }  | 
1598  |  |  | 
1599  |  | const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA = { | 
1600  |  |   3,                                     /* dwVersion */  | 
1601  |  |   NULL,                                  /* EnumerateSecurityPackages */  | 
1602  |  |   negotiate_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */  | 
1603  |  |   negotiate_AcquireCredentialsHandleA,   /* AcquireCredentialsHandle */  | 
1604  |  |   negotiate_FreeCredentialsHandle,       /* FreeCredentialsHandle */  | 
1605  |  |   NULL,                                  /* Reserved2 */  | 
1606  |  |   negotiate_InitializeSecurityContextA,  /* InitializeSecurityContext */  | 
1607  |  |   negotiate_AcceptSecurityContext,       /* AcceptSecurityContext */  | 
1608  |  |   negotiate_CompleteAuthToken,           /* CompleteAuthToken */  | 
1609  |  |   negotiate_DeleteSecurityContext,       /* DeleteSecurityContext */  | 
1610  |  |   NULL,                                  /* ApplyControlToken */  | 
1611  |  |   negotiate_QueryContextAttributesA,     /* QueryContextAttributes */  | 
1612  |  |   negotiate_ImpersonateSecurityContext,  /* ImpersonateSecurityContext */  | 
1613  |  |   negotiate_RevertSecurityContext,       /* RevertSecurityContext */  | 
1614  |  |   negotiate_MakeSignature,               /* MakeSignature */  | 
1615  |  |   negotiate_VerifySignature,             /* VerifySignature */  | 
1616  |  |   NULL,                                  /* FreeContextBuffer */  | 
1617  |  |   NULL,                                  /* QuerySecurityPackageInfo */  | 
1618  |  |   NULL,                                  /* Reserved3 */  | 
1619  |  |   NULL,                                  /* Reserved4 */  | 
1620  |  |   NULL,                                  /* ExportSecurityContext */  | 
1621  |  |   NULL,                                  /* ImportSecurityContext */  | 
1622  |  |   NULL,                                  /* AddCredentials */  | 
1623  |  |   NULL,                                  /* Reserved8 */  | 
1624  |  |   NULL,                                  /* QuerySecurityContextToken */  | 
1625  |  |   negotiate_EncryptMessage,              /* EncryptMessage */  | 
1626  |  |   negotiate_DecryptMessage,              /* DecryptMessage */  | 
1627  |  |   negotiate_SetContextAttributesA,       /* SetContextAttributes */  | 
1628  |  |   negotiate_SetCredentialsAttributesA,   /* SetCredentialsAttributes */  | 
1629  |  | };  | 
1630  |  |  | 
1631  |  | const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW = { | 
1632  |  |   3,                                     /* dwVersion */  | 
1633  |  |   NULL,                                  /* EnumerateSecurityPackages */  | 
1634  |  |   negotiate_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */  | 
1635  |  |   negotiate_AcquireCredentialsHandleW,   /* AcquireCredentialsHandle */  | 
1636  |  |   negotiate_FreeCredentialsHandle,       /* FreeCredentialsHandle */  | 
1637  |  |   NULL,                                  /* Reserved2 */  | 
1638  |  |   negotiate_InitializeSecurityContextW,  /* InitializeSecurityContext */  | 
1639  |  |   negotiate_AcceptSecurityContext,       /* AcceptSecurityContext */  | 
1640  |  |   negotiate_CompleteAuthToken,           /* CompleteAuthToken */  | 
1641  |  |   negotiate_DeleteSecurityContext,       /* DeleteSecurityContext */  | 
1642  |  |   NULL,                                  /* ApplyControlToken */  | 
1643  |  |   negotiate_QueryContextAttributesW,     /* QueryContextAttributes */  | 
1644  |  |   negotiate_ImpersonateSecurityContext,  /* ImpersonateSecurityContext */  | 
1645  |  |   negotiate_RevertSecurityContext,       /* RevertSecurityContext */  | 
1646  |  |   negotiate_MakeSignature,               /* MakeSignature */  | 
1647  |  |   negotiate_VerifySignature,             /* VerifySignature */  | 
1648  |  |   NULL,                                  /* FreeContextBuffer */  | 
1649  |  |   NULL,                                  /* QuerySecurityPackageInfo */  | 
1650  |  |   NULL,                                  /* Reserved3 */  | 
1651  |  |   NULL,                                  /* Reserved4 */  | 
1652  |  |   NULL,                                  /* ExportSecurityContext */  | 
1653  |  |   NULL,                                  /* ImportSecurityContext */  | 
1654  |  |   NULL,                                  /* AddCredentials */  | 
1655  |  |   NULL,                                  /* Reserved8 */  | 
1656  |  |   NULL,                                  /* QuerySecurityContextToken */  | 
1657  |  |   negotiate_EncryptMessage,              /* EncryptMessage */  | 
1658  |  |   negotiate_DecryptMessage,              /* DecryptMessage */  | 
1659  |  |   negotiate_SetContextAttributesW,       /* SetContextAttributes */  | 
1660  |  |   negotiate_SetCredentialsAttributesW,   /* SetCredentialsAttributes */  | 
1661  |  | };  | 
1662  |  |  | 
1663  |  | BOOL NEGOTIATE_init(void)  | 
1664  | 0  | { | 
1665  | 0  |   InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer,  | 
1666  | 0  |                                ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer));  | 
1667  | 0  |   InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer,  | 
1668  | 0  |                                ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer));  | 
1669  |  | 
  | 
1670  | 0  |   return TRUE;  | 
1671  | 0  | }  |