Coverage Report

Created: 2026-10-28 06:54

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