Coverage Report

Created: 2026-09-01 06:39

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