Coverage Report

Created: 2025-10-10 06:50

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