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