Coverage Report

Created: 2026-10-15 06:26

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