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 NULL;
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 if (!rdstls_write_cookie(s, settings->ServerAutoReconnectCookie))
351 return FALSE;
352
353 return TRUE;
354}
355
356static BOOL rdstls_write_authentication_response(rdpRdstls* rdstls, wStream* s)
357{
358 WINPR_ASSERT(rdstls);
359 if (!Stream_EnsureRemainingCapacity(s, 8))
360 return FALSE;
361
362 Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHRSP);
363 Stream_Write_UINT16(s, RDSTLS_DATA_RESULT_CODE);
364 Stream_Write_UINT32(s, rdstls->resultCode);
365
366 return TRUE;
367}
368
369static BOOL rdstls_process_capabilities(rdpRdstls* rdstls, wStream* s)
370{
371 WINPR_ASSERT(rdstls);
372 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
373 return FALSE;
374
375 const UINT16 dataType = Stream_Get_UINT16(s);
376 if (dataType != RDSTLS_DATA_CAPABILITIES)
377 {
378 WLog_Print(rdstls->log, WLOG_ERROR,
379 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
380 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_CAPABILITIES));
381 return FALSE;
382 }
383
384 const UINT16 supportedVersions = Stream_Get_UINT16(s);
385 if ((supportedVersions & RDSTLS_VERSION_1) == 0)
386 {
387 WLog_Print(rdstls->log, WLOG_ERROR,
388 "received invalid supportedVersions=0x%04" PRIX16 ", expected 0x%04" PRIX32,
389 supportedVersions, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_VERSION_1));
390 return FALSE;
391 }
392
393 return TRUE;
394}
395
396static BOOL rdstls_read_unicode_string(WINPR_ATTR_UNUSED wLog* log, wStream* s, char** str)
397{
398 WINPR_ASSERT(str);
399
400 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
401 return FALSE;
402
403 const UINT16 length = Stream_Get_UINT16(s);
404
405 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
406 return FALSE;
407
408 if (length <= 2)
409 {
410 Stream_Seek(s, length);
411 return TRUE;
412 }
413
414 *str = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), NULL);
415 if (!*str)
416 return FALSE;
417
418 return TRUE;
419}
420
421static BOOL rdstls_read_data(WINPR_ATTR_UNUSED wLog* log, wStream* s, UINT16* pLength,
422 const BYTE** pData)
423{
424 WINPR_ASSERT(pLength);
425 WINPR_ASSERT(pData);
426
427 *pData = NULL;
428 *pLength = 0;
429 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
430 return FALSE;
431
432 const UINT16 length = Stream_Get_UINT16(s);
433
434 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
435 return FALSE;
436
437 if (length <= 2)
438 {
439 Stream_Seek(s, length);
440 return TRUE;
441 }
442
443 *pData = Stream_ConstPointer(s);
444 *pLength = length;
445 Stream_Seek(s, length);
446 return TRUE;
447}
448
449static BOOL rdstls_cmp_data(wLog* log, const char* field, const BYTE* serverData,
450 const UINT32 serverDataLength, const BYTE* clientData,
451 const UINT16 clientDataLength)
452{
453 if (serverDataLength > 0)
454 {
455 if (clientDataLength == 0)
456 {
457 WLog_Print(log, WLOG_ERROR, "expected %s", field);
458 return FALSE;
459 }
460
461 if (serverDataLength > UINT16_MAX || serverDataLength != clientDataLength ||
462 memcmp(serverData, clientData, serverDataLength) != 0)
463 {
464 WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
465 return FALSE;
466 }
467 }
468
469 return TRUE;
470}
471
472static BOOL rdstls_cmp_str(wLog* log, const char* field, const char* serverStr,
473 const char* clientStr)
474{
475 if (!utils_str_is_empty(serverStr))
476 {
477 if (utils_str_is_empty(clientStr))
478 {
479 WLog_Print(log, WLOG_ERROR, "expected %s", field);
480 return FALSE;
481 }
482
483 WINPR_ASSERT(serverStr);
484 WINPR_ASSERT(clientStr);
485 if (strcmp(serverStr, clientStr) != 0)
486 {
487 WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
488 return FALSE;
489 }
490 }
491
492 return TRUE;
493}
494
495static BOOL rdstls_process_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
496{
497 WINPR_ASSERT(rdstls);
498 WINPR_ASSERT(rdstls->context);
499
500 BOOL rc = FALSE;
501
502 const BYTE* clientRedirectionGuid = NULL;
503 UINT16 clientRedirectionGuidLength = 0;
504 char* clientPassword = NULL;
505 char* clientUsername = NULL;
506 char* clientDomain = NULL;
507
508 const rdpSettings* settings = rdstls->context->settings;
509 WINPR_ASSERT(settings);
510
511 if (!rdstls_read_data(rdstls->log, s, &clientRedirectionGuidLength, &clientRedirectionGuid))
512 goto fail;
513
514 if (!rdstls_read_unicode_string(rdstls->log, s, &clientUsername))
515 goto fail;
516
517 if (!rdstls_read_unicode_string(rdstls->log, s, &clientDomain))
518 goto fail;
519
520 if (!rdstls_read_unicode_string(rdstls->log, s, &clientPassword))
521 goto fail;
522
523 {
524 const BYTE* serverRedirectionGuid =
525 freerdp_settings_get_pointer(settings, FreeRDP_RedirectionGuid);
526 const UINT32 serverRedirectionGuidLength =
527 freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength);
528 const char* serverUsername = freerdp_settings_get_string(settings, FreeRDP_Username);
529 const char* serverDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
530 const char* serverPassword = freerdp_settings_get_string(settings, FreeRDP_Password);
531
532 rdstls->resultCode = RDSTLS_RESULT_SUCCESS;
533
534 if (!rdstls_cmp_data(rdstls->log, "RedirectionGuid", serverRedirectionGuid,
535 serverRedirectionGuidLength, clientRedirectionGuid,
536 clientRedirectionGuidLength))
537 rdstls->resultCode = RDSTLS_RESULT_ACCESS_DENIED;
538
539 if (!rdstls_cmp_str(rdstls->log, "UserName", serverUsername, clientUsername))
540 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
541
542 if (!rdstls_cmp_str(rdstls->log, "Domain", serverDomain, clientDomain))
543 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
544
545 if (!rdstls_cmp_str(rdstls->log, "Password", serverPassword, clientPassword))
546 rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
547 }
548 rc = TRUE;
549fail:
550 return rc;
551}
552
553static BOOL rdstls_process_authentication_request_with_cookie(WINPR_ATTR_UNUSED rdpRdstls* rdstls,
554 WINPR_ATTR_UNUSED wStream* s)
555{
556 // TODO
557 WLog_Print(rdstls->log, WLOG_ERROR, "TODO: RDSTLS Cookie authentication not implemented");
558 return FALSE;
559}
560
561static BOOL rdstls_process_authentication_request(rdpRdstls* rdstls, wStream* s)
562{
563 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
564 return FALSE;
565
566 const UINT16 dataType = Stream_Get_UINT16(s);
567 switch (dataType)
568 {
569 case RDSTLS_DATA_PASSWORD_CREDS:
570 if (!rdstls_process_authentication_request_with_password(rdstls, s))
571 return FALSE;
572 break;
573 case RDSTLS_DATA_AUTORECONNECT_COOKIE:
574 if (!rdstls_process_authentication_request_with_cookie(rdstls, s))
575 return FALSE;
576 break;
577 default:
578 WLog_Print(rdstls->log, WLOG_ERROR,
579 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32
580 " or 0x%04" PRIX32,
581 dataType, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_PASSWORD_CREDS),
582 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_AUTORECONNECT_COOKIE));
583 return FALSE;
584 }
585
586 return TRUE;
587}
588
589static BOOL rdstls_process_authentication_response(rdpRdstls* rdstls, wStream* s)
590{
591 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 6))
592 return FALSE;
593
594 const UINT16 dataType = Stream_Get_UINT16(s);
595 if (dataType != RDSTLS_DATA_RESULT_CODE)
596 {
597 WLog_Print(rdstls->log, WLOG_ERROR,
598 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
599 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_RESULT_CODE));
600 return FALSE;
601 }
602
603 const UINT32 resultCode = Stream_Get_UINT32(s);
604 if (resultCode != RDSTLS_RESULT_SUCCESS)
605 {
606 WLog_Print(rdstls->log, WLOG_ERROR, "resultCode: %s [0x%08" PRIX32 "]",
607 rdstls_result_code_str(resultCode), resultCode);
608
609 UINT32 error = FREERDP_ERROR_CONNECT_UNDEFINED;
610 switch (resultCode)
611 {
612 case RDSTLS_RESULT_ACCESS_DENIED:
613 error = FREERDP_ERROR_CONNECT_ACCESS_DENIED;
614 break;
615 case RDSTLS_RESULT_ACCOUNT_DISABLED:
616 error = FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED;
617 break;
618 case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
619 error = FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT;
620 break;
621 case RDSTLS_RESULT_LOGON_FAILURE:
622 error = FREERDP_ERROR_CONNECT_LOGON_FAILURE;
623 break;
624 case RDSTLS_RESULT_INVALID_LOGON_HOURS:
625 error = FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION;
626 break;
627 case RDSTLS_RESULT_PASSWORD_EXPIRED:
628 error = FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED;
629 break;
630 case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
631 error = FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE;
632 break;
633 default:
634 WLog_Print(rdstls->log, WLOG_ERROR,
635 "Unexpected resultCode: [0x%08" PRIX32 "], NTSTATUS=%s, Win32Error=%s",
636 resultCode, GetSecurityStatusString((SECURITY_STATUS)resultCode),
637 Win32ErrorCode2Tag(resultCode & 0xFFFF));
638 error = FREERDP_ERROR_CONNECT_UNDEFINED;
639 break;
640 }
641
642 freerdp_set_last_error_if_not(rdstls->context, error);
643 return FALSE;
644 }
645
646 return TRUE;
647}
648
649static BOOL rdstls_send(WINPR_ATTR_UNUSED rdpTransport* transport, wStream* s, void* extra)
650{
651 rdpRdstls* rdstls = (rdpRdstls*)extra;
652 rdpSettings* settings = NULL;
653
654 WINPR_ASSERT(transport);
655 WINPR_ASSERT(s);
656 WINPR_ASSERT(rdstls);
657
658 settings = rdstls->context->settings;
659 WINPR_ASSERT(settings);
660
661 if (!Stream_EnsureRemainingCapacity(s, 2))
662 return FALSE;
663
664 Stream_Write_UINT16(s, RDSTLS_VERSION_1);
665
666 const RDSTLS_STATE state = rdstls_get_state(rdstls);
667 switch (state)
668 {
669 case RDSTLS_STATE_CAPABILITIES:
670 if (!rdstls_write_capabilities(rdstls, s))
671 return FALSE;
672 break;
673 case RDSTLS_STATE_AUTH_REQ:
674 if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
675 {
676 if (!rdstls_write_authentication_request_with_password(rdstls, s))
677 return FALSE;
678 }
679 else if (settings->ServerAutoReconnectCookie != NULL)
680 {
681 if (!rdstls_write_authentication_request_with_cookie(rdstls, s))
682 return FALSE;
683 }
684 else
685 {
686 WLog_Print(rdstls->log, WLOG_ERROR,
687 "cannot authenticate with password or auto-reconnect cookie");
688 return FALSE;
689 }
690 break;
691 case RDSTLS_STATE_AUTH_RSP:
692 if (!rdstls_write_authentication_response(rdstls, s))
693 return FALSE;
694 break;
695 default:
696 WLog_Print(rdstls->log, WLOG_ERROR, "Invalid rdstls state %s [%" PRIu32 "]",
697 rdstls_get_state_str(state), state);
698 return FALSE;
699 }
700
701 if (transport_write(rdstls->transport, s) < 0)
702 return FALSE;
703
704 return TRUE;
705}
706
707static int rdstls_recv(WINPR_ATTR_UNUSED rdpTransport* transport, wStream* s, void* extra)
708{
709 rdpRdstls* rdstls = (rdpRdstls*)extra;
710
711 WINPR_ASSERT(transport);
712 WINPR_ASSERT(s);
713 WINPR_ASSERT(rdstls);
714
715 if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
716 return -1;
717
718 const UINT16 version = Stream_Get_UINT16(s);
719 if (version != RDSTLS_VERSION_1)
720 {
721 WLog_Print(rdstls->log, WLOG_ERROR,
722 "received invalid RDSTLS Version=0x%04" PRIX16 ", expected 0x%04" PRIX16,
723 version, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_VERSION_1));
724 return -1;
725 }
726
727 const UINT16 pduType = Stream_Get_UINT16(s);
728 switch (pduType)
729 {
730 case RDSTLS_TYPE_CAPABILITIES:
731 if (!rdstls_process_capabilities(rdstls, s))
732 return -1;
733 break;
734 case RDSTLS_TYPE_AUTHREQ:
735 if (!rdstls_process_authentication_request(rdstls, s))
736 return -1;
737 break;
738 case RDSTLS_TYPE_AUTHRSP:
739 if (!rdstls_process_authentication_response(rdstls, s))
740 return -1;
741 break;
742 default:
743 WLog_Print(rdstls->log, WLOG_ERROR, "unknown RDSTLS PDU type [0x%04" PRIx16 "]",
744 pduType);
745 return -1;
746 }
747
748 return 1;
749}
750
751#define rdstls_check_state_requirements(rdstls, expected) \
752 rdstls_check_state_requirements_((rdstls), (expected), __FILE__, __func__, __LINE__)
753static BOOL rdstls_check_state_requirements_(rdpRdstls* rdstls, RDSTLS_STATE expected,
754 const char* file, const char* fkt, size_t line)
755{
756 const RDSTLS_STATE current = rdstls_get_state(rdstls);
757 if (current == expected)
758 return TRUE;
759
760 WINPR_ASSERT(rdstls);
761
762 const DWORD log_level = WLOG_ERROR;
763 if (WLog_IsLevelActive(rdstls->log, log_level))
764 WLog_PrintTextMessage(rdstls->log, log_level, line, file, fkt,
765 "Unexpected rdstls state %s [%u], expected %s [%u]",
766 rdstls_get_state_str(current), current,
767 rdstls_get_state_str(expected), expected);
768
769 return FALSE;
770}
771
772static BOOL rdstls_send_capabilities(rdpRdstls* rdstls)
773{
774 BOOL rc = FALSE;
775
776 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
777 return FALSE;
778
779 wStream* s = Stream_New(NULL, 512);
780 if (!s)
781 goto fail;
782
783 WINPR_ASSERT(rdstls);
784 if (!rdstls_send(rdstls->transport, s, rdstls))
785 goto fail;
786
787 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
788fail:
789 Stream_Free(s, TRUE);
790 return rc;
791}
792
793static BOOL rdstls_recv_authentication_request(rdpRdstls* rdstls)
794{
795 BOOL rc = FALSE;
796
797 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
798 return FALSE;
799
800 wStream* s = Stream_New(NULL, 4096);
801 if (!s)
802 goto fail;
803
804 WINPR_ASSERT(rdstls);
805
806 {
807 const int res = transport_read_pdu(rdstls->transport, s);
808 if (res < 0)
809 goto fail;
810 }
811
812 {
813 const int status = rdstls_recv(rdstls->transport, s, rdstls);
814 if (status < 0)
815 goto fail;
816 }
817
818 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
819fail:
820 Stream_Free(s, TRUE);
821 return rc;
822}
823
824static BOOL rdstls_send_authentication_response(rdpRdstls* rdstls)
825{
826 BOOL rc = FALSE;
827
828 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
829 return FALSE;
830
831 wStream* s = Stream_New(NULL, 512);
832 if (!s)
833 goto fail;
834
835 WINPR_ASSERT(rdstls);
836 if (!rdstls_send(rdstls->transport, s, rdstls))
837 goto fail;
838
839 rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
840fail:
841 Stream_Free(s, TRUE);
842 return rc;
843}
844
845static BOOL rdstls_recv_capabilities(rdpRdstls* rdstls)
846{
847 BOOL rc = FALSE;
848
849 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
850 return FALSE;
851
852 wStream* s = Stream_New(NULL, 512);
853 if (!s)
854 goto fail;
855
856 WINPR_ASSERT(rdstls);
857
858 {
859 const int res = transport_read_pdu(rdstls->transport, s);
860 if (res < 0)
861 goto fail;
862 }
863
864 {
865 const int status = rdstls_recv(rdstls->transport, s, rdstls);
866 if (status < 0)
867 goto fail;
868 }
869
870 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
871fail:
872 Stream_Free(s, TRUE);
873 return rc;
874}
875
876static BOOL rdstls_send_authentication_request(rdpRdstls* rdstls)
877{
878 BOOL rc = FALSE;
879
880 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
881 return FALSE;
882
883 wStream* s = Stream_New(NULL, 4096);
884 if (!s)
885 goto fail;
886
887 WINPR_ASSERT(rdstls);
888 if (!rdstls_send(rdstls->transport, s, rdstls))
889 goto fail;
890
891 rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
892fail:
893 Stream_Free(s, TRUE);
894 return rc;
895}
896
897static BOOL rdstls_recv_authentication_response(rdpRdstls* rdstls)
898{
899 BOOL rc = FALSE;
900
901 WINPR_ASSERT(rdstls);
902
903 if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
904 return FALSE;
905
906 wStream* s = Stream_New(NULL, 512);
907 if (!s)
908 goto fail;
909
910 {
911 const int res = transport_read_pdu(rdstls->transport, s);
912 if (res < 0)
913 goto fail;
914 }
915
916 {
917 const int status = rdstls_recv(rdstls->transport, s, rdstls);
918 if (status < 0)
919 goto fail;
920 }
921
922 rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
923fail:
924 Stream_Free(s, TRUE);
925 return rc;
926}
927
928static int rdstls_server_authenticate(rdpRdstls* rdstls)
929{
930 WINPR_ASSERT(rdstls);
931
932 if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
933 return -1;
934
935 if (!rdstls_send_capabilities(rdstls))
936 return -1;
937
938 if (!rdstls_recv_authentication_request(rdstls))
939 return -1;
940
941 if (!rdstls_send_authentication_response(rdstls))
942 return -1;
943
944 if (rdstls->resultCode != RDSTLS_RESULT_SUCCESS)
945 return -1;
946
947 return 1;
948}
949
950static int rdstls_client_authenticate(rdpRdstls* rdstls)
951{
952 if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
953 return -1;
954
955 if (!rdstls_recv_capabilities(rdstls))
956 return -1;
957
958 if (!rdstls_send_authentication_request(rdstls))
959 return -1;
960
961 if (!rdstls_recv_authentication_response(rdstls))
962 return -1;
963
964 return 1;
965}
966
974int rdstls_authenticate(rdpRdstls* rdstls)
975{
976 WINPR_ASSERT(rdstls);
977
978 if (rdstls->server)
979 return rdstls_server_authenticate(rdstls);
980 else
981 return rdstls_client_authenticate(rdstls);
982}
983
984static SSIZE_T rdstls_parse_pdu_data_type(wLog* log, UINT16 dataType, wStream* s)
985{
986 size_t pduLength = 0;
987
988 switch (dataType)
989 {
990 case RDSTLS_DATA_PASSWORD_CREDS:
991 {
992 if (Stream_GetRemainingLength(s) < 2)
993 return 0;
994
995 const UINT16 redirGuidLength = Stream_Get_UINT16(s);
996
997 if (Stream_GetRemainingLength(s) < redirGuidLength)
998 return 0;
999 Stream_Seek(s, redirGuidLength);
1000
1001 if (Stream_GetRemainingLength(s) < 2)
1002 return 0;
1003
1004 const UINT16 usernameLength = Stream_Get_UINT16(s);
1005
1006 if (Stream_GetRemainingLength(s) < usernameLength)
1007 return 0;
1008 Stream_Seek(s, usernameLength);
1009
1010 if (Stream_GetRemainingLength(s) < 2)
1011 return 0;
1012 const UINT16 domainLength = Stream_Get_UINT16(s);
1013
1014 if (Stream_GetRemainingLength(s) < domainLength)
1015 return 0;
1016 Stream_Seek(s, domainLength);
1017
1018 if (Stream_GetRemainingLength(s) < 2)
1019 return 0;
1020 const UINT16 passwordLength = Stream_Get_UINT16(s);
1021
1022 pduLength = Stream_GetPosition(s) + passwordLength;
1023 }
1024 break;
1025 case RDSTLS_DATA_AUTORECONNECT_COOKIE:
1026 {
1027 if (Stream_GetRemainingLength(s) < 4)
1028 return 0;
1029 Stream_Seek(s, 4);
1030
1031 if (Stream_GetRemainingLength(s) < 2)
1032 return 0;
1033 const UINT16 cookieLength = Stream_Get_UINT16(s);
1034
1035 pduLength = Stream_GetPosition(s) + cookieLength;
1036 }
1037 break;
1038 default:
1039 WLog_Print(log, WLOG_ERROR, "invalid RDSLTS dataType");
1040 return -1;
1041 }
1042
1043 if (pduLength > SSIZE_MAX)
1044 return 0;
1045 return (SSIZE_T)pduLength;
1046}
1047
1048SSIZE_T rdstls_parse_pdu(wLog* log, wStream* stream)
1049{
1050 SSIZE_T pduLength = -1;
1051 wStream sbuffer = { 0 };
1052 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(stream), Stream_Length(stream));
1053
1054 if (Stream_GetRemainingLength(s) < 2)
1055 return 0;
1056
1057 const UINT16 version = Stream_Get_UINT16(s);
1058 if (version != RDSTLS_VERSION_1)
1059 {
1060 WLog_Print(log, WLOG_ERROR, "invalid RDSTLS version");
1061 return -1;
1062 }
1063
1064 if (Stream_GetRemainingLength(s) < 2)
1065 return 0;
1066
1067 const UINT16 pduType = Stream_Get_UINT16(s);
1068 switch (pduType)
1069 {
1070 case RDSTLS_TYPE_CAPABILITIES:
1071 pduLength = 8;
1072 break;
1073 case RDSTLS_TYPE_AUTHREQ:
1074 {
1075 if (Stream_GetRemainingLength(s) < 2)
1076 return 0;
1077
1078 const UINT16 dataType = Stream_Get_UINT16(s);
1079 pduLength = rdstls_parse_pdu_data_type(log, dataType, s);
1080 }
1081 break;
1082 case RDSTLS_TYPE_AUTHRSP:
1083 pduLength = 10;
1084 break;
1085 default:
1086 WLog_Print(log, WLOG_ERROR, "invalid RDSTLS PDU type");
1087 return -1;
1088 }
1089
1090 return pduLength;
1091}
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.