Coverage Report

Created: 2026-07-16 07:14

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