Coverage Report

Created: 2026-05-30 06:46

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