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