Coverage Report

Created: 2026-09-04 06:58

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