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