Coverage Report

Created: 2026-05-11 07:01

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