Coverage Report

Created: 2026-08-13 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * NTLM Security Package (Message)
4
 *
5
 * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include "ntlm.h"
23
#include "../sspi.h"
24
25
#include <winpr/crt.h>
26
#include <winpr/assert.h>
27
#include <winpr/print.h>
28
#include <winpr/stream.h>
29
#include <winpr/sysinfo.h>
30
31
#include "ntlm_compute.h"
32
33
#include "ntlm_message.h"
34
35
#include "../../log.h"
36
1.84k
#define TAG WINPR_TAG("sspi.NTLM")
37
38
#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what)                                    \
39
9.46k
  Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
40
9.46k
                                       __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.64k
{
48
2.64k
  if (!nego)
49
0
    return;
50
51
2.64k
  ntlm_free_message_fields_buffer(&nego->Workstation);
52
2.64k
  ntlm_free_message_fields_buffer(&nego->DomainName);
53
2.64k
}
54
55
static void ntlm_free_challenge_message(NTLM_CHALLENGE_MESSAGE* challenge)
56
2.45k
{
57
2.45k
  if (!challenge)
58
0
    return;
59
2.45k
  ntlm_free_message_fields_buffer(&challenge->TargetName);
60
2.45k
  ntlm_free_message_fields_buffer(&challenge->TargetInfo);
61
2.45k
}
62
63
static void ntlm_free_authenticate_message(NTLM_AUTHENTICATE_MESSAGE* auth)
64
2.33k
{
65
2.33k
  if (!auth)
66
0
    return;
67
2.33k
  ntlm_free_message_fields_buffer(&auth->DomainName);
68
2.33k
  ntlm_free_message_fields_buffer(&auth->UserName);
69
2.33k
  ntlm_free_message_fields_buffer(&auth->Workstation);
70
2.33k
  ntlm_free_message_fields_buffer(&auth->LmChallengeResponse);
71
2.33k
  ntlm_free_message_fields_buffer(&auth->EncryptedRandomSessionKey);
72
2.33k
  ntlm_free_message_fields_buffer(&(auth->NtChallengeResponse));
73
2.33k
}
74
75
void ntlm_free_messages(NTLM_CONTEXT* context)
76
1.86k
{
77
1.86k
  if (!context)
78
0
    return;
79
1.86k
  ntlm_free_negotiate_message(&context->NEGOTIATE_MESSAGE);
80
1.86k
  ntlm_free_challenge_message(&context->CHALLENGE_MESSAGE);
81
1.86k
  ntlm_free_authenticate_message(&context->AUTHENTICATE_MESSAGE);
82
1.86k
}
83
84
static SecBuffer ntlm_move_buffer(NTLM_MESSAGE_FIELDS* field)
85
694
{
86
87
694
  WINPR_ASSERT(field);
88
694
  SecBuffer buffer = { .cbBuffer = field->Len,
89
694
                     .pvBuffer = field->Buffer,
90
694
                     .BufferType = SECBUFFER_TOKEN };
91
92
694
  field->Buffer = nullptr;
93
694
  field->BufferOffset = 0;
94
694
  field->Len = 0;
95
694
  field->MaxLen = 0;
96
97
694
  return buffer;
98
694
}
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
40.0k
{
113
40.0k
  if (flag & NTLMSSP_NEGOTIATE_56)
114
0
    return "NTLMSSP_NEGOTIATE_56";
115
40.0k
  if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH)
116
2.35k
    return "NTLMSSP_NEGOTIATE_KEY_EXCH";
117
37.7k
  if (flag & NTLMSSP_NEGOTIATE_128)
118
2.48k
    return "NTLMSSP_NEGOTIATE_128";
119
35.2k
  if (flag & NTLMSSP_RESERVED1)
120
272
    return "NTLMSSP_RESERVED1";
121
34.9k
  if (flag & NTLMSSP_RESERVED2)
122
346
    return "NTLMSSP_RESERVED2";
123
34.6k
  if (flag & NTLMSSP_RESERVED3)
124
427
    return "NTLMSSP_RESERVED3";
125
34.1k
  if (flag & NTLMSSP_NEGOTIATE_VERSION)
126
2.59k
    return "NTLMSSP_NEGOTIATE_VERSION";
127
31.5k
  if (flag & NTLMSSP_RESERVED4)
128
410
    return "NTLMSSP_RESERVED4";
129
31.1k
  if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO)
130
1.01k
    return "NTLMSSP_NEGOTIATE_TARGET_INFO";
131
30.1k
  if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY)
132
389
    return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY";
133
29.7k
  if (flag & NTLMSSP_RESERVED5)
134
513
    return "NTLMSSP_RESERVED5";
135
29.2k
  if (flag & NTLMSSP_NEGOTIATE_IDENTIFY)
136
380
    return "NTLMSSP_NEGOTIATE_IDENTIFY";
137
28.8k
  if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY)
138
2.53k
    return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY";
139
26.3k
  if (flag & NTLMSSP_RESERVED6)
140
391
    return "NTLMSSP_RESERVED6";
141
25.9k
  if (flag & NTLMSSP_TARGET_TYPE_SERVER)
142
394
    return "NTLMSSP_TARGET_TYPE_SERVER";
143
25.5k
  if (flag & NTLMSSP_TARGET_TYPE_DOMAIN)
144
541
    return "NTLMSSP_TARGET_TYPE_DOMAIN";
145
25.0k
  if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
146
2.54k
    return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN";
147
22.4k
  if (flag & NTLMSSP_RESERVED7)
148
587
    return "NTLMSSP_RESERVED7";
149
21.8k
  if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
150
836
    return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED";
151
21.0k
  if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
152
772
    return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED";
153
20.2k
  if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS)
154
451
    return "NTLMSSP_NEGOTIATE_ANONYMOUS";
155
19.8k
  if (flag & NTLMSSP_RESERVED8)
156
395
    return "NTLMSSP_RESERVED8";
157
19.4k
  if (flag & NTLMSSP_NEGOTIATE_NTLM)
158
2.89k
    return "NTLMSSP_NEGOTIATE_NTLM";
159
16.5k
  if (flag & NTLMSSP_RESERVED9)
160
502
    return "NTLMSSP_RESERVED9";
161
16.0k
  if (flag & NTLMSSP_NEGOTIATE_LM_KEY)
162
2.24k
    return "NTLMSSP_NEGOTIATE_LM_KEY";
163
13.7k
  if (flag & NTLMSSP_NEGOTIATE_DATAGRAM)
164
313
    return "NTLMSSP_NEGOTIATE_DATAGRAM";
165
13.4k
  if (flag & NTLMSSP_NEGOTIATE_SEAL)
166
2.58k
    return "NTLMSSP_NEGOTIATE_SEAL";
167
10.8k
  if (flag & NTLMSSP_NEGOTIATE_SIGN)
168
2.51k
    return "NTLMSSP_NEGOTIATE_SIGN";
169
8.37k
  if (flag & NTLMSSP_RESERVED10)
170
327
    return "NTLMSSP_RESERVED10";
171
8.04k
  if (flag & NTLMSSP_REQUEST_TARGET)
172
2.74k
    return "NTLMSSP_REQUEST_TARGET";
173
5.30k
  if (flag & NTLMSSP_NEGOTIATE_OEM)
174
2.46k
    return "NTLMSSP_NEGOTIATE_OEM";
175
2.84k
  if (flag & NTLMSSP_NEGOTIATE_UNICODE)
176
2.84k
    return "NTLMSSP_NEGOTIATE_UNICODE";
177
0
  return "NTLMSSP_NEGOTIATE_UNKNOWN";
178
2.84k
}
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.86k
{
311
1.86k
  WINPR_ASSERT(s);
312
1.86k
  WINPR_ASSERT(header);
313
314
1.86k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
315
87
    return FALSE;
316
317
1.77k
  Stream_Read(s, header->Signature, 8);
318
1.77k
  Stream_Read_UINT32(s, header->MessageType);
319
320
1.77k
  if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
321
85
  {
322
85
    char Signature[sizeof(header->Signature) * 3 + 1] = WINPR_C_ARRAY_INIT;
323
85
    winpr_BinToHexStringBuffer(header->Signature, sizeof(header->Signature), Signature,
324
85
                               sizeof(Signature), TRUE);
325
326
85
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s", Signature,
327
85
             NTLM_SIGNATURE);
328
85
    return FALSE;
329
85
  }
330
331
1.68k
  if (header->MessageType != expected)
332
84
  {
333
84
    WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message type, got %s, expected %s",
334
84
             ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected));
335
84
    return FALSE;
336
84
  }
337
338
1.60k
  return TRUE;
339
1.68k
}
340
341
static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header)
342
1.84k
{
343
1.84k
  WINPR_ASSERT(s);
344
1.84k
  WINPR_ASSERT(header);
345
346
1.84k
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, sizeof(NTLM_SIGNATURE) + 4ull,
347
1.84k
                                        "NTLM_MESSAGE_HEADER::header"))
348
0
    return FALSE;
349
350
1.84k
  Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE));
351
1.84k
  Stream_Write_UINT32(s, header->MessageType);
352
353
1.84k
  return TRUE;
354
1.84k
}
355
356
static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType)
357
1.84k
{
358
1.84k
  WINPR_ASSERT(header);
359
360
1.84k
  CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
361
1.84k
  header->MessageType = MessageType;
362
1.84k
  return TRUE;
363
1.84k
}
364
365
static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
366
4.63k
{
367
4.63k
  WINPR_ASSERT(s);
368
4.63k
  WINPR_ASSERT(fields);
369
370
4.63k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
371
35
    return FALSE;
372
373
4.60k
  ntlm_free_message_fields_buffer(fields);
374
375
4.60k
  Stream_Read_UINT16(s, fields->Len);          /* Len (2 bytes) */
376
4.60k
  Stream_Read_UINT16(s, fields->MaxLen);       /* MaxLen (2 bytes) */
377
4.60k
  Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
378
4.60k
  return TRUE;
379
4.63k
}
380
381
static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
382
4.40k
{
383
4.40k
  UINT16 MaxLen = 0;
384
4.40k
  WINPR_ASSERT(s);
385
4.40k
  WINPR_ASSERT(fields);
386
387
4.40k
  MaxLen = fields->MaxLen;
388
4.40k
  if (fields->MaxLen < 1)
389
4.40k
    MaxLen = fields->Len;
390
391
4.40k
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header"))
392
0
    return FALSE;
393
394
4.40k
  Stream_Write_UINT16(s, fields->Len);          /* Len (2 bytes) */
395
4.40k
  Stream_Write_UINT16(s, MaxLen);               /* MaxLen (2 bytes) */
396
4.40k
  Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
397
4.40k
  return TRUE;
398
4.40k
}
399
400
static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
401
4.00k
{
402
4.00k
  WINPR_ASSERT(s);
403
4.00k
  WINPR_ASSERT(fields);
404
405
4.00k
  ntlm_free_message_fields_buffer(fields);
406
4.00k
  if (fields->Len > 0)
407
1.80k
  {
408
1.80k
    const size_t offset = 1ull * fields->BufferOffset + fields->Len;
409
410
1.80k
    if (fields->BufferOffset > UINT32_MAX - fields->Len)
411
16
    {
412
16
      WLog_ERR(TAG,
413
16
               "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32
414
16
               " too large, maximum allowed is %" PRIu32,
415
16
               fields->BufferOffset, UINT32_MAX - fields->Len);
416
16
      return FALSE;
417
16
    }
418
419
1.79k
    if (offset > Stream_Length(s))
420
169
    {
421
169
      WLog_ERR(TAG,
422
169
               "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIuz " beyond received data %" PRIuz,
423
169
               offset, Stream_Length(s));
424
169
      return FALSE;
425
169
    }
426
427
1.62k
    fields->Buffer = (PBYTE)malloc(fields->Len);
428
429
1.62k
    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.62k
    if (!Stream_SetPosition(s, fields->BufferOffset))
437
0
    {
438
0
      ntlm_free_message_fields_buffer(fields);
439
0
      return FALSE;
440
0
    }
441
1.62k
    Stream_Read(s, fields->Buffer, fields->Len);
442
1.62k
  }
443
444
3.82k
  return TRUE;
445
4.00k
}
446
447
static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
448
1.94k
{
449
1.94k
  WINPR_ASSERT(s);
450
1.94k
  WINPR_ASSERT(fields);
451
452
1.94k
  if (fields->Len > 0)
453
1.94k
  {
454
1.94k
    if (!Stream_SetPosition(s, fields->BufferOffset))
455
0
      return FALSE;
456
1.94k
    if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len"))
457
18
      return FALSE;
458
459
1.92k
    Stream_Write(s, fields->Buffer, fields->Len);
460
1.92k
  }
461
1.92k
  return TRUE;
462
1.94k
}
463
464
void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields)
465
32.8k
{
466
32.8k
  if (!fields)
467
0
    return;
468
32.8k
  if (!fields->Buffer)
469
31.9k
    return;
470
471
929
  free(fields->Buffer);
472
929
  fields->Len = 0;
473
929
  fields->MaxLen = 0;
474
929
  fields->Buffer = nullptr;
475
929
  fields->BufferOffset = 0;
476
929
}
477
478
static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name)
479
1.59k
{
480
1.59k
  UINT32 NegotiateFlags = 0;
481
1.59k
  char buffer[1024] = WINPR_C_ARRAY_INIT;
482
1.59k
  WINPR_ASSERT(s);
483
1.59k
  WINPR_ASSERT(flags);
484
1.59k
  WINPR_ASSERT(name);
485
486
1.59k
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
487
5
    return FALSE;
488
489
1.58k
  Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */
490
491
1.58k
  if ((NegotiateFlags & required) != required)
492
6
  {
493
6
    WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required",
494
6
             name, NegotiateFlags, required);
495
6
    return FALSE;
496
6
  }
497
498
1.58k
  WLog_DBG(TAG, "Read flags %s",
499
1.58k
           ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags));
500
1.58k
  *flags = NegotiateFlags;
501
1.58k
  return TRUE;
502
1.58k
}
503
504
static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name)
505
1.84k
{
506
1.84k
  char buffer[1024] = WINPR_C_ARRAY_INIT;
507
1.84k
  WINPR_ASSERT(s);
508
1.84k
  WINPR_ASSERT(name);
509
510
1.84k
  if (!Stream_CheckAndLogRequiredCapacityEx(TAG, WLOG_WARN, s, 4ull, 1ull,
511
1.84k
                                            "%s(%s:%" PRIuz ") %s::NegotiateFlags", __func__,
512
1.84k
                                            __FILE__, (size_t)__LINE__, name))
513
0
    return FALSE;
514
515
1.84k
  WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags));
516
1.84k
  Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */
517
1.84k
  return TRUE;
518
1.84k
}
519
520
static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size,
521
                                              WINPR_ATTR_UNUSED const char* name)
522
108
{
523
108
  WINPR_ASSERT(s);
524
108
  WINPR_ASSERT(offset);
525
108
  WINPR_ASSERT(data);
526
108
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
527
108
  WINPR_ASSERT(name);
528
529
108
  *offset = Stream_GetPosition(s);
530
531
108
  if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
532
2
    return FALSE;
533
534
106
  Stream_Read(s, data, size);
535
106
  return TRUE;
536
108
}
537
538
static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data,
539
                                               size_t size, WINPR_ATTR_UNUSED const char* name)
540
338
{
541
338
  WINPR_ASSERT(s);
542
338
  WINPR_ASSERT(data);
543
338
  WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
544
338
  WINPR_ASSERT(name);
545
546
338
  const size_t pos = Stream_GetPosition(s);
547
548
338
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset"))
549
10
    return FALSE;
550
551
328
  if (!Stream_SetPosition(s, offset))
552
0
    return FALSE;
553
328
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size"))
554
0
    return FALSE;
555
556
328
  Stream_Write(s, data, size);
557
328
  return Stream_SetPosition(s, pos);
558
328
}
559
560
SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
561
784
{
562
784
  wStream sbuffer = WINPR_C_ARRAY_INIT;
563
564
784
  WINPR_ASSERT(context);
565
784
  WINPR_ASSERT(buffer);
566
567
784
  NTLM_NEGOTIATE_MESSAGE* message = &context->NEGOTIATE_MESSAGE;
568
784
  WINPR_ASSERT(message);
569
570
784
  ntlm_free_negotiate_message(message);
571
572
784
  SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
573
574
784
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
575
576
784
  if (!s)
577
0
    goto fail;
578
579
784
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE))
580
68
    goto fail;
581
582
716
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags,
583
716
                                 NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
584
716
                                     NTLMSSP_NEGOTIATE_UNICODE,
585
716
                                 "NTLM_NEGOTIATE_MESSAGE"))
586
7
    goto fail;
587
588
709
  context->NegotiateFlags = message->NegotiateFlags;
589
590
  /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
591
  // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
592
709
  {
593
709
    if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
594
17
      goto fail;
595
709
  }
596
597
  /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
598
  // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
599
692
  {
600
692
    if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
601
2
      goto fail;
602
692
  }
603
604
690
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
605
586
  {
606
586
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
607
1
      goto fail;
608
586
  }
609
610
689
  if (!ntlm_read_message_fields_buffer(s, &message->DomainName))
611
53
    goto fail;
612
613
636
  if (!ntlm_read_message_fields_buffer(s, &message->Workstation))
614
42
    goto fail;
615
616
594
  const size_t length = Stream_GetPosition(s);
617
594
  WINPR_ASSERT(length <= UINT32_MAX);
618
594
  if (buffer->cbBuffer < length)
619
0
    return SEC_E_INVALID_PARAMETER;
620
621
594
  status = SEC_E_INTERNAL_ERROR;
622
594
  if (!ntlm_SecBufferRealloc(&context->NegotiateMessage, (ULONG)length))
623
0
    goto fail;
624
625
594
  CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, length);
626
594
  context->NegotiateMessage.BufferType = buffer->BufferType;
627
#if defined(WITH_DEBUG_NTLM)
628
  ntlm_print_negotiate_message(&context->NegotiateMessage, message);
629
#endif
630
594
  ntlm_change_state(context, NTLM_STATE_CHALLENGE);
631
594
  return SEC_I_CONTINUE_NEEDED;
632
633
190
fail:
634
190
  return status;
635
594
}
636
637
SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
638
1.07k
{
639
1.07k
  wStream sbuffer = WINPR_C_ARRAY_INIT;
640
1.07k
  size_t length = 0;
641
1.07k
  NTLM_NEGOTIATE_MESSAGE empty = WINPR_C_ARRAY_INIT;
642
643
1.07k
  WINPR_ASSERT(context);
644
1.07k
  WINPR_ASSERT(buffer);
645
646
1.07k
  NTLM_NEGOTIATE_MESSAGE* message = &empty;
647
648
1.07k
  wStream* s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
649
650
1.07k
  if (!s)
651
0
    return SEC_E_INTERNAL_ERROR;
652
653
1.07k
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE))
654
0
    return SEC_E_INTERNAL_ERROR;
655
656
1.07k
  if (context->NTLMv2)
657
1.07k
  {
658
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
659
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
660
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
661
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
662
1.07k
  }
663
664
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
665
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
666
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
667
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
668
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
669
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
670
1.07k
  message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
671
1.07k
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
672
673
1.07k
  if (context->confidentiality)
674
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
675
676
1.07k
  if (context->SendVersionInfo)
677
1.07k
    message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
678
679
1.07k
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
680
1.07k
  {
681
1.07k
    if (!ntlm_get_version_info(&(message->Version)))
682
0
      return SEC_E_INTERNAL_ERROR;
683
1.07k
  }
684
685
1.07k
  context->NegotiateFlags = message->NegotiateFlags;
686
  /* Message Header (12 bytes) */
687
1.07k
  if (!ntlm_write_message_header(s, &message->header))
688
0
    return SEC_E_INTERNAL_ERROR;
689
690
1.07k
  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.07k
  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.07k
  if (!ntlm_write_message_fields(s, &(message->Workstation)))
701
0
    return SEC_E_INTERNAL_ERROR;
702
703
1.07k
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
704
1.07k
  {
705
1.07k
    if (!ntlm_write_version_info(s, &(message->Version)))
706
0
      return SEC_E_INTERNAL_ERROR;
707
1.07k
  }
708
709
1.07k
  length = Stream_GetPosition(s);
710
1.07k
  WINPR_ASSERT(length <= UINT32_MAX);
711
1.07k
  buffer->cbBuffer = (ULONG)length;
712
713
1.07k
  if (!ntlm_SecBufferRealloc(&context->NegotiateMessage, (ULONG)length))
714
0
    return SEC_E_INTERNAL_ERROR;
715
716
1.07k
  CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
717
1.07k
  context->NegotiateMessage.BufferType = buffer->BufferType;
718
#if defined(WITH_DEBUG_NTLM)
719
  ntlm_print_negotiate_message(&context->NegotiateMessage, message);
720
#endif
721
1.07k
  ntlm_change_state(context, NTLM_STATE_CHALLENGE);
722
1.07k
  return SEC_I_CONTINUE_NEEDED;
723
1.07k
}
724
725
SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
726
597
{
727
597
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
728
597
  wStream sbuffer = WINPR_C_ARRAY_INIT;
729
597
  size_t length = 0;
730
597
  size_t StartOffset = 0;
731
597
  size_t PayloadOffset = 0;
732
733
597
  if (!context || !buffer)
734
0
    return SEC_E_INTERNAL_ERROR;
735
736
597
  if (!ntlm_generate_client_challenge(context))
737
0
    return SEC_E_INTERNAL_ERROR;
738
739
597
  NTLM_CHALLENGE_MESSAGE* message = &context->CHALLENGE_MESSAGE;
740
597
  WINPR_ASSERT(message);
741
742
597
  ntlm_free_challenge_message(message);
743
744
597
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
745
746
597
  if (!s)
747
0
    return SEC_E_INTERNAL_ERROR;
748
749
597
  StartOffset = Stream_GetPosition(s);
750
751
597
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE))
752
83
    goto fail;
753
754
514
  if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */
755
2
    goto fail;
756
757
512
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE"))
758
3
    goto fail;
759
760
509
  context->NegotiateFlags = message->NegotiateFlags;
761
762
509
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
763
5
    goto fail;
764
765
504
  Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
766
504
  CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
767
504
  Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
768
769
504
  if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */
770
5
    goto fail;
771
772
499
  if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
773
69
  {
774
69
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
775
1
      goto fail;
776
69
  }
777
778
  /* Payload (variable) */
779
498
  PayloadOffset = Stream_GetPosition(s);
780
781
498
  status = SEC_E_INTERNAL_ERROR;
782
498
  if (message->TargetName.Len > 0)
783
280
  {
784
280
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetName)))
785
18
      goto fail;
786
280
  }
787
788
480
  if (message->TargetInfo.Len > 0)
789
471
  {
790
471
    size_t cbAvTimestamp = 0;
791
792
471
    if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo)))
793
30
      goto fail;
794
795
441
    sspi_SecBufferFree(&context->ChallengeTargetInfo);
796
441
    context->ChallengeTargetInfo = ntlm_move_buffer(&message->TargetInfo);
797
798
441
    NTLM_AV_PAIR* AvTimestamp =
799
441
        ntlm_av_pair_get((NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer,
800
441
                         context->ChallengeTargetInfo.cbBuffer, MsvAvTimestamp, &cbAvTimestamp);
801
802
441
    if (AvTimestamp)
803
115
    {
804
115
      PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp, cbAvTimestamp);
805
806
115
      if (!ptr || (AvTimestamp->AvLen < 8))
807
10
        goto fail;
808
809
105
      if (context->NTLMv2)
810
105
        context->UseMIC = TRUE;
811
812
105
      CopyMemory(context->ChallengeTimestamp, ptr, 8);
813
105
    }
814
441
  }
815
816
440
  length = (PayloadOffset - StartOffset) + message->TargetName.Len +
817
440
           context->ChallengeTargetInfo.cbBuffer;
818
440
  if (length > buffer->cbBuffer)
819
105
    goto fail;
820
821
335
  if (!ntlm_SecBufferRealloc(&context->ChallengeMessage, (ULONG)length))
822
0
    goto fail;
823
824
335
  if (context->ChallengeMessage.pvBuffer)
825
335
    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
335
  if (context->NTLMv2)
832
335
  {
833
335
    if (!ntlm_construct_authenticate_target_info(context))
834
157
      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
597
fail:
890
597
  return status;
891
178
}
892
893
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
894
594
{
895
594
  wStream sbuffer = WINPR_C_ARRAY_INIT;
896
594
  size_t length = 0;
897
594
  UINT32 PayloadOffset = 0;
898
594
  NTLM_CHALLENGE_MESSAGE empty = WINPR_C_ARRAY_INIT;
899
900
594
  WINPR_ASSERT(context);
901
594
  WINPR_ASSERT(buffer);
902
903
594
  NTLM_CHALLENGE_MESSAGE* message = &empty;
904
905
594
  wStream* s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
906
907
594
  if (!s)
908
0
    return SEC_E_INTERNAL_ERROR;
909
910
594
  if (!ntlm_get_version_info(&(message->Version))) /* Version */
911
0
    return SEC_E_INTERNAL_ERROR;
912
594
  if (!ntlm_generate_server_challenge(context)) /* Server Challenge */
913
0
    return SEC_E_INTERNAL_ERROR;
914
594
  ntlm_generate_timestamp(context);           /* Timestamp */
915
916
594
  if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */
917
0
    return SEC_E_INTERNAL_ERROR;
918
919
594
  CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
920
594
  message->NegotiateFlags = context->NegotiateFlags;
921
594
  if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE))
922
0
    return SEC_E_INTERNAL_ERROR;
923
924
  /* Message Header (12 bytes) */
925
594
  if (!ntlm_write_message_header(s, &message->header))
926
0
    return SEC_E_INTERNAL_ERROR;
927
928
594
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
929
594
  {
930
594
    message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
931
594
    message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
932
594
  }
933
934
594
  message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
935
936
594
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
937
594
  {
938
594
    message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
939
594
    message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
940
594
  }
941
942
594
  PayloadOffset = 48;
943
944
594
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
945
560
    PayloadOffset += 8;
946
947
594
  message->TargetName.BufferOffset = PayloadOffset;
948
594
  message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
949
  /* TargetNameFields (8 bytes) */
950
594
  if (!ntlm_write_message_fields(s, &(message->TargetName)))
951
0
    return SEC_E_INTERNAL_ERROR;
952
953
594
  if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE"))
954
0
    return SEC_E_INTERNAL_ERROR;
955
956
594
  if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge"))
957
0
    return SEC_E_INTERNAL_ERROR;
958
959
594
  Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
960
594
  Stream_Write(s, message->Reserved, 8);        /* Reserved (8 bytes), should be ignored */
961
962
  /* TargetInfoFields (8 bytes) */
963
594
  if (!ntlm_write_message_fields(s, &(message->TargetInfo)))
964
0
    return SEC_E_INTERNAL_ERROR;
965
966
594
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
967
560
  {
968
560
    if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
969
0
      return SEC_E_INTERNAL_ERROR;
970
560
  }
971
972
  /* Payload (variable) */
973
594
  if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
974
594
  {
975
594
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetName)))
976
0
      return SEC_E_INTERNAL_ERROR;
977
594
  }
978
979
594
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
980
594
  {
981
594
    if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo)))
982
0
      return SEC_E_INTERNAL_ERROR;
983
594
  }
984
985
594
  length = Stream_GetPosition(s);
986
594
  WINPR_ASSERT(length <= UINT32_MAX);
987
594
  buffer->cbBuffer = (ULONG)length;
988
989
594
  if (!ntlm_SecBufferRealloc(&context->ChallengeMessage, (ULONG)length))
990
0
    return SEC_E_INTERNAL_ERROR;
991
992
594
  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
594
  ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
998
594
  return SEC_I_CONTINUE_NEEDED;
999
594
}
1000
1001
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
1002
479
{
1003
479
  SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
1004
479
  wStream sbuffer = WINPR_C_ARRAY_INIT;
1005
479
  size_t length = 0;
1006
479
  UINT32 flags = 0;
1007
479
  NTLM_AV_PAIR* AvFlags = nullptr;
1008
479
  size_t PayloadBufferOffset = 0;
1009
1010
479
  WINPR_ASSERT(context);
1011
479
  WINPR_ASSERT(buffer);
1012
1013
479
  SSPI_CREDENTIALS* credentials = context->credentials;
1014
479
  WINPR_ASSERT(credentials);
1015
1016
479
  NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
1017
479
  WINPR_ASSERT(message);
1018
1019
479
  ntlm_free_authenticate_message(message);
1020
1021
479
  wStream* s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
1022
1023
479
  if (!s)
1024
0
    return SEC_E_INTERNAL_ERROR;
1025
1026
479
  if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE))
1027
105
    goto fail;
1028
1029
374
  if (!ntlm_read_message_fields(
1030
374
          s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
1031
1
    goto fail;
1032
1033
373
  if (!ntlm_read_message_fields(
1034
373
          s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
1035
3
    goto fail;
1036
1037
370
  if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
1038
2
    goto fail;
1039
1040
368
  if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
1041
1
    goto fail;
1042
1043
367
  if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
1044
1
    goto fail;
1045
1046
366
  if (!ntlm_read_message_fields(
1047
366
          s,
1048
366
          &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
1049
1
    goto fail;
1050
1051
365
  if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE"))
1052
1
    goto fail;
1053
1054
364
  context->NegotiateKeyExchange = (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0;
1055
1056
364
  if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
1057
362
      (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
1058
21
    goto fail;
1059
1060
343
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1061
105
  {
1062
105
    if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
1063
6
      goto fail;
1064
105
  }
1065
1066
337
  PayloadBufferOffset = Stream_GetPosition(s);
1067
1068
337
  status = SEC_E_INTERNAL_ERROR;
1069
337
  if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1070
6
    goto fail;
1071
1072
331
  if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */
1073
3
    goto fail;
1074
1075
328
  if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1076
6
    goto fail;
1077
1078
322
  if (!ntlm_read_message_fields_buffer(s,
1079
322
                                       &(message->LmChallengeResponse))) /* LmChallengeResponse */
1080
4
    goto fail;
1081
1082
318
  if (!ntlm_read_message_fields_buffer(s,
1083
318
                                       &(message->NtChallengeResponse))) /* NtChallengeResponse */
1084
10
    goto fail;
1085
1086
308
  if (ntlm_SetContextWorkstationX(context, TRUE, message->Workstation.Buffer,
1087
308
                                  message->Workstation.Len) != SEC_E_OK)
1088
1
    goto fail;
1089
1090
307
  if (message->NtChallengeResponse.Len > 0)
1091
258
  {
1092
258
    size_t cbAvFlags = 0;
1093
258
    wStream ssbuffer;
1094
258
    wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
1095
258
                                          message->NtChallengeResponse.Len);
1096
1097
258
    if (!snt)
1098
0
      goto fail;
1099
1100
258
    status = SEC_E_INVALID_TOKEN;
1101
258
    if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
1102
5
      goto fail;
1103
253
    status = SEC_E_INTERNAL_ERROR;
1104
1105
253
    sspi_SecBufferFree(&context->NtChallengeResponse);
1106
253
    context->NtChallengeResponse = ntlm_move_buffer(&message->NtChallengeResponse);
1107
1108
253
    sspi_SecBufferFree(&(context->ChallengeTargetInfo));
1109
253
    context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
1110
253
    context->ChallengeTargetInfo.cbBuffer = context->NtChallengeResponse.cbBuffer - (28 + 16);
1111
253
    CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
1112
253
    AvFlags =
1113
253
        ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
1114
253
                         context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
1115
1116
253
    if (AvFlags)
1117
116
    {
1118
116
      const BYTE* ptr = ntlm_av_pair_get_value_pointer(AvFlags, cbAvFlags);
1119
116
      if (!ptr || (AvFlags->AvLen < 4))
1120
6
        goto fail;
1121
110
      flags = winpr_Data_Get_UINT32(ptr);
1122
110
    }
1123
253
  }
1124
1125
296
  if (!ntlm_read_message_fields_buffer(
1126
296
          s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1127
13
    goto fail;
1128
1129
283
  if (message->EncryptedRandomSessionKey.Len > 0)
1130
18
  {
1131
18
    if (message->EncryptedRandomSessionKey.Len != 16)
1132
14
      goto fail;
1133
1134
4
    CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
1135
4
               16);
1136
4
  }
1137
1138
269
  length = Stream_GetPosition(s);
1139
269
  WINPR_ASSERT(length <= UINT32_MAX);
1140
1141
269
  if (!ntlm_SecBufferRealloc(&context->AuthenticateMessage, (ULONG)length))
1142
0
    goto fail;
1143
1144
269
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1145
269
  buffer->cbBuffer = (ULONG)length;
1146
269
  if (!Stream_SetPosition(s, PayloadBufferOffset))
1147
0
    goto fail;
1148
1149
269
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1150
108
  {
1151
108
    status = SEC_E_INVALID_TOKEN;
1152
108
    if (!ntlm_read_message_integrity_check(
1153
108
            s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1154
108
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1155
2
      goto fail;
1156
108
  }
1157
1158
267
  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
267
  if (message->UserName.Len > 0)
1165
80
  {
1166
80
    credentials->identity.User = (UINT16*)calloc(message->UserName.Len + sizeof(WCHAR), 1);
1167
1168
80
    if (!credentials->identity.User)
1169
0
      goto fail;
1170
1171
80
    CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
1172
80
    credentials->identity.UserLength = message->UserName.Len / sizeof(WCHAR);
1173
80
  }
1174
1175
267
  if (message->DomainName.Len > 0)
1176
53
  {
1177
53
    credentials->identity.Domain = (UINT16*)calloc(message->DomainName.Len + sizeof(WCHAR), 1);
1178
1179
53
    if (!credentials->identity.Domain)
1180
0
      goto fail;
1181
1182
53
    CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
1183
53
               message->DomainName.Len);
1184
53
    credentials->identity.DomainLength = message->DomainName.Len / sizeof(WCHAR);
1185
53
  }
1186
1187
267
  if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
1188
267
  {
1189
267
    const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */
1190
267
    if (rc != SEC_E_OK)
1191
0
    {
1192
0
      status = rc;
1193
0
      goto fail;
1194
0
    }
1195
267
  }
1196
1197
267
  {
1198
267
    const SECURITY_STATUS rc = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */
1199
267
    if (rc != SEC_E_OK)
1200
0
    {
1201
0
      status = rc;
1202
0
      goto fail;
1203
0
    }
1204
267
  }
1205
1206
  /* KeyExchangeKey */
1207
267
  if (!ntlm_generate_key_exchange_key(context))
1208
0
    goto fail;
1209
  /* EncryptedRandomSessionKey */
1210
267
  if (!ntlm_decrypt_random_session_key(context))
1211
0
    goto fail;
1212
  /* ExportedSessionKey */
1213
267
  if (!ntlm_generate_exported_session_key(context))
1214
0
    goto fail;
1215
1216
267
  if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1217
106
  {
1218
106
    BYTE messageIntegrityCheck[16] = WINPR_C_ARRAY_INIT;
1219
1220
106
    if (!ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
1221
106
                                              sizeof(messageIntegrityCheck)))
1222
7
      goto fail;
1223
99
    CopyMemory(
1224
99
        &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
1225
99
        message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
1226
1227
99
    if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
1228
99
               sizeof(message->MessageIntegrityCheck)) != 0)
1229
99
    {
1230
99
      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
99
      status = SEC_E_MESSAGE_ALTERED;
1239
99
      goto fail;
1240
99
    }
1241
99
  }
1242
161
  else
1243
161
  {
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
161
    if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
1259
161
    {
1260
161
      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
161
      status = SEC_E_LOGON_DENIED;
1269
161
      goto fail;
1270
161
    }
1271
161
  }
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
479
fail:
1293
479
  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
56
    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
56
  {
1379
56
    message->EncryptedRandomSessionKey.Len = 16;
1380
56
    message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
1381
56
  }
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
18
    return SEC_E_INTERNAL_ERROR;
1463
1464
160
  if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1465
47
  {
1466
47
    if (!ntlm_write_message_fields_buffer(
1467
47
            s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1468
0
      return SEC_E_INTERNAL_ERROR;
1469
47
  }
1470
1471
160
  length = Stream_GetPosition(s);
1472
160
  WINPR_ASSERT(length <= UINT32_MAX);
1473
1474
160
  if (!ntlm_SecBufferRealloc(&context->AuthenticateMessage, (ULONG)length))
1475
0
    return SEC_E_INTERNAL_ERROR;
1476
1477
160
  CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1478
160
  buffer->cbBuffer = (ULONG)length;
1479
1480
160
  if (context->UseMIC)
1481
160
  {
1482
    /* Message Integrity Check */
1483
160
    if (!ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
1484
160
                                              sizeof(message->MessageIntegrityCheck)))
1485
0
      return SEC_E_INTERNAL_ERROR;
1486
160
    if (!ntlm_write_message_integrity_check(
1487
160
            s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1488
160
            sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1489
10
      return SEC_E_INTERNAL_ERROR;
1490
160
  }
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
150
  ntlm_change_state(context, NTLM_STATE_FINAL);
1498
150
  return SEC_E_OK;
1499
160
}