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