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