Coverage Report

Created: 2026-06-09 06:17

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
10.0k
  Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
40
10.0k
                                       __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.4k
{
48
42.4k
  if (flag & NTLMSSP_NEGOTIATE_56)
49
0
    return "NTLMSSP_NEGOTIATE_56";
50
42.4k
  if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH)
51
2.55k
    return "NTLMSSP_NEGOTIATE_KEY_EXCH";
52
39.9k
  if (flag & NTLMSSP_NEGOTIATE_128)
53
2.72k
    return "NTLMSSP_NEGOTIATE_128";
54
37.2k
  if (flag & NTLMSSP_RESERVED1)
55
291
    return "NTLMSSP_RESERVED1";
56
36.9k
  if (flag & NTLMSSP_RESERVED2)
57
377
    return "NTLMSSP_RESERVED2";
58
36.5k
  if (flag & NTLMSSP_RESERVED3)
59
457
    return "NTLMSSP_RESERVED3";
60
36.0k
  if (flag & NTLMSSP_NEGOTIATE_VERSION)
61
2.79k
    return "NTLMSSP_NEGOTIATE_VERSION";
62
33.2k
  if (flag & NTLMSSP_RESERVED4)
63
387
    return "NTLMSSP_RESERVED4";
64
32.9k
  if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO)
65
1.04k
    return "NTLMSSP_NEGOTIATE_TARGET_INFO";
66
31.8k
  if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY)
67
356
    return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY";
68
31.4k
  if (flag & NTLMSSP_RESERVED5)
69
476
    return "NTLMSSP_RESERVED5";
70
31.0k
  if (flag & NTLMSSP_NEGOTIATE_IDENTIFY)
71
390
    return "NTLMSSP_NEGOTIATE_IDENTIFY";
72
30.6k
  if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY)
73
2.74k
    return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY";
74
27.8k
  if (flag & NTLMSSP_RESERVED6)
75
406
    return "NTLMSSP_RESERVED6";
76
27.4k
  if (flag & NTLMSSP_TARGET_TYPE_SERVER)
77
381
    return "NTLMSSP_TARGET_TYPE_SERVER";
78
27.0k
  if (flag & NTLMSSP_TARGET_TYPE_DOMAIN)
79
591
    return "NTLMSSP_TARGET_TYPE_DOMAIN";
80
26.5k
  if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
81
2.72k
    return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN";
82
23.7k
  if (flag & NTLMSSP_RESERVED7)
83
583
    return "NTLMSSP_RESERVED7";
84
23.2k
  if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
85
891
    return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED";
86
22.3k
  if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
87
790
    return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED";
88
21.5k
  if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS)
89
533
    return "NTLMSSP_NEGOTIATE_ANONYMOUS";
90
20.9k
  if (flag & NTLMSSP_RESERVED8)
91
399
    return "NTLMSSP_RESERVED8";
92
20.5k
  if (flag & NTLMSSP_NEGOTIATE_NTLM)
93
3.08k
    return "NTLMSSP_NEGOTIATE_NTLM";
94
17.5k
  if (flag & NTLMSSP_RESERVED9)
95
546
    return "NTLMSSP_RESERVED9";
96
16.9k
  if (flag & NTLMSSP_NEGOTIATE_LM_KEY)
97
2.43k
    return "NTLMSSP_NEGOTIATE_LM_KEY";
98
14.5k
  if (flag & NTLMSSP_NEGOTIATE_DATAGRAM)
99
298
    return "NTLMSSP_NEGOTIATE_DATAGRAM";
100
14.2k
  if (flag & NTLMSSP_NEGOTIATE_SEAL)
101
2.78k
    return "NTLMSSP_NEGOTIATE_SEAL";
102
11.4k
  if (flag & NTLMSSP_NEGOTIATE_SIGN)
103
2.73k
    return "NTLMSSP_NEGOTIATE_SIGN";
104
8.71k
  if (flag & NTLMSSP_RESERVED10)
105
342
    return "NTLMSSP_RESERVED10";
106
8.37k
  if (flag & NTLMSSP_REQUEST_TARGET)
107
2.86k
    return "NTLMSSP_REQUEST_TARGET";
108
5.50k
  if (flag & NTLMSSP_NEGOTIATE_OEM)
109
2.57k
    return "NTLMSSP_NEGOTIATE_OEM";
110
2.93k
  if (flag & NTLMSSP_NEGOTIATE_UNICODE)
111
2.93k
    return "NTLMSSP_NEGOTIATE_UNICODE";
112
0
  return "NTLMSSP_NEGOTIATE_UNKNOWN";
113
2.93k
}
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.97k
{
246
1.97k
  WINPR_ASSERT(s);
247
1.97k
  WINPR_ASSERT(header);
248
249
1.97k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
250
95
    return FALSE;
251
252
1.87k
  Stream_Read(s, header->Signature, 8);
253
1.87k
  Stream_Read_UINT32(s, header->MessageType);
254
255
1.87k
  if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
256
97
  {
257
97
    char Signature[sizeof(header->Signature) * 3 + 1] = WINPR_C_ARRAY_INIT;
258
97
    winpr_BinToHexStringBuffer(header->Signature, sizeof(header->Signature), Signature,
259
97
                               sizeof(Signature), TRUE);
260
261
97
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s", Signature,
262
97
             NTLM_SIGNATURE);
263
97
    return FALSE;
264
97
  }
265
266
1.77k
  if (header->MessageType != expected)
267
88
  {
268
88
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message type, got %s, expected %s",
269
88
             ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected));
270
88
    return FALSE;
271
88
  }
272
273
1.69k
  return TRUE;
274
1.77k
}
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.94k
{
302
4.94k
  WINPR_ASSERT(s);
303
4.94k
  WINPR_ASSERT(fields);
304
305
4.94k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
306
38
    return FALSE;
307
308
4.90k
  ntlm_free_message_fields_buffer(fields);
309
310
4.90k
  Stream_Read_UINT16(s, fields->Len);          /* Len (2 bytes) */
311
4.90k
  Stream_Read_UINT16(s, fields->MaxLen);       /* MaxLen (2 bytes) */
312
4.90k
  Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
313
4.90k
  return TRUE;
314
4.94k
}
315
316
static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
317
4.70k
{
318
4.70k
  UINT16 MaxLen = 0;
319
4.70k
  WINPR_ASSERT(s);
320
4.70k
  WINPR_ASSERT(fields);
321
322
4.70k
  MaxLen = fields->MaxLen;
323
4.70k
  if (fields->MaxLen < 1)
324
4.70k
    MaxLen = fields->Len;
325
326
4.70k
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header"))
327
0
    return FALSE;
328
329
4.70k
  Stream_Write_UINT16(s, fields->Len);          /* Len (2 bytes) */
330
4.70k
  Stream_Write_UINT16(s, MaxLen);               /* MaxLen (2 bytes) */
331
4.70k
  Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
332
4.70k
  return TRUE;
333
4.70k
}
334
335
static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
336
4.23k
{
337
4.23k
  WINPR_ASSERT(s);
338
4.23k
  WINPR_ASSERT(fields);
339
340
4.23k
  ntlm_free_message_fields_buffer(fields);
341
4.23k
  if (fields->Len > 0)
342
1.88k
  {
343
1.88k
    const size_t offset = 1ull * fields->BufferOffset + fields->Len;
344
345
1.88k
    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.86k
    if (offset > Stream_Length(s))
355
194
    {
356
194
      WLog_ERR(TAG,
357
194
               "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIuz " beyond received data %" PRIuz,
358
194
               offset, Stream_Length(s));
359
194
      return FALSE;
360
194
    }
361
362
1.67k
    fields->Buffer = (PBYTE)malloc(fields->Len);
363
364
1.67k
    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.67k
    if (!Stream_SetPosition(s, fields->BufferOffset))
372
0
    {
373
0
      ntlm_free_message_fields_buffer(fields);
374
0
      return FALSE;
375
0
    }
376
1.67k
    Stream_Read(s, fields->Buffer, fields->Len);
377
1.67k
  }
378
379
4.02k
  return TRUE;
380
4.23k
}
381
382
static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
383
2.02k
{
384
2.02k
  WINPR_ASSERT(s);
385
2.02k
  WINPR_ASSERT(fields);
386
387
2.02k
  if (fields->Len > 0)
388
2.02k
  {
389
2.02k
    if (!Stream_SetPosition(s, fields->BufferOffset))
390
0
      return FALSE;
391
2.02k
    if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len"))
392
18
      return FALSE;
393
394
2.00k
    Stream_Write(s, fields->Buffer, fields->Len);
395
2.00k
  }
396
2.00k
  return TRUE;
397
2.02k
}
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.06k
    {
405
1.06k
      free(fields->Buffer);
406
1.06k
      fields->Len = 0;
407
1.06k
      fields->MaxLen = 0;
408
1.06k
      fields->Buffer = nullptr;
409
1.06k
      fields->BufferOffset = 0;
410
1.06k
    }
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
7
    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
104
{
459
104
  WINPR_ASSERT(s);
460
104
  WINPR_ASSERT(offset);
461
104
  WINPR_ASSERT(data);
462
104
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
463
104
  WINPR_ASSERT(name);
464
465
104
  *offset = Stream_GetPosition(s);
466
467
104
  if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
468
4
    return FALSE;
469
470
100
  Stream_Read(s, data, size);
471
100
  return TRUE;
472
104
}
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
380
{
477
380
  WINPR_ASSERT(s);
478
380
  WINPR_ASSERT(data);
479
380
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
480
380
  WINPR_ASSERT(name);
481
482
380
  const size_t pos = Stream_GetPosition(s);
483
484
380
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset"))
485
12
    return FALSE;
486
487
368
  if (!Stream_SetPosition(s, offset))
488
0
    return FALSE;
489
368
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size"))
490
0
    return FALSE;
491
492
368
  Stream_Write(s, data, size);
493
368
  return Stream_SetPosition(s, pos);
494
368
}
495
496
SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
497
797
{
498
797
  wStream sbuffer = WINPR_C_ARRAY_INIT;
499
797
  size_t length = 0;
500
797
  const NTLM_NEGOTIATE_MESSAGE empty = WINPR_C_ARRAY_INIT;
501
502
797
  WINPR_ASSERT(context);
503
797
  WINPR_ASSERT(buffer);
504
505
797
  NTLM_NEGOTIATE_MESSAGE* message = &context->NEGOTIATE_MESSAGE;
506
797
  WINPR_ASSERT(message);
507
508
797
  *message = empty;
509
510
797
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
511
512
797
  if (!s)
513
0
    return SEC_E_INTERNAL_ERROR;
514
515
797
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE))
516
86
    return SEC_E_INVALID_TOKEN;
517
518
711
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags,
519
711
                                 NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
520
711
                                     NTLMSSP_NEGOTIATE_UNICODE,
521
711
                                 "NTLM_NEGOTIATE_MESSAGE"))
522
5
    return SEC_E_INVALID_TOKEN;
523
524
706
  context->NegotiateFlags = message->NegotiateFlags;
525
526
  /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
527
  // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
528
706
  {
529
706
    if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
530
18
      return SEC_E_INVALID_TOKEN;
531
706
  }
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
1
      return SEC_E_INVALID_TOKEN;
538
688
  }
539
540
687
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
541
605
  {
542
605
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
543
3
      return SEC_E_INVALID_TOKEN;
544
605
  }
545
546
684
  if (!ntlm_read_message_fields_buffer(s, &message->DomainName))
547
68
    return SEC_E_INVALID_TOKEN;
548
549
616
  if (!ntlm_read_message_fields_buffer(s, &message->Workstation))
550
32
    return SEC_E_INVALID_TOKEN;
551
552
584
  length = Stream_GetPosition(s);
553
584
  WINPR_ASSERT(length <= UINT32_MAX);
554
584
  buffer->cbBuffer = (ULONG)length;
555
556
584
  if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
557
0
    return SEC_E_INTERNAL_ERROR;
558
559
584
  CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
560
584
  context->NegotiateMessage.BufferType = buffer->BufferType;
561
#if defined(WITH_DEBUG_NTLM)
562
  ntlm_print_negotiate_message(&context->NegotiateMessage, message);
563
#endif
564
584
  ntlm_change_state(context, NTLM_STATE_CHALLENGE);
565
584
  return SEC_I_CONTINUE_NEEDED;
566
584
}
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
652
{
661
652
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
662
652
  wStream sbuffer = WINPR_C_ARRAY_INIT;
663
652
  size_t length = 0;
664
652
  size_t StartOffset = 0;
665
652
  size_t PayloadOffset = 0;
666
652
  const NTLM_CHALLENGE_MESSAGE empty = WINPR_C_ARRAY_INIT;
667
668
652
  if (!context || !buffer)
669
0
    return SEC_E_INTERNAL_ERROR;
670
671
652
  if (!ntlm_generate_client_challenge(context))
672
0
    return SEC_E_INTERNAL_ERROR;
673
674
652
  NTLM_CHALLENGE_MESSAGE* message = &context->CHALLENGE_MESSAGE;
675
652
  WINPR_ASSERT(message);
676
677
652
  *message = empty;
678
679
652
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
680
681
652
  if (!s)
682
0
    return SEC_E_INTERNAL_ERROR;
683
684
652
  StartOffset = Stream_GetPosition(s);
685
686
652
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE))
687
84
    goto fail;
688
689
568
  if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */
690
3
    goto fail;
691
692
565
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE"))
693
4
    goto fail;
694
695
561
  context->NegotiateFlags = message->NegotiateFlags;
696
697
561
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
698
12
    goto fail;
699
700
549
  Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
701
549
  CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
702
549
  Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
703
704
549
  if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */
705
4
    goto fail;
706
707
545
  if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
708
90
  {
709
90
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
710
1
      goto fail;
711
90
  }
712
713
  /* Payload (variable) */
714
544
  PayloadOffset = Stream_GetPosition(s);
715
716
544
  status = SEC_E_INTERNAL_ERROR;
717
544
  if (message->TargetName.Len > 0)
718
352
  {
719
352
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetName)))
720
24
      goto fail;
721
352
  }
722
723
520
  if (message->TargetInfo.Len > 0)
724
517
  {
725
517
    size_t cbAvTimestamp = 0;
726
727
517
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo)))
728
35
      goto fail;
729
730
482
    context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
731
482
    context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
732
482
    NTLM_AV_PAIR* AvTimestamp =
733
482
        ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer, message->TargetInfo.Len,
734
482
                         MsvAvTimestamp, &cbAvTimestamp);
735
736
482
    if (AvTimestamp)
737
126
    {
738
126
      PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp, cbAvTimestamp);
739
740
126
      if (!ptr || (AvTimestamp->AvLen < 8))
741
14
        goto fail;
742
743
112
      if (context->NTLMv2)
744
112
        context->UseMIC = TRUE;
745
746
112
      CopyMemory(context->ChallengeTimestamp, ptr, 8);
747
112
    }
748
482
  }
749
750
471
  length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;
751
471
  if (length > buffer->cbBuffer)
752
111
    goto fail;
753
754
360
  if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
755
0
    goto fail;
756
757
360
  if (context->ChallengeMessage.pvBuffer)
758
360
    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
360
  if (context->NTLMv2)
765
360
  {
766
360
    if (!ntlm_construct_authenticate_target_info(context))
767
161
      goto fail;
768
769
199
    sspi_SecBufferFree(&context->ChallengeTargetInfo);
770
199
    context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer;
771
199
    context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer;
772
199
  }
773
774
199
  ntlm_generate_timestamp(context); /* Timestamp */
775
776
199
  {
777
199
    const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */
778
199
    if (rc != SEC_E_OK)
779
0
    {
780
0
      status = rc;
781
0
      goto fail;
782
0
    }
783
199
  }
784
785
199
  {
786
199
    const SECURITY_STATUS rc2 =
787
199
        ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */
788
199
    if (rc2 != SEC_E_OK)
789
0
    {
790
0
      status = rc2;
791
0
      goto fail;
792
0
    }
793
199
  }
794
795
199
  if (!ntlm_generate_key_exchange_key(context)) /* KeyExchangeKey */
796
0
    goto fail;
797
199
  if (!ntlm_generate_random_session_key(context)) /* RandomSessionKey */
798
0
    goto fail;
799
199
  if (!ntlm_generate_exported_session_key(context)) /* ExportedSessionKey */
800
0
    goto fail;
801
199
  if (!ntlm_encrypt_random_session_key(context)) /* EncryptedRandomSessionKey */
802
0
    goto fail;
803
804
  /* Generate signing keys */
805
199
  status = SEC_E_ENCRYPT_FAILURE;
806
199
  if (!ntlm_generate_client_signing_key(context))
807
0
    goto fail;
808
199
  if (!ntlm_generate_server_signing_key(context))
809
0
    goto fail;
810
  /* Generate sealing keys */
811
199
  if (!ntlm_generate_client_sealing_key(context))
812
0
    goto fail;
813
199
  if (!ntlm_generate_server_sealing_key(context))
814
0
    goto fail;
815
  /* Initialize RC4 seal state using client sealing key */
816
199
  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
199
  ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
822
199
  status = SEC_I_CONTINUE_NEEDED;
823
652
fail:
824
652
  ntlm_free_message_fields_buffer(&(message->TargetName));
825
652
  return status;
826
199
}
827
828
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
829
584
{
830
584
  wStream sbuffer = WINPR_C_ARRAY_INIT;
831
584
  size_t length = 0;
832
584
  UINT32 PayloadOffset = 0;
833
584
  const NTLM_CHALLENGE_MESSAGE empty = WINPR_C_ARRAY_INIT;
834
835
584
  WINPR_ASSERT(context);
836
584
  WINPR_ASSERT(buffer);
837
838
584
  NTLM_CHALLENGE_MESSAGE* message = &context->CHALLENGE_MESSAGE;
839
584
  WINPR_ASSERT(message);
840
841
584
  *message = empty;
842
843
584
  wStream* s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
844
845
584
  if (!s)
846
0
    return SEC_E_INTERNAL_ERROR;
847
848
584
  if (!ntlm_get_version_info(&(message->Version))) /* Version */
849
0
    return SEC_E_INTERNAL_ERROR;
850
584
  if (!ntlm_generate_server_challenge(context)) /* Server Challenge */
851
0
    return SEC_E_INTERNAL_ERROR;
852
584
  ntlm_generate_timestamp(context);           /* Timestamp */
853
854
584
  if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */
855
0
    return SEC_E_INTERNAL_ERROR;
856
857
584
  CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
858
584
  message->NegotiateFlags = context->NegotiateFlags;
859
584
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE))
860
0
    return SEC_E_INTERNAL_ERROR;
861
862
  /* Message Header (12 bytes) */
863
584
  if (!ntlm_write_message_header(s, &message->header))
864
0
    return SEC_E_INTERNAL_ERROR;
865
866
584
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
867
584
  {
868
584
    message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
869
584
    message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
870
584
  }
871
872
584
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
873
874
584
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
875
584
  {
876
584
    message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
877
584
    message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
878
584
  }
879
880
584
  PayloadOffset = 48;
881
882
584
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
883
570
    PayloadOffset += 8;
884
885
584
  message->TargetName.BufferOffset = PayloadOffset;
886
584
  message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
887
  /* TargetNameFields (8 bytes) */
888
584
  if (!ntlm_write_message_fields(s, &(message->TargetName)))
889
0
    return SEC_E_INTERNAL_ERROR;
890
891
584
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE"))
892
0
    return SEC_E_INTERNAL_ERROR;
893
894
584
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge"))
895
0
    return SEC_E_INTERNAL_ERROR;
896
897
584
  Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
898
584
  Stream_Write(s, message->Reserved, 8);        /* Reserved (8 bytes), should be ignored */
899
900
  /* TargetInfoFields (8 bytes) */
901
584
  if (!ntlm_write_message_fields(s, &(message->TargetInfo)))
902
0
    return SEC_E_INTERNAL_ERROR;
903
904
584
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
905
570
  {
906
570
    if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
907
0
      return SEC_E_INTERNAL_ERROR;
908
570
  }
909
910
  /* Payload (variable) */
911
584
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
912
584
  {
913
584
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetName)))
914
0
      return SEC_E_INTERNAL_ERROR;
915
584
  }
916
917
584
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
918
584
  {
919
584
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo)))
920
0
      return SEC_E_INTERNAL_ERROR;
921
584
  }
922
923
584
  length = Stream_GetPosition(s);
924
584
  WINPR_ASSERT(length <= UINT32_MAX);
925
584
  buffer->cbBuffer = (ULONG)length;
926
927
584
  if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
928
0
    return SEC_E_INTERNAL_ERROR;
929
930
584
  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
584
  ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
936
584
  return SEC_I_CONTINUE_NEEDED;
937
584
}
938
939
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
940
521
{
941
521
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
942
521
  wStream sbuffer = WINPR_C_ARRAY_INIT;
943
521
  size_t length = 0;
944
521
  UINT32 flags = 0;
945
521
  NTLM_AV_PAIR* AvFlags = nullptr;
946
521
  size_t PayloadBufferOffset = 0;
947
521
  const NTLM_AUTHENTICATE_MESSAGE empty = WINPR_C_ARRAY_INIT;
948
949
521
  WINPR_ASSERT(context);
950
521
  WINPR_ASSERT(buffer);
951
952
521
  SSPI_CREDENTIALS* credentials = context->credentials;
953
521
  WINPR_ASSERT(credentials);
954
955
521
  NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
956
521
  WINPR_ASSERT(message);
957
958
521
  *message = empty;
959
960
521
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
961
962
521
  if (!s)
963
0
    return SEC_E_INTERNAL_ERROR;
964
965
521
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE))
966
110
    goto fail;
967
968
411
  if (!ntlm_read_message_fields(
969
411
          s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
970
2
    goto fail;
971
972
409
  if (!ntlm_read_message_fields(
973
409
          s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
974
3
    goto fail;
975
976
406
  if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
977
3
    goto fail;
978
979
403
  if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
980
2
    goto fail;
981
982
401
  if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
983
1
    goto fail;
984
985
400
  if (!ntlm_read_message_fields(
986
400
          s,
987
400
          &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
988
1
    goto fail;
989
990
399
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE"))
991
2
    goto fail;
992
993
397
  context->NegotiateKeyExchange = (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0;
994
995
397
  if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
996
394
      (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
997
27
    goto fail;
998
999
370
  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
365
  PayloadBufferOffset = Stream_GetPosition(s);
1006
1007
365
  status = SEC_E_INTERNAL_ERROR;
1008
365
  if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1009
11
    goto fail;
1010
1011
354
  if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */
1012
6
    goto fail;
1013
1014
348
  if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1015
3
    goto fail;
1016
1017
345
  if (!ntlm_read_message_fields_buffer(s,
1018
345
                                       &(message->LmChallengeResponse))) /* LmChallengeResponse */
1019
4
    goto fail;
1020
1021
341
  if (!ntlm_read_message_fields_buffer(s,
1022
341
                                       &(message->NtChallengeResponse))) /* NtChallengeResponse */
1023
15
    goto fail;
1024
1025
326
  if (message->NtChallengeResponse.Len > 0)
1026
254
  {
1027
254
    size_t cbAvFlags = 0;
1028
254
    wStream ssbuffer;
1029
254
    wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
1030
254
                                          message->NtChallengeResponse.Len);
1031
1032
254
    if (!snt)
1033
0
      goto fail;
1034
1035
254
    status = SEC_E_INVALID_TOKEN;
1036
254
    if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
1037
3
      goto fail;
1038
251
    status = SEC_E_INTERNAL_ERROR;
1039
1040
251
    context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
1041
251
    context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
1042
251
    sspi_SecBufferFree(&(context->ChallengeTargetInfo));
1043
251
    context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
1044
251
    context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
1045
251
    CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
1046
251
    AvFlags =
1047
251
        ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
1048
251
                         context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
1049
1050
251
    if (AvFlags)
1051
118
    {
1052
118
      const BYTE* ptr = ntlm_av_pair_get_value_pointer(AvFlags, cbAvFlags);
1053
118
      if (!ptr || (AvFlags->AvLen < 4))
1054
8
        goto fail;
1055
110
      flags = winpr_Data_Get_UINT32(ptr);
1056
110
    }
1057
251
  }
1058
1059
315
  if (!ntlm_read_message_fields_buffer(
1060
315
          s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1061
13
    goto fail;
1062
1063
302
  if (message->EncryptedRandomSessionKey.Len > 0)
1064
30
  {
1065
30
    if (message->EncryptedRandomSessionKey.Len != 16)
1066
25
      goto fail;
1067
1068
5
    CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
1069
5
               16);
1070
5
  }
1071
1072
277
  length = Stream_GetPosition(s);
1073
277
  WINPR_ASSERT(length <= UINT32_MAX);
1074
1075
277
  if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1076
0
    goto fail;
1077
1078
277
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1079
277
  buffer->cbBuffer = (ULONG)length;
1080
277
  if (!Stream_SetPosition(s, PayloadBufferOffset))
1081
0
    goto fail;
1082
1083
277
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1084
104
  {
1085
104
    status = SEC_E_INVALID_TOKEN;
1086
104
    if (!ntlm_read_message_integrity_check(
1087
104
            s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1088
104
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1089
4
      goto fail;
1090
104
  }
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
71
  {
1100
71
    credentials->identity.User = (UINT16*)calloc(message->UserName.Len + sizeof(WCHAR), 1);
1101
1102
71
    if (!credentials->identity.User)
1103
0
      goto fail;
1104
1105
71
    CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
1106
71
    credentials->identity.UserLength = message->UserName.Len / sizeof(WCHAR);
1107
71
  }
1108
1109
273
  if (message->DomainName.Len > 0)
1110
57
  {
1111
57
    credentials->identity.Domain = (UINT16*)calloc(message->DomainName.Len + sizeof(WCHAR), 1);
1112
1113
57
    if (!credentials->identity.Domain)
1114
0
      goto fail;
1115
1116
57
    CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
1117
57
               message->DomainName.Len);
1118
57
    credentials->identity.DomainLength = message->DomainName.Len / sizeof(WCHAR);
1119
57
  }
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
100
  {
1152
100
    BYTE messageIntegrityCheck[16] = WINPR_C_ARRAY_INIT;
1153
1154
100
    if (!ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
1155
100
                                              sizeof(messageIntegrityCheck)))
1156
9
      goto fail;
1157
91
    CopyMemory(
1158
91
        &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
1159
91
        message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
1160
1161
91
    if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
1162
91
               sizeof(message->MessageIntegrityCheck)) != 0)
1163
91
    {
1164
91
      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
91
      status = SEC_E_MESSAGE_ALTERED;
1173
91
      goto fail;
1174
91
    }
1175
91
  }
1176
173
  else
1177
173
  {
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
173
    if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
1193
173
    {
1194
173
      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
173
      status = SEC_E_LOGON_DENIED;
1203
173
      goto fail;
1204
173
    }
1205
173
  }
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
521
fail:
1227
521
  ntlm_free_message_fields_buffer(&(message->DomainName));
1228
521
  ntlm_free_message_fields_buffer(&(message->UserName));
1229
521
  ntlm_free_message_fields_buffer(&(message->Workstation));
1230
521
  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
521
  if (context->NtChallengeResponse.pvBuffer != message->NtChallengeResponse.Buffer)
1236
276
    ntlm_free_message_fields_buffer(&(message->NtChallengeResponse));
1237
521
  ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey));
1238
521
  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
199
{
1250
199
  wStream sbuffer = WINPR_C_ARRAY_INIT;
1251
199
  size_t length = 0;
1252
199
  UINT32 PayloadBufferOffset = 0;
1253
199
  const NTLM_AUTHENTICATE_MESSAGE empty = WINPR_C_ARRAY_INIT;
1254
1255
199
  WINPR_ASSERT(context);
1256
199
  WINPR_ASSERT(buffer);
1257
1258
199
  SSPI_CREDENTIALS* credentials = context->credentials;
1259
199
  WINPR_ASSERT(credentials);
1260
1261
199
  NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
1262
199
  WINPR_ASSERT(message);
1263
1264
199
  *message = empty;
1265
1266
199
  wStream* s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
1267
1268
199
  if (!s)
1269
0
    return SEC_E_INTERNAL_ERROR;
1270
1271
199
  if (context->NTLMv2)
1272
199
  {
1273
199
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
1274
1275
199
    if (context->SendVersionInfo)
1276
199
      message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
1277
199
  }
1278
1279
199
  if (context->UseMIC)
1280
199
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
1281
1282
199
  if (context->SendWorkstationName)
1283
199
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
1284
1285
199
  if (context->confidentiality)
1286
199
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
1287
1288
199
  if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1289
66
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
1290
1291
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
1292
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
1293
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
1294
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
1295
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
1296
199
  message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
1297
199
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
1298
1299
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1300
199
  {
1301
199
    if (!ntlm_get_version_info(&(message->Version)))
1302
0
      return SEC_E_INTERNAL_ERROR;
1303
199
  }
1304
1305
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1306
199
  {
1307
199
    message->Workstation.Len = context->Workstation.Length;
1308
199
    message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
1309
199
  }
1310
1311
199
  if (credentials->identity.DomainLength > 0)
1312
199
  {
1313
199
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
1314
199
    message->DomainName.Len = (UINT16)credentials->identity.DomainLength * sizeof(WCHAR);
1315
199
    message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
1316
199
  }
1317
1318
199
  message->UserName.Len = (UINT16)credentials->identity.UserLength * sizeof(WCHAR);
1319
199
  message->UserName.Buffer = (BYTE*)credentials->identity.User;
1320
199
  message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer;
1321
199
  message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer;
1322
199
  message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer;
1323
199
  message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer;
1324
1325
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1326
66
  {
1327
66
    message->EncryptedRandomSessionKey.Len = 16;
1328
66
    message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
1329
66
  }
1330
1331
199
  PayloadBufferOffset = 64;
1332
1333
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1334
199
    PayloadBufferOffset += 8; /* Version (8 bytes) */
1335
1336
199
  if (context->UseMIC)
1337
199
    PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
1338
1339
199
  message->DomainName.BufferOffset = PayloadBufferOffset;
1340
199
  message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
1341
199
  message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
1342
199
  message->LmChallengeResponse.BufferOffset =
1343
199
      message->Workstation.BufferOffset + message->Workstation.Len;
1344
199
  message->NtChallengeResponse.BufferOffset =
1345
199
      message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len;
1346
199
  message->EncryptedRandomSessionKey.BufferOffset =
1347
199
      message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len;
1348
199
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE))
1349
0
    return SEC_E_INVALID_TOKEN;
1350
199
  if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */
1351
0
    return SEC_E_INTERNAL_ERROR;
1352
199
  if (!ntlm_write_message_fields(
1353
199
          s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
1354
0
    return SEC_E_INTERNAL_ERROR;
1355
199
  if (!ntlm_write_message_fields(
1356
199
          s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
1357
0
    return SEC_E_INTERNAL_ERROR;
1358
199
  if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
1359
0
    return SEC_E_INTERNAL_ERROR;
1360
199
  if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
1361
0
    return SEC_E_INTERNAL_ERROR;
1362
199
  if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
1363
0
    return SEC_E_INTERNAL_ERROR;
1364
199
  if (!ntlm_write_message_fields(
1365
199
          s,
1366
199
          &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
1367
0
    return SEC_E_INTERNAL_ERROR;
1368
199
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE"))
1369
0
    return SEC_E_INTERNAL_ERROR;
1370
1371
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1372
199
  {
1373
199
    if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
1374
0
      return SEC_E_INTERNAL_ERROR;
1375
199
  }
1376
1377
199
  if (context->UseMIC)
1378
199
  {
1379
199
    const BYTE data[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1380
1381
199
    context->MessageIntegrityCheckOffset = Stream_GetPosition(s);
1382
199
    if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data),
1383
199
                                            "NTLM_AUTHENTICATE_MESSAGE"))
1384
0
      return SEC_E_INTERNAL_ERROR;
1385
199
  }
1386
1387
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
1388
199
  {
1389
199
    if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1390
0
      return SEC_E_INTERNAL_ERROR;
1391
199
  }
1392
1393
199
  if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */
1394
0
    return SEC_E_INTERNAL_ERROR;
1395
1396
199
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1397
199
  {
1398
199
    if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1399
0
      return SEC_E_INTERNAL_ERROR;
1400
199
  }
1401
1402
199
  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
199
  if (!ntlm_write_message_fields_buffer(
1409
199
          s, &(message->NtChallengeResponse))) /* NtChallengeResponse */
1410
17
    return SEC_E_INTERNAL_ERROR;
1411
1412
182
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1413
61
  {
1414
61
    if (!ntlm_write_message_fields_buffer(
1415
61
            s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1416
1
      return SEC_E_INTERNAL_ERROR;
1417
61
  }
1418
1419
181
  length = Stream_GetPosition(s);
1420
181
  WINPR_ASSERT(length <= UINT32_MAX);
1421
1422
181
  if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1423
0
    return SEC_E_INTERNAL_ERROR;
1424
1425
181
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1426
181
  buffer->cbBuffer = (ULONG)length;
1427
1428
181
  if (context->UseMIC)
1429
181
  {
1430
    /* Message Integrity Check */
1431
181
    if (!ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
1432
181
                                              sizeof(message->MessageIntegrityCheck)))
1433
0
      return SEC_E_INTERNAL_ERROR;
1434
181
    if (!ntlm_write_message_integrity_check(
1435
181
            s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1436
181
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1437
12
      return SEC_E_INTERNAL_ERROR;
1438
181
  }
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
169
  ntlm_change_state(context, NTLM_STATE_FINAL);
1446
169
  return SEC_E_OK;
1447
181
}