Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * NTLM Security Package (Message)
4
 *
5
 * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include "ntlm.h"
23
#include "../sspi.h"
24
25
#include <winpr/crt.h>
26
#include <winpr/assert.h>
27
#include <winpr/print.h>
28
#include <winpr/stream.h>
29
#include <winpr/sysinfo.h>
30
31
#include "ntlm_compute.h"
32
33
#include "ntlm_message.h"
34
35
#include "../../log.h"
36
0
#define TAG WINPR_TAG("sspi.NTLM")
37
38
#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what)                                    \
39
0
  Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
40
0
                                       __func__, __FILE__, (size_t)__LINE__)
41
42
static const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' };
43
44
static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields);
45
46
const char* ntlm_get_negotiate_string(UINT32 flag)
47
0
{
48
0
  if (flag & NTLMSSP_NEGOTIATE_56)
49
0
    return "NTLMSSP_NEGOTIATE_56";
50
0
  if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH)
51
0
    return "NTLMSSP_NEGOTIATE_KEY_EXCH";
52
0
  if (flag & NTLMSSP_NEGOTIATE_128)
53
0
    return "NTLMSSP_NEGOTIATE_128";
54
0
  if (flag & NTLMSSP_RESERVED1)
55
0
    return "NTLMSSP_RESERVED1";
56
0
  if (flag & NTLMSSP_RESERVED2)
57
0
    return "NTLMSSP_RESERVED2";
58
0
  if (flag & NTLMSSP_RESERVED3)
59
0
    return "NTLMSSP_RESERVED3";
60
0
  if (flag & NTLMSSP_NEGOTIATE_VERSION)
61
0
    return "NTLMSSP_NEGOTIATE_VERSION";
62
0
  if (flag & NTLMSSP_RESERVED4)
63
0
    return "NTLMSSP_RESERVED4";
64
0
  if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO)
65
0
    return "NTLMSSP_NEGOTIATE_TARGET_INFO";
66
0
  if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY)
67
0
    return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY";
68
0
  if (flag & NTLMSSP_RESERVED5)
69
0
    return "NTLMSSP_RESERVED5";
70
0
  if (flag & NTLMSSP_NEGOTIATE_IDENTIFY)
71
0
    return "NTLMSSP_NEGOTIATE_IDENTIFY";
72
0
  if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY)
73
0
    return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY";
74
0
  if (flag & NTLMSSP_RESERVED6)
75
0
    return "NTLMSSP_RESERVED6";
76
0
  if (flag & NTLMSSP_TARGET_TYPE_SERVER)
77
0
    return "NTLMSSP_TARGET_TYPE_SERVER";
78
0
  if (flag & NTLMSSP_TARGET_TYPE_DOMAIN)
79
0
    return "NTLMSSP_TARGET_TYPE_DOMAIN";
80
0
  if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
81
0
    return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN";
82
0
  if (flag & NTLMSSP_RESERVED7)
83
0
    return "NTLMSSP_RESERVED7";
84
0
  if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
85
0
    return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED";
86
0
  if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
87
0
    return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED";
88
0
  if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS)
89
0
    return "NTLMSSP_NEGOTIATE_ANONYMOUS";
90
0
  if (flag & NTLMSSP_RESERVED8)
91
0
    return "NTLMSSP_RESERVED8";
92
0
  if (flag & NTLMSSP_NEGOTIATE_NTLM)
93
0
    return "NTLMSSP_NEGOTIATE_NTLM";
94
0
  if (flag & NTLMSSP_RESERVED9)
95
0
    return "NTLMSSP_RESERVED9";
96
0
  if (flag & NTLMSSP_NEGOTIATE_LM_KEY)
97
0
    return "NTLMSSP_NEGOTIATE_LM_KEY";
98
0
  if (flag & NTLMSSP_NEGOTIATE_DATAGRAM)
99
0
    return "NTLMSSP_NEGOTIATE_DATAGRAM";
100
0
  if (flag & NTLMSSP_NEGOTIATE_SEAL)
101
0
    return "NTLMSSP_NEGOTIATE_SEAL";
102
0
  if (flag & NTLMSSP_NEGOTIATE_SIGN)
103
0
    return "NTLMSSP_NEGOTIATE_SIGN";
104
0
  if (flag & NTLMSSP_RESERVED10)
105
0
    return "NTLMSSP_RESERVED10";
106
0
  if (flag & NTLMSSP_REQUEST_TARGET)
107
0
    return "NTLMSSP_REQUEST_TARGET";
108
0
  if (flag & NTLMSSP_NEGOTIATE_OEM)
109
0
    return "NTLMSSP_NEGOTIATE_OEM";
110
0
  if (flag & NTLMSSP_NEGOTIATE_UNICODE)
111
0
    return "NTLMSSP_NEGOTIATE_UNICODE";
112
0
  return "NTLMSSP_NEGOTIATE_UNKNOWN";
113
0
}
114
115
#if defined(WITH_DEBUG_NTLM)
116
static void ntlm_print_message_fields(const NTLM_MESSAGE_FIELDS* fields, const char* name)
117
{
118
  WINPR_ASSERT(fields);
119
  WINPR_ASSERT(name);
120
121
  WLog_VRB(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name,
122
           fields->Len, fields->MaxLen, fields->BufferOffset);
123
124
  if (fields->Len > 0)
125
    winpr_HexDump(TAG, WLOG_TRACE, fields->Buffer, fields->Len);
126
}
127
128
static void ntlm_print_negotiate_flags(UINT32 flags)
129
{
130
  WLog_VRB(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags);
131
132
  for (int i = 31; i >= 0; i--)
133
  {
134
    if ((flags >> i) & 1)
135
    {
136
      const char* str = ntlm_get_negotiate_string(1 << i);
137
      WLog_VRB(TAG, "\t%s (%d),", str, (31 - i));
138
    }
139
  }
140
}
141
142
static void ntlm_print_negotiate_message(const SecBuffer* NegotiateMessage,
143
                                         const NTLM_NEGOTIATE_MESSAGE* message)
144
{
145
  WINPR_ASSERT(NegotiateMessage);
146
  WINPR_ASSERT(message);
147
148
  WLog_VRB(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", NegotiateMessage->cbBuffer);
149
  winpr_HexDump(TAG, WLOG_TRACE, NegotiateMessage->pvBuffer, NegotiateMessage->cbBuffer);
150
  ntlm_print_negotiate_flags(message->NegotiateFlags);
151
152
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
153
    ntlm_print_version_info(&(message->Version));
154
}
155
156
static void ntlm_print_challenge_message(const SecBuffer* ChallengeMessage,
157
                                         const NTLM_CHALLENGE_MESSAGE* message,
158
                                         const SecBuffer* ChallengeTargetInfo)
159
{
160
  WINPR_ASSERT(ChallengeMessage);
161
  WINPR_ASSERT(message);
162
163
  WLog_VRB(TAG, "CHALLENGE_MESSAGE (length = %" PRIu32 ")", ChallengeMessage->cbBuffer);
164
  winpr_HexDump(TAG, WLOG_TRACE, ChallengeMessage->pvBuffer, ChallengeMessage->cbBuffer);
165
  ntlm_print_negotiate_flags(message->NegotiateFlags);
166
167
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
168
    ntlm_print_version_info(&(message->Version));
169
170
  ntlm_print_message_fields(&(message->TargetName), "TargetName");
171
  ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");
172
173
  if (ChallengeTargetInfo && (ChallengeTargetInfo->cbBuffer > 0))
174
  {
175
    WLog_VRB(TAG, "ChallengeTargetInfo (%" PRIu32 "):", ChallengeTargetInfo->cbBuffer);
176
    ntlm_print_av_pair_list(ChallengeTargetInfo->pvBuffer, ChallengeTargetInfo->cbBuffer);
177
  }
178
}
179
180
static void ntlm_print_authenticate_message(const SecBuffer* AuthenticateMessage,
181
                                            const NTLM_AUTHENTICATE_MESSAGE* message, UINT32 flags,
182
                                            const SecBuffer* AuthenticateTargetInfo)
183
{
184
  WINPR_ASSERT(AuthenticateMessage);
185
  WINPR_ASSERT(message);
186
187
  WLog_VRB(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", AuthenticateMessage->cbBuffer);
188
  winpr_HexDump(TAG, WLOG_TRACE, AuthenticateMessage->pvBuffer, AuthenticateMessage->cbBuffer);
189
  ntlm_print_negotiate_flags(message->NegotiateFlags);
190
191
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
192
    ntlm_print_version_info(&(message->Version));
193
194
  if (AuthenticateTargetInfo && (AuthenticateTargetInfo->cbBuffer > 0))
195
  {
196
    WLog_VRB(TAG, "AuthenticateTargetInfo (%" PRIu32 "):", AuthenticateTargetInfo->cbBuffer);
197
    ntlm_print_av_pair_list(AuthenticateTargetInfo->pvBuffer, AuthenticateTargetInfo->cbBuffer);
198
  }
199
200
  ntlm_print_message_fields(&(message->DomainName), "DomainName");
201
  ntlm_print_message_fields(&(message->UserName), "UserName");
202
  ntlm_print_message_fields(&(message->Workstation), "Workstation");
203
  ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse");
204
  ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse");
205
  ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey");
206
207
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
208
  {
209
    WLog_VRB(TAG, "MessageIntegrityCheck (length = 16)");
210
    winpr_HexDump(TAG, WLOG_TRACE, message->MessageIntegrityCheck,
211
                  sizeof(message->MessageIntegrityCheck));
212
  }
213
}
214
215
static void ntlm_print_authentication_complete(const NTLM_CONTEXT* context)
216
{
217
  WINPR_ASSERT(context);
218
219
  WLog_VRB(TAG, "ClientChallenge");
220
  winpr_HexDump(TAG, WLOG_TRACE, context->ClientChallenge, 8);
221
  WLog_VRB(TAG, "ServerChallenge");
222
  winpr_HexDump(TAG, WLOG_TRACE, context->ServerChallenge, 8);
223
  WLog_VRB(TAG, "SessionBaseKey");
224
  winpr_HexDump(TAG, WLOG_TRACE, context->SessionBaseKey, 16);
225
  WLog_VRB(TAG, "KeyExchangeKey");
226
  winpr_HexDump(TAG, WLOG_TRACE, context->KeyExchangeKey, 16);
227
  WLog_VRB(TAG, "ExportedSessionKey");
228
  winpr_HexDump(TAG, WLOG_TRACE, context->ExportedSessionKey, 16);
229
  WLog_VRB(TAG, "RandomSessionKey");
230
  winpr_HexDump(TAG, WLOG_TRACE, context->RandomSessionKey, 16);
231
  WLog_VRB(TAG, "ClientSigningKey");
232
  winpr_HexDump(TAG, WLOG_TRACE, context->ClientSigningKey, 16);
233
  WLog_VRB(TAG, "ClientSealingKey");
234
  winpr_HexDump(TAG, WLOG_TRACE, context->ClientSealingKey, 16);
235
  WLog_VRB(TAG, "ServerSigningKey");
236
  winpr_HexDump(TAG, WLOG_TRACE, context->ServerSigningKey, 16);
237
  WLog_VRB(TAG, "ServerSealingKey");
238
  winpr_HexDump(TAG, WLOG_TRACE, context->ServerSealingKey, 16);
239
  WLog_VRB(TAG, "Timestamp");
240
  winpr_HexDump(TAG, WLOG_TRACE, context->Timestamp, 8);
241
}
242
#endif
243
244
static BOOL ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header, UINT32 expected)
245
0
{
246
0
  WINPR_ASSERT(s);
247
0
  WINPR_ASSERT(header);
248
249
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
250
0
    return FALSE;
251
252
0
  Stream_Read(s, header->Signature, 8);
253
0
  Stream_Read_UINT32(s, header->MessageType);
254
255
0
  if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
256
0
  {
257
0
    char Signature[sizeof(header->Signature) * 3 + 1] = { 0 };
258
0
    winpr_BinToHexStringBuffer(header->Signature, sizeof(header->Signature), Signature,
259
0
                               sizeof(Signature), TRUE);
260
261
0
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s", Signature,
262
0
             NTLM_SIGNATURE);
263
0
    return FALSE;
264
0
  }
265
266
0
  if (header->MessageType != expected)
267
0
  {
268
0
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message type, got %s, expected %s",
269
0
             ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected));
270
0
    return FALSE;
271
0
  }
272
273
0
  return TRUE;
274
0
}
275
276
static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header)
277
0
{
278
0
  WINPR_ASSERT(s);
279
0
  WINPR_ASSERT(header);
280
281
0
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, sizeof(NTLM_SIGNATURE) + 4ull,
282
0
                                        "NTLM_MESSAGE_HEADER::header"))
283
0
    return FALSE;
284
285
0
  Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE));
286
0
  Stream_Write_UINT32(s, header->MessageType);
287
288
0
  return TRUE;
289
0
}
290
291
static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType)
292
0
{
293
0
  WINPR_ASSERT(header);
294
295
0
  CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
296
0
  header->MessageType = MessageType;
297
0
  return TRUE;
298
0
}
299
300
static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
301
0
{
302
0
  WINPR_ASSERT(s);
303
0
  WINPR_ASSERT(fields);
304
305
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
306
0
    return FALSE;
307
308
0
  ntlm_free_message_fields_buffer(fields);
309
310
0
  Stream_Read_UINT16(s, fields->Len);          /* Len (2 bytes) */
311
0
  Stream_Read_UINT16(s, fields->MaxLen);       /* MaxLen (2 bytes) */
312
0
  Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
313
0
  return TRUE;
314
0
}
315
316
static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
317
0
{
318
0
  UINT16 MaxLen = 0;
319
0
  WINPR_ASSERT(s);
320
0
  WINPR_ASSERT(fields);
321
322
0
  MaxLen = fields->MaxLen;
323
0
  if (fields->MaxLen < 1)
324
0
    MaxLen = fields->Len;
325
326
0
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header"))
327
0
    return FALSE;
328
329
0
  Stream_Write_UINT16(s, fields->Len);          /* Len (2 bytes) */
330
0
  Stream_Write_UINT16(s, MaxLen);               /* MaxLen (2 bytes) */
331
0
  Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
332
0
  return TRUE;
333
0
}
334
335
static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
336
0
{
337
0
  WINPR_ASSERT(s);
338
0
  WINPR_ASSERT(fields);
339
340
0
  if (fields->Len > 0)
341
0
  {
342
0
    const UINT32 offset = fields->BufferOffset + fields->Len;
343
344
0
    if (fields->BufferOffset > UINT32_MAX - fields->Len)
345
0
    {
346
0
      WLog_ERR(TAG,
347
0
               "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32
348
0
               " too large, maximum allowed is %" PRIu32,
349
0
               fields->BufferOffset, UINT32_MAX - fields->Len);
350
0
      return FALSE;
351
0
    }
352
353
0
    if (offset > Stream_Length(s))
354
0
    {
355
0
      WLog_ERR(TAG,
356
0
               "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIu32 " beyond received data %" PRIuz,
357
0
               offset, Stream_Length(s));
358
0
      return FALSE;
359
0
    }
360
361
0
    fields->Buffer = (PBYTE)malloc(fields->Len);
362
363
0
    if (!fields->Buffer)
364
0
    {
365
0
      WLog_ERR(TAG, "NTLM_MESSAGE_FIELDS::Buffer allocation of %" PRIu16 "bytes failed",
366
0
               fields->Len);
367
0
      return FALSE;
368
0
    }
369
370
0
    Stream_SetPosition(s, fields->BufferOffset);
371
0
    Stream_Read(s, fields->Buffer, fields->Len);
372
0
  }
373
374
0
  return TRUE;
375
0
}
376
377
static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
378
0
{
379
0
  WINPR_ASSERT(s);
380
0
  WINPR_ASSERT(fields);
381
382
0
  if (fields->Len > 0)
383
0
  {
384
0
    Stream_SetPosition(s, fields->BufferOffset);
385
0
    if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len"))
386
0
      return FALSE;
387
388
0
    Stream_Write(s, fields->Buffer, fields->Len);
389
0
  }
390
0
  return TRUE;
391
0
}
392
393
void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields)
394
0
{
395
0
  if (fields)
396
0
  {
397
0
    if (fields->Buffer)
398
0
    {
399
0
      free(fields->Buffer);
400
0
      fields->Len = 0;
401
0
      fields->MaxLen = 0;
402
0
      fields->Buffer = NULL;
403
0
      fields->BufferOffset = 0;
404
0
    }
405
0
  }
406
0
}
407
408
static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name)
409
0
{
410
0
  UINT32 NegotiateFlags = 0;
411
0
  char buffer[1024] = { 0 };
412
0
  WINPR_ASSERT(s);
413
0
  WINPR_ASSERT(flags);
414
0
  WINPR_ASSERT(name);
415
416
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
417
0
    return FALSE;
418
419
0
  Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */
420
421
0
  if ((NegotiateFlags & required) != required)
422
0
  {
423
0
    WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required",
424
0
             name, NegotiateFlags, required);
425
0
    return FALSE;
426
0
  }
427
428
0
  WLog_DBG(TAG, "Read flags %s",
429
0
           ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags));
430
0
  *flags = NegotiateFlags;
431
0
  return TRUE;
432
0
}
433
434
static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name)
435
0
{
436
0
  char buffer[1024] = { 0 };
437
0
  WINPR_ASSERT(s);
438
0
  WINPR_ASSERT(name);
439
440
0
  if (!Stream_CheckAndLogRequiredCapacityEx(TAG, WLOG_WARN, s, 4ull, 1ull,
441
0
                                            "%s(%s:%" PRIuz ") %s::NegotiateFlags", __func__,
442
0
                                            __FILE__, (size_t)__LINE__, name))
443
0
    return FALSE;
444
445
0
  WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags));
446
0
  Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */
447
0
  return TRUE;
448
0
}
449
450
static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size,
451
                                              WINPR_ATTR_UNUSED const char* name)
452
0
{
453
0
  WINPR_ASSERT(s);
454
0
  WINPR_ASSERT(offset);
455
0
  WINPR_ASSERT(data);
456
0
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
457
0
  WINPR_ASSERT(name);
458
459
0
  *offset = Stream_GetPosition(s);
460
461
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
462
0
    return FALSE;
463
464
0
  Stream_Read(s, data, size);
465
0
  return TRUE;
466
0
}
467
468
static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data,
469
                                               size_t size, WINPR_ATTR_UNUSED const char* name)
470
0
{
471
0
  size_t pos = 0;
472
473
0
  WINPR_ASSERT(s);
474
0
  WINPR_ASSERT(data);
475
0
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
476
0
  WINPR_ASSERT(name);
477
478
0
  pos = Stream_GetPosition(s);
479
480
0
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset"))
481
0
    return FALSE;
482
483
0
  Stream_SetPosition(s, offset);
484
0
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size"))
485
0
    return FALSE;
486
487
0
  Stream_Write(s, data, size);
488
0
  Stream_SetPosition(s, pos);
489
0
  return TRUE;
490
0
}
491
492
SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
493
0
{
494
0
  wStream sbuffer;
495
0
  wStream* s = NULL;
496
0
  size_t length = 0;
497
0
  const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
498
0
  NTLM_NEGOTIATE_MESSAGE* message = NULL;
499
500
0
  WINPR_ASSERT(context);
501
0
  WINPR_ASSERT(buffer);
502
503
0
  message = &context->NEGOTIATE_MESSAGE;
504
0
  WINPR_ASSERT(message);
505
506
0
  *message = empty;
507
508
0
  s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
509
510
0
  if (!s)
511
0
    return SEC_E_INTERNAL_ERROR;
512
513
0
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE))
514
0
    return SEC_E_INVALID_TOKEN;
515
516
0
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags,
517
0
                                 NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
518
0
                                     NTLMSSP_NEGOTIATE_UNICODE,
519
0
                                 "NTLM_NEGOTIATE_MESSAGE"))
520
0
    return SEC_E_INVALID_TOKEN;
521
522
0
  context->NegotiateFlags = message->NegotiateFlags;
523
524
  /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
525
  // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
526
0
  {
527
0
    if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
528
0
      return SEC_E_INVALID_TOKEN;
529
0
  }
530
531
  /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
532
  // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
533
0
  {
534
0
    if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
535
0
      return SEC_E_INVALID_TOKEN;
536
0
  }
537
538
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
539
0
  {
540
0
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
541
0
      return SEC_E_INVALID_TOKEN;
542
0
  }
543
544
0
  if (!ntlm_read_message_fields_buffer(s, &message->DomainName))
545
0
    return SEC_E_INVALID_TOKEN;
546
547
0
  if (!ntlm_read_message_fields_buffer(s, &message->Workstation))
548
0
    return SEC_E_INVALID_TOKEN;
549
550
0
  length = Stream_GetPosition(s);
551
0
  WINPR_ASSERT(length <= UINT32_MAX);
552
0
  buffer->cbBuffer = (ULONG)length;
553
554
0
  if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
555
0
    return SEC_E_INTERNAL_ERROR;
556
557
0
  CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
558
0
  context->NegotiateMessage.BufferType = buffer->BufferType;
559
#if defined(WITH_DEBUG_NTLM)
560
  ntlm_print_negotiate_message(&context->NegotiateMessage, message);
561
#endif
562
0
  ntlm_change_state(context, NTLM_STATE_CHALLENGE);
563
0
  return SEC_I_CONTINUE_NEEDED;
564
0
}
565
566
SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
567
0
{
568
0
  wStream sbuffer;
569
0
  wStream* s = NULL;
570
0
  size_t length = 0;
571
0
  const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
572
0
  NTLM_NEGOTIATE_MESSAGE* message = NULL;
573
574
0
  WINPR_ASSERT(context);
575
0
  WINPR_ASSERT(buffer);
576
577
0
  message = &context->NEGOTIATE_MESSAGE;
578
0
  WINPR_ASSERT(message);
579
580
0
  *message = empty;
581
582
0
  s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
583
584
0
  if (!s)
585
0
    return SEC_E_INTERNAL_ERROR;
586
587
0
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE))
588
0
    return SEC_E_INTERNAL_ERROR;
589
590
0
  if (context->NTLMv2)
591
0
  {
592
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
593
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
594
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
595
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
596
0
  }
597
598
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
599
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
600
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
601
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
602
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
603
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
604
0
  message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
605
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
606
607
0
  if (context->confidentiality)
608
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
609
610
0
  if (context->SendVersionInfo)
611
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
612
613
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
614
0
    ntlm_get_version_info(&(message->Version));
615
616
0
  context->NegotiateFlags = message->NegotiateFlags;
617
  /* Message Header (12 bytes) */
618
0
  if (!ntlm_write_message_header(s, &message->header))
619
0
    return SEC_E_INTERNAL_ERROR;
620
621
0
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_NEGOTIATE_MESSAGE"))
622
0
    return SEC_E_INTERNAL_ERROR;
623
624
  /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
625
  /* DomainNameFields (8 bytes) */
626
0
  if (!ntlm_write_message_fields(s, &(message->DomainName)))
627
0
    return SEC_E_INTERNAL_ERROR;
628
629
  /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
630
  /* WorkstationFields (8 bytes) */
631
0
  if (!ntlm_write_message_fields(s, &(message->Workstation)))
632
0
    return SEC_E_INTERNAL_ERROR;
633
634
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
635
0
  {
636
0
    if (!ntlm_write_version_info(s, &(message->Version)))
637
0
      return SEC_E_INTERNAL_ERROR;
638
0
  }
639
640
0
  length = Stream_GetPosition(s);
641
0
  WINPR_ASSERT(length <= UINT32_MAX);
642
0
  buffer->cbBuffer = (ULONG)length;
643
644
0
  if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
645
0
    return SEC_E_INTERNAL_ERROR;
646
647
0
  CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
648
0
  context->NegotiateMessage.BufferType = buffer->BufferType;
649
#if defined(WITH_DEBUG_NTLM)
650
  ntlm_print_negotiate_message(&context->NegotiateMessage, message);
651
#endif
652
0
  ntlm_change_state(context, NTLM_STATE_CHALLENGE);
653
0
  return SEC_I_CONTINUE_NEEDED;
654
0
}
655
656
SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
657
0
{
658
0
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
659
0
  wStream sbuffer;
660
0
  wStream* s = NULL;
661
0
  size_t length = 0;
662
0
  size_t StartOffset = 0;
663
0
  size_t PayloadOffset = 0;
664
0
  NTLM_AV_PAIR* AvTimestamp = NULL;
665
0
  const NTLM_CHALLENGE_MESSAGE empty = { 0 };
666
0
  NTLM_CHALLENGE_MESSAGE* message = NULL;
667
668
0
  if (!context || !buffer)
669
0
    return SEC_E_INTERNAL_ERROR;
670
671
0
  ntlm_generate_client_challenge(context);
672
0
  message = &context->CHALLENGE_MESSAGE;
673
0
  WINPR_ASSERT(message);
674
675
0
  *message = empty;
676
677
0
  s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
678
679
0
  if (!s)
680
0
    return SEC_E_INTERNAL_ERROR;
681
682
0
  StartOffset = Stream_GetPosition(s);
683
684
0
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE))
685
0
    goto fail;
686
687
0
  if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */
688
0
    goto fail;
689
690
0
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE"))
691
0
    goto fail;
692
693
0
  context->NegotiateFlags = message->NegotiateFlags;
694
695
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
696
0
    goto fail;
697
698
0
  Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
699
0
  CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
700
0
  Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
701
702
0
  if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */
703
0
    goto fail;
704
705
0
  if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
706
0
  {
707
0
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
708
0
      goto fail;
709
0
  }
710
711
  /* Payload (variable) */
712
0
  PayloadOffset = Stream_GetPosition(s);
713
714
0
  status = SEC_E_INTERNAL_ERROR;
715
0
  if (message->TargetName.Len > 0)
716
0
  {
717
0
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetName)))
718
0
      goto fail;
719
0
  }
720
721
0
  if (message->TargetInfo.Len > 0)
722
0
  {
723
0
    size_t cbAvTimestamp = 0;
724
725
0
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo)))
726
0
      goto fail;
727
728
0
    context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
729
0
    context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
730
0
    AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer,
731
0
                                   message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp);
732
733
0
    if (AvTimestamp)
734
0
    {
735
0
      PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp);
736
737
0
      if (!ptr)
738
0
        goto fail;
739
740
0
      if (context->NTLMv2)
741
0
        context->UseMIC = TRUE;
742
743
0
      CopyMemory(context->ChallengeTimestamp, ptr, 8);
744
0
    }
745
0
  }
746
747
0
  length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;
748
0
  if (length > buffer->cbBuffer)
749
0
    goto fail;
750
751
0
  if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
752
0
    goto fail;
753
754
0
  if (context->ChallengeMessage.pvBuffer)
755
0
    CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s) + StartOffset, length);
756
#if defined(WITH_DEBUG_NTLM)
757
  ntlm_print_challenge_message(&context->ChallengeMessage, message, NULL);
758
#endif
759
  /* AV_PAIRs */
760
761
0
  if (context->NTLMv2)
762
0
  {
763
0
    if (!ntlm_construct_authenticate_target_info(context))
764
0
      goto fail;
765
766
0
    sspi_SecBufferFree(&context->ChallengeTargetInfo);
767
0
    context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer;
768
0
    context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer;
769
0
  }
770
771
0
  ntlm_generate_timestamp(context); /* Timestamp */
772
773
0
  const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */
774
0
  if (rc != SEC_E_OK)
775
0
  {
776
0
    status = rc;
777
0
    goto fail;
778
0
  }
779
780
0
  const SECURITY_STATUS rc2 = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */
781
0
  if (rc2 != SEC_E_OK)
782
0
  {
783
0
    status = rc2;
784
0
    goto fail;
785
0
  }
786
787
0
  ntlm_generate_key_exchange_key(context);     /* KeyExchangeKey */
788
0
  ntlm_generate_random_session_key(context);   /* RandomSessionKey */
789
0
  ntlm_generate_exported_session_key(context); /* ExportedSessionKey */
790
0
  ntlm_encrypt_random_session_key(context);    /* EncryptedRandomSessionKey */
791
792
  /* Generate signing keys */
793
0
  status = SEC_E_ENCRYPT_FAILURE;
794
0
  if (!ntlm_generate_client_signing_key(context))
795
0
    goto fail;
796
0
  if (!ntlm_generate_server_signing_key(context))
797
0
    goto fail;
798
  /* Generate sealing keys */
799
0
  if (!ntlm_generate_client_sealing_key(context))
800
0
    goto fail;
801
0
  if (!ntlm_generate_server_sealing_key(context))
802
0
    goto fail;
803
  /* Initialize RC4 seal state using client sealing key */
804
0
  if (!ntlm_init_rc4_seal_states(context))
805
0
    goto fail;
806
#if defined(WITH_DEBUG_NTLM)
807
  ntlm_print_authentication_complete(context);
808
#endif
809
0
  ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
810
0
  status = SEC_I_CONTINUE_NEEDED;
811
0
fail:
812
0
  ntlm_free_message_fields_buffer(&(message->TargetName));
813
0
  return status;
814
0
}
815
816
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
817
0
{
818
0
  wStream sbuffer;
819
0
  wStream* s = NULL;
820
0
  size_t length = 0;
821
0
  UINT32 PayloadOffset = 0;
822
0
  const NTLM_CHALLENGE_MESSAGE empty = { 0 };
823
0
  NTLM_CHALLENGE_MESSAGE* message = NULL;
824
825
0
  WINPR_ASSERT(context);
826
0
  WINPR_ASSERT(buffer);
827
828
0
  message = &context->CHALLENGE_MESSAGE;
829
0
  WINPR_ASSERT(message);
830
831
0
  *message = empty;
832
833
0
  s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
834
835
0
  if (!s)
836
0
    return SEC_E_INTERNAL_ERROR;
837
838
0
  ntlm_get_version_info(&(message->Version)); /* Version */
839
0
  ntlm_generate_server_challenge(context);    /* Server Challenge */
840
0
  ntlm_generate_timestamp(context);           /* Timestamp */
841
842
0
  if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */
843
0
    return SEC_E_INTERNAL_ERROR;
844
845
0
  CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
846
0
  message->NegotiateFlags = context->NegotiateFlags;
847
0
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE))
848
0
    return SEC_E_INTERNAL_ERROR;
849
850
  /* Message Header (12 bytes) */
851
0
  if (!ntlm_write_message_header(s, &message->header))
852
0
    return SEC_E_INTERNAL_ERROR;
853
854
0
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
855
0
  {
856
0
    message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
857
0
    message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
858
0
  }
859
860
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
861
862
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
863
0
  {
864
0
    message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
865
0
    message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
866
0
  }
867
868
0
  PayloadOffset = 48;
869
870
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
871
0
    PayloadOffset += 8;
872
873
0
  message->TargetName.BufferOffset = PayloadOffset;
874
0
  message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
875
  /* TargetNameFields (8 bytes) */
876
0
  if (!ntlm_write_message_fields(s, &(message->TargetName)))
877
0
    return SEC_E_INTERNAL_ERROR;
878
879
0
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE"))
880
0
    return SEC_E_INTERNAL_ERROR;
881
882
0
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge"))
883
0
    return SEC_E_INTERNAL_ERROR;
884
885
0
  Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
886
0
  Stream_Write(s, message->Reserved, 8);        /* Reserved (8 bytes), should be ignored */
887
888
  /* TargetInfoFields (8 bytes) */
889
0
  if (!ntlm_write_message_fields(s, &(message->TargetInfo)))
890
0
    return SEC_E_INTERNAL_ERROR;
891
892
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
893
0
  {
894
0
    if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
895
0
      return SEC_E_INTERNAL_ERROR;
896
0
  }
897
898
  /* Payload (variable) */
899
0
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
900
0
  {
901
0
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetName)))
902
0
      return SEC_E_INTERNAL_ERROR;
903
0
  }
904
905
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
906
0
  {
907
0
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo)))
908
0
      return SEC_E_INTERNAL_ERROR;
909
0
  }
910
911
0
  length = Stream_GetPosition(s);
912
0
  WINPR_ASSERT(length <= UINT32_MAX);
913
0
  buffer->cbBuffer = (ULONG)length;
914
915
0
  if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
916
0
    return SEC_E_INTERNAL_ERROR;
917
918
0
  CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length);
919
#if defined(WITH_DEBUG_NTLM)
920
  ntlm_print_challenge_message(&context->ChallengeMessage, message,
921
                               &context->ChallengeTargetInfo);
922
#endif
923
0
  ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
924
0
  return SEC_I_CONTINUE_NEEDED;
925
0
}
926
927
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
928
0
{
929
0
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
930
0
  wStream sbuffer;
931
0
  wStream* s = NULL;
932
0
  size_t length = 0;
933
0
  UINT32 flags = 0;
934
0
  NTLM_AV_PAIR* AvFlags = NULL;
935
0
  size_t PayloadBufferOffset = 0;
936
0
  const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
937
0
  NTLM_AUTHENTICATE_MESSAGE* message = NULL;
938
0
  SSPI_CREDENTIALS* credentials = NULL;
939
940
0
  WINPR_ASSERT(context);
941
0
  WINPR_ASSERT(buffer);
942
943
0
  credentials = context->credentials;
944
0
  WINPR_ASSERT(credentials);
945
946
0
  message = &context->AUTHENTICATE_MESSAGE;
947
0
  WINPR_ASSERT(message);
948
949
0
  *message = empty;
950
951
0
  s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
952
953
0
  if (!s)
954
0
    return SEC_E_INTERNAL_ERROR;
955
956
0
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE))
957
0
    goto fail;
958
959
0
  if (!ntlm_read_message_fields(
960
0
          s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
961
0
    goto fail;
962
963
0
  if (!ntlm_read_message_fields(
964
0
          s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
965
0
    goto fail;
966
967
0
  if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
968
0
    goto fail;
969
970
0
  if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
971
0
    goto fail;
972
973
0
  if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
974
0
    goto fail;
975
976
0
  if (!ntlm_read_message_fields(
977
0
          s,
978
0
          &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
979
0
    goto fail;
980
981
0
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE"))
982
0
    goto fail;
983
984
0
  context->NegotiateKeyExchange =
985
0
      (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE;
986
987
0
  if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
988
0
      (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
989
0
    goto fail;
990
991
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
992
0
  {
993
0
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
994
0
      goto fail;
995
0
  }
996
997
0
  PayloadBufferOffset = Stream_GetPosition(s);
998
999
0
  status = SEC_E_INTERNAL_ERROR;
1000
0
  if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1001
0
    goto fail;
1002
1003
0
  if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */
1004
0
    goto fail;
1005
1006
0
  if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1007
0
    goto fail;
1008
1009
0
  if (!ntlm_read_message_fields_buffer(s,
1010
0
                                       &(message->LmChallengeResponse))) /* LmChallengeResponse */
1011
0
    goto fail;
1012
1013
0
  if (!ntlm_read_message_fields_buffer(s,
1014
0
                                       &(message->NtChallengeResponse))) /* NtChallengeResponse */
1015
0
    goto fail;
1016
1017
0
  if (message->NtChallengeResponse.Len > 0)
1018
0
  {
1019
0
    size_t cbAvFlags = 0;
1020
0
    wStream ssbuffer;
1021
0
    wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
1022
0
                                          message->NtChallengeResponse.Len);
1023
1024
0
    if (!snt)
1025
0
      goto fail;
1026
1027
0
    status = SEC_E_INVALID_TOKEN;
1028
0
    if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
1029
0
      goto fail;
1030
0
    status = SEC_E_INTERNAL_ERROR;
1031
1032
0
    context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
1033
0
    context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
1034
0
    sspi_SecBufferFree(&(context->ChallengeTargetInfo));
1035
0
    context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
1036
0
    context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
1037
0
    CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
1038
0
    AvFlags =
1039
0
        ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
1040
0
                         context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
1041
1042
0
    if (AvFlags)
1043
0
      flags = winpr_Data_Get_UINT32(ntlm_av_pair_get_value_pointer(AvFlags));
1044
0
  }
1045
1046
0
  if (!ntlm_read_message_fields_buffer(
1047
0
          s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1048
0
    goto fail;
1049
1050
0
  if (message->EncryptedRandomSessionKey.Len > 0)
1051
0
  {
1052
0
    if (message->EncryptedRandomSessionKey.Len != 16)
1053
0
      goto fail;
1054
1055
0
    CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
1056
0
               16);
1057
0
  }
1058
1059
0
  length = Stream_GetPosition(s);
1060
0
  WINPR_ASSERT(length <= UINT32_MAX);
1061
1062
0
  if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1063
0
    goto fail;
1064
1065
0
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1066
0
  buffer->cbBuffer = (ULONG)length;
1067
0
  Stream_SetPosition(s, PayloadBufferOffset);
1068
1069
0
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1070
0
  {
1071
0
    status = SEC_E_INVALID_TOKEN;
1072
0
    if (!ntlm_read_message_integrity_check(
1073
0
            s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1074
0
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1075
0
      goto fail;
1076
0
  }
1077
1078
0
  status = SEC_E_INTERNAL_ERROR;
1079
1080
#if defined(WITH_DEBUG_NTLM)
1081
  ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, NULL);
1082
#endif
1083
1084
0
  if (message->UserName.Len > 0)
1085
0
  {
1086
0
    credentials->identity.User = (UINT16*)malloc(message->UserName.Len);
1087
1088
0
    if (!credentials->identity.User)
1089
0
      goto fail;
1090
1091
0
    CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
1092
0
    credentials->identity.UserLength = message->UserName.Len / 2;
1093
0
  }
1094
1095
0
  if (message->DomainName.Len > 0)
1096
0
  {
1097
0
    credentials->identity.Domain = (UINT16*)malloc(message->DomainName.Len);
1098
1099
0
    if (!credentials->identity.Domain)
1100
0
      goto fail;
1101
1102
0
    CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
1103
0
               message->DomainName.Len);
1104
0
    credentials->identity.DomainLength = message->DomainName.Len / 2;
1105
0
  }
1106
1107
0
  if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
1108
0
  {
1109
0
    const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */
1110
0
    if (rc != SEC_E_OK)
1111
0
      return rc;
1112
0
  }
1113
1114
0
  const SECURITY_STATUS rc = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */
1115
0
  if (rc != SEC_E_OK)
1116
0
    return rc;
1117
1118
  /* KeyExchangeKey */
1119
0
  ntlm_generate_key_exchange_key(context);
1120
  /* EncryptedRandomSessionKey */
1121
0
  ntlm_decrypt_random_session_key(context);
1122
  /* ExportedSessionKey */
1123
0
  ntlm_generate_exported_session_key(context);
1124
1125
0
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1126
0
  {
1127
0
    BYTE messageIntegrityCheck[16] = { 0 };
1128
1129
0
    ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
1130
0
                                         sizeof(messageIntegrityCheck));
1131
0
    CopyMemory(
1132
0
        &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
1133
0
        message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
1134
1135
0
    if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
1136
0
               sizeof(message->MessageIntegrityCheck)) != 0)
1137
0
    {
1138
0
      WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!");
1139
#ifdef WITH_DEBUG_NTLM
1140
      WLog_ERR(TAG, "Expected MIC:");
1141
      winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, sizeof(messageIntegrityCheck));
1142
      WLog_ERR(TAG, "Actual MIC:");
1143
      winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck,
1144
                    sizeof(message->MessageIntegrityCheck));
1145
#endif
1146
0
      return SEC_E_MESSAGE_ALTERED;
1147
0
    }
1148
0
  }
1149
0
  else
1150
0
  {
1151
    /* no mic message was present
1152
1153
       https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f9e6fbc4-a953-4f24-b229-ccdcc213b9ec
1154
       the mic is optional, as not supported in Windows NT, Windows 2000, Windows XP, and
1155
       Windows Server 2003 and, as it seems, in the NTLMv2 implementation of Qt5.
1156
1157
       now check the NtProofString, to detect if the entered client password matches the
1158
       expected password.
1159
       */
1160
1161
#ifdef WITH_DEBUG_NTLM
1162
    WLog_VRB(TAG, "No MIC present, using NtProofString for verification.");
1163
#endif
1164
1165
0
    if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
1166
0
    {
1167
0
      WLog_ERR(TAG, "NtProofString verification failed!");
1168
#ifdef WITH_DEBUG_NTLM
1169
      WLog_ERR(TAG, "Expected NtProofString:");
1170
      winpr_HexDump(TAG, WLOG_ERROR, context->NtProofString, sizeof(context->NtProofString));
1171
      WLog_ERR(TAG, "Actual NtProofString:");
1172
      winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response,
1173
                    sizeof(context->NTLMv2Response));
1174
#endif
1175
0
      return SEC_E_LOGON_DENIED;
1176
0
    }
1177
0
  }
1178
1179
  /* Generate signing keys */
1180
0
  if (!ntlm_generate_client_signing_key(context))
1181
0
    return SEC_E_INTERNAL_ERROR;
1182
0
  if (!ntlm_generate_server_signing_key(context))
1183
0
    return SEC_E_INTERNAL_ERROR;
1184
  /* Generate sealing keys */
1185
0
  if (!ntlm_generate_client_sealing_key(context))
1186
0
    return SEC_E_INTERNAL_ERROR;
1187
0
  if (!ntlm_generate_server_sealing_key(context))
1188
0
    return SEC_E_INTERNAL_ERROR;
1189
  /* Initialize RC4 seal state */
1190
0
  if (!ntlm_init_rc4_seal_states(context))
1191
0
    return SEC_E_INTERNAL_ERROR;
1192
#if defined(WITH_DEBUG_NTLM)
1193
  ntlm_print_authentication_complete(context);
1194
#endif
1195
0
  ntlm_change_state(context, NTLM_STATE_FINAL);
1196
0
  ntlm_free_message_fields_buffer(&(message->DomainName));
1197
0
  ntlm_free_message_fields_buffer(&(message->UserName));
1198
0
  ntlm_free_message_fields_buffer(&(message->Workstation));
1199
0
  ntlm_free_message_fields_buffer(&(message->LmChallengeResponse));
1200
0
  ntlm_free_message_fields_buffer(&(message->NtChallengeResponse));
1201
0
  ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey));
1202
0
  return SEC_E_OK;
1203
1204
0
fail:
1205
0
  return status;
1206
0
}
1207
1208
/**
1209
 * Send NTLMSSP AUTHENTICATE_MESSAGE. msdn{cc236643}
1210
 *
1211
 * @param context Pointer to the NTLM context
1212
 * @param buffer The buffer to write
1213
 */
1214
1215
SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
1216
0
{
1217
0
  wStream sbuffer;
1218
0
  wStream* s = NULL;
1219
0
  size_t length = 0;
1220
0
  UINT32 PayloadBufferOffset = 0;
1221
0
  const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
1222
0
  NTLM_AUTHENTICATE_MESSAGE* message = NULL;
1223
0
  SSPI_CREDENTIALS* credentials = NULL;
1224
1225
0
  WINPR_ASSERT(context);
1226
0
  WINPR_ASSERT(buffer);
1227
1228
0
  credentials = context->credentials;
1229
0
  WINPR_ASSERT(credentials);
1230
1231
0
  message = &context->AUTHENTICATE_MESSAGE;
1232
0
  WINPR_ASSERT(message);
1233
1234
0
  *message = empty;
1235
1236
0
  s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
1237
1238
0
  if (!s)
1239
0
    return SEC_E_INTERNAL_ERROR;
1240
1241
0
  if (context->NTLMv2)
1242
0
  {
1243
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
1244
1245
0
    if (context->SendVersionInfo)
1246
0
      message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
1247
0
  }
1248
1249
0
  if (context->UseMIC)
1250
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
1251
1252
0
  if (context->SendWorkstationName)
1253
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
1254
1255
0
  if (context->confidentiality)
1256
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
1257
1258
0
  if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1259
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
1260
1261
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
1262
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
1263
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
1264
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
1265
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
1266
0
  message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
1267
0
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
1268
1269
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1270
0
    ntlm_get_version_info(&(message->Version));
1271
1272
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1273
0
  {
1274
0
    message->Workstation.Len = context->Workstation.Length;
1275
0
    message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
1276
0
  }
1277
1278
0
  if (credentials->identity.DomainLength > 0)
1279
0
  {
1280
0
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
1281
0
    message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2;
1282
0
    message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
1283
0
  }
1284
1285
0
  message->UserName.Len = (UINT16)credentials->identity.UserLength * 2;
1286
0
  message->UserName.Buffer = (BYTE*)credentials->identity.User;
1287
0
  message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer;
1288
0
  message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer;
1289
0
  message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer;
1290
0
  message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer;
1291
1292
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1293
0
  {
1294
0
    message->EncryptedRandomSessionKey.Len = 16;
1295
0
    message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
1296
0
  }
1297
1298
0
  PayloadBufferOffset = 64;
1299
1300
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1301
0
    PayloadBufferOffset += 8; /* Version (8 bytes) */
1302
1303
0
  if (context->UseMIC)
1304
0
    PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
1305
1306
0
  message->DomainName.BufferOffset = PayloadBufferOffset;
1307
0
  message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
1308
0
  message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
1309
0
  message->LmChallengeResponse.BufferOffset =
1310
0
      message->Workstation.BufferOffset + message->Workstation.Len;
1311
0
  message->NtChallengeResponse.BufferOffset =
1312
0
      message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len;
1313
0
  message->EncryptedRandomSessionKey.BufferOffset =
1314
0
      message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len;
1315
0
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE))
1316
0
    return SEC_E_INVALID_TOKEN;
1317
0
  if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */
1318
0
    return SEC_E_INTERNAL_ERROR;
1319
0
  if (!ntlm_write_message_fields(
1320
0
          s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
1321
0
    return SEC_E_INTERNAL_ERROR;
1322
0
  if (!ntlm_write_message_fields(
1323
0
          s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
1324
0
    return SEC_E_INTERNAL_ERROR;
1325
0
  if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
1326
0
    return SEC_E_INTERNAL_ERROR;
1327
0
  if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
1328
0
    return SEC_E_INTERNAL_ERROR;
1329
0
  if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
1330
0
    return SEC_E_INTERNAL_ERROR;
1331
0
  if (!ntlm_write_message_fields(
1332
0
          s,
1333
0
          &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
1334
0
    return SEC_E_INTERNAL_ERROR;
1335
0
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE"))
1336
0
    return SEC_E_INTERNAL_ERROR;
1337
1338
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1339
0
  {
1340
0
    if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
1341
0
      return SEC_E_INTERNAL_ERROR;
1342
0
  }
1343
1344
0
  if (context->UseMIC)
1345
0
  {
1346
0
    const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1347
1348
0
    context->MessageIntegrityCheckOffset = Stream_GetPosition(s);
1349
0
    if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data),
1350
0
                                            "NTLM_AUTHENTICATE_MESSAGE"))
1351
0
      return SEC_E_INTERNAL_ERROR;
1352
0
  }
1353
1354
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
1355
0
  {
1356
0
    if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1357
0
      return SEC_E_INTERNAL_ERROR;
1358
0
  }
1359
1360
0
  if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */
1361
0
    return SEC_E_INTERNAL_ERROR;
1362
1363
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1364
0
  {
1365
0
    if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1366
0
      return SEC_E_INTERNAL_ERROR;
1367
0
  }
1368
1369
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
1370
0
  {
1371
0
    if (!ntlm_write_message_fields_buffer(
1372
0
            s, &(message->LmChallengeResponse))) /* LmChallengeResponse */
1373
0
      return SEC_E_INTERNAL_ERROR;
1374
0
  }
1375
0
  if (!ntlm_write_message_fields_buffer(
1376
0
          s, &(message->NtChallengeResponse))) /* NtChallengeResponse */
1377
0
    return SEC_E_INTERNAL_ERROR;
1378
1379
0
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1380
0
  {
1381
0
    if (!ntlm_write_message_fields_buffer(
1382
0
            s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1383
0
      return SEC_E_INTERNAL_ERROR;
1384
0
  }
1385
1386
0
  length = Stream_GetPosition(s);
1387
0
  WINPR_ASSERT(length <= UINT32_MAX);
1388
1389
0
  if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1390
0
    return SEC_E_INTERNAL_ERROR;
1391
1392
0
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1393
0
  buffer->cbBuffer = (ULONG)length;
1394
1395
0
  if (context->UseMIC)
1396
0
  {
1397
    /* Message Integrity Check */
1398
0
    ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
1399
0
                                         sizeof(message->MessageIntegrityCheck));
1400
0
    if (!ntlm_write_message_integrity_check(
1401
0
            s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1402
0
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1403
0
      return SEC_E_INTERNAL_ERROR;
1404
0
  }
1405
1406
#if defined(WITH_DEBUG_NTLM)
1407
  ntlm_print_authenticate_message(&context->AuthenticateMessage, message,
1408
                                  context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0,
1409
                                  &context->AuthenticateTargetInfo);
1410
#endif
1411
0
  ntlm_change_state(context, NTLM_STATE_FINAL);
1412
0
  return SEC_E_OK;
1413
0
}