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