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