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 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);
519
520 if (!WinPrAsn1DecPeekTag(&dec, &tag))
521 return FALSE;
522
523 if (tag == 0x60)
524 {
525 /* initialContextToken [APPLICATION 0] */
526 if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
527 return FALSE;
528 dec = dec2;
529
530 /* thisMech OID */
531 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
532 return FALSE;
533
534 if (!sspi_gss_oid_compare(&spnego_OID, &oid))
535 return FALSE;
536
537 /* [0] NegTokenInit */
538 if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
539 return FALSE;
540
541 token->init = TRUE;
542 }
543 /* [1] NegTokenResp */
544 else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
545 return FALSE;
546 dec = dec2;
547
548 WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp...");
549
550 /* Read NegTokenResp sequence members */
551 do
552 {
553 if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
554 return FALSE;
555
556 switch (contextual)
557 {
558 case 0:
559 if (token->init)
560 {
561 /* mechTypes [0] MechTypeList */
562 wStream s = WinPrAsn1DecGetStream(&dec2);
563 token->mechTypes.BufferType = SECBUFFER_TOKEN;
564 const size_t mlen = Stream_Length(&s);
565 if (mlen > UINT32_MAX)
566 return FALSE;
567 token->mechTypes.cbBuffer = (UINT32)mlen;
568 token->mechTypes.pvBuffer = Stream_Buffer(&s);
569 WLog_DBG(TAG, "\tmechTypes [0] (%" PRIu32 " bytes)", token->mechTypes.cbBuffer);
570 }
571 else
572 {
573 /* negState [0] ENUMERATED */
574 WinPrAsn1_ENUMERATED rd = 0;
575 if (!WinPrAsn1DecReadEnumerated(&dec2, &rd))
576 return FALSE;
577 switch (rd)
578 {
579 case NOSTATE:
580 case ACCEPT_COMPLETED:
581 case ACCEPT_INCOMPLETE:
582 case REJECT:
583 case REQUEST_MIC:
584 break;
585 default:
586 WLog_ERR(TAG, "Invalid negState enumeration value %d", rd);
587 return FALSE;
588 }
589
590 token->negState = WINPR_ASSERTING_INT_CAST(enum NegState, rd);
591 WLog_DBG(TAG, "\tnegState [0] (%d)", rd);
592 }
593 break;
594 case 1:
595 if (token->init)
596 {
597 /* reqFlags [1] ContextFlags BIT STRING (ignored) */
598 if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
599 return FALSE;
600 WLog_DBG(TAG, "\treqFlags [1] (%" PRIuz " bytes)", len);
601 }
602 else
603 {
604 /* supportedMech [1] MechType */
605 if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
606 return FALSE;
607 WLog_DBG(TAG, "\tsupportedMech [1] (%s)",
608 negotiate_mech_name(&token->supportedMech));
609 }
610 break;
611 case 2:
612 /* mechToken [2] OCTET STRING */
613 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
614 return FALSE;
615 if (octet_string.len > UINT32_MAX)
616 return FALSE;
617 token->mechToken.cbBuffer = (UINT32)octet_string.len;
618 token->mechToken.pvBuffer = octet_string.data;
619 token->mechToken.BufferType = SECBUFFER_TOKEN;
620 WLog_DBG(TAG, "\tmechToken [2] (%" PRIuz " bytes)", octet_string.len);
621 break;
622 case 3:
623 /* mechListMic [3] OCTET STRING */
624 if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
625 return FALSE;
626 if (octet_string.len > UINT32_MAX)
627 return FALSE;
628 token->mic.cbBuffer = (UINT32)octet_string.len;
629 token->mic.pvBuffer = octet_string.data;
630 token->mic.BufferType = SECBUFFER_TOKEN;
631 WLog_DBG(TAG, "\tmechListMIC [3] (%" PRIuz " bytes)", octet_string.len);
632 break;
633 default:
634 WLog_ERR(TAG, "unknown contextual item %d", contextual);
635 return FALSE;
636 }
637 } while (WinPrAsn1DecPeekTag(&dec, &tag));
638
639 return TRUE;
640}
641
642static SECURITY_STATUS negotiate_mic_exchange(NEGOTIATE_CONTEXT* context, NegToken* input_token,
643 NegToken* output_token, PSecBuffer output_buffer)
644{
645 SecBuffer mic_buffers[2] = WINPR_C_ARRAY_INIT;
646 SecBufferDesc mic_buffer_desc = { SECBUFFER_VERSION, 2, mic_buffers };
647 SECURITY_STATUS status = 0;
648
649 WINPR_ASSERT(context);
650 WINPR_ASSERT(input_token);
651 WINPR_ASSERT(output_token);
652 WINPR_ASSERT(context->mech);
653 WINPR_ASSERT(context->mech->pkg);
654
655 const SecurityFunctionTableA* table = context->mech->pkg->table;
656 WINPR_ASSERT(table);
657
658 mic_buffers[0] = context->mechTypes;
659
660 /* Verify MIC if we received one */
661 if (input_token->mic.cbBuffer > 0)
662 {
663 mic_buffers[1] = input_token->mic;
664
665 status = table->VerifySignature(&context->sub_context, &mic_buffer_desc, 0, nullptr);
666 if (status != SEC_E_OK)
667 return status;
668
669 output_token->negState = ACCEPT_COMPLETED;
670 }
671
672 /* If peer expects a MIC then generate it */
673 if (input_token->negState != ACCEPT_COMPLETED)
674 {
675 /* Store the mic token after the mech token in the output buffer */
676 output_token->mic.BufferType = SECBUFFER_TOKEN;
677 if (output_buffer)
678 {
679 output_token->mic.cbBuffer = output_buffer->cbBuffer - output_token->mechToken.cbBuffer;
680 output_token->mic.pvBuffer =
681 (BYTE*)output_buffer->pvBuffer + output_token->mechToken.cbBuffer;
682 }
683 mic_buffers[1] = output_token->mic;
684
685 status = table->MakeSignature(&context->sub_context, 0, &mic_buffer_desc, 0);
686 if (status != SEC_E_OK)
687 return status;
688
689 output_token->mic = mic_buffers[1];
690 }
691
692 /* When using NTLM cipher states need to be reset after mic exchange */
693 const SSPI_PACKAGE_ID sub_id = sspi_SecureHandleGetPackageId(&context->sub_context);
694 if (!sub_id)
695 return SEC_E_INTERNAL_ERROR;
696
697 if (sub_id == SSPI_PACKAGE_NTLM)
698 {
699 if (!ntlm_reset_cipher_state(&context->sub_context))
700 return SEC_E_INTERNAL_ERROR;
701 }
702
703 return SEC_E_OK;
704}
705
706static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextW(
707 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
708 ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
709 PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
710{
711 NEGOTIATE_CONTEXT* context = nullptr;
712 NEGOTIATE_CONTEXT init_context = NEGOTIATE_CONTEXT_init();
713 MechCred* creds = nullptr;
714 PCtxtHandle sub_context = nullptr;
715 PCredHandle sub_cred = nullptr;
716 NegToken input_token = empty_neg_token;
717 NegToken output_token = empty_neg_token;
718 PSecBuffer input_buffer = nullptr;
719 PSecBuffer output_buffer = nullptr;
720 PSecBuffer bindings_buffer = nullptr;
721 SecBuffer mech_input_buffers[2] = WINPR_C_ARRAY_INIT;
722 SecBufferDesc mech_input = { SECBUFFER_VERSION, 2, mech_input_buffers };
723 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
724 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
725 SECURITY_STATUS sub_status = SEC_E_INTERNAL_ERROR;
726 WinPrAsn1Encoder* enc = nullptr;
727 wStream s;
728 const Mech* mech = nullptr;
729
730 if (!phCredential || !SecIsValidHandle(phCredential))
731 return SEC_E_NO_CREDENTIALS;
732
733 creds = sspi_SecureHandleGetLowerPointer(phCredential);
734
735 /* behave like windows SSPIs that don't want empty context */
736 if (phContext && !phContext->dwLower && !phContext->dwUpper)
737 return SEC_E_INVALID_HANDLE;
738
739 context = sspi_SecureHandleGetLowerPointer(phContext);
740
741 if (pInput)
742 {
743 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
744 bindings_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
745 }
746 if (pOutput)
747 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
748
749 if (!context)
750 {
751 enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
752 if (!enc)
753 return SEC_E_INSUFFICIENT_MEMORY;
754
755 if (!WinPrAsn1EncSeqContainer(enc))
756 goto cleanup;
757
758 for (size_t i = 0; i < MECH_COUNT; i++)
759 {
760 MechCred* cred = &creds[i];
761 const SecPkg* pkg = MechTable[i].pkg;
762 WINPR_ASSERT(pkg);
763 WINPR_ASSERT(pkg->table_w);
764
765 if (!cred->valid)
766 {
767 WLog_DBG(TAG, "Unavailable mechanism: %s", negotiate_mech_name(cred->mech->oid));
768 continue;
769 }
770
771 /* Send an optimistic token for the first valid mechanism */
772 if (!init_context.mech)
773 {
774 /* Use the output buffer to store the optimistic token */
775 if (!output_buffer)
776 goto cleanup;
777
778 CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
779
780 if (bindings_buffer)
781 mech_input_buffers[0] = *bindings_buffer;
782
783 WINPR_ASSERT(pkg->table_w->InitializeSecurityContextW);
784 sub_status = pkg->table_w->InitializeSecurityContextW(
785 &cred->cred, nullptr, pszTargetName, fContextReq | cred->mech->flags, Reserved1,
786 TargetDataRep, &mech_input, Reserved2, &init_context.sub_context, &mech_output,
787 pfContextAttr, ptsExpiry);
788
789 /* If the mechanism failed we can't use it; skip */
790 if (IsSecurityStatusError(sub_status))
791 {
792 if (SecIsValidHandle(&init_context.sub_context))
793 {
794 WINPR_ASSERT(pkg->table_w->DeleteSecurityContext);
795 pkg->table_w->DeleteSecurityContext(&init_context.sub_context);
796 }
797 cred->valid = FALSE;
798 continue;
799 }
800
801 init_context.mech = cred->mech;
802 }
803
804 if (!WinPrAsn1EncOID(enc, cred->mech->oid))
805 goto cleanup;
806 WLog_DBG(TAG, "Available mechanism: %s", negotiate_mech_name(cred->mech->oid));
807 }
808
809 /* No usable mechanisms were found */
810 if (!init_context.mech)
811 goto cleanup;
812
813 /* If the only available mech is NTLM use it directly otherwise use spnego */
814 if (init_context.mech->oid == &ntlm_OID)
815 {
816 init_context.spnego = FALSE;
817 output_buffer->cbBuffer = output_token.mechToken.cbBuffer;
818 WLog_DBG(TAG, "Using direct NTLM");
819 }
820 else
821 {
822 init_context.spnego = TRUE;
823 init_context.mechTypes.BufferType = SECBUFFER_DATA;
824 const size_t cb = WinPrAsn1EncEndContainer(enc);
825 WINPR_ASSERT(cb <= UINT32_MAX);
826 init_context.mechTypes.cbBuffer = (UINT32)cb;
827 }
828
829 /* Allocate memory for the new context */
830 context = negotiate_ContextNew(&init_context);
831 if (!context)
832 {
833 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
834 WinPrAsn1Encoder_Free(&enc);
835 return SEC_E_INSUFFICIENT_MEMORY;
836 }
837
838 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NEGOTIATE);
839 sspi_SecureHandleSetLowerPointer(phNewContext, context);
840
841 if (!context->spnego)
842 {
843 status = sub_status;
844 goto cleanup;
845 }
846
847 /* Write mechTypesList */
848 Stream_StaticInit(&s, context->mechTypes.pvBuffer, context->mechTypes.cbBuffer);
849 if (!WinPrAsn1EncToStream(enc, &s))
850 goto cleanup;
851
852 output_token.mechTypes.cbBuffer = context->mechTypes.cbBuffer;
853 output_token.mechTypes.pvBuffer = context->mechTypes.pvBuffer;
854 output_token.init = TRUE;
855
856 if (sub_status == SEC_E_OK)
857 context->state = NEGOTIATE_STATE_FINAL_OPTIMISTIC;
858 }
859 else
860 {
861 if (!input_buffer)
862 return SEC_E_INVALID_TOKEN;
863
864 sub_context = &context->sub_context;
865 sub_cred = negotiate_FindCredential(creds, context->mech);
866
867 if (!context->spnego)
868 {
869 return context->mech->pkg->table_w->InitializeSecurityContextW(
870 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
871 TargetDataRep, pInput, Reserved2, sub_context, pOutput, pfContextAttr, ptsExpiry);
872 }
873
874 if (!negotiate_read_neg_token(input_buffer, &input_token))
875 return SEC_E_INVALID_TOKEN;
876
877 /* On first response check if the server doesn't like out preferred mech */
878 if (context->state < NEGOTIATE_STATE_NEGORESP && input_token.supportedMech.len &&
879 !sspi_gss_oid_compare(&input_token.supportedMech, context->mech->oid))
880 {
881 mech = negotiate_GetMechByOID(&input_token.supportedMech);
882 if (!mech)
883 return SEC_E_INVALID_TOKEN;
884
885 /* Make sure the specified mech is supported and get the appropriate credential */
886 sub_cred = negotiate_FindCredential(creds, mech);
887 if (!sub_cred)
888 return SEC_E_INVALID_TOKEN;
889
890 /* Clean up the optimistic mech */
891 context->mech->pkg->table_w->DeleteSecurityContext(&context->sub_context);
892 sub_context = nullptr;
893
894 context->mech = mech;
895 context->mic = TRUE;
896 }
897
898 /* Check neg_state (required on first response) */
899 if (context->state < NEGOTIATE_STATE_NEGORESP)
900 {
901 switch (input_token.negState)
902 {
903 case NOSTATE:
904 return SEC_E_INVALID_TOKEN;
905 case REJECT:
906 return SEC_E_LOGON_DENIED;
907 case REQUEST_MIC:
908 context->mic = TRUE;
909 /* fallthrough */
910 WINPR_FALLTHROUGH
911 case ACCEPT_INCOMPLETE:
912 context->state = NEGOTIATE_STATE_NEGORESP;
913 break;
914 case ACCEPT_COMPLETED:
915 if (context->state == NEGOTIATE_STATE_INITIAL)
916 context->state = NEGOTIATE_STATE_NEGORESP;
917 else
918 context->state = NEGOTIATE_STATE_FINAL;
919 break;
920 default:
921 break;
922 }
923
924 WLog_DBG(TAG, "Negotiated mechanism: %s", negotiate_mech_name(context->mech->oid));
925 }
926
927 if (context->state == NEGOTIATE_STATE_NEGORESP)
928 {
929 /* Store the mech token in the output buffer */
930 if (!output_buffer)
931 goto cleanup;
932 CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
933
934 mech_input_buffers[0] = input_token.mechToken;
935 if (bindings_buffer)
936 mech_input_buffers[1] = *bindings_buffer;
937
938 status = context->mech->pkg->table_w->InitializeSecurityContextW(
939 sub_cred, sub_context, pszTargetName, fContextReq | context->mech->flags, Reserved1,
940 TargetDataRep, input_token.mechToken.cbBuffer ? &mech_input : nullptr, Reserved2,
941 &context->sub_context, &mech_output, pfContextAttr, ptsExpiry);
942
943 if (IsSecurityStatusError(status))
944 return status;
945 }
946
947 if (status == SEC_E_OK)
948 {
949 if (output_token.mechToken.cbBuffer > 0)
950 context->state = NEGOTIATE_STATE_MIC;
951 else
952 context->state = NEGOTIATE_STATE_FINAL;
953 }
954
955 /* Check if the acceptor sent its final token without a mic */
956 if (context->state == NEGOTIATE_STATE_FINAL && input_token.mic.cbBuffer == 0)
957 {
958 if (context->mic || input_token.negState != ACCEPT_COMPLETED)
959 return SEC_E_INVALID_TOKEN;
960
961 if (output_buffer)
962 output_buffer->cbBuffer = 0;
963 return SEC_E_OK;
964 }
965
966 if ((context->state == NEGOTIATE_STATE_MIC && context->mic) ||
967 context->state == NEGOTIATE_STATE_FINAL)
968 {
969 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
970 if (status != SEC_E_OK)
971 return status;
972 }
973 }
974
975 if (input_token.negState == ACCEPT_COMPLETED)
976 {
977 if (output_buffer)
978 output_buffer->cbBuffer = 0;
979 return SEC_E_OK;
980 }
981
982 if (output_token.negState == ACCEPT_COMPLETED)
983 status = SEC_E_OK;
984 else
985 status = SEC_I_CONTINUE_NEEDED;
986
987 if (!negotiate_write_neg_token(output_buffer, &output_token))
988 status = SEC_E_INTERNAL_ERROR;
989
990cleanup:
991 WinPrAsn1Encoder_Free(&enc);
992 return status;
993}
994
995static SECURITY_STATUS SEC_ENTRY negotiate_InitializeSecurityContextA(
996 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
997 ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
998 PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
999{
1000 SECURITY_STATUS status = 0;
1001 SEC_WCHAR* pszTargetNameW = nullptr;
1002
1003 if (pszTargetName)
1004 {
1005 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, nullptr);
1006 if (!pszTargetNameW)
1007 return SEC_E_INTERNAL_ERROR;
1008 }
1009
1010 status = negotiate_InitializeSecurityContextW(
1011 phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
1012 Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
1013 free(pszTargetNameW);
1014 return status;
1015}
1016
1017WINPR_ATTR_NODISCARD
1018static const Mech* guessMech(PSecBuffer input_buffer, BOOL* spNego, WinPrAsn1_OID* oid)
1019{
1020 WinPrAsn1Decoder decoder = WinPrAsn1Decoder_init();
1021 WinPrAsn1Decoder appDecoder = WinPrAsn1Decoder_init();
1022 WinPrAsn1_tagId tag = 0;
1023 const char ssp[] = "NTLMSSP";
1024
1025 if (!input_buffer || !input_buffer->pvBuffer || (input_buffer->cbBuffer == 0))
1026 return nullptr;
1027
1028 *spNego = FALSE;
1029
1030 /* Check for NTLM token */
1031 if (input_buffer->cbBuffer >= 8 && strncmp(input_buffer->pvBuffer, ssp, sizeof(ssp)) == 0)
1032 {
1033 *oid = ntlm_OID;
1034 return negotiate_GetMechByOID(&ntlm_OID);
1035 }
1036
1037 /* Read initialContextToken or raw Kerberos token */
1038 WinPrAsn1Decoder_InitMem(&decoder, WINPR_ASN1_DER, input_buffer->pvBuffer,
1039 input_buffer->cbBuffer);
1040
1041 if (!WinPrAsn1DecReadApp(&decoder, &tag, &appDecoder) || tag != 0)
1042 return nullptr;
1043
1044 if (!WinPrAsn1DecReadOID(&appDecoder, oid, FALSE))
1045 return nullptr;
1046
1047 if (sspi_gss_oid_compare(oid, &spnego_OID))
1048 {
1049 *spNego = TRUE;
1050 return nullptr;
1051 }
1052
1053 return negotiate_GetMechByOID(oid);
1054}
1055
1056static SECURITY_STATUS SEC_ENTRY negotiate_AcceptSecurityContext(
1057 PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq,
1058 ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr,
1059 PTimeStamp ptsTimeStamp)
1060{
1061 NEGOTIATE_CONTEXT* context = nullptr;
1062 NEGOTIATE_CONTEXT init_context = NEGOTIATE_CONTEXT_init();
1063 MechCred* creds = nullptr;
1064 PCredHandle sub_cred = nullptr;
1065 NegToken input_token = empty_neg_token;
1066 NegToken output_token = empty_neg_token;
1067 PSecBuffer input_buffer = nullptr;
1068 PSecBuffer output_buffer = nullptr;
1069 SecBufferDesc mech_input = { SECBUFFER_VERSION, 1, &input_token.mechToken };
1070 SecBufferDesc mech_output = { SECBUFFER_VERSION, 1, &output_token.mechToken };
1071 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1072 WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
1073 WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
1074 WinPrAsn1_tagId tag = 0;
1075 WinPrAsn1_OID oid = WINPR_C_ARRAY_INIT;
1076 const Mech* first_mech = nullptr;
1077
1078 if (!phCredential || !SecIsValidHandle(phCredential))
1079 return SEC_E_NO_CREDENTIALS;
1080
1081 creds = sspi_SecureHandleGetLowerPointer(phCredential);
1082
1083 if (!pInput)
1084 return SEC_E_INVALID_TOKEN;
1085
1086 /* behave like windows SSPIs that don't want empty context */
1087 if (phContext && !phContext->dwLower && !phContext->dwUpper)
1088 return SEC_E_INVALID_HANDLE;
1089
1090 context = sspi_SecureHandleGetLowerPointer(phContext);
1091
1092 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
1093 if (pOutput)
1094 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
1095
1096 if (!context)
1097 {
1098 init_context.mech = guessMech(input_buffer, &init_context.spnego, &oid);
1099 if (!init_context.mech && !init_context.spnego)
1100 return SEC_E_INVALID_TOKEN;
1101
1102 WLog_DBG(TAG, "Mechanism: %s", negotiate_mech_name(&oid));
1103
1104 if (init_context.spnego)
1105 {
1106 /* Process spnego token */
1107 if (!negotiate_read_neg_token(input_buffer, &input_token))
1108 return SEC_E_INVALID_TOKEN;
1109
1110 /* First token must be negoTokenInit and must contain a mechList */
1111 if (!input_token.init || input_token.mechTypes.cbBuffer == 0)
1112 return SEC_E_INVALID_TOKEN;
1113
1114 init_context.mechTypes.BufferType = SECBUFFER_DATA;
1115 init_context.mechTypes.cbBuffer = input_token.mechTypes.cbBuffer;
1116
1117 /* Prepare to read mechList */
1118 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input_token.mechTypes.pvBuffer,
1119 input_token.mechTypes.cbBuffer);
1120
1121 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
1122 return SEC_E_INVALID_TOKEN;
1123 dec = dec2;
1124
1125 /* If an optimistic token was provided pass it into the first mech */
1126 if (input_token.mechToken.cbBuffer)
1127 {
1128 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1129 return SEC_E_INVALID_TOKEN;
1130
1131 init_context.mech = negotiate_GetMechByOID(&oid);
1132
1133 if (init_context.mech)
1134 {
1135 if (output_buffer)
1136 output_token.mechToken = *output_buffer;
1137 WLog_DBG(TAG, "Requested mechanism: %s",
1138 negotiate_mech_name(init_context.mech->oid));
1139 }
1140 }
1141 }
1142
1143 if (init_context.mech)
1144 {
1145 sub_cred = negotiate_FindCredential(creds, init_context.mech);
1146
1147 if (sub_cred)
1148 {
1149 status = init_context.mech->pkg->table->AcceptSecurityContext(
1150 sub_cred, nullptr, init_context.spnego ? &mech_input : pInput, fContextReq,
1151 TargetDataRep, &init_context.sub_context,
1152 init_context.spnego ? &mech_output : pOutput, pfContextAttr, ptsTimeStamp);
1153 }
1154 }
1155
1156 if (IsSecurityStatusError(status))
1157 {
1158 if (!init_context.spnego)
1159 return status;
1160
1161 /* Clean up any partially created sub context before falling back to
1162 * another mechanism (mirrors the mech change handling in
1163 * negotiate_InitializeSecurityContextW) */
1164 if (init_context.mech)
1165 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
1166
1167 init_context.mic = TRUE;
1168 first_mech = init_context.mech;
1169 init_context.mech = nullptr;
1170 output_token.mechToken.cbBuffer = 0;
1171 }
1172
1173 while (!init_context.mech && WinPrAsn1DecPeekTag(&dec, &tag))
1174 {
1175 /* Read each mechanism */
1176 if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
1177 return SEC_E_INVALID_TOKEN;
1178
1179 init_context.mech = negotiate_GetMechByOID(&oid);
1180 WLog_DBG(TAG, "Requested mechanism: %s", negotiate_mech_name(&oid));
1181
1182 /* Microsoft may send two versions of the kerberos OID */
1183 if (init_context.mech == first_mech)
1184 init_context.mech = nullptr;
1185
1186 if (init_context.mech && !negotiate_FindCredential(creds, init_context.mech))
1187 init_context.mech = nullptr;
1188 }
1189
1190 if (!init_context.mech)
1191 return SEC_E_INTERNAL_ERROR;
1192
1193 context = negotiate_ContextNew(&init_context);
1194 if (!context)
1195 {
1196 if (!IsSecurityStatusError(status))
1197 init_context.mech->pkg->table->DeleteSecurityContext(&init_context.sub_context);
1198 return SEC_E_INSUFFICIENT_MEMORY;
1199 }
1200
1201 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NEGOTIATE);
1202 sspi_SecureHandleSetLowerPointer(phNewContext, context);
1203
1204 if (!init_context.spnego)
1205 return status;
1206
1207 CopyMemory(init_context.mechTypes.pvBuffer, input_token.mechTypes.pvBuffer,
1208 input_token.mechTypes.cbBuffer);
1209
1210 if (!context->mech->preferred)
1211 {
1212 output_token.negState = REQUEST_MIC;
1213 context->mic = TRUE;
1214 }
1215 else
1216 {
1217 output_token.negState = ACCEPT_INCOMPLETE;
1218 }
1219
1220 if (status == SEC_E_OK)
1221 context->state = NEGOTIATE_STATE_FINAL;
1222 else
1223 context->state = NEGOTIATE_STATE_NEGORESP;
1224
1225 output_token.supportedMech = oid;
1226 WLog_DBG(TAG, "Accepted mechanism: %s", negotiate_mech_name(&output_token.supportedMech));
1227 }
1228 else
1229 {
1230 sub_cred = negotiate_FindCredential(creds, context->mech);
1231 if (!sub_cred)
1232 return SEC_E_NO_CREDENTIALS;
1233
1234 if (!context->spnego)
1235 {
1236 return context->mech->pkg->table->AcceptSecurityContext(
1237 sub_cred, &context->sub_context, pInput, fContextReq, TargetDataRep,
1238 &context->sub_context, pOutput, pfContextAttr, ptsTimeStamp);
1239 }
1240
1241 if (!negotiate_read_neg_token(input_buffer, &input_token))
1242 return SEC_E_INVALID_TOKEN;
1243
1244 /* Process the mechanism token */
1245 if (input_token.mechToken.cbBuffer > 0)
1246 {
1247 if (context->state != NEGOTIATE_STATE_NEGORESP)
1248 return SEC_E_INVALID_TOKEN;
1249
1250 /* Use the output buffer to store the optimistic token */
1251 if (output_buffer)
1252 CopyMemory(&output_token.mechToken, output_buffer, sizeof(SecBuffer));
1253
1254 /* If the client's optimistic mechanism was not accepted the sub
1255 * context was never created (or was cleaned up); pass NULL so the
1256 * selected mechanism creates a fresh context instead of rejecting
1257 * the empty handle with SEC_E_INVALID_HANDLE */
1258 PCtxtHandle sub_context = sspi_SecureHandleGetLowerPointer(&context->sub_context)
1259 ? &context->sub_context
1260 : nullptr;
1261 status = context->mech->pkg->table->AcceptSecurityContext(
1262 sub_cred, sub_context, &mech_input, fContextReq | context->mech->flags,
1263 TargetDataRep, &context->sub_context, &mech_output, pfContextAttr, ptsTimeStamp);
1264
1265 if (IsSecurityStatusError(status))
1266 return status;
1267
1268 if (status == SEC_E_OK)
1269 context->state = NEGOTIATE_STATE_FINAL;
1270 }
1271 else if (context->state == NEGOTIATE_STATE_NEGORESP)
1272 return SEC_E_INVALID_TOKEN;
1273 }
1274
1275 if (context->state == NEGOTIATE_STATE_FINAL)
1276 {
1277 /* Check if initiator sent the last mech token without a mic and a mic was required */
1278 if (context->mic && output_token.mechToken.cbBuffer == 0 && input_token.mic.cbBuffer == 0)
1279 return SEC_E_INVALID_TOKEN;
1280
1281 if (context->mic || input_token.mic.cbBuffer > 0)
1282 {
1283 status = negotiate_mic_exchange(context, &input_token, &output_token, output_buffer);
1284 if (status != SEC_E_OK)
1285 return status;
1286 }
1287 else
1288 output_token.negState = ACCEPT_COMPLETED;
1289 }
1290
1291 if (input_token.negState == ACCEPT_COMPLETED)
1292 {
1293 if (output_buffer)
1294 output_buffer->cbBuffer = 0;
1295 return SEC_E_OK;
1296 }
1297
1298 if (output_token.negState == ACCEPT_COMPLETED)
1299 status = SEC_E_OK;
1300 else
1301 status = SEC_I_CONTINUE_NEEDED;
1302
1303 if (!negotiate_write_neg_token(output_buffer, &output_token))
1304 return SEC_E_INTERNAL_ERROR;
1305
1306 return status;
1307}
1308
1309static SECURITY_STATUS SEC_ENTRY negotiate_CompleteAuthToken(PCtxtHandle phContext,
1310 PSecBufferDesc pToken)
1311{
1312 NEGOTIATE_CONTEXT* context = nullptr;
1313 SECURITY_STATUS status = SEC_E_OK;
1314 context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1315
1316 if (!context)
1317 return SEC_E_INVALID_HANDLE;
1318
1319 WINPR_ASSERT(context->mech);
1320 WINPR_ASSERT(context->mech->pkg);
1321 WINPR_ASSERT(context->mech->pkg->table);
1322 if (context->mech->pkg->table->CompleteAuthToken)
1323 status = context->mech->pkg->table->CompleteAuthToken(&context->sub_context, pToken);
1324
1325 return status;
1326}
1327
1328static SECURITY_STATUS SEC_ENTRY negotiate_DeleteSecurityContext(PCtxtHandle phContext)
1329{
1330 SECURITY_STATUS status = SEC_E_OK;
1331 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1332 sspi_SecureHandleInvalidate(phContext);
1333 const SecPkg* pkg = nullptr;
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);
1341 pkg = context->mech->pkg;
1342
1343 if (pkg->table->DeleteSecurityContext)
1344 status = pkg->table->DeleteSecurityContext(&context->sub_context);
1345
1346 negotiate_ContextFree(context);
1347 return status;
1348}
1349
1350static SECURITY_STATUS SEC_ENTRY
1351negotiate_ImpersonateSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
1352{
1353 return SEC_E_OK;
1354}
1355
1356static SECURITY_STATUS SEC_ENTRY
1357negotiate_RevertSecurityContext(WINPR_ATTR_UNUSED PCtxtHandle phContext)
1358{
1359 return SEC_E_OK;
1360}
1361
1362static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesW(PCtxtHandle phContext,
1363 ULONG ulAttribute, void* pBuffer)
1364{
1365 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1366
1367 if (!context)
1368 return SEC_E_INVALID_HANDLE;
1369
1370 WINPR_ASSERT(context->mech);
1371 WINPR_ASSERT(context->mech->pkg);
1372 WINPR_ASSERT(context->mech->pkg->table_w);
1373 if (context->mech->pkg->table_w->QueryContextAttributesW)
1374 return context->mech->pkg->table_w->QueryContextAttributesW(&context->sub_context,
1375 ulAttribute, pBuffer);
1376
1377 return SEC_E_UNSUPPORTED_FUNCTION;
1378}
1379
1380static SECURITY_STATUS SEC_ENTRY negotiate_QueryContextAttributesA(PCtxtHandle phContext,
1381 ULONG ulAttribute, void* pBuffer)
1382{
1383 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1384
1385 if (!context)
1386 return SEC_E_INVALID_HANDLE;
1387
1388 WINPR_ASSERT(context->mech);
1389 WINPR_ASSERT(context->mech->pkg);
1390 WINPR_ASSERT(context->mech->pkg->table);
1391 if (context->mech->pkg->table->QueryContextAttributesA)
1392 return context->mech->pkg->table->QueryContextAttributesA(&context->sub_context,
1393 ulAttribute, pBuffer);
1394
1395 return SEC_E_UNSUPPORTED_FUNCTION;
1396}
1397
1398static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesW(PCtxtHandle phContext,
1399 ULONG ulAttribute, void* pBuffer,
1400 ULONG cbBuffer)
1401{
1402 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1403
1404 if (!context)
1405 return SEC_E_INVALID_HANDLE;
1406
1407 WINPR_ASSERT(context->mech);
1408 WINPR_ASSERT(context->mech->pkg);
1409 WINPR_ASSERT(context->mech->pkg->table_w);
1410 if (context->mech->pkg->table_w->SetContextAttributesW)
1411 return context->mech->pkg->table_w->SetContextAttributesW(&context->sub_context,
1412 ulAttribute, pBuffer, cbBuffer);
1413
1414 return SEC_E_UNSUPPORTED_FUNCTION;
1415}
1416
1417static SECURITY_STATUS SEC_ENTRY negotiate_SetContextAttributesA(PCtxtHandle phContext,
1418 ULONG ulAttribute, void* pBuffer,
1419 ULONG cbBuffer)
1420{
1421 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1422
1423 if (!context)
1424 return SEC_E_INVALID_HANDLE;
1425
1426 WINPR_ASSERT(context->mech);
1427 WINPR_ASSERT(context->mech->pkg);
1428 WINPR_ASSERT(context->mech->pkg->table);
1429 if (context->mech->pkg->table->SetContextAttributesA)
1430 return context->mech->pkg->table->SetContextAttributesA(&context->sub_context, ulAttribute,
1431 pBuffer, cbBuffer);
1432
1433 return SEC_E_UNSUPPORTED_FUNCTION;
1434}
1435
1436static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesW(PCredHandle phCredential,
1437 ULONG ulAttribute,
1438 void* pBuffer, ULONG cbBuffer)
1439{
1440 BOOL success = FALSE;
1441 MechCred* creds = sspi_SecureHandleGetLowerPointer(phCredential);
1442
1443 if (!creds)
1444 return SEC_E_INVALID_HANDLE;
1445
1446 for (size_t i = 0; i < MECH_COUNT; i++)
1447 {
1448 MechCred* cred = &creds[i];
1449
1450 WINPR_ASSERT(cred->mech);
1451 WINPR_ASSERT(cred->mech->pkg);
1452 WINPR_ASSERT(cred->mech->pkg->table);
1453 WINPR_ASSERT(cred->mech->pkg->table_w->SetCredentialsAttributesW);
1454 const SECURITY_STATUS secStatus = cred->mech->pkg->table_w->SetCredentialsAttributesW(
1455 &cred->cred, ulAttribute, pBuffer, cbBuffer);
1456
1457 if (secStatus == SEC_E_OK)
1458 success = TRUE;
1459 else
1460 WLog_WARN(TAG, "SetCredentialsAttributesW returned %s",
1461 GetSecurityStatusString(secStatus));
1462 }
1463
1464 // return success if at least one submodule accepts the credential attribute
1465 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1466}
1467
1468static SECURITY_STATUS SEC_ENTRY negotiate_SetCredentialsAttributesA(PCredHandle phCredential,
1469 ULONG ulAttribute,
1470 void* pBuffer, ULONG cbBuffer)
1471{
1472 BOOL success = FALSE;
1473
1474 MechCred* creds = sspi_SecureHandleGetLowerPointer(phCredential);
1475
1476 if (!creds)
1477 return SEC_E_INVALID_HANDLE;
1478
1479 for (size_t i = 0; i < MECH_COUNT; i++)
1480 {
1481 MechCred* cred = &creds[i];
1482
1483 if (!cred->valid)
1484 continue;
1485
1486 WINPR_ASSERT(cred->mech);
1487 WINPR_ASSERT(cred->mech->pkg);
1488 WINPR_ASSERT(cred->mech->pkg->table);
1489 WINPR_ASSERT(cred->mech->pkg->table->SetCredentialsAttributesA);
1490 const SECURITY_STATUS secStatus = cred->mech->pkg->table->SetCredentialsAttributesA(
1491 &cred->cred, ulAttribute, pBuffer, cbBuffer);
1492
1493 if (secStatus == SEC_E_OK)
1494 {
1495 success = TRUE;
1496 }
1497 else
1498 WLog_WARN(TAG, "SetCredentialsAttributesA returned %s",
1499 GetSecurityStatusString(secStatus));
1500 }
1501
1502 // return success if at least one submodule accepts the credential attribute
1503 return (success ? SEC_E_OK : SEC_E_UNSUPPORTED_FUNCTION);
1504}
1505
1506WINPR_ATTR_NODISCARD
1507static BOOL checkMechCredValid(void* pAuthData, MechCred* cred)
1508{
1509 WINPR_ASSERT(cred);
1510 WINPR_ASSERT(cred->mech);
1511 WINPR_ASSERT(cred->mech->pkg);
1512
1513 BOOL kerberos = FALSE;
1514 BOOL ntlm = FALSE;
1515 BOOL u2u = FALSE;
1516 if (!negotiate_get_config(pAuthData, &kerberos, &ntlm, &u2u))
1517 {
1518 WLog_DBG(TAG, "Failed to get negotiate configuration");
1519 return FALSE;
1520 }
1521
1522 if (!kerberos && sspi_gss_oid_compare(cred->mech->oid, &kerberos_OID))
1523 {
1524 WLog_DBG(TAG, "Kerberos disabled, skipping");
1525 return FALSE;
1526 }
1527
1528 if (!u2u && sspi_gss_oid_compare(cred->mech->oid, &kerberos_u2u_OID))
1529 {
1530 WLog_DBG(TAG, "Kerberos U2U disabled, skipping");
1531 return FALSE;
1532 }
1533
1534 if (!ntlm && _tcsncmp(cred->mech->pkg->name, NTLM_SSP_NAME, ARRAYSIZE(NTLM_SSP_NAME)) == 0)
1535 {
1536 WLog_DBG(TAG, "NTLM disabled, skipping");
1537 return FALSE;
1538 }
1539 return TRUE;
1540}
1541
1542static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleW(
1543 SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
1544 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
1545 PTimeStamp ptsExpiry)
1546{
1547 MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));
1548
1549 if (!creds)
1550 return SEC_E_INTERNAL_ERROR;
1551
1552 for (size_t i = 0; i < MECH_COUNT; i++)
1553 {
1554 MechCred* cred = &creds[i];
1555 const SecPkg* pkg = MechTable[i].pkg;
1556 cred->mech = &MechTable[i];
1557
1558 if (!checkMechCredValid(pAuthData, cred))
1559 continue;
1560
1561 WINPR_ASSERT(pkg->table_w);
1562 WINPR_ASSERT(pkg->table_w->AcquireCredentialsHandleW);
1563 const SECURITY_STATUS rc = pkg->table_w->AcquireCredentialsHandleW(
1564 pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
1565 pvGetKeyArgument, &cred->cred, ptsExpiry);
1566 if (rc != SEC_E_OK)
1567 {
1568 WLog_DBG(TAG, "AcquireCredentialsHandleW returned %s", GetSecurityStatusString(rc));
1569 continue;
1570 }
1571
1572 cred->valid = TRUE;
1573 }
1574
1575 sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);
1576 sspi_SecureHandleSetPackageId(phCredential, SSPI_PACKAGE_NEGOTIATE);
1577 return SEC_E_OK;
1578}
1579
1580static SECURITY_STATUS SEC_ENTRY negotiate_AcquireCredentialsHandleA(
1581 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
1582 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
1583 PTimeStamp ptsExpiry)
1584{
1585 MechCred* creds = calloc(MECH_COUNT, sizeof(MechCred));
1586
1587 if (!creds)
1588 return SEC_E_INTERNAL_ERROR;
1589
1590 for (size_t i = 0; i < MECH_COUNT; i++)
1591 {
1592 const SecPkg* pkg = MechTable[i].pkg;
1593 MechCred* cred = &creds[i];
1594
1595 cred->mech = &MechTable[i];
1596
1597 if (!checkMechCredValid(pAuthData, cred))
1598 continue;
1599
1600 WINPR_ASSERT(pkg->table);
1601 WINPR_ASSERT(pkg->table->AcquireCredentialsHandleA);
1602 const SECURITY_STATUS rc = pkg->table->AcquireCredentialsHandleA(
1603 pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
1604 pvGetKeyArgument, &cred->cred, ptsExpiry);
1605 if (rc != SEC_E_OK)
1606 {
1607 WLog_DBG(TAG, "AcquireCredentialsHandleA returned %s", GetSecurityStatusString(rc));
1608 continue;
1609 }
1610
1611 cred->valid = TRUE;
1612 }
1613
1614 sspi_SecureHandleSetLowerPointer(phCredential, (void*)creds);
1615 sspi_SecureHandleSetPackageId(phCredential, SSPI_PACKAGE_NEGOTIATE);
1616 return SEC_E_OK;
1617}
1618
1619static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesW(
1620 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1621 WINPR_ATTR_UNUSED void* pBuffer)
1622{
1623 WLog_ERR(TAG, "TODO: Implement");
1624 return SEC_E_UNSUPPORTED_FUNCTION;
1625}
1626
1627static SECURITY_STATUS SEC_ENTRY negotiate_QueryCredentialsAttributesA(
1628 WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1629 WINPR_ATTR_UNUSED void* pBuffer)
1630{
1631 WLog_ERR(TAG, "TODO: Implement");
1632 return SEC_E_UNSUPPORTED_FUNCTION;
1633}
1634
1635static SECURITY_STATUS SEC_ENTRY negotiate_FreeCredentialsHandle(PCredHandle phCredential)
1636{
1637 MechCred* creds = sspi_SecureHandleGetLowerPointer(phCredential);
1638 sspi_SecureHandleInvalidate(phCredential);
1639 if (!creds)
1640 return SEC_E_INVALID_HANDLE;
1641
1642 for (size_t i = 0; i < MECH_COUNT; i++)
1643 {
1644 MechCred* cred = &creds[i];
1645
1646 WINPR_ASSERT(cred->mech);
1647 WINPR_ASSERT(cred->mech->pkg);
1648 WINPR_ASSERT(cred->mech->pkg->table);
1649 WINPR_ASSERT(cred->mech->pkg->table->FreeCredentialsHandle);
1650 const SECURITY_STATUS rc = cred->mech->pkg->table->FreeCredentialsHandle(&cred->cred);
1651 if (rc != SEC_E_OK)
1652 WLog_DBG(TAG, "FreeCredentialsHandle returned %s", GetSecurityStatusString(rc));
1653 }
1654 free(creds);
1655
1656 return SEC_E_OK;
1657}
1658
1659static SECURITY_STATUS SEC_ENTRY negotiate_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
1660 PSecBufferDesc pMessage,
1661 ULONG MessageSeqNo)
1662{
1663 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1664
1665 if (!context)
1666 return SEC_E_INVALID_HANDLE;
1667
1668 if (context->mic)
1669 MessageSeqNo++;
1670
1671 WINPR_ASSERT(context->mech);
1672 WINPR_ASSERT(context->mech->pkg);
1673 WINPR_ASSERT(context->mech->pkg->table);
1674 if (context->mech->pkg->table->EncryptMessage)
1675 return context->mech->pkg->table->EncryptMessage(&context->sub_context, fQOP, pMessage,
1676 MessageSeqNo);
1677
1678 return SEC_E_UNSUPPORTED_FUNCTION;
1679}
1680
1681static SECURITY_STATUS SEC_ENTRY negotiate_DecryptMessage(PCtxtHandle phContext,
1682 PSecBufferDesc pMessage,
1683 ULONG MessageSeqNo, ULONG* pfQOP)
1684{
1685 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1686
1687 if (!context)
1688 return SEC_E_INVALID_HANDLE;
1689
1690 if (context->mic)
1691 MessageSeqNo++;
1692
1693 WINPR_ASSERT(context->mech);
1694 WINPR_ASSERT(context->mech->pkg);
1695 WINPR_ASSERT(context->mech->pkg->table);
1696 if (context->mech->pkg->table->DecryptMessage)
1697 return context->mech->pkg->table->DecryptMessage(&context->sub_context, pMessage,
1698 MessageSeqNo, pfQOP);
1699
1700 return SEC_E_UNSUPPORTED_FUNCTION;
1701}
1702
1703static SECURITY_STATUS SEC_ENTRY negotiate_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
1704 PSecBufferDesc pMessage,
1705 ULONG MessageSeqNo)
1706{
1707 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1708
1709 if (!context)
1710 return SEC_E_INVALID_HANDLE;
1711
1712 if (context->mic)
1713 MessageSeqNo++;
1714
1715 WINPR_ASSERT(context->mech);
1716 WINPR_ASSERT(context->mech->pkg);
1717 WINPR_ASSERT(context->mech->pkg->table);
1718 if (context->mech->pkg->table->MakeSignature)
1719 return context->mech->pkg->table->MakeSignature(&context->sub_context, fQOP, pMessage,
1720 MessageSeqNo);
1721
1722 return SEC_E_UNSUPPORTED_FUNCTION;
1723}
1724
1725static SECURITY_STATUS SEC_ENTRY negotiate_VerifySignature(PCtxtHandle phContext,
1726 PSecBufferDesc pMessage,
1727 ULONG MessageSeqNo, ULONG* pfQOP)
1728{
1729 NEGOTIATE_CONTEXT* context = (NEGOTIATE_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
1730
1731 if (!context)
1732 return SEC_E_INVALID_HANDLE;
1733
1734 if (context->mic)
1735 MessageSeqNo++;
1736
1737 WINPR_ASSERT(context->mech);
1738 WINPR_ASSERT(context->mech->pkg);
1739 WINPR_ASSERT(context->mech->pkg->table);
1740 if (context->mech->pkg->table->VerifySignature)
1741 return context->mech->pkg->table->VerifySignature(&context->sub_context, pMessage,
1742 MessageSeqNo, pfQOP);
1743
1744 return SEC_E_UNSUPPORTED_FUNCTION;
1745}
1746
1747const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA = {
1748 3, /* dwVersion */
1749 nullptr, /* EnumerateSecurityPackages */
1750 negotiate_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
1751 negotiate_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
1752 negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */
1753 nullptr, /* Reserved2 */
1754 negotiate_InitializeSecurityContextA, /* InitializeSecurityContext */
1755 negotiate_AcceptSecurityContext, /* AcceptSecurityContext */
1756 negotiate_CompleteAuthToken, /* CompleteAuthToken */
1757 negotiate_DeleteSecurityContext, /* DeleteSecurityContext */
1758 nullptr, /* ApplyControlToken */
1759 negotiate_QueryContextAttributesA, /* 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_SetContextAttributesA, /* SetContextAttributes */
1776 negotiate_SetCredentialsAttributesA, /* SetCredentialsAttributes */
1777};
1778
1779const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW = {
1780 3, /* dwVersion */
1781 nullptr, /* EnumerateSecurityPackages */
1782 negotiate_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
1783 negotiate_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
1784 negotiate_FreeCredentialsHandle, /* FreeCredentialsHandle */
1785 nullptr, /* Reserved2 */
1786 negotiate_InitializeSecurityContextW, /* InitializeSecurityContext */
1787 negotiate_AcceptSecurityContext, /* AcceptSecurityContext */
1788 negotiate_CompleteAuthToken, /* CompleteAuthToken */
1789 negotiate_DeleteSecurityContext, /* DeleteSecurityContext */
1790 nullptr, /* ApplyControlToken */
1791 negotiate_QueryContextAttributesW, /* QueryContextAttributes */
1792 negotiate_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
1793 negotiate_RevertSecurityContext, /* RevertSecurityContext */
1794 negotiate_MakeSignature, /* MakeSignature */
1795 negotiate_VerifySignature, /* VerifySignature */
1796 nullptr, /* FreeContextBuffer */
1797 nullptr, /* QuerySecurityPackageInfo */
1798 nullptr, /* Reserved3 */
1799 nullptr, /* Reserved4 */
1800 nullptr, /* ExportSecurityContext */
1801 nullptr, /* ImportSecurityContext */
1802 nullptr, /* AddCredentials */
1803 nullptr, /* Reserved8 */
1804 nullptr, /* QuerySecurityContextToken */
1805 negotiate_EncryptMessage, /* EncryptMessage */
1806 negotiate_DecryptMessage, /* DecryptMessage */
1807 negotiate_SetContextAttributesW, /* SetContextAttributes */
1808 negotiate_SetCredentialsAttributesW, /* SetCredentialsAttributes */
1809};
1810
1811BOOL NEGOTIATE_init(void)
1812{
1813 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Name, NEGOTIATE_SecPkgInfoW_NameBuffer,
1814 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_NameBuffer));
1815 InitializeConstWCharFromUtf8(NEGOTIATE_SecPkgInfoA.Comment, NEGOTIATE_SecPkgInfoW_CommentBuffer,
1816 ARRAYSIZE(NEGOTIATE_SecPkgInfoW_CommentBuffer));
1817
1818 return TRUE;
1819}
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103