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