Coverage Report

Created: 2026-08-31 06:29

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