Coverage Report

Created: 2026-06-15 06:57

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