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 (ntlm_SetContextWorkstationX(context, TRUE, message->Workstation.Buffer,
1026 message->Workstation.Len) != SEC_E_OK)
1027 goto fail;
1028
1029 if (message->NtChallengeResponse.Len > 0)
1030 {
1031 size_t cbAvFlags = 0;
1032 wStream ssbuffer;
1033 wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
1034 message->NtChallengeResponse.Len);
1035
1036 if (!snt)
1037 goto fail;
1038
1039 status = SEC_E_INVALID_TOKEN;
1040 if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
1041 goto fail;
1042 status = SEC_E_INTERNAL_ERROR;
1043
1044 context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
1045 context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
1046 sspi_SecBufferFree(&(context->ChallengeTargetInfo));
1047 context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
1048 context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
1049 CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
1050 AvFlags =
1051 ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
1052 context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
1053
1054 if (AvFlags)
1055 {
1056 const BYTE* ptr = ntlm_av_pair_get_value_pointer(AvFlags, cbAvFlags);
1057 if (!ptr || (AvFlags->AvLen < 4))
1058 goto fail;
1059 flags = winpr_Data_Get_UINT32(ptr);
1060 }
1061 }
1062
1063 if (!ntlm_read_message_fields_buffer(
1064 s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1065 goto fail;
1066
1067 if (message->EncryptedRandomSessionKey.Len > 0)
1068 {
1069 if (message->EncryptedRandomSessionKey.Len != 16)
1070 goto fail;
1071
1072 CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
1073 16);
1074 }
1075
1076 length = Stream_GetPosition(s);
1077 WINPR_ASSERT(length <= UINT32_MAX);
1078
1079 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1080 goto fail;
1081
1082 CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1083 buffer->cbBuffer = (ULONG)length;
1084 if (!Stream_SetPosition(s, PayloadBufferOffset))
1085 goto fail;
1086
1087 if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1088 {
1089 status = SEC_E_INVALID_TOKEN;
1090 if (!ntlm_read_message_integrity_check(
1091 s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1092 sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1093 goto fail;
1094 }
1095
1096 status = SEC_E_INTERNAL_ERROR;
1097
1098#if defined(WITH_DEBUG_NTLM)
1099 ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, nullptr);
1100#endif
1101
1102 if (message->UserName.Len > 0)
1103 {
1104 credentials->identity.User = (UINT16*)calloc(message->UserName.Len + sizeof(WCHAR), 1);
1105
1106 if (!credentials->identity.User)
1107 goto fail;
1108
1109 CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
1110 credentials->identity.UserLength = message->UserName.Len / sizeof(WCHAR);
1111 }
1112
1113 if (message->DomainName.Len > 0)
1114 {
1115 credentials->identity.Domain = (UINT16*)calloc(message->DomainName.Len + sizeof(WCHAR), 1);
1116
1117 if (!credentials->identity.Domain)
1118 goto fail;
1119
1120 CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
1121 message->DomainName.Len);
1122 credentials->identity.DomainLength = message->DomainName.Len / sizeof(WCHAR);
1123 }
1124
1125 if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
1126 {
1127 const SECURITY_STATUS rc = ntlm_compute_lm_v2_response(context); /* LmChallengeResponse */
1128 if (rc != SEC_E_OK)
1129 {
1130 status = rc;
1131 goto fail;
1132 }
1133 }
1134
1135 {
1136 const SECURITY_STATUS rc = ntlm_compute_ntlm_v2_response(context); /* NtChallengeResponse */
1137 if (rc != SEC_E_OK)
1138 {
1139 status = rc;
1140 goto fail;
1141 }
1142 }
1143
1144 /* KeyExchangeKey */
1145 if (!ntlm_generate_key_exchange_key(context))
1146 goto fail;
1147 /* EncryptedRandomSessionKey */
1148 if (!ntlm_decrypt_random_session_key(context))
1149 goto fail;
1150 /* ExportedSessionKey */
1151 if (!ntlm_generate_exported_session_key(context))
1152 goto fail;
1153
1154 if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
1155 {
1156 BYTE messageIntegrityCheck[16] = WINPR_C_ARRAY_INIT;
1157
1158 if (!ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
1159 sizeof(messageIntegrityCheck)))
1160 goto fail;
1161 CopyMemory(
1162 &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
1163 message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
1164
1165 if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
1166 sizeof(message->MessageIntegrityCheck)) != 0)
1167 {
1168 WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!");
1169#ifdef WITH_DEBUG_NTLM
1170 WLog_ERR(TAG, "Expected MIC:");
1171 winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, sizeof(messageIntegrityCheck));
1172 WLog_ERR(TAG, "Actual MIC:");
1173 winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck,
1174 sizeof(message->MessageIntegrityCheck));
1175#endif
1176 status = SEC_E_MESSAGE_ALTERED;
1177 goto fail;
1178 }
1179 }
1180 else
1181 {
1182 /* no mic message was present
1183
1184 https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f9e6fbc4-a953-4f24-b229-ccdcc213b9ec
1185 the mic is optional, as not supported in Windows NT, Windows 2000, Windows XP, and
1186 Windows Server 2003 and, as it seems, in the NTLMv2 implementation of Qt5.
1187
1188 now check the NtProofString, to detect if the entered client password matches the
1189 expected password.
1190 */
1191
1192#ifdef WITH_DEBUG_NTLM
1193 WLog_VRB(TAG, "No MIC present, using NtProofString for verification.");
1194#endif
1195
1196 if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
1197 {
1198 WLog_ERR(TAG, "NtProofString verification failed!");
1199#ifdef WITH_DEBUG_NTLM
1200 WLog_ERR(TAG, "Expected NtProofString:");
1201 winpr_HexDump(TAG, WLOG_ERROR, context->NtProofString, sizeof(context->NtProofString));
1202 WLog_ERR(TAG, "Actual NtProofString:");
1203 winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response,
1204 sizeof(context->NTLMv2Response));
1205#endif
1206 status = SEC_E_LOGON_DENIED;
1207 goto fail;
1208 }
1209 }
1210
1211 /* Generate signing keys */
1212 if (!ntlm_generate_client_signing_key(context))
1213 goto fail;
1214 if (!ntlm_generate_server_signing_key(context))
1215 goto fail;
1216 /* Generate sealing keys */
1217 if (!ntlm_generate_client_sealing_key(context))
1218 goto fail;
1219 if (!ntlm_generate_server_sealing_key(context))
1220 goto fail;
1221 /* Initialize RC4 seal state */
1222 if (!ntlm_init_rc4_seal_states(context))
1223 goto fail;
1224#if defined(WITH_DEBUG_NTLM)
1225 ntlm_print_authentication_complete(context);
1226#endif
1227 ntlm_change_state(context, NTLM_STATE_FINAL);
1228 status = SEC_E_OK;
1229
1230fail:
1231 ntlm_free_message_fields_buffer(&(message->DomainName));
1232 ntlm_free_message_fields_buffer(&(message->UserName));
1233 ntlm_free_message_fields_buffer(&(message->Workstation));
1234 ntlm_free_message_fields_buffer(&(message->LmChallengeResponse));
1235 /* NtChallengeResponse.Buffer is aliased to context->NtChallengeResponse.pvBuffer at
1236 * L1048 until ntlm_compute_ntlm_v2_response() reallocates the context buffer. Only
1237 * free message->NtChallengeResponse when the alias does not hold, otherwise
1238 * ntlm_ContextFree() frees the same pointer via context->NtChallengeResponse. */
1239 if (context->NtChallengeResponse.pvBuffer != message->NtChallengeResponse.Buffer)
1240 ntlm_free_message_fields_buffer(&(message->NtChallengeResponse));
1241 ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey));
1242 return status;
1243}
1244
1252SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer)
1253{
1254 wStream sbuffer = WINPR_C_ARRAY_INIT;
1255 size_t length = 0;
1256 UINT32 PayloadBufferOffset = 0;
1257 const NTLM_AUTHENTICATE_MESSAGE empty = WINPR_C_ARRAY_INIT;
1258
1259 WINPR_ASSERT(context);
1260 WINPR_ASSERT(buffer);
1261
1262 SSPI_CREDENTIALS* credentials = context->credentials;
1263 WINPR_ASSERT(credentials);
1264
1265 NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
1266 WINPR_ASSERT(message);
1267
1268 *message = empty;
1269
1270 wStream* s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
1271
1272 if (!s)
1273 return SEC_E_INTERNAL_ERROR;
1274
1275 if (context->NTLMv2)
1276 {
1277 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
1278
1279 if (context->SendVersionInfo)
1280 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
1281 }
1282
1283 if (context->UseMIC)
1284 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
1285
1286 if (context->SendWorkstationName)
1287 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
1288
1289 if (context->confidentiality)
1290 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
1291
1292 if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1293 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
1294
1295 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
1296 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
1297 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
1298 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
1299 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
1300 message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
1301 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
1302
1303 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1304 {
1305 if (!ntlm_get_version_info(&(message->Version)))
1306 return SEC_E_INTERNAL_ERROR;
1307 }
1308
1309 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1310 {
1311 message->Workstation.Len = context->Workstation.Length;
1312 message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
1313 }
1314
1315 if (credentials->identity.DomainLength > 0)
1316 {
1317 message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
1318 message->DomainName.Len = (UINT16)credentials->identity.DomainLength * sizeof(WCHAR);
1319 message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
1320 }
1321
1322 message->UserName.Len = (UINT16)credentials->identity.UserLength * sizeof(WCHAR);
1323 message->UserName.Buffer = (BYTE*)credentials->identity.User;
1324 message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer;
1325 message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer;
1326 message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer;
1327 message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer;
1328
1329 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1330 {
1331 message->EncryptedRandomSessionKey.Len = 16;
1332 message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
1333 }
1334
1335 PayloadBufferOffset = 64;
1336
1337 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1338 PayloadBufferOffset += 8; /* Version (8 bytes) */
1339
1340 if (context->UseMIC)
1341 PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
1342
1343 message->DomainName.BufferOffset = PayloadBufferOffset;
1344 message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
1345 message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
1346 message->LmChallengeResponse.BufferOffset =
1347 message->Workstation.BufferOffset + message->Workstation.Len;
1348 message->NtChallengeResponse.BufferOffset =
1349 message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len;
1350 message->EncryptedRandomSessionKey.BufferOffset =
1351 message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len;
1352 if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE))
1353 return SEC_E_INVALID_TOKEN;
1354 if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */
1355 return SEC_E_INTERNAL_ERROR;
1356 if (!ntlm_write_message_fields(
1357 s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
1358 return SEC_E_INTERNAL_ERROR;
1359 if (!ntlm_write_message_fields(
1360 s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
1361 return SEC_E_INTERNAL_ERROR;
1362 if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
1363 return SEC_E_INTERNAL_ERROR;
1364 if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
1365 return SEC_E_INTERNAL_ERROR;
1366 if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
1367 return SEC_E_INTERNAL_ERROR;
1368 if (!ntlm_write_message_fields(
1369 s,
1370 &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
1371 return SEC_E_INTERNAL_ERROR;
1372 if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE"))
1373 return SEC_E_INTERNAL_ERROR;
1374
1375 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
1376 {
1377 if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
1378 return SEC_E_INTERNAL_ERROR;
1379 }
1380
1381 if (context->UseMIC)
1382 {
1383 const BYTE data[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1384
1385 context->MessageIntegrityCheckOffset = Stream_GetPosition(s);
1386 if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data),
1387 "NTLM_AUTHENTICATE_MESSAGE"))
1388 return SEC_E_INTERNAL_ERROR;
1389 }
1390
1391 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
1392 {
1393 if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
1394 return SEC_E_INTERNAL_ERROR;
1395 }
1396
1397 if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */
1398 return SEC_E_INTERNAL_ERROR;
1399
1400 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
1401 {
1402 if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
1403 return SEC_E_INTERNAL_ERROR;
1404 }
1405
1406 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
1407 {
1408 if (!ntlm_write_message_fields_buffer(
1409 s, &(message->LmChallengeResponse))) /* LmChallengeResponse */
1410 return SEC_E_INTERNAL_ERROR;
1411 }
1412 if (!ntlm_write_message_fields_buffer(
1413 s, &(message->NtChallengeResponse))) /* NtChallengeResponse */
1414 return SEC_E_INTERNAL_ERROR;
1415
1416 if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
1417 {
1418 if (!ntlm_write_message_fields_buffer(
1419 s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
1420 return SEC_E_INTERNAL_ERROR;
1421 }
1422
1423 length = Stream_GetPosition(s);
1424 WINPR_ASSERT(length <= UINT32_MAX);
1425
1426 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
1427 return SEC_E_INTERNAL_ERROR;
1428
1429 CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
1430 buffer->cbBuffer = (ULONG)length;
1431
1432 if (context->UseMIC)
1433 {
1434 /* Message Integrity Check */
1435 if (!ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
1436 sizeof(message->MessageIntegrityCheck)))
1437 return SEC_E_INTERNAL_ERROR;
1438 if (!ntlm_write_message_integrity_check(
1439 s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
1440 sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
1441 return SEC_E_INTERNAL_ERROR;
1442 }
1443
1444#if defined(WITH_DEBUG_NTLM)
1445 ntlm_print_authenticate_message(&context->AuthenticateMessage, message,
1446 context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0,
1447 &context->AuthenticateTargetInfo);
1448#endif
1449 ntlm_change_state(context, NTLM_STATE_FINAL);
1450 return SEC_E_OK;
1451}