Coverage Report

Created: 2026-07-16 07:14

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 0x01u
37
0
#define RDSTLS_VERSION_2 0x02u
38
39
0
#define RDSTLS_TYPE_CAPABILITIES 0x01
40
0
#define RDSTLS_TYPE_AUTHREQ 0x02
41
0
#define RDSTLS_TYPE_AUTHRSP 0x04
42
43
0
#define RDSTLS_DATA_CAPABILITIES 0x01
44
0
#define RDSTLS_DATA_PASSWORD_CREDS 0x01
45
0
#define RDSTLS_DATA_AUTORECONNECT_COOKIE 0x02
46
0
#define RDSTLS_DATA_FEDAUTH_TOKEN 0x03
47
0
#define RDSTLS_DATA_RESULT_CODE 0x01
48
49
typedef enum
50
{
51
  RDSTLS_STATE_INITIAL,
52
  RDSTLS_STATE_CAPABILITIES,
53
  RDSTLS_STATE_AUTH_REQ,
54
  RDSTLS_STATE_AUTH_RSP,
55
  RDSTLS_STATE_FINAL,
56
} RDSTLS_STATE;
57
58
typedef enum
59
{
60
61
  RDSTLS_RESULT_SUCCESS = 0x00000000,
62
  RDSTLS_RESULT_ACCESS_DENIED = 0x00000005,
63
  RDSTLS_RESULT_LOGON_FAILURE = 0x0000052e,
64
  RDSTLS_RESULT_INVALID_LOGON_HOURS = 0x00000530,
65
  RDSTLS_RESULT_PASSWORD_EXPIRED = 0x00000532,
66
  RDSTLS_RESULT_ACCOUNT_DISABLED = 0x00000533,
67
  RDSTLS_RESULT_PASSWORD_MUST_CHANGE = 0x00000773,
68
  RDSTLS_RESULT_ACCOUNT_LOCKED_OUT = 0x00000775
69
} RDSTLS_RESULT_CODE;
70
71
struct rdp_rdstls
72
{
73
  BOOL server;
74
  RDSTLS_STATE state;
75
  rdpContext* context;
76
  rdpTransport* transport;
77
78
  RDSTLS_RESULT_CODE resultCode;
79
  wLog* log;
80
  uint16_t supportedVersions;
81
};
82
83
static const uint16_t RDSTLS_VERSION_MASK = RDSTLS_VERSION_1 | RDSTLS_VERSION_2;
84
85
WINPR_ATTR_NODISCARD
86
static const char* rdstls_result_code_str(UINT32 resultCode)
87
0
{
88
0
  switch (resultCode)
89
0
  {
90
0
    case RDSTLS_RESULT_SUCCESS:
91
0
      return "RDSTLS_RESULT_SUCCESS";
92
0
    case RDSTLS_RESULT_ACCESS_DENIED:
93
0
      return "RDSTLS_RESULT_ACCESS_DENIED";
94
0
    case RDSTLS_RESULT_LOGON_FAILURE:
95
0
      return "RDSTLS_RESULT_LOGON_FAILURE";
96
0
    case RDSTLS_RESULT_INVALID_LOGON_HOURS:
97
0
      return "RDSTLS_RESULT_INVALID_LOGON_HOURS";
98
0
    case RDSTLS_RESULT_PASSWORD_EXPIRED:
99
0
      return "RDSTLS_RESULT_PASSWORD_EXPIRED";
100
0
    case RDSTLS_RESULT_ACCOUNT_DISABLED:
101
0
      return "RDSTLS_RESULT_ACCOUNT_DISABLED";
102
0
    case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
103
0
      return "RDSTLS_RESULT_PASSWORD_MUST_CHANGE";
104
0
    case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
105
0
      return "RDSTLS_RESULT_ACCOUNT_LOCKED_OUT";
106
0
    default:
107
0
      return "RDSTLS_RESULT_UNKNOWN";
108
0
  }
109
0
}
110
111
#define rdstls_required_role_is_server(rdstls, isServer) \
112
0
  rdstls_required_role_is_server_((rdstls), (isServer), __FILE__, __func__, __LINE__)
113
114
WINPR_ATTR_NODISCARD
115
static BOOL rdstls_required_role_is_server_(const rdpRdstls* rdstls, BOOL isServer,
116
                                            const char* file, const char* fkt, size_t line)
117
0
{
118
0
  WINPR_ASSERT(rdstls);
119
0
  const BOOL rc = rdstls->server == isServer;
120
0
  if (!rc)
121
0
  {
122
0
    const DWORD level = WLOG_ERROR;
123
0
    if (WLog_IsLevelActive(rdstls->log, level))
124
0
      WLog_PrintTextMessage(rdstls->log, level, line, file, fkt,
125
0
                            "Message not allowed in current role '%s'",
126
0
                            rdstls->server ? "server" : "client");
127
0
  }
128
0
  return rc;
129
0
}
130
131
/**
132
 * Create new RDSTLS state machine.
133
 *
134
 * @param context A pointer to the rdp context to use
135
 *
136
 * @return new RDSTLS state machine.
137
 */
138
139
rdpRdstls* rdstls_new(rdpContext* context, rdpTransport* transport)
140
0
{
141
0
  WINPR_ASSERT(context);
142
0
  WINPR_ASSERT(transport);
143
144
0
  rdpSettings* settings = context->settings;
145
0
  WINPR_ASSERT(settings);
146
147
0
  rdpRdstls* rdstls = (rdpRdstls*)calloc(1, sizeof(rdpRdstls));
148
149
0
  if (!rdstls)
150
0
    return nullptr;
151
0
  rdstls->log = WLog_Get(FREERDP_TAG("core.rdstls"));
152
0
  rdstls->supportedVersions = RDSTLS_VERSION_MASK;
153
0
  rdstls->context = context;
154
0
  rdstls->transport = transport;
155
0
  rdstls->server = settings->ServerMode;
156
157
0
  rdstls->resultCode = RDSTLS_RESULT_ACCESS_DENIED;
158
0
  rdstls->state = RDSTLS_STATE_INITIAL;
159
160
0
  return rdstls;
161
0
}
162
163
/**
164
 * Free RDSTLS state machine.
165
 * @param rdstls The RDSTLS instance to free
166
 */
167
168
void rdstls_free(rdpRdstls* rdstls)
169
0
{
170
0
  free(rdstls);
171
0
}
172
173
WINPR_ATTR_NODISCARD
174
static const char* rdstls_get_state_str(RDSTLS_STATE state)
175
0
{
176
0
  switch (state)
177
0
  {
178
0
    case RDSTLS_STATE_INITIAL:
179
0
      return "RDSTLS_STATE_INITIAL";
180
0
    case RDSTLS_STATE_CAPABILITIES:
181
0
      return "RDSTLS_STATE_CAPABILITIES";
182
0
    case RDSTLS_STATE_AUTH_REQ:
183
0
      return "RDSTLS_STATE_AUTH_REQ";
184
0
    case RDSTLS_STATE_AUTH_RSP:
185
0
      return "RDSTLS_STATE_AUTH_RSP";
186
0
    case RDSTLS_STATE_FINAL:
187
0
      return "RDSTLS_STATE_FINAL";
188
0
    default:
189
0
      return "UNKNOWN";
190
0
  }
191
0
}
192
193
WINPR_ATTR_NODISCARD
194
static RDSTLS_STATE rdstls_get_state(rdpRdstls* rdstls)
195
0
{
196
0
  WINPR_ASSERT(rdstls);
197
0
  return rdstls->state;
198
0
}
199
200
WINPR_ATTR_NODISCARD
201
static BOOL check_transition(wLog* log, RDSTLS_STATE current, RDSTLS_STATE expected,
202
                             RDSTLS_STATE requested)
203
0
{
204
0
  if (requested != expected)
205
0
  {
206
0
    WLog_Print(log, WLOG_ERROR,
207
0
               "Unexpected rdstls state transition from %s [%u] to %s [%u], expected %s [%u]",
208
0
               rdstls_get_state_str(current), current, rdstls_get_state_str(requested),
209
0
               requested, rdstls_get_state_str(expected), expected);
210
0
    return FALSE;
211
0
  }
212
0
  return TRUE;
213
0
}
214
215
WINPR_ATTR_NODISCARD
216
static BOOL rdstls_set_state(rdpRdstls* rdstls, RDSTLS_STATE state)
217
0
{
218
0
  BOOL rc = FALSE;
219
0
  WINPR_ASSERT(rdstls);
220
221
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "-- %s\t--> %s", rdstls_get_state_str(rdstls->state),
222
0
             rdstls_get_state_str(state));
223
224
0
  switch (rdstls->state)
225
0
  {
226
0
    case RDSTLS_STATE_INITIAL:
227
0
      rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_CAPABILITIES, state);
228
0
      break;
229
0
    case RDSTLS_STATE_CAPABILITIES:
230
0
      rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_AUTH_REQ, state);
231
0
      break;
232
0
    case RDSTLS_STATE_AUTH_REQ:
233
0
      rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_AUTH_RSP, state);
234
0
      break;
235
0
    case RDSTLS_STATE_AUTH_RSP:
236
0
      rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_FINAL, state);
237
0
      break;
238
0
    case RDSTLS_STATE_FINAL:
239
0
      rc = check_transition(rdstls->log, rdstls->state, RDSTLS_STATE_CAPABILITIES, state);
240
0
      break;
241
0
    default:
242
0
      WLog_Print(rdstls->log, WLOG_ERROR,
243
0
                 "Invalid rdstls state %s [%u], requested transition to %s [%u]",
244
0
                 rdstls_get_state_str(rdstls->state), rdstls->state,
245
0
                 rdstls_get_state_str(state), state);
246
0
      break;
247
0
  }
248
0
  if (rc)
249
0
    rdstls->state = state;
250
251
0
  return rc;
252
0
}
253
254
#define rdstls_check_state_requirements(rdstls, expected) \
255
0
  rdstls_check_state_requirements_((rdstls), (expected), __FILE__, __func__, __LINE__)
256
257
WINPR_ATTR_NODISCARD
258
static BOOL rdstls_check_state_requirements_(rdpRdstls* rdstls, RDSTLS_STATE expected,
259
                                             const char* file, const char* fkt, size_t line)
260
0
{
261
0
  const RDSTLS_STATE current = rdstls_get_state(rdstls);
262
0
  if (current == expected)
263
0
    return TRUE;
264
265
0
  WINPR_ASSERT(rdstls);
266
267
0
  const DWORD log_level = WLOG_ERROR;
268
0
  if (WLog_IsLevelActive(rdstls->log, log_level))
269
0
    WLog_PrintTextMessage(rdstls->log, log_level, line, file, fkt,
270
0
                          "Unexpected rdstls state %s [%u], expected %s [%u]",
271
0
                          rdstls_get_state_str(current), current,
272
0
                          rdstls_get_state_str(expected), expected);
273
274
0
  return FALSE;
275
0
}
276
277
WINPR_ATTR_NODISCARD
278
static BOOL rdstls_write_capabilities(WINPR_ATTR_UNUSED rdpRdstls* rdstls, wStream* s)
279
0
{
280
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
281
0
    return FALSE;
282
283
0
  Stream_Write_UINT16(s, RDSTLS_VERSION_1);
284
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_CAPABILITIES);
285
0
  Stream_Write_UINT16(s, RDSTLS_DATA_CAPABILITIES);
286
0
  Stream_Write_UINT16(s, rdstls->supportedVersions);
287
288
0
  return TRUE;
289
0
}
290
291
WINPR_ATTR_NODISCARD
292
static SSIZE_T rdstls_write_string(wStream* s, const char* str)
293
0
{
294
0
  const size_t pos = Stream_GetPosition(s);
295
296
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
297
0
    return -1;
298
299
0
  if (!str)
300
0
  {
301
    /* Write unicode null */
302
0
    Stream_Write_UINT16(s, 2);
303
0
    if (!Stream_EnsureRemainingCapacity(s, 2))
304
0
      return -1;
305
306
0
    Stream_Write_UINT16(s, 0);
307
0
    return (SSIZE_T)(Stream_GetPosition(s) - pos);
308
0
  }
309
310
0
  const SSIZE_T devNameWLen = ConvertUtf8ToWChar(str, nullptr, 0);
311
0
  if (devNameWLen < 0)
312
0
    return -1;
313
0
  const size_t length = WINPR_ASSERTING_INT_CAST(size_t, devNameWLen) + 1;
314
0
  const size_t slen = strlen(str);
315
316
0
  Stream_Write_UINT16(s, (UINT16)length * sizeof(WCHAR));
317
318
0
  if (!Stream_EnsureRemainingCapacity(s, length * sizeof(WCHAR)))
319
0
    return -1;
320
321
0
  if (Stream_Write_UTF16_String_From_UTF8(s, length, str, slen, TRUE) < 0)
322
0
    return -1;
323
324
0
  return (SSIZE_T)(Stream_GetPosition(s) - pos);
325
0
}
326
327
WINPR_ATTR_NODISCARD
328
static BOOL rdstls_write_data(wStream* s, UINT32 length, const BYTE* data)
329
0
{
330
0
  WINPR_ASSERT(data || (length == 0));
331
332
0
  if (!Stream_EnsureRemainingCapacity(s, 2) || (length > UINT16_MAX))
333
0
    return FALSE;
334
335
0
  Stream_Write_UINT16(s, (UINT16)length);
336
337
0
  if (!Stream_EnsureRemainingCapacity(s, length))
338
0
    return FALSE;
339
340
0
  Stream_Write(s, data, length);
341
342
0
  return TRUE;
343
0
}
344
345
WINPR_ATTR_NODISCARD
346
static BOOL rdstls_write_cookie(wStream* s, const ARC_SC_PRIVATE_PACKET* cookie)
347
0
{
348
0
  WINPR_ASSERT(cookie);
349
0
  const uint16_t length = sizeof(ARC_SC_PRIVATE_PACKET);
350
0
  WINPR_STATIC_ASSERT(sizeof(ARC_SC_PRIVATE_PACKET) == 28);
351
352
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
353
0
    return FALSE;
354
355
0
  Stream_Write_UINT16(s, length);
356
357
0
  if (!Stream_EnsureRemainingCapacity(s, length))
358
0
    return FALSE;
359
360
0
  Stream_Write_UINT32(s, cookie->cbLen);
361
0
  Stream_Write_UINT32(s, cookie->version);
362
0
  Stream_Write_UINT32(s, cookie->logonId);
363
0
  Stream_Write(s, cookie->arcRandomBits, sizeof(cookie->arcRandomBits));
364
0
  return TRUE;
365
0
}
366
367
WINPR_ATTR_NODISCARD
368
static BOOL rdstls_read_cookie(wLog* log, wStream* s, ARC_SC_PRIVATE_PACKET* cookie)
369
0
{
370
0
  WINPR_ASSERT(cookie);
371
0
  const uint16_t length = sizeof(ARC_SC_PRIVATE_PACKET);
372
0
  WINPR_STATIC_ASSERT(sizeof(ARC_SC_PRIVATE_PACKET) == 28);
373
374
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length + 2ull))
375
0
    return FALSE;
376
377
0
  const uint16_t len = Stream_Get_UINT16(s);
378
0
  if (len != length)
379
0
  {
380
0
    WLog_Print(log, WLOG_ERROR,
381
0
               "RDSTLS Cookie: Unexpected length %" PRIu16 ",  expected %" PRIu16, len, length);
382
0
    return FALSE;
383
0
  }
384
385
0
  cookie->cbLen = Stream_Get_UINT32(s);
386
0
  cookie->version = Stream_Get_UINT32(s);
387
0
  cookie->logonId = Stream_Get_UINT32(s);
388
0
  Stream_Read(s, cookie->arcRandomBits, sizeof(cookie->arcRandomBits));
389
0
  return TRUE;
390
0
}
391
392
WINPR_ATTR_NODISCARD
393
static BOOL rdstls_write_authentication_request_with_password(rdpRdstls* rdstls, wStream* s)
394
0
{
395
0
  WINPR_ASSERT(rdstls);
396
0
  WINPR_ASSERT(rdstls->context);
397
398
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
399
0
    return FALSE;
400
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
401
0
    return FALSE;
402
403
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS password authentication message");
404
405
0
  rdpSettings* settings = rdstls->context->settings;
406
0
  WINPR_ASSERT(settings);
407
408
0
  if (!Stream_EnsureRemainingCapacity(s, 4))
409
0
    return FALSE;
410
411
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
412
0
  Stream_Write_UINT16(s, RDSTLS_DATA_PASSWORD_CREDS);
413
414
0
  if (!rdstls_write_data(s, settings->RedirectionGuidLength, settings->RedirectionGuid))
415
0
    return FALSE;
416
417
0
  if (rdstls_write_string(s, settings->Username) < 0)
418
0
    return FALSE;
419
420
0
  if (rdstls_write_string(s, settings->Domain) < 0)
421
0
    return FALSE;
422
423
0
  if (!rdstls_write_data(s, settings->RedirectionPasswordLength, settings->RedirectionPassword))
424
0
    return FALSE;
425
426
0
  return TRUE;
427
0
}
428
429
WINPR_ATTR_NODISCARD
430
static BOOL rdstls_write_authentication_request_with_cookie(WINPR_ATTR_UNUSED rdpRdstls* rdstls,
431
                                                            WINPR_ATTR_UNUSED wStream* s)
432
0
{
433
0
  WINPR_ASSERT(rdstls);
434
0
  WINPR_ASSERT(rdstls->context);
435
436
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS cookie authentication message");
437
438
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
439
0
    return FALSE;
440
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
441
0
    return FALSE;
442
443
0
  rdpSettings* settings = rdstls->context->settings;
444
0
  WINPR_ASSERT(settings);
445
446
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
447
0
    return FALSE;
448
449
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
450
0
  Stream_Write_UINT16(s, RDSTLS_DATA_AUTORECONNECT_COOKIE);
451
0
  Stream_Write_UINT32(s, settings->RedirectedSessionId);
452
453
0
  return (rdstls_write_cookie(s, settings->ServerAutoReconnectCookie));
454
0
}
455
456
/*
457
 * Warn if the endpoint FedAuth token targets a different virtual machine
458
 * than the VM identifier passed via the .rdp `pcb` field / /pcb command
459
 * line switch. The token payload starts with "VMID=<guid>&..."; a
460
 * mismatch would be silently rejected by the server later on. This is a
461
 * best-effort local sanity check.
462
 */
463
static void rdstls_check_fedauth_vmid(rdpRdstls* rdstls, const char* token, const char* selectedVm)
464
0
{
465
0
  WINPR_ASSERT(rdstls);
466
0
  WINPR_ASSERT(token);
467
468
0
  if (!selectedVm || !*selectedVm)
469
0
    return;
470
471
0
  const char* vmidField = strstr(token, "VMID=");
472
0
  if (!vmidField)
473
0
    return;
474
0
  vmidField += 5;
475
476
0
  const size_t vmLen = strlen(selectedVm);
477
0
  const BOOL matches = (_strnicmp(vmidField, selectedVm, vmLen) == 0) &&
478
0
                       (vmidField[vmLen] == '\0' || vmidField[vmLen] == '&');
479
0
  if (!matches)
480
0
  {
481
0
    WLog_Print(rdstls->log, WLOG_WARN,
482
0
               "endpoint FedAuth token is issued for a different virtual machine "
483
0
               "than the one selected for connection");
484
0
  }
485
0
}
486
487
WINPR_ATTR_NODISCARD
488
static BOOL rdstls_write_authentication_request_with_fedauth_token(rdpRdstls* rdstls, wStream* s)
489
0
{
490
0
  WINPR_ASSERT(rdstls);
491
0
  WINPR_ASSERT(rdstls->context);
492
493
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "Writing RDSTLS FedAuth token authentication message");
494
495
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
496
0
    return FALSE;
497
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
498
0
    return FALSE;
499
500
0
  const rdpSettings* settings = rdstls->context->settings;
501
0
  WINPR_ASSERT(settings);
502
503
0
  const char* token = freerdp_settings_get_string(settings, FreeRDP_EndpointFedAuthToken);
504
0
  if (!token || !*token)
505
0
  {
506
0
    WLog_Print(rdstls->log, WLOG_ERROR, "EndpointFedAuthToken not set");
507
0
    return FALSE;
508
0
  }
509
510
0
  rdstls_check_fedauth_vmid(rdstls, token,
511
0
                            freerdp_settings_get_string(settings, FreeRDP_PreconnectionBlob));
512
513
0
  const size_t utf8Length = strlen(token);
514
  /* The wire length prefix is a UINT16 counting the token in UTF-16LE
515
   * including a terminating NUL character. */
516
0
  if (utf8Length >= UINT16_MAX / sizeof(WCHAR))
517
0
  {
518
0
    WLog_Print(rdstls->log, WLOG_ERROR,
519
0
               "EndpointFedAuthToken length %" PRIuz " exceeds RDSTLS wire limit", utf8Length);
520
0
    return FALSE;
521
0
  }
522
523
0
  const size_t wideLength = utf8Length + 1;
524
0
  const size_t wideBytes = wideLength * sizeof(WCHAR);
525
526
0
  if (!Stream_EnsureRemainingCapacity(s, 6 + wideBytes))
527
0
    return FALSE;
528
529
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
530
0
  Stream_Write_UINT16(s, RDSTLS_DATA_FEDAUTH_TOKEN);
531
0
  Stream_Write_UINT16(s, (UINT16)wideBytes);
532
533
0
  return Stream_Write_UTF16_String_From_UTF8(s, wideLength, token, utf8Length, TRUE) >= 0;
534
0
}
535
536
WINPR_ATTR_NODISCARD
537
static BOOL rdstls_write_authentication_response(rdpRdstls* rdstls, wStream* s)
538
0
{
539
0
  WINPR_ASSERT(rdstls);
540
541
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
542
0
    return FALSE;
543
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
544
0
    return FALSE;
545
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
546
0
    return FALSE;
547
548
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHRSP);
549
0
  Stream_Write_UINT16(s, RDSTLS_DATA_RESULT_CODE);
550
0
  Stream_Write_UINT32(s, rdstls->resultCode);
551
552
0
  return TRUE;
553
0
}
554
555
#define rdstls_version_required(log, expected, actual) \
556
0
  rdstls_version_required_((log), (expected), (actual), __FILE__, __func__, __LINE__)
557
WINPR_ATTR_NODISCARD
558
static BOOL rdstls_version_required_(wLog* log, uint16_t expected, uint16_t actual,
559
                                     const char* file, const char* fkt, size_t line)
560
0
{
561
0
  if (actual < expected)
562
0
  {
563
0
    const DWORD level = WLOG_ERROR;
564
0
    if (WLog_IsLevelActive(log, level))
565
0
    {
566
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
567
0
                            "version=0x%04" PRIx16 ", expected at least 0x%04" PRIx16, actual,
568
0
                            expected);
569
0
    }
570
0
    return FALSE;
571
0
  }
572
0
  return TRUE;
573
0
}
574
575
#define rdstls_are_some_versions_supported(log, version, mask) \
576
0
  rdstls_are_some_versions_supported_((log), (version), (mask), __FILE__, __func__, __LINE__)
577
WINPR_ATTR_NODISCARD
578
static BOOL rdstls_are_some_versions_supported_(wLog* log, uint16_t version, BOOL isMask,
579
                                                const char* file, const char* fkt, size_t line)
580
0
{
581
0
  if (!isMask)
582
0
  {
583
0
    size_t cnt = 0;
584
0
    for (size_t x = 0; x < 16; x++)
585
0
    {
586
0
      const unsigned val = 1 << x;
587
0
      if ((version & val) != 0)
588
0
        cnt++;
589
0
    }
590
0
    if (cnt != 1)
591
0
    {
592
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
593
0
                            "received invalid version mask=0x%04" PRIx16
594
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
595
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
596
0
      return FALSE;
597
0
    }
598
0
  }
599
600
0
  if ((version & RDSTLS_VERSION_MASK) == 0)
601
0
  {
602
0
    const DWORD level = WLOG_ERROR;
603
0
    if (WLog_IsLevelActive(log, level))
604
0
    {
605
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
606
0
                            "received invalid version mask=0x%04" PRIx16
607
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
608
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
609
0
    }
610
0
    return FALSE;
611
0
  }
612
0
  return TRUE;
613
0
}
614
615
#define rdstls_is_version_supported(rdstls, versions) \
616
0
  rdstls_is_version_supported_((rdstls), (version), __FILE__, __func__, __LINE__)
617
WINPR_ATTR_NODISCARD
618
static BOOL rdstls_is_version_supported_(rdpRdstls* rdstls, uint16_t version, const char* file,
619
                                         const char* fkt, size_t line)
620
0
{
621
0
  WINPR_ASSERT(rdstls);
622
623
0
  if ((rdstls->supportedVersions & version) == 0)
624
0
  {
625
0
    const DWORD level = WLOG_ERROR;
626
0
    if (WLog_IsLevelActive(rdstls->log, level))
627
0
    {
628
0
      WLog_PrintTextMessage(rdstls->log, WLOG_ERROR, line, file, fkt,
629
0
                            "received invalid version=0x%04" PRIx16
630
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
631
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
632
0
    }
633
0
    return FALSE;
634
0
  }
635
0
  return TRUE;
636
0
}
637
638
WINPR_ATTR_NODISCARD
639
static BOOL rdstls_process_capabilities(rdpRdstls* rdstls, wStream* s)
640
0
{
641
0
  WINPR_ASSERT(rdstls);
642
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
643
0
    return FALSE;
644
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
645
0
    return FALSE;
646
647
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
648
0
    return FALSE;
649
650
0
  const UINT16 dataType = Stream_Get_UINT16(s);
651
0
  if (dataType != RDSTLS_DATA_CAPABILITIES)
652
0
  {
653
0
    WLog_Print(rdstls->log, WLOG_ERROR,
654
0
               "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
655
0
               WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_CAPABILITIES));
656
0
    return FALSE;
657
0
  }
658
659
0
  const UINT16 supportedVersions = Stream_Get_UINT16(s);
660
0
  if (!rdstls_are_some_versions_supported(rdstls->log, supportedVersions, TRUE))
661
0
    return FALSE;
662
0
  rdstls->supportedVersions = supportedVersions & RDSTLS_VERSION_MASK;
663
664
0
  return TRUE;
665
0
}
666
667
WINPR_ATTR_NODISCARD
668
static BOOL rdstls_read_unicode_string(WINPR_ATTR_UNUSED wLog* log, wStream* s, char** str)
669
0
{
670
0
  WINPR_ASSERT(str);
671
672
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
673
0
    return FALSE;
674
675
0
  const UINT16 length = Stream_Get_UINT16(s);
676
677
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
678
0
    return FALSE;
679
680
0
  if (length <= 2)
681
0
  {
682
0
    *str = nullptr;
683
0
    Stream_Seek(s, length);
684
0
    return TRUE;
685
0
  }
686
687
0
  *str = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
688
0
  return (*str) != nullptr;
689
0
}
690
691
WINPR_ATTR_NODISCARD
692
static BOOL rdstls_read_data(WINPR_ATTR_UNUSED wLog* log, wStream* s, UINT16* pLength,
693
                             const BYTE** pData)
694
0
{
695
0
  WINPR_ASSERT(pLength);
696
0
  WINPR_ASSERT(pData);
697
698
0
  *pData = nullptr;
699
0
  *pLength = 0;
700
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
701
0
    return FALSE;
702
703
0
  const UINT16 length = Stream_Get_UINT16(s);
704
705
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
706
0
    return FALSE;
707
708
0
  if (length <= 2)
709
0
  {
710
0
    Stream_Seek(s, length);
711
0
    return TRUE;
712
0
  }
713
714
0
  *pData = Stream_ConstPointer(s);
715
0
  *pLength = length;
716
0
  Stream_Seek(s, length);
717
0
  return TRUE;
718
0
}
719
720
WINPR_ATTR_NODISCARD
721
static BOOL rdstls_cmp_data(wLog* log, const char* field, const BYTE* serverData,
722
                            const UINT32 serverDataLength, const BYTE* clientData,
723
                            const UINT16 clientDataLength)
724
0
{
725
0
  if (serverDataLength > 0)
726
0
  {
727
0
    if (clientDataLength == 0)
728
0
    {
729
0
      WLog_Print(log, WLOG_ERROR, "expected %s", field);
730
0
      return FALSE;
731
0
    }
732
733
0
    if (serverDataLength > UINT16_MAX || serverDataLength != clientDataLength ||
734
0
        memcmp(serverData, clientData, serverDataLength) != 0)
735
0
    {
736
0
      WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
737
0
      return FALSE;
738
0
    }
739
0
  }
740
741
0
  return TRUE;
742
0
}
743
744
WINPR_ATTR_NODISCARD
745
static BOOL rdstls_cmp_str(wLog* log, const char* field, const char* serverStr,
746
                           const char* clientStr)
747
0
{
748
0
  if (!utils_str_is_empty(serverStr))
749
0
  {
750
0
    if (utils_str_is_empty(clientStr))
751
0
    {
752
0
      WLog_Print(log, WLOG_ERROR, "expected %s", field);
753
0
      return FALSE;
754
0
    }
755
756
0
    WINPR_ASSERT(serverStr);
757
0
    WINPR_ASSERT(clientStr);
758
0
    if (strcmp(serverStr, clientStr) != 0)
759
0
    {
760
0
      WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
761
0
      return FALSE;
762
0
    }
763
0
  }
764
765
0
  return TRUE;
766
0
}
767
768
WINPR_ATTR_NODISCARD
769
static BOOL rdstls_process_authentication_request_with_password(rdpRdstls* rdstls, wStream* s,
770
                                                                uint16_t version)
771
0
{
772
0
  WINPR_ASSERT(rdstls);
773
0
  WINPR_ASSERT(rdstls->context);
774
775
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_1, version))
776
0
    return FALSE;
777
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
778
0
    return FALSE;
779
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
780
0
    return FALSE;
781
782
0
  BOOL rc = FALSE;
783
784
0
  const BYTE* clientRedirectionGuid = nullptr;
785
0
  UINT16 clientRedirectionGuidLength = 0;
786
0
  char* clientPassword = nullptr;
787
0
  char* clientUsername = nullptr;
788
0
  char* clientDomain = nullptr;
789
790
0
  const rdpSettings* settings = rdstls->context->settings;
791
0
  WINPR_ASSERT(settings);
792
793
0
  if (!rdstls_read_data(rdstls->log, s, &clientRedirectionGuidLength, &clientRedirectionGuid))
794
0
    goto fail;
795
796
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientUsername))
797
0
    goto fail;
798
799
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientDomain))
800
0
    goto fail;
801
802
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientPassword))
803
0
    goto fail;
804
805
0
  {
806
0
    const BYTE* serverRedirectionGuid =
807
0
        freerdp_settings_get_pointer(settings, FreeRDP_RedirectionGuid);
808
0
    const UINT32 serverRedirectionGuidLength =
809
0
        freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength);
810
0
    const char* serverUsername = freerdp_settings_get_string(settings, FreeRDP_Username);
811
0
    const char* serverDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
812
0
    const char* serverPassword = freerdp_settings_get_string(settings, FreeRDP_Password);
813
814
0
    if (!rdstls_cmp_data(rdstls->log, "RedirectionGuid", serverRedirectionGuid,
815
0
                         serverRedirectionGuidLength, clientRedirectionGuid,
816
0
                         clientRedirectionGuidLength))
817
0
      rdstls->resultCode = RDSTLS_RESULT_ACCESS_DENIED;
818
0
    else if (!rdstls_cmp_str(rdstls->log, "UserName", serverUsername, clientUsername))
819
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
820
0
    else if (!rdstls_cmp_str(rdstls->log, "Domain", serverDomain, clientDomain))
821
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
822
0
    else if (!rdstls_cmp_str(rdstls->log, "Password", serverPassword, clientPassword))
823
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
824
0
    else
825
0
      rdstls->resultCode = RDSTLS_RESULT_SUCCESS;
826
0
  }
827
0
  rc = TRUE;
828
0
fail:
829
0
  return rc;
830
0
}
831
832
WINPR_ATTR_NODISCARD
833
static BOOL rdstls_process_authentication_request_with_cookie(rdpRdstls* rdstls, wStream* s,
834
                                                              uint16_t version)
835
0
{
836
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_1, version))
837
0
    return FALSE;
838
839
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
840
0
    return FALSE;
841
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
842
0
    return FALSE;
843
844
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
845
0
    return FALSE;
846
847
0
  const rdpSettings* settings = rdstls->context->settings;
848
0
  WINPR_ASSERT(settings);
849
850
0
  const uint32_t id = Stream_Get_UINT32(s);
851
0
  const uint32_t expected = freerdp_settings_get_uint32(settings, FreeRDP_RedirectedSessionId);
852
0
  if (id != expected)
853
0
  {
854
0
    WLog_Print(rdstls->log, WLOG_ERROR,
855
0
               "RDSTLS Cookie SessionId does not match RedirectedSessionId. Deny access.");
856
0
    return FALSE;
857
0
  }
858
859
0
  ARC_SC_PRIVATE_PACKET cookie = WINPR_C_ARRAY_INIT;
860
0
  if (!rdstls_read_cookie(rdstls->log, s, &cookie))
861
0
    return FALSE;
862
863
0
  const ARC_SC_PRIVATE_PACKET* expect =
864
0
      freerdp_settings_get_pointer(settings, FreeRDP_ServerAutoReconnectCookie);
865
0
  if (!expect)
866
0
  {
867
0
    WLog_Print(rdstls->log, WLOG_ERROR, "No RDSTLS Cookie provided by server. Deny access.");
868
0
    return FALSE;
869
0
  }
870
871
0
  if (memcmp(expect, &cookie, sizeof(ARC_SC_PRIVATE_PACKET)) != 0)
872
0
  {
873
0
    WLog_Print(rdstls->log, WLOG_ERROR, "RDSTLS Cookie does not match. Deny access.");
874
0
    return FALSE;
875
0
  }
876
877
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "RDSTLS Cookie matches. Grant access.");
878
0
  return FALSE;
879
0
}
880
881
WINPR_ATTR_NODISCARD
882
static BOOL rdstls_process_authentication_request_with_fedauth_token(rdpRdstls* rdstls, wStream* s,
883
                                                                     uint16_t version)
884
0
{
885
0
  WINPR_ASSERT(rdstls);
886
887
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_2, version))
888
0
    return FALSE;
889
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
890
0
    return FALSE;
891
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
892
0
    return FALSE;
893
0
  if ((rdstls->supportedVersions & RDSTLS_VERSION_2) == 0)
894
0
  {
895
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token only supported with RDSTLS_VERSION_2");
896
0
    return FALSE;
897
0
  }
898
899
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
900
0
    return FALSE;
901
0
  const uint16_t wbytes = Stream_Get_UINT16(s);
902
0
  if (wbytes == 0)
903
0
  {
904
0
    WLog_Print(rdstls->log, WLOG_ERROR, "Empty FedAuth token given by client. Deny access");
905
0
    return FALSE;
906
0
  }
907
0
  if ((wbytes % sizeof(WCHAR)) != 0)
908
0
  {
909
0
    WLog_Print(rdstls->log, WLOG_ERROR,
910
0
               "Invalid FedAuth token length %" PRIu16 "given by client. Must be even", wbytes);
911
0
    return FALSE;
912
0
  }
913
0
  const size_t wcharlen = wbytes / sizeof(WCHAR);
914
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, wbytes))
915
0
    return FALSE;
916
917
0
  const rdpSettings* settings = rdstls->context->settings;
918
0
  WINPR_ASSERT(settings);
919
920
0
  size_t len = 0;
921
0
  WCHAR* token =
922
0
      freerdp_settings_get_string_as_utf16(settings, FreeRDP_EndpointFedAuthToken, &len);
923
0
  if (!token || (len == 0))
924
0
  {
925
0
    free(token);
926
0
    WLog_Print(rdstls->log, WLOG_ERROR,
927
0
               "No FedAuth token provided by server to compare. Deny access");
928
0
    return FALSE;
929
0
  }
930
931
0
  if (len != wcharlen)
932
0
  {
933
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token length does not match. Deny access");
934
0
    free(token);
935
0
    return FALSE;
936
0
  }
937
938
0
  const int rc = memcmp(token, Stream_Pointer(s), len * sizeof(WCHAR));
939
0
  free(token);
940
0
  if (rc != 0)
941
0
  {
942
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token does not match. Deny access");
943
0
    return FALSE;
944
0
  }
945
946
0
  WLog_Print(rdstls->log, WLOG_INFO, "FedAuth token does match. Grant access");
947
0
  return TRUE;
948
0
}
949
950
WINPR_ATTR_NODISCARD
951
static BOOL rdstls_process_authentication_request(rdpRdstls* rdstls, wStream* s, uint16_t version)
952
0
{
953
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
954
0
    return FALSE;
955
956
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
957
0
    return FALSE;
958
959
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
960
0
    return FALSE;
961
962
0
  const UINT16 dataType = Stream_Get_UINT16(s);
963
0
  switch (dataType)
964
0
  {
965
0
    case RDSTLS_DATA_PASSWORD_CREDS:
966
0
      if (!rdstls_process_authentication_request_with_password(rdstls, s, version))
967
0
        return FALSE;
968
0
      break;
969
0
    case RDSTLS_DATA_AUTORECONNECT_COOKIE:
970
0
      if (!rdstls_process_authentication_request_with_cookie(rdstls, s, version))
971
0
        return FALSE;
972
0
      break;
973
0
    case RDSTLS_DATA_FEDAUTH_TOKEN:
974
0
      if (!rdstls_process_authentication_request_with_fedauth_token(rdstls, s, version))
975
0
        return FALSE;
976
0
      break;
977
0
    default:
978
0
      WLog_Print(rdstls->log, WLOG_ERROR,
979
0
                 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32
980
0
                 " or 0x%04" PRIX32,
981
0
                 dataType, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_PASSWORD_CREDS),
982
0
                 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_AUTORECONNECT_COOKIE));
983
0
      return FALSE;
984
0
  }
985
986
0
  return TRUE;
987
0
}
988
989
WINPR_ATTR_NODISCARD
990
static BOOL rdstls_process_authentication_response(rdpRdstls* rdstls, wStream* s)
991
0
{
992
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
993
0
    return FALSE;
994
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
995
0
    return FALSE;
996
997
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 6))
998
0
    return FALSE;
999
1000
0
  const UINT16 dataType = Stream_Get_UINT16(s);
1001
0
  if (dataType != RDSTLS_DATA_RESULT_CODE)
1002
0
  {
1003
0
    WLog_Print(rdstls->log, WLOG_ERROR,
1004
0
               "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
1005
0
               WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_RESULT_CODE));
1006
0
    return FALSE;
1007
0
  }
1008
1009
0
  const UINT32 resultCode = Stream_Get_UINT32(s);
1010
0
  if (resultCode != RDSTLS_RESULT_SUCCESS)
1011
0
  {
1012
0
    WLog_Print(rdstls->log, WLOG_ERROR, "resultCode: %s [0x%08" PRIX32 "]",
1013
0
               rdstls_result_code_str(resultCode), resultCode);
1014
1015
0
    UINT32 error = FREERDP_ERROR_CONNECT_UNDEFINED;
1016
0
    switch (resultCode)
1017
0
    {
1018
0
      case RDSTLS_RESULT_ACCESS_DENIED:
1019
0
        error = FREERDP_ERROR_CONNECT_ACCESS_DENIED;
1020
0
        break;
1021
0
      case RDSTLS_RESULT_ACCOUNT_DISABLED:
1022
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED;
1023
0
        break;
1024
0
      case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
1025
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT;
1026
0
        break;
1027
0
      case RDSTLS_RESULT_LOGON_FAILURE:
1028
0
        error = FREERDP_ERROR_CONNECT_LOGON_FAILURE;
1029
0
        break;
1030
0
      case RDSTLS_RESULT_INVALID_LOGON_HOURS:
1031
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION;
1032
0
        break;
1033
0
      case RDSTLS_RESULT_PASSWORD_EXPIRED:
1034
0
        error = FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED;
1035
0
        break;
1036
0
      case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
1037
0
        error = FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE;
1038
0
        break;
1039
0
      default:
1040
0
        WLog_Print(rdstls->log, WLOG_ERROR,
1041
0
                   "Unexpected resultCode: [0x%08" PRIX32 "], NTSTATUS=%s, Win32Error=%s",
1042
0
                   resultCode, GetSecurityStatusString((SECURITY_STATUS)resultCode),
1043
0
                   Win32ErrorCode2Tag(resultCode & 0xFFFF));
1044
0
        error = FREERDP_ERROR_CONNECT_UNDEFINED;
1045
0
        break;
1046
0
    }
1047
1048
0
    freerdp_set_last_error_if_not(rdstls->context, error);
1049
0
    return FALSE;
1050
0
  }
1051
1052
0
  return TRUE;
1053
0
}
1054
1055
WINPR_ATTR_NODISCARD
1056
static BOOL rdstls_send_capabilities(rdpRdstls* rdstls)
1057
0
{
1058
0
  BOOL rc = FALSE;
1059
1060
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1061
0
    return FALSE;
1062
1063
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
1064
0
    return FALSE;
1065
1066
0
  wStream* s = Stream_New(nullptr, 512);
1067
0
  if (!s)
1068
0
    goto fail;
1069
1070
0
  if (!rdstls_write_capabilities(rdstls, s))
1071
0
    goto fail;
1072
0
  if (transport_write(rdstls->transport, s) < 0)
1073
0
    goto fail;
1074
1075
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
1076
0
fail:
1077
0
  Stream_Free(s, TRUE);
1078
0
  return rc;
1079
0
}
1080
1081
WINPR_ATTR_NODISCARD
1082
static BOOL rdstls_recv_authentication_request(rdpRdstls* rdstls, uint16_t* pVersion)
1083
0
{
1084
0
  BOOL rc = FALSE;
1085
0
  WINPR_ASSERT(pVersion);
1086
1087
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1088
0
    return FALSE;
1089
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
1090
0
    return FALSE;
1091
1092
0
  wStream* s = Stream_New(nullptr, 4096);
1093
0
  if (!s)
1094
0
    goto fail;
1095
1096
0
  WINPR_ASSERT(rdstls);
1097
1098
0
  {
1099
0
    const int res = transport_read_pdu(rdstls->transport, s);
1100
0
    if (res < 0)
1101
0
      goto fail;
1102
0
  }
1103
1104
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1105
0
    goto fail;
1106
1107
0
  const UINT16 version = Stream_Get_UINT16(s);
1108
0
  if (!rdstls_is_version_supported(rdstls, version))
1109
0
    goto fail;
1110
0
  *pVersion = version;
1111
1112
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1113
0
  switch (pduType)
1114
0
  {
1115
0
    case RDSTLS_TYPE_AUTHREQ:
1116
0
      if (!rdstls_process_authentication_request(rdstls, s, version))
1117
0
        goto fail;
1118
0
      break;
1119
0
    default:
1120
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1121
0
                 "Invalid RDSTLS PDU type [0x%04" PRIx16 "] while reading AUTHREQ", pduType);
1122
0
      goto fail;
1123
0
  }
1124
1125
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
1126
0
fail:
1127
0
  Stream_Free(s, TRUE);
1128
0
  return rc;
1129
0
}
1130
1131
WINPR_ATTR_NODISCARD
1132
static BOOL rdstls_send_authentication_response(rdpRdstls* rdstls, uint16_t version)
1133
0
{
1134
0
  BOOL rc = FALSE;
1135
1136
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1137
0
    return FALSE;
1138
1139
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
1140
0
    return FALSE;
1141
1142
0
  wStream* s = Stream_New(nullptr, 512);
1143
0
  if (!s)
1144
0
    goto fail;
1145
1146
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
1147
0
    goto fail;
1148
1149
0
  Stream_Write_UINT16(s, version);
1150
1151
0
  if (!rdstls_write_authentication_response(rdstls, s))
1152
0
    goto fail;
1153
1154
0
  if (transport_write(rdstls->transport, s) < 0)
1155
0
    goto fail;
1156
1157
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
1158
0
fail:
1159
0
  Stream_Free(s, TRUE);
1160
0
  return rc;
1161
0
}
1162
1163
WINPR_ATTR_NODISCARD
1164
static BOOL rdstls_recv_capabilities(rdpRdstls* rdstls)
1165
0
{
1166
0
  BOOL rc = FALSE;
1167
1168
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1169
0
    return FALSE;
1170
1171
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
1172
0
    return FALSE;
1173
1174
0
  wStream* s = Stream_New(nullptr, 512);
1175
0
  if (!s)
1176
0
    goto fail;
1177
1178
0
  WINPR_ASSERT(rdstls);
1179
1180
0
  {
1181
0
    const int res = transport_read_pdu(rdstls->transport, s);
1182
0
    if (res < 0)
1183
0
      goto fail;
1184
0
  }
1185
1186
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1187
0
    goto fail;
1188
1189
0
  const UINT16 version = Stream_Get_UINT16(s);
1190
0
  if (!rdstls_is_version_supported(rdstls, version))
1191
0
    goto fail;
1192
1193
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1194
0
  switch (pduType)
1195
0
  {
1196
0
    case RDSTLS_TYPE_CAPABILITIES:
1197
0
      if (!rdstls_process_capabilities(rdstls, s))
1198
0
        goto fail;
1199
0
      break;
1200
0
    default:
1201
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1202
0
                 "Invalid pduType 0x%04" PRIx16 " while reading capability", pduType);
1203
0
      goto fail;
1204
0
  }
1205
1206
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
1207
0
fail:
1208
0
  Stream_Free(s, TRUE);
1209
0
  return rc;
1210
0
}
1211
1212
WINPR_ATTR_NODISCARD
1213
static BOOL rdstls_send_authentication_request(rdpRdstls* rdstls, uint16_t* pVersion)
1214
0
{
1215
0
  WINPR_ASSERT(pVersion);
1216
1217
0
  BOOL rc = FALSE;
1218
1219
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1220
0
    return FALSE;
1221
1222
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
1223
0
    return FALSE;
1224
1225
0
  wStream* s = Stream_New(nullptr, 4096);
1226
0
  if (!s)
1227
0
    goto fail;
1228
1229
0
  WINPR_ASSERT(rdstls->context);
1230
1231
0
  const rdpSettings* settings = rdstls->context->settings;
1232
0
  WINPR_ASSERT(settings);
1233
1234
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
1235
0
    goto fail;
1236
1237
0
  const RDSTLS_STATE state = rdstls_get_state(rdstls);
1238
0
  const char* fedAuthToken = freerdp_settings_get_string(settings, FreeRDP_EndpointFedAuthToken);
1239
0
  BOOL useFedAuth = (state == RDSTLS_STATE_AUTH_REQ) && !utils_str_is_empty(fedAuthToken);
1240
0
  if ((rdstls->supportedVersions & RDSTLS_VERSION_2) == 0)
1241
0
  {
1242
0
    useFedAuth = FALSE;
1243
0
    WLog_Print(rdstls->log, WLOG_WARN,
1244
0
               "Client has FedAuthToken ready, but server did not announce RDSTLS_VERSION_2.");
1245
0
  }
1246
1247
0
  *pVersion = useFedAuth ? RDSTLS_VERSION_2 : RDSTLS_VERSION_1;
1248
0
  Stream_Write_UINT16(s, *pVersion);
1249
1250
0
  if (useFedAuth)
1251
0
  {
1252
0
    if (!rdstls_write_authentication_request_with_fedauth_token(rdstls, s))
1253
0
      goto fail;
1254
0
  }
1255
0
  else if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
1256
0
  {
1257
0
    if (!rdstls_write_authentication_request_with_password(rdstls, s))
1258
0
      goto fail;
1259
0
  }
1260
0
  else if (settings->ServerAutoReconnectCookie != nullptr)
1261
0
  {
1262
0
    if (!rdstls_write_authentication_request_with_cookie(rdstls, s))
1263
0
      goto fail;
1264
0
  }
1265
0
  else
1266
0
  {
1267
0
    WLog_Print(rdstls->log, WLOG_ERROR,
1268
0
               "cannot authenticate with FedAuth token, password or "
1269
0
               "auto-reconnect cookie");
1270
0
    goto fail;
1271
0
  }
1272
1273
0
  WINPR_ASSERT(rdstls);
1274
0
  if (transport_write(rdstls->transport, s) < 0)
1275
0
    goto fail;
1276
1277
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
1278
0
fail:
1279
0
  Stream_Free(s, TRUE);
1280
0
  return rc;
1281
0
}
1282
1283
WINPR_ATTR_NODISCARD
1284
static BOOL rdstls_recv_authentication_response(rdpRdstls* rdstls, uint16_t expected)
1285
0
{
1286
0
  BOOL rc = FALSE;
1287
1288
0
  WINPR_ASSERT(rdstls);
1289
1290
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1291
0
    return FALSE;
1292
1293
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
1294
0
    return FALSE;
1295
1296
0
  wStream* s = Stream_New(nullptr, 512);
1297
0
  if (!s)
1298
0
    goto fail;
1299
1300
0
  {
1301
0
    const int res = transport_read_pdu(rdstls->transport, s);
1302
0
    if (res < 0)
1303
0
      goto fail;
1304
0
  }
1305
1306
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1307
0
    goto fail;
1308
1309
0
  const UINT16 version = Stream_Get_UINT16(s);
1310
0
  if (!rdstls_is_version_supported(rdstls, version))
1311
0
    goto fail;
1312
0
  if (version != expected)
1313
0
    goto fail;
1314
1315
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1316
0
  switch (pduType)
1317
0
  {
1318
0
    case RDSTLS_TYPE_AUTHRSP:
1319
0
      if (!rdstls_process_authentication_response(rdstls, s))
1320
0
        goto fail;
1321
0
      break;
1322
0
    default:
1323
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1324
0
                 "Invalid RDSTLS PDU type [0x%04" PRIx16 "] while reading AUTHRSP", pduType);
1325
0
      goto fail;
1326
0
  }
1327
1328
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
1329
0
fail:
1330
0
  Stream_Free(s, TRUE);
1331
0
  return rc;
1332
0
}
1333
1334
WINPR_ATTR_NODISCARD
1335
static int rdstls_server_authenticate(rdpRdstls* rdstls)
1336
0
{
1337
0
  WINPR_ASSERT(rdstls);
1338
0
  uint16_t version = 0;
1339
1340
0
  if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1341
0
    return -1;
1342
1343
0
  if (!rdstls_send_capabilities(rdstls))
1344
0
    return -1;
1345
1346
0
  if (!rdstls_recv_authentication_request(rdstls, &version))
1347
0
    return -1;
1348
1349
0
  if (!rdstls_send_authentication_response(rdstls, version))
1350
0
    return -1;
1351
1352
0
  if (rdstls->resultCode != RDSTLS_RESULT_SUCCESS)
1353
0
    return -1;
1354
1355
0
  return 1;
1356
0
}
1357
1358
WINPR_ATTR_NODISCARD
1359
static int rdstls_client_authenticate(rdpRdstls* rdstls)
1360
0
{
1361
0
  if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1362
0
    return -1;
1363
1364
0
  if (!rdstls_recv_capabilities(rdstls))
1365
0
    return -1;
1366
1367
0
  uint16_t version = 0;
1368
0
  if (!rdstls_send_authentication_request(rdstls, &version))
1369
0
    return -1;
1370
1371
0
  if (!rdstls_recv_authentication_response(rdstls, version))
1372
0
    return -1;
1373
1374
0
  return 1;
1375
0
}
1376
1377
/**
1378
 * Authenticate using RDSTLS.
1379
 * @param rdstls The RDSTLS instance to use
1380
 *
1381
 * @return 1 if authentication is successful
1382
 */
1383
1384
int rdstls_authenticate(rdpRdstls* rdstls)
1385
0
{
1386
0
  WINPR_ASSERT(rdstls);
1387
1388
0
  if (rdstls->server)
1389
0
    return rdstls_server_authenticate(rdstls);
1390
0
  else
1391
0
    return rdstls_client_authenticate(rdstls);
1392
0
}
1393
1394
WINPR_ATTR_NODISCARD
1395
static SSIZE_T rdstls_parse_pdu_data_type(wLog* log, UINT16 dataType, wStream* s)
1396
0
{
1397
0
  size_t pduLength = 0;
1398
1399
0
  switch (dataType)
1400
0
  {
1401
0
    case RDSTLS_DATA_PASSWORD_CREDS:
1402
0
    {
1403
0
      if (Stream_GetRemainingLength(s) < 2)
1404
0
        return 0;
1405
1406
0
      const UINT16 redirGuidLength = Stream_Get_UINT16(s);
1407
1408
0
      if (Stream_GetRemainingLength(s) < redirGuidLength)
1409
0
        return 0;
1410
0
      Stream_Seek(s, redirGuidLength);
1411
1412
0
      if (Stream_GetRemainingLength(s) < 2)
1413
0
        return 0;
1414
1415
0
      const UINT16 usernameLength = Stream_Get_UINT16(s);
1416
1417
0
      if (Stream_GetRemainingLength(s) < usernameLength)
1418
0
        return 0;
1419
0
      Stream_Seek(s, usernameLength);
1420
1421
0
      if (Stream_GetRemainingLength(s) < 2)
1422
0
        return 0;
1423
0
      const UINT16 domainLength = Stream_Get_UINT16(s);
1424
1425
0
      if (Stream_GetRemainingLength(s) < domainLength)
1426
0
        return 0;
1427
0
      Stream_Seek(s, domainLength);
1428
1429
0
      if (Stream_GetRemainingLength(s) < 2)
1430
0
        return 0;
1431
0
      const UINT16 passwordLength = Stream_Get_UINT16(s);
1432
1433
0
      if (passwordLength == 0)
1434
0
      {
1435
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS PASSWORD_CREDS: empty password");
1436
0
        return -1;
1437
0
      }
1438
0
      else if ((redirGuidLength == 0) && (usernameLength == 0) && (domainLength == 0) &&
1439
0
               (passwordLength == 0))
1440
0
      {
1441
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS PASSWORD_CREDS: lengths 0");
1442
0
        return -1;
1443
0
      }
1444
0
      pduLength = Stream_GetPosition(s) + passwordLength;
1445
0
    }
1446
0
    break;
1447
0
    case RDSTLS_DATA_AUTORECONNECT_COOKIE:
1448
0
    {
1449
0
      if (Stream_GetRemainingLength(s) < 6)
1450
0
        return 0;
1451
0
      Stream_Seek(s, 4);
1452
0
      const UINT16 cookieLength = Stream_Get_UINT16(s);
1453
0
      if (cookieLength == 0)
1454
0
      {
1455
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS COOKIE::length");
1456
0
        return -1;
1457
0
      }
1458
0
      pduLength = Stream_GetPosition(s) + cookieLength;
1459
0
    }
1460
0
    break;
1461
0
    case RDSTLS_DATA_FEDAUTH_TOKEN:
1462
0
    {
1463
0
      if (Stream_GetRemainingLength(s) < 6)
1464
0
        return 0;
1465
0
      Stream_Seek(s, 4);
1466
0
      const UINT16 tokenLength = Stream_Get_UINT16(s);
1467
0
      if (tokenLength == 0)
1468
0
      {
1469
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS FEDAUTH_TOKEN::length");
1470
0
        return -1;
1471
0
      }
1472
0
      pduLength = Stream_GetPosition(s) + tokenLength;
1473
0
    }
1474
0
    break;
1475
0
    default:
1476
0
      WLog_Print(log, WLOG_ERROR, "invalid RDSLTS dataType");
1477
0
      return -1;
1478
0
  }
1479
1480
0
  if (pduLength > SSIZE_MAX)
1481
0
    return 0;
1482
0
  return (SSIZE_T)pduLength;
1483
0
}
1484
1485
SSIZE_T rdstls_parse_pdu(wLog* log, wStream* stream)
1486
0
{
1487
0
  SSIZE_T pduLength = -1;
1488
0
  wStream sbuffer = WINPR_C_ARRAY_INIT;
1489
0
  wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(stream), Stream_Length(stream));
1490
1491
0
  if (Stream_GetRemainingLength(s) < 2)
1492
0
    return 0;
1493
1494
0
  const UINT16 version = Stream_Get_UINT16(s);
1495
0
  if (!rdstls_are_some_versions_supported(log, version, FALSE))
1496
0
    return -1;
1497
1498
0
  if (Stream_GetRemainingLength(s) < 2)
1499
0
    return 0;
1500
1501
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1502
0
  switch (pduType)
1503
0
  {
1504
0
    case RDSTLS_TYPE_CAPABILITIES:
1505
0
      pduLength = 8;
1506
0
      break;
1507
0
    case RDSTLS_TYPE_AUTHREQ:
1508
0
    {
1509
0
      if (Stream_GetRemainingLength(s) < 2)
1510
0
        return 0;
1511
1512
0
      const UINT16 dataType = Stream_Get_UINT16(s);
1513
0
      pduLength = rdstls_parse_pdu_data_type(log, dataType, s);
1514
0
    }
1515
0
    break;
1516
0
    case RDSTLS_TYPE_AUTHRSP:
1517
0
      pduLength = 10;
1518
0
      break;
1519
0
    default:
1520
0
      WLog_Print(log, WLOG_ERROR, "invalid RDSTLS PDU type");
1521
0
      return -1;
1522
0
  }
1523
1524
0
  return pduLength;
1525
0
}