Coverage Report

Created: 2026-10-15 06:31

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