Coverage Report

Created: 2024-05-20 06:11

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