FreeRDP
Loading...
Searching...
No Matches
rdstls.c
1
20#include <freerdp/config.h>
21
22#include "settings.h"
23
24#include <freerdp/log.h>
25#include <freerdp/error.h>
26#include <freerdp/settings.h>
27
28#include <winpr/assert.h>
29#include <winpr/stream.h>
30#include <winpr/wlog.h>
31
32#include "rdstls.h"
33#include "transport.h"
34#include "utils.h"
35
36#define RDSTLS_VERSION_1 0x01
37#define RDSTLS_VERSION_2 0x02
38
39#define RDSTLS_TYPE_CAPABILITIES 0x01
40#define RDSTLS_TYPE_AUTHREQ 0x02
41#define RDSTLS_TYPE_AUTHRSP 0x04
42
43#define RDSTLS_DATA_CAPABILITIES 0x01
44#define RDSTLS_DATA_PASSWORD_CREDS 0x01
45#define RDSTLS_DATA_AUTORECONNECT_COOKIE 0x02
46#define RDSTLS_DATA_FEDAUTH_TOKEN 0x03
47#define RDSTLS_DATA_RESULT_CODE 0x01
48
49typedef enum
50{
51 RDSTLS_STATE_INITIAL,
52 RDSTLS_STATE_CAPABILITIES,
53 RDSTLS_STATE_AUTH_REQ,
54 RDSTLS_STATE_AUTH_RSP,
55 RDSTLS_STATE_FINAL,
56} RDSTLS_STATE;
57
58typedef enum
59{
60
61 RDSTLS_RESULT_SUCCESS = 0x00000000,
62 RDSTLS_RESULT_ACCESS_DENIED = 0x00000005,
63 RDSTLS_RESULT_LOGON_FAILURE = 0x0000052e,
64 RDSTLS_RESULT_INVALID_LOGON_HOURS = 0x00000530,
65 RDSTLS_RESULT_PASSWORD_EXPIRED = 0x00000532,
66 RDSTLS_RESULT_ACCOUNT_DISABLED = 0x00000533,
67 RDSTLS_RESULT_PASSWORD_MUST_CHANGE = 0x00000773,
68 RDSTLS_RESULT_ACCOUNT_LOCKED_OUT = 0x00000775
69} RDSTLS_RESULT_CODE;
70
71struct rdp_rdstls
72{
73 BOOL server;
74 RDSTLS_STATE state;
75 rdpContext* context;
76 rdpTransport* transport;
77
78 RDSTLS_RESULT_CODE resultCode;
79 wLog* log;
80};
81
82static const char* rdstls_result_code_str(UINT32 resultCode)
83{
84 switch (resultCode)
85 {
86 case RDSTLS_RESULT_SUCCESS:
87 return "RDSTLS_RESULT_SUCCESS";
88 case RDSTLS_RESULT_ACCESS_DENIED:
89 return "RDSTLS_RESULT_ACCESS_DENIED";
90 case RDSTLS_RESULT_LOGON_FAILURE:
91 return "RDSTLS_RESULT_LOGON_FAILURE";
92 case RDSTLS_RESULT_INVALID_LOGON_HOURS:
93 return "RDSTLS_RESULT_INVALID_LOGON_HOURS";
94 case RDSTLS_RESULT_PASSWORD_EXPIRED:
95 return "RDSTLS_RESULT_PASSWORD_EXPIRED";
96 case RDSTLS_RESULT_ACCOUNT_DISABLED:
97 return "RDSTLS_RESULT_ACCOUNT_DISABLED";
98 case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
99 return "RDSTLS_RESULT_PASSWORD_MUST_CHANGE";
100 case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
101 return "RDSTLS_RESULT_ACCOUNT_LOCKED_OUT";
102 default:
103 return "RDSTLS_RESULT_UNKNOWN";
104 }
105}
114rdpRdstls* rdstls_new(rdpContext* context, rdpTransport* transport)
115{
116 WINPR_ASSERT(context);
117 WINPR_ASSERT(transport);
118
119 rdpSettings* settings = context->settings;
120 WINPR_ASSERT(settings);
121
122 rdpRdstls* rdstls = (rdpRdstls*)calloc(1, sizeof(rdpRdstls));
123
124 if (!rdstls)
125 return nullptr;
126 rdstls->log = WLog_Get(FREERDP_TAG("core.rdstls"));
127 rdstls->context = context;
128 rdstls->transport = transport;
129 rdstls->server = settings->ServerMode;
130
131 rdstls->state = RDSTLS_STATE_INITIAL;
132
133 return rdstls;
134}
135
141void rdstls_free(rdpRdstls* rdstls)
142{
143 free(rdstls);
144}
145
146static const char* rdstls_get_state_str(RDSTLS_STATE state)
147{
148 switch (state)
149 {
150 case RDSTLS_STATE_INITIAL:
151 return "RDSTLS_STATE_INITIAL";
152 case RDSTLS_STATE_CAPABILITIES:
153 return "RDSTLS_STATE_CAPABILITIES";
154 case RDSTLS_STATE_AUTH_REQ:
155 return "RDSTLS_STATE_AUTH_REQ";
156 case RDSTLS_STATE_AUTH_RSP:
157 return "RDSTLS_STATE_AUTH_RSP";
158 case RDSTLS_STATE_FINAL:
159 return "RDSTLS_STATE_FINAL";
160 default:
161 return "UNKNOWN";
162 }
163}
164
165static RDSTLS_STATE rdstls_get_state(rdpRdstls* rdstls)
166{
167 WINPR_ASSERT(rdstls);
168 return rdstls->state;
169}
170
171static BOOL check_transition(wLog* log, RDSTLS_STATE current, RDSTLS_STATE expected,
172 RDSTLS_STATE requested)
173{
174 if (requested != expected)
175 {
176 WLog_Print(log, WLOG_ERROR,
177 "Unexpected rdstls state transition from %s [%u] to %s [%u], expected %s [%u]",
178 rdstls_get_state_str(current), current, rdstls_get_state_str(requested),
179 requested, rdstls_get_state_str(expected), expected);
180 return FALSE;
181 }
182 return TRUE;
183}
184
185static BOOL rdstls_set_state(rdpRdstls* rdstls, RDSTLS_STATE state)
186{
187 BOOL rc = FALSE;
188 WINPR_ASSERT(rdstls);
189
190 WLog_Print(rdstls->log, WLOG_DEBUG, "-- %s\t--> %s", rdstls_get_state_str(rdstls->state),
191 rdstls_get_state_str(state));
192
193 switch (rdstls->state)
194 {
195 case RDSTLS_STATE_INITIAL:
196 rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_CAPABILITIES, state);
197 break;
198 case RDSTLS_STATE_CAPABILITIES:
199 rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_AUTH_REQ, state);
200 break;
201 case RDSTLS_STATE_AUTH_REQ:
202 rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_AUTH_RSP, state);
203 break;
204 case RDSTLS_STATE_AUTH_RSP:
205 rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_FINAL, state);
206 break;
207 case RDSTLS_STATE_FINAL:
208 rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_CAPABILITIES, state);
209 break;
210 default:
211 WLog_Print(rdstls->log, WLOG_ERROR,
212 "Invalid rdstls state %s [%u], requested transition to %s [%u]",
213 rdstls_get_state_str(rdstls->state), rdstls->state,
214 rdstls_get_state_str(state), state);
215 break;
216 }
217 if (rc)
218 rdstls->state = state;
219
220 return rc;
221}
222
223static BOOL rdstls_write_capabilities(WINPR_ATTR_UNUSED rdpRdstls* rdstls, wStream* s)
224{
225 if (!Stream_EnsureRemainingCapacity(s, 6))
226 return FALSE;
227
228 Stream_Write_UINT16(s, RDSTLS_TYPE_CAPABILITIES);
229 Stream_Write_UINT16(s, RDSTLS_DATA_CAPABILITIES);
230 Stream_Write_UINT16(s, RDSTLS_VERSION_1);
231
232 return TRUE;
233}
234
235static SSIZE_T rdstls_write_string(wStream* s, const char* str)
236{
237 const size_t pos = Stream_GetPosition(s);
238
239 if (!Stream_EnsureRemainingCapacity(s, 2))
240 return -1;
241
242 if (!str)
243 {
244 /* Write unicode null */
245 Stream_Write_UINT16(s, 2);
246 if (!Stream_EnsureRemainingCapacity(s, 2))
247 return -1;
248
249 Stream_Write_UINT16(s, 0);
250 return (SSIZE_T)(Stream_GetPosition(s) - pos);
251 }
252
253 const SSIZE_T devNameWLen = ConvertUtf8ToWChar(str, nullptr, 0);
254 if (devNameWLen < 0)
255 return -1;
256 const size_t length = WINPR_ASSERTING_INT_CAST(size_t, devNameWLen) + 1;
257 const size_t slen = strlen(str);
258
259 Stream_Write_UINT16(s, (UINT16)length * sizeof(WCHAR));
260
261 if (!Stream_EnsureRemainingCapacity(s, length * sizeof(WCHAR)))
262 return -1;
263
264 if (Stream_Write_UTF16_String_From_UTF8(s, length, str, slen, TRUE) < 0)
265 return -1;
266
267 return (SSIZE_T)(Stream_GetPosition(s) - pos);
268}
269
270static BOOL rdstls_write_data(wStream* s, UINT32 length, const BYTE* data)
271{
272 WINPR_ASSERT(data || (length == 0));
273
274 if (!Stream_EnsureRemainingCapacity(s, 2) || (length > UINT16_MAX))
275 return FALSE;
276
277 Stream_Write_UINT16(s, (UINT16)length);
278
279 if (!Stream_EnsureRemainingCapacity(s, length))
280 return FALSE;
281
282 Stream_Write(s, data, length);
283
284 return TRUE;
285}
286
287static BOOL rdstls_write_cookie(wStream* s, const ARC_SC_PRIVATE_PACKET* cookie)
288{
289 WINPR_ASSERT(cookie);
290 const uint16_t length = 28;
291
292 if (!Stream_EnsureRemainingCapacity(s, 2))
293 return FALSE;
294
295 Stream_Write_UINT16(s, length);
296
297 if (!Stream_EnsureRemainingCapacity(s, length))
298 return FALSE;
299
300 Stream_Write_UINT32(s, cookie->cbLen);
301 Stream_Write_UINT32(s, cookie->version);
302 Stream_Write_UINT32(s, cookie->logonId);
303 Stream_Write(s, cookie->arcRandomBits, sizeof(cookie->arcRandomBits));
304 return TRUE;
305}
306
307static BOOL rdstls_write_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
308{
309 WINPR_ASSERT(rdstls);
310 WINPR_ASSERT(rdstls->context);
311
312 WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS password authentication message");
313
314 rdpSettings* settings = rdstls->context->settings;
315 WINPR_ASSERT(settings);
316
317 if (!Stream_EnsureRemainingCapacity(s, 4))
318 return FALSE;
319
320 Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
321 Stream_Write_UINT16(s, RDSTLS_DATA_PASSWORD_CREDS);
322
323 if (!rdstls_write_data(s, settings->RedirectionGuidLength, settings->RedirectionGuid))
324 return FALSE;
325
326 if (rdstls_write_string(s, settings->Username) < 0)
327 return FALSE;
328
329 if (rdstls_write_string(s, settings->Domain) < 0)
330 return FALSE;
331
332 if (!rdstls_write_data(s, settings->RedirectionPasswordLength, settings->RedirectionPassword))
333 return FALSE;
334
335 return TRUE;
336}
337
338static BOOL rdstls_write_authentication_request_with_cookie(WINPR_ATTR_UNUSED rdpRdstls* rdstls,
339 WINPR_ATTR_UNUSED wStream* s)
340{
341 WINPR_ASSERT(rdstls);
342 WINPR_ASSERT(rdstls->context);
343
344 WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS cookie authentication message");
345
346 rdpSettings* settings = rdstls->context->settings;
347 WINPR_ASSERT(settings);
348
349 if (!Stream_EnsureRemainingCapacity(s, 8))
350 return FALSE;
351
352 Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
353 Stream_Write_UINT16(s, RDSTLS_DATA_AUTORECONNECT_COOKIE);
354 Stream_Write_UINT32(s, settings->RedirectedSessionId);
355
356 return (rdstls_write_cookie(s, settings->ServerAutoReconnectCookie));
357}
358
359/*
360 * Warn if the endpoint FedAuth token targets a different virtual machine
361 * than the VM identifier passed via the .rdp `pcb` field / /pcb command
362 * line switch. The token payload starts with "VMID=<guid>&..."; a
363 * mismatch would be silently rejected by the server later on. This is a
364 * best-effort local sanity check.
365 */
366static void rdstls_check_fedauth_vmid(rdpRdstls* rdstls, const char* token, const char* selectedVm)
367{
368 WINPR_ASSERT(rdstls);
369 WINPR_ASSERT(token);
370
371 if (!selectedVm || !*selectedVm)
372 return;
373
374 const char* vmidField = strstr(token, "VMID=");
375 if (!vmidField)
376 return;
377 vmidField += 5;
378
379 const size_t vmLen = strlen(selectedVm);
380 const BOOL matches = (_strnicmp(vmidField, selectedVm, vmLen) == 0) &&
381 (vmidField[vmLen] == '\0' || vmidField[vmLen] == '&');
382 if (!matches)
383 {
384 WLog_Print(rdstls->log, WLOG_WARN,
385 "endpoint FedAuth token is issued for a different virtual machine "
386 "than the one selected for connection");
387 }
388}
389
390WINPR_ATTR_NODISCARD
391static BOOL rdstls_write_authentication_request_with_fedauth_token(rdpRdstls* rdstls, wStream* s)
392{
393 WINPR_ASSERT(rdstls);
394 WINPR_ASSERT(rdstls->context);
395
396 WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS FedAuth token authentication message");
397
398 const rdpSettings* settings = rdstls->context->settings;
399 WINPR_ASSERT(settings);
400
401 const char* token = freerdp_settings_get_string(settings, FreeRDP_EndpointFedAuthToken);
402 if (!token || !*token)
403 {
404 WLog_Print(rdstls->log, WLOG_ERROR, "EndpointFedAuthToken not set");
405 return FALSE;
406 }
407
408 rdstls_check_fedauth_vmid(rdstls, token,
409 freerdp_settings_get_string(settings, FreeRDP_PreconnectionBlob));
410
411 const size_t utf8Length = strlen(token);
412 /* The wire length prefix is a UINT16 counting the token in UTF-16LE
413 * including a terminating NUL character. */
414 if (utf8Length >= UINT16_MAX / sizeof(WCHAR))
415 {
416 WLog_Print(rdstls->log, WLOG_ERROR,
417 "EndpointFedAuthToken length %" PRIuz " exceeds RDSTLS wire limit", utf8Length);
418 return FALSE;
419 }
420
421 const size_t wideLength = utf8Length + 1;
422 const size_t wideBytes = wideLength * sizeof(WCHAR);
423
424 if (!Stream_EnsureRemainingCapacity(s, 6 + wideBytes))
425 return FALSE;
426
427 Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
428 Stream_Write_UINT16(s, RDSTLS_DATA_FEDAUTH_TOKEN);
429 Stream_Write_UINT16(s, (UINT16)wideBytes);
430
431 return Stream_Write_UTF16_String_From_UTF8(s, wideLength, token, utf8Length, TRUE) >= 0;
432}
433
434static BOOL rdstls_write_authentication_response(rdpRdstls* rdstls, wStream* s)
435{
436 WINPR_ASSERT(rdstls);
437 if (!Stream_EnsureRemainingCapacity(s, 8))
438 return FALSE;
439
440 Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHRSP);
441 Stream_Write_UINT16(s, RDSTLS_DATA_RESULT_CODE);
442 Stream_Write_UINT32(s, rdstls->resultCode);
443
444 return TRUE;
445}
446
447static BOOL rdstls_process_capabilities(rdpRdstls* rdstls, wStream* s)
448{
449 WINPR_ASSERT(rdstls);
450 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
451 return FALSE;
452
453 const UINT16 dataType = Stream_Get_UINT16(s);
454 if (dataType != RDSTLS_DATA_CAPABILITIES)
455 {
456 WLog_Print(rdstls->log, WLOG_ERROR,
457 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
458 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_CAPABILITIES));
459 return FALSE;
460 }
461
462 const UINT16 supportedVersions = Stream_Get_UINT16(s);
463 if ((supportedVersions & RDSTLS_VERSION_1) == 0)
464 {
465 WLog_Print(rdstls->log, WLOG_ERROR,
466 "received invalid supportedVersions=0x%04" PRIX16 ", expected 0x%04" PRIX32,
467 supportedVersions, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_VERSION_1));
468 return FALSE;
469 }
470
471 return TRUE;
472}
473
474static BOOL rdstls_read_unicode_string(WINPR_ATTR_UNUSED wLog* log, wStream* s, char** str)
475{
476 WINPR_ASSERT(str);
477
478 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
479 return FALSE;
480
481 const UINT16 length = Stream_Get_UINT16(s);
482
483 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
484 return FALSE;
485
486 if (length <= 2)
487 {
488 Stream_Seek(s, length);
489 return TRUE;
490 }
491
492 *str = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
493 return (*str) != nullptr;
494}
495
496static BOOL rdstls_read_data(WINPR_ATTR_UNUSED wLog* log, wStream* s, UINT16* pLength,
497 const BYTE** pData)
498{
499 WINPR_ASSERT(pLength);
500 WINPR_ASSERT(pData);
501
502 *pData = nullptr;
503 *pLength = 0;
504 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
505 return FALSE;
506
507 const UINT16 length = Stream_Get_UINT16(s);
508
509 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
510 return FALSE;
511
512 if (length <= 2)
513 {
514 Stream_Seek(s, length);
515 return TRUE;
516 }
517
518 *pData = Stream_ConstPointer(s);
519 *pLength = length;
520 Stream_Seek(s, length);
521 return TRUE;
522}
523
524static BOOL rdstls_cmp_data(wLog* log, const char* field, const BYTE* serverData,
525 const UINT32 serverDataLength, const BYTE* clientData,
526 const UINT16 clientDataLength)
527{
528 if (serverDataLength > 0)
529 {
530 if (clientDataLength == 0)
531 {
532 WLog_Print(log, WLOG_ERROR, "expected %s", field);
533 return FALSE;
534 }
535
536 if (serverDataLength > UINT16_MAX || serverDataLength != clientDataLength ||
537 memcmp(serverData, clientData, serverDataLength) != 0)
538 {
539 WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
540 return FALSE;
541 }
542 }
543
544 return TRUE;
545}
546
547static BOOL rdstls_cmp_str(wLog* log, const char* field, const char* serverStr,
548 const char* clientStr)
549{
550 if (!utils_str_is_empty(serverStr))
551 {
552 if (utils_str_is_empty(clientStr))
553 {
554 WLog_Print(log, WLOG_ERROR, "expected %s", field);
555 return FALSE;
556 }
557
558 WINPR_ASSERT(serverStr);
559 WINPR_ASSERT(clientStr);
560 if (strcmp(serverStr, clientStr) != 0)
561 {
562 WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
563 return FALSE;
564 }
565 }
566
567 return TRUE;
568}
569
570static BOOL rdstls_process_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
571{
572 WINPR_ASSERT(rdstls);
573 WINPR_ASSERT(rdstls->context);
574
575 BOOL rc = FALSE;
576
577 const BYTE* clientRedirectionGuid = nullptr;
578 UINT16 clientRedirectionGuidLength = 0;
579 char* clientPassword = nullptr;
580 char* clientUsername = nullptr;
581 char* clientDomain = nullptr;
582
583 const rdpSettings* settings = rdstls->context->settings;
584 WINPR_ASSERT(settings);
585
586 if (!rdstls_read_data(rdstls->log, s, &clientRedirectionGuidLength, &clientRedirectionGuid))
587 goto fail;
588
589 if (!rdstls_read_unicode_string(rdstls->log, s, &clientUsername))
590 goto fail;
591
592 if (!rdstls_read_unicode_string(rdstls->log, s, &clientDomain))
593 goto fail;
594
595 if (!rdstls_read_unicode_string(rdstls->log, s, &clientPassword))
596 goto fail;
597
598 {
599 const BYTE* serverRedirectionGuid =
600 freerdp_settings_get_pointer(settings, FreeRDP_RedirectionGuid);
601 const UINT32 serverRedirectionGuidLength =
602 freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength);
603 const char* serverUsername = freerdp_settings_get_string(settings, FreeRDP_Username);
604 const char* serverDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
605 const char* serverPassword = freerdp_settings_get_string(settings, FreeRDP_Password);
606
607 rdstls->resultCode = RDSTLS_RESULT_SUCCESS;
608
609 if (!rdstls_cmp_data(rdstls->log, "RedirectionGuid", serverRedirectionGuid,
610 serverRedirectionGuidLength, clientRedirectionGuid,
611 clientRedirectionGuidLength))
612 rdstls->resultCode = RDSTLS_RESULT_ACCESS_DENIED;
613
614 if (!rdstls_cmp_str(rdstls->log, "UserName", serverUsername, clientUsername))
615 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
616
617 if (!rdstls_cmp_str(rdstls->log, "Domain", serverDomain, clientDomain))
618 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
619
620 if (!rdstls_cmp_str(rdstls->log, "Password", serverPassword, clientPassword))
621 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
622 }
623 rc = TRUE;
624fail:
625 return rc;
626}
627
628static BOOL rdstls_process_authentication_request_with_cookie(WINPR_ATTR_UNUSED rdpRdstls* rdstls,
629 WINPR_ATTR_UNUSED wStream* s)
630{
631 // TODO
632 WLog_Print(rdstls->log, WLOG_ERROR, "TODO: RDSTLS Cookie authentication not implemented");
633 return FALSE;
634}
635
636static BOOL rdstls_process_authentication_request(rdpRdstls* rdstls, wStream* s)
637{
638 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
639 return FALSE;
640
641 const UINT16 dataType = Stream_Get_UINT16(s);
642 switch (dataType)
643 {
644 case RDSTLS_DATA_PASSWORD_CREDS:
645 if (!rdstls_process_authentication_request_with_password(rdstls, s))
646 return FALSE;
647 break;
648 case RDSTLS_DATA_AUTORECONNECT_COOKIE:
649 if (!rdstls_process_authentication_request_with_cookie(rdstls, s))
650 return FALSE;
651 break;
652 default:
653 WLog_Print(rdstls->log, WLOG_ERROR,
654 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32
655 " or 0x%04" PRIX32,
656 dataType, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_PASSWORD_CREDS),
657 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_AUTORECONNECT_COOKIE));
658 return FALSE;
659 }
660
661 return TRUE;
662}
663
664static BOOL rdstls_process_authentication_response(rdpRdstls* rdstls, wStream* s)
665{
666 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 6))
667 return FALSE;
668
669 const UINT16 dataType = Stream_Get_UINT16(s);
670 if (dataType != RDSTLS_DATA_RESULT_CODE)
671 {
672 WLog_Print(rdstls->log, WLOG_ERROR,
673 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
674 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_RESULT_CODE));
675 return FALSE;
676 }
677
678 const UINT32 resultCode = Stream_Get_UINT32(s);
679 if (resultCode != RDSTLS_RESULT_SUCCESS)
680 {
681 WLog_Print(rdstls->log, WLOG_ERROR, "resultCode: %s [0x%08" PRIX32 "]",
682 rdstls_result_code_str(resultCode), resultCode);
683
684 UINT32 error = FREERDP_ERROR_CONNECT_UNDEFINED;
685 switch (resultCode)
686 {
687 case RDSTLS_RESULT_ACCESS_DENIED:
688 error = FREERDP_ERROR_CONNECT_ACCESS_DENIED;
689 break;
690 case RDSTLS_RESULT_ACCOUNT_DISABLED:
691 error = FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED;
692 break;
693 case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
694 error = FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT;
695 break;
696 case RDSTLS_RESULT_LOGON_FAILURE:
697 error = FREERDP_ERROR_CONNECT_LOGON_FAILURE;
698 break;
699 case RDSTLS_RESULT_INVALID_LOGON_HOURS:
700 error = FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION;
701 break;
702 case RDSTLS_RESULT_PASSWORD_EXPIRED:
703 error = FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED;
704 break;
705 case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
706 error = FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE;
707 break;
708 default:
709 WLog_Print(rdstls->log, WLOG_ERROR,
710 "Unexpected resultCode: [0x%08" PRIX32 "], NTSTATUS=%s, Win32Error=%s",
711 resultCode, GetSecurityStatusString((SECURITY_STATUS)resultCode),
712 Win32ErrorCode2Tag(resultCode & 0xFFFF));
713 error = FREERDP_ERROR_CONNECT_UNDEFINED;
714 break;
715 }
716
717 freerdp_set_last_error_if_not(rdstls->context, error);
718 return FALSE;
719 }
720
721 return TRUE;
722}
723
724static BOOL rdstls_send(WINPR_ATTR_UNUSED rdpTransport* transport, wStream* s, void* extra)
725{
726 rdpRdstls* rdstls = (rdpRdstls*)extra;
727 rdpSettings* settings = nullptr;
728
729 WINPR_ASSERT(transport);
730 WINPR_ASSERT(s);
731 WINPR_ASSERT(rdstls);
732
733 settings = rdstls->context->settings;
734 WINPR_ASSERT(settings);
735
736 if (!Stream_EnsureRemainingCapacity(s, 2))
737 return FALSE;
738
739 const RDSTLS_STATE state = rdstls_get_state(rdstls);
740 const char* fedAuthToken = freerdp_settings_get_string(settings, FreeRDP_EndpointFedAuthToken);
741 const BOOL useFedAuth = (state == RDSTLS_STATE_AUTH_REQ) && !utils_str_is_empty(fedAuthToken);
742
743 Stream_Write_UINT16(s, useFedAuth ? RDSTLS_VERSION_2 : RDSTLS_VERSION_1);
744
745 switch (state)
746 {
747 case RDSTLS_STATE_CAPABILITIES:
748 if (!rdstls_write_capabilities(rdstls, s))
749 return FALSE;
750 break;
751 case RDSTLS_STATE_AUTH_REQ:
752 if (useFedAuth)
753 {
754 if (!rdstls_write_authentication_request_with_fedauth_token(rdstls, s))
755 return FALSE;
756 }
757 else if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
758 {
759 if (!rdstls_write_authentication_request_with_password(rdstls, s))
760 return FALSE;
761 }
762 else if (settings->ServerAutoReconnectCookie != nullptr)
763 {
764 if (!rdstls_write_authentication_request_with_cookie(rdstls, s))
765 return FALSE;
766 }
767 else
768 {
769 WLog_Print(rdstls->log, WLOG_ERROR,
770 "cannot authenticate with FedAuth token, password or "
771 "auto-reconnect cookie");
772 return FALSE;
773 }
774 break;
775 case RDSTLS_STATE_AUTH_RSP:
776 if (!rdstls_write_authentication_response(rdstls, s))
777 return FALSE;
778 break;
779 default:
780 WLog_Print(rdstls->log, WLOG_ERROR, "Invalid rdstls state %s [%" PRIu32 "]",
781 rdstls_get_state_str(state), state);
782 return FALSE;
783 }
784
785 return (transport_write(rdstls->transport, s) >= 0);
786}
787
788static int rdstls_recv(WINPR_ATTR_UNUSED rdpTransport* transport, wStream* s, void* extra)
789{
790 rdpRdstls* rdstls = (rdpRdstls*)extra;
791
792 WINPR_ASSERT(transport);
793 WINPR_ASSERT(s);
794 WINPR_ASSERT(rdstls);
795
796 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
797 return -1;
798
799 const UINT16 version = Stream_Get_UINT16(s);
800 if (version != RDSTLS_VERSION_1)
801 {
802 WLog_Print(rdstls->log, WLOG_ERROR,
803 "received invalid RDSTLS Version=0x%04" PRIX16 ", expected 0x%04" PRIX16,
804 version, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_VERSION_1));
805 return -1;
806 }
807
808 const UINT16 pduType = Stream_Get_UINT16(s);
809 switch (pduType)
810 {
811 case RDSTLS_TYPE_CAPABILITIES:
812 if (!rdstls_process_capabilities(rdstls, s))
813 return -1;
814 break;
815 case RDSTLS_TYPE_AUTHREQ:
816 if (!rdstls_process_authentication_request(rdstls, s))
817 return -1;
818 break;
819 case RDSTLS_TYPE_AUTHRSP:
820 if (!rdstls_process_authentication_response(rdstls, s))
821 return -1;
822 break;
823 default:
824 WLog_Print(rdstls->log, WLOG_ERROR, "unknown RDSTLS PDU type [0x%04" PRIx16 "]",
825 pduType);
826 return -1;
827 }
828
829 return 1;
830}
831
832#define rdstls_check_state_requirements(rdstls, expected) \
833 rdstls_check_state_requirements_((rdstls), (expected), __FILE__, __func__, __LINE__)
834static BOOL rdstls_check_state_requirements_(rdpRdstls* rdstls, RDSTLS_STATE expected,
835 const char* file, const char* fkt, size_t line)
836{
837 const RDSTLS_STATE current = rdstls_get_state(rdstls);
838 if (current == expected)
839 return TRUE;
840
841 WINPR_ASSERT(rdstls);
842
843 const DWORD log_level = WLOG_ERROR;
844 if (WLog_IsLevelActive(rdstls->log, log_level))
845 WLog_PrintTextMessage(rdstls->log, log_level, line, file, fkt,
846 "Unexpected rdstls state %s [%u], expected %s [%u]",
847 rdstls_get_state_str(current), current,
848 rdstls_get_state_str(expected), expected);
849
850 return FALSE;
851}
852
853static BOOL rdstls_send_capabilities(rdpRdstls* rdstls)
854{
855 BOOL rc = FALSE;
856
857 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
858 return FALSE;
859
860 wStream* s = Stream_New(nullptr, 512);
861 if (!s)
862 goto fail;
863
864 WINPR_ASSERT(rdstls);
865 if (!rdstls_send(rdstls->transport, s, rdstls))
866 goto fail;
867
868 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
869fail:
870 Stream_Free(s, TRUE);
871 return rc;
872}
873
874static BOOL rdstls_recv_authentication_request(rdpRdstls* rdstls)
875{
876 BOOL rc = FALSE;
877
878 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
879 return FALSE;
880
881 wStream* s = Stream_New(nullptr, 4096);
882 if (!s)
883 goto fail;
884
885 WINPR_ASSERT(rdstls);
886
887 {
888 const int res = transport_read_pdu(rdstls->transport, s);
889 if (res < 0)
890 goto fail;
891 }
892
893 {
894 const int status = rdstls_recv(rdstls->transport, s, rdstls);
895 if (status < 0)
896 goto fail;
897 }
898
899 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
900fail:
901 Stream_Free(s, TRUE);
902 return rc;
903}
904
905static BOOL rdstls_send_authentication_response(rdpRdstls* rdstls)
906{
907 BOOL rc = FALSE;
908
909 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
910 return FALSE;
911
912 wStream* s = Stream_New(nullptr, 512);
913 if (!s)
914 goto fail;
915
916 WINPR_ASSERT(rdstls);
917 if (!rdstls_send(rdstls->transport, s, rdstls))
918 goto fail;
919
920 rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
921fail:
922 Stream_Free(s, TRUE);
923 return rc;
924}
925
926static BOOL rdstls_recv_capabilities(rdpRdstls* rdstls)
927{
928 BOOL rc = FALSE;
929
930 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
931 return FALSE;
932
933 wStream* s = Stream_New(nullptr, 512);
934 if (!s)
935 goto fail;
936
937 WINPR_ASSERT(rdstls);
938
939 {
940 const int res = transport_read_pdu(rdstls->transport, s);
941 if (res < 0)
942 goto fail;
943 }
944
945 {
946 const int status = rdstls_recv(rdstls->transport, s, rdstls);
947 if (status < 0)
948 goto fail;
949 }
950
951 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
952fail:
953 Stream_Free(s, TRUE);
954 return rc;
955}
956
957static BOOL rdstls_send_authentication_request(rdpRdstls* rdstls)
958{
959 BOOL rc = FALSE;
960
961 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
962 return FALSE;
963
964 wStream* s = Stream_New(nullptr, 4096);
965 if (!s)
966 goto fail;
967
968 WINPR_ASSERT(rdstls);
969 if (!rdstls_send(rdstls->transport, s, rdstls))
970 goto fail;
971
972 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
973fail:
974 Stream_Free(s, TRUE);
975 return rc;
976}
977
978static BOOL rdstls_recv_authentication_response(rdpRdstls* rdstls)
979{
980 BOOL rc = FALSE;
981
982 WINPR_ASSERT(rdstls);
983
984 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
985 return FALSE;
986
987 wStream* s = Stream_New(nullptr, 512);
988 if (!s)
989 goto fail;
990
991 {
992 const int res = transport_read_pdu(rdstls->transport, s);
993 if (res < 0)
994 goto fail;
995 }
996
997 {
998 const int status = rdstls_recv(rdstls->transport, s, rdstls);
999 if (status < 0)
1000 goto fail;
1001 }
1002
1003 rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
1004fail:
1005 Stream_Free(s, TRUE);
1006 return rc;
1007}
1008
1009static int rdstls_server_authenticate(rdpRdstls* rdstls)
1010{
1011 WINPR_ASSERT(rdstls);
1012
1013 if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1014 return -1;
1015
1016 if (!rdstls_send_capabilities(rdstls))
1017 return -1;
1018
1019 if (!rdstls_recv_authentication_request(rdstls))
1020 return -1;
1021
1022 if (!rdstls_send_authentication_response(rdstls))
1023 return -1;
1024
1025 if (rdstls->resultCode != RDSTLS_RESULT_SUCCESS)
1026 return -1;
1027
1028 return 1;
1029}
1030
1031static int rdstls_client_authenticate(rdpRdstls* rdstls)
1032{
1033 if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1034 return -1;
1035
1036 if (!rdstls_recv_capabilities(rdstls))
1037 return -1;
1038
1039 if (!rdstls_send_authentication_request(rdstls))
1040 return -1;
1041
1042 if (!rdstls_recv_authentication_response(rdstls))
1043 return -1;
1044
1045 return 1;
1046}
1047
1055int rdstls_authenticate(rdpRdstls* rdstls)
1056{
1057 WINPR_ASSERT(rdstls);
1058
1059 if (rdstls->server)
1060 return rdstls_server_authenticate(rdstls);
1061 else
1062 return rdstls_client_authenticate(rdstls);
1063}
1064
1065static SSIZE_T rdstls_parse_pdu_data_type(wLog* log, UINT16 dataType, wStream* s)
1066{
1067 size_t pduLength = 0;
1068
1069 switch (dataType)
1070 {
1071 case RDSTLS_DATA_PASSWORD_CREDS:
1072 {
1073 if (Stream_GetRemainingLength(s) < 2)
1074 return 0;
1075
1076 const UINT16 redirGuidLength = Stream_Get_UINT16(s);
1077
1078 if (Stream_GetRemainingLength(s) < redirGuidLength)
1079 return 0;
1080 Stream_Seek(s, redirGuidLength);
1081
1082 if (Stream_GetRemainingLength(s) < 2)
1083 return 0;
1084
1085 const UINT16 usernameLength = Stream_Get_UINT16(s);
1086
1087 if (Stream_GetRemainingLength(s) < usernameLength)
1088 return 0;
1089 Stream_Seek(s, usernameLength);
1090
1091 if (Stream_GetRemainingLength(s) < 2)
1092 return 0;
1093 const UINT16 domainLength = Stream_Get_UINT16(s);
1094
1095 if (Stream_GetRemainingLength(s) < domainLength)
1096 return 0;
1097 Stream_Seek(s, domainLength);
1098
1099 if (Stream_GetRemainingLength(s) < 2)
1100 return 0;
1101 const UINT16 passwordLength = Stream_Get_UINT16(s);
1102
1103 pduLength = Stream_GetPosition(s) + passwordLength;
1104 }
1105 break;
1106 case RDSTLS_DATA_AUTORECONNECT_COOKIE:
1107 {
1108 if (Stream_GetRemainingLength(s) < 4)
1109 return 0;
1110 Stream_Seek(s, 4);
1111
1112 if (Stream_GetRemainingLength(s) < 2)
1113 return 0;
1114 const UINT16 cookieLength = Stream_Get_UINT16(s);
1115
1116 pduLength = Stream_GetPosition(s) + cookieLength;
1117 }
1118 break;
1119 default:
1120 WLog_Print(log, WLOG_ERROR, "invalid RDSLTS dataType");
1121 return -1;
1122 }
1123
1124 if (pduLength > SSIZE_MAX)
1125 return 0;
1126 return (SSIZE_T)pduLength;
1127}
1128
1129SSIZE_T rdstls_parse_pdu(wLog* log, wStream* stream)
1130{
1131 SSIZE_T pduLength = -1;
1132 wStream sbuffer = WINPR_C_ARRAY_INIT;
1133 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(stream), Stream_Length(stream));
1134
1135 if (Stream_GetRemainingLength(s) < 2)
1136 return 0;
1137
1138 const UINT16 version = Stream_Get_UINT16(s);
1139 if (version != RDSTLS_VERSION_1)
1140 {
1141 WLog_Print(log, WLOG_ERROR, "invalid RDSTLS version");
1142 return -1;
1143 }
1144
1145 if (Stream_GetRemainingLength(s) < 2)
1146 return 0;
1147
1148 const UINT16 pduType = Stream_Get_UINT16(s);
1149 switch (pduType)
1150 {
1151 case RDSTLS_TYPE_CAPABILITIES:
1152 pduLength = 8;
1153 break;
1154 case RDSTLS_TYPE_AUTHREQ:
1155 {
1156 if (Stream_GetRemainingLength(s) < 2)
1157 return 0;
1158
1159 const UINT16 dataType = Stream_Get_UINT16(s);
1160 pduLength = rdstls_parse_pdu_data_type(log, dataType, s);
1161 }
1162 break;
1163 case RDSTLS_TYPE_AUTHRSP:
1164 pduLength = 10;
1165 break;
1166 default:
1167 WLog_Print(log, WLOG_ERROR, "invalid RDSTLS PDU type");
1168 return -1;
1169 }
1170
1171 return pduLength;
1172}
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.