Coverage Report

Created: 2026-07-30 07:11

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