Coverage Report

Created: 2025-08-26 06:37

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