Coverage Report

Created: 2026-03-04 06:17

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