Coverage Report

Created: 2026-09-14 06:32

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