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