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