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