Coverage Report

Created: 2026-10-15 06:31

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 SSIZE_T wideLength = ConvertUtf8NToWChar(token, utf8Length, nullptr, 0);
524
0
  if (wideLength < 0)
525
0
    return FALSE;
526
0
  const size_t wideLengthZero = WINPR_ASSERTING_INT_CAST(size_t, wideLength);
527
0
  const size_t wideBytes = (wideLengthZero + 1ull) * sizeof(WCHAR);
528
529
0
  if (!Stream_EnsureRemainingCapacity(s, 6ull + wideBytes))
530
0
    return FALSE;
531
532
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHREQ);
533
0
  Stream_Write_UINT16(s, RDSTLS_DATA_FEDAUTH_TOKEN);
534
0
  Stream_Write_UINT16(s, (UINT16)wideBytes);
535
536
0
  return Stream_Write_UTF16_String_From_UTF8(s, wideLengthZero, token, utf8Length, TRUE) >= 0;
537
0
}
538
539
WINPR_ATTR_NODISCARD
540
static BOOL rdstls_write_authentication_response(rdpRdstls* rdstls, wStream* s)
541
0
{
542
0
  WINPR_ASSERT(rdstls);
543
544
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
545
0
    return FALSE;
546
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
547
0
    return FALSE;
548
0
  if (!Stream_EnsureRemainingCapacity(s, 8))
549
0
    return FALSE;
550
551
0
  Stream_Write_UINT16(s, RDSTLS_TYPE_AUTHRSP);
552
0
  Stream_Write_UINT16(s, RDSTLS_DATA_RESULT_CODE);
553
0
  Stream_Write_UINT32(s, rdstls->resultCode);
554
555
0
  return TRUE;
556
0
}
557
558
#define rdstls_version_required(log, expected, actual) \
559
0
  rdstls_version_required_((log), (expected), (actual), __FILE__, __func__, __LINE__)
560
WINPR_ATTR_NODISCARD
561
static BOOL rdstls_version_required_(wLog* log, uint16_t expected, uint16_t actual,
562
                                     const char* file, const char* fkt, size_t line)
563
0
{
564
0
  if (actual < expected)
565
0
  {
566
0
    const DWORD level = WLOG_ERROR;
567
0
    if (WLog_IsLevelActive(log, level))
568
0
    {
569
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
570
0
                            "version=0x%04" PRIx16 ", expected at least 0x%04" PRIx16, actual,
571
0
                            expected);
572
0
    }
573
0
    return FALSE;
574
0
  }
575
0
  return TRUE;
576
0
}
577
578
#define rdstls_are_some_versions_supported(log, version, mask) \
579
0
  rdstls_are_some_versions_supported_((log), (version), (mask), __FILE__, __func__, __LINE__)
580
WINPR_ATTR_NODISCARD
581
static BOOL rdstls_are_some_versions_supported_(wLog* log, uint16_t version, BOOL isMask,
582
                                                const char* file, const char* fkt, size_t line)
583
0
{
584
0
  if (!isMask)
585
0
  {
586
0
    size_t cnt = 0;
587
0
    for (size_t x = 0; x < 16; x++)
588
0
    {
589
0
      const unsigned val = 1 << x;
590
0
      if ((version & val) != 0)
591
0
        cnt++;
592
0
    }
593
0
    if (cnt != 1)
594
0
    {
595
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
596
0
                            "received invalid version mask=0x%04" PRIx16
597
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
598
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
599
0
      return FALSE;
600
0
    }
601
0
  }
602
603
0
  if ((version & RDSTLS_VERSION_MASK) == 0)
604
0
  {
605
0
    const DWORD level = WLOG_ERROR;
606
0
    if (WLog_IsLevelActive(log, level))
607
0
    {
608
0
      WLog_PrintTextMessage(log, WLOG_ERROR, line, file, fkt,
609
0
                            "received invalid version mask=0x%04" PRIx16
610
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
611
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
612
0
    }
613
0
    return FALSE;
614
0
  }
615
0
  return TRUE;
616
0
}
617
618
#define rdstls_is_version_supported(rdstls, version) \
619
0
  rdstls_is_version_supported_((rdstls), (version), __FILE__, __func__, __LINE__)
620
WINPR_ATTR_NODISCARD
621
static BOOL rdstls_is_version_supported_(rdpRdstls* rdstls, uint16_t version, const char* file,
622
                                         const char* fkt, size_t line)
623
0
{
624
0
  WINPR_ASSERT(rdstls);
625
626
0
  if ((rdstls->supportedVersions & version) == 0)
627
0
  {
628
0
    const DWORD level = WLOG_ERROR;
629
0
    if (WLog_IsLevelActive(rdstls->log, level))
630
0
    {
631
0
      WLog_PrintTextMessage(rdstls->log, WLOG_ERROR, line, file, fkt,
632
0
                            "received invalid version=0x%04" PRIx16
633
0
                            ", expected { 0x%04" PRIx32 ", 0x%04" PRIx32 "}",
634
0
                            version, RDSTLS_VERSION_1, RDSTLS_VERSION_2);
635
0
    }
636
0
    return FALSE;
637
0
  }
638
0
  return TRUE;
639
0
}
640
641
WINPR_ATTR_NODISCARD
642
static BOOL rdstls_process_capabilities(rdpRdstls* rdstls, wStream* s)
643
0
{
644
0
  WINPR_ASSERT(rdstls);
645
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
646
0
    return FALSE;
647
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
648
0
    return FALSE;
649
650
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
651
0
    return FALSE;
652
653
0
  const UINT16 dataType = Stream_Get_UINT16(s);
654
0
  if (dataType != RDSTLS_DATA_CAPABILITIES)
655
0
  {
656
0
    WLog_Print(rdstls->log, WLOG_ERROR,
657
0
               "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
658
0
               WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_CAPABILITIES));
659
0
    return FALSE;
660
0
  }
661
662
0
  const UINT16 supportedVersions = Stream_Get_UINT16(s);
663
0
  if (!rdstls_are_some_versions_supported(rdstls->log, supportedVersions, TRUE))
664
0
    return FALSE;
665
0
  rdstls->supportedVersions = supportedVersions & RDSTLS_VERSION_MASK;
666
667
0
  return TRUE;
668
0
}
669
670
WINPR_ATTR_NODISCARD
671
static BOOL rdstls_read_unicode_string(WINPR_ATTR_UNUSED wLog* log, wStream* s, char** str)
672
0
{
673
0
  WINPR_ASSERT(str);
674
675
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
676
0
    return FALSE;
677
678
0
  const UINT16 length = Stream_Get_UINT16(s);
679
680
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
681
0
    return FALSE;
682
683
0
  if (length <= 2)
684
0
  {
685
0
    *str = nullptr;
686
0
    Stream_Seek(s, length);
687
0
    return TRUE;
688
0
  }
689
690
0
  *str = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
691
0
  return (*str) != nullptr;
692
0
}
693
694
WINPR_ATTR_NODISCARD
695
static BOOL rdstls_read_data(WINPR_ATTR_UNUSED wLog* log, wStream* s, UINT16* pLength,
696
                             const BYTE** pData)
697
0
{
698
0
  WINPR_ASSERT(pLength);
699
0
  WINPR_ASSERT(pData);
700
701
0
  *pData = nullptr;
702
0
  *pLength = 0;
703
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 2))
704
0
    return FALSE;
705
706
0
  const UINT16 length = Stream_Get_UINT16(s);
707
708
0
  if (!Stream_CheckAndLogRequiredLengthWLog(log, s, length))
709
0
    return FALSE;
710
711
0
  if (length <= 2)
712
0
  {
713
0
    Stream_Seek(s, length);
714
0
    return TRUE;
715
0
  }
716
717
0
  *pData = Stream_ConstPointer(s);
718
0
  *pLength = length;
719
0
  Stream_Seek(s, length);
720
0
  return TRUE;
721
0
}
722
723
WINPR_ATTR_NODISCARD
724
static BOOL rdstls_cmp_data(wLog* log, const char* field, const BYTE* serverData,
725
                            const UINT32 serverDataLength, const BYTE* clientData,
726
                            const UINT16 clientDataLength)
727
0
{
728
0
  if (serverDataLength > 0)
729
0
  {
730
0
    if (clientDataLength == 0)
731
0
    {
732
0
      WLog_Print(log, WLOG_ERROR, "expected %s", field);
733
0
      return FALSE;
734
0
    }
735
736
0
    if (serverDataLength > UINT16_MAX || serverDataLength != clientDataLength ||
737
0
        memcmp(serverData, clientData, serverDataLength) != 0)
738
0
    {
739
0
      WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
740
0
      return FALSE;
741
0
    }
742
0
  }
743
744
0
  return TRUE;
745
0
}
746
747
WINPR_ATTR_NODISCARD
748
static BOOL rdstls_cmp_str(wLog* log, const char* field, const char* serverStr,
749
                           const char* clientStr)
750
0
{
751
0
  if (!utils_str_is_empty(serverStr))
752
0
  {
753
0
    if (utils_str_is_empty(clientStr))
754
0
    {
755
0
      WLog_Print(log, WLOG_ERROR, "expected %s", field);
756
0
      return FALSE;
757
0
    }
758
759
0
    WINPR_ASSERT(serverStr);
760
0
    WINPR_ASSERT(clientStr);
761
0
    if (strcmp(serverStr, clientStr) != 0)
762
0
    {
763
0
      WLog_Print(log, WLOG_ERROR, "%s verification failed", field);
764
0
      return FALSE;
765
0
    }
766
0
  }
767
768
0
  return TRUE;
769
0
}
770
771
WINPR_ATTR_NODISCARD
772
static BOOL rdstls_process_authentication_request_with_password(rdpRdstls* rdstls, wStream* s,
773
                                                                uint16_t version)
774
0
{
775
0
  WINPR_ASSERT(rdstls);
776
0
  WINPR_ASSERT(rdstls->context);
777
778
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_1, version))
779
0
    return FALSE;
780
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
781
0
    return FALSE;
782
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
783
0
    return FALSE;
784
785
0
  BOOL rc = FALSE;
786
787
0
  const BYTE* clientRedirectionGuid = nullptr;
788
0
  UINT16 clientRedirectionGuidLength = 0;
789
0
  char* clientPassword = nullptr;
790
0
  char* clientUsername = nullptr;
791
0
  char* clientDomain = nullptr;
792
793
0
  const rdpSettings* settings = rdstls->context->settings;
794
0
  WINPR_ASSERT(settings);
795
796
0
  if (!rdstls_read_data(rdstls->log, s, &clientRedirectionGuidLength, &clientRedirectionGuid))
797
0
    goto fail;
798
799
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientUsername))
800
0
    goto fail;
801
802
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientDomain))
803
0
    goto fail;
804
805
0
  if (!rdstls_read_unicode_string(rdstls->log, s, &clientPassword))
806
0
    goto fail;
807
808
0
  {
809
0
    const BYTE* serverRedirectionGuid =
810
0
        freerdp_settings_get_pointer(settings, FreeRDP_RedirectionGuid);
811
0
    const UINT32 serverRedirectionGuidLength =
812
0
        freerdp_settings_get_uint32(settings, FreeRDP_RedirectionGuidLength);
813
0
    const char* serverUsername = freerdp_settings_get_string(settings, FreeRDP_Username);
814
0
    const char* serverDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
815
0
    const char* serverPassword = freerdp_settings_get_string(settings, FreeRDP_Password);
816
817
0
    if (!rdstls_cmp_data(rdstls->log, "RedirectionGuid", serverRedirectionGuid,
818
0
                         serverRedirectionGuidLength, clientRedirectionGuid,
819
0
                         clientRedirectionGuidLength))
820
0
      rdstls->resultCode = RDSTLS_RESULT_ACCESS_DENIED;
821
0
    else if (!rdstls_cmp_str(rdstls->log, "UserName", serverUsername, clientUsername))
822
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
823
0
    else if (!rdstls_cmp_str(rdstls->log, "Domain", serverDomain, clientDomain))
824
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
825
0
    else if (!rdstls_cmp_str(rdstls->log, "Password", serverPassword, clientPassword))
826
0
      rdstls->resultCode = RDSTLS_RESULT_LOGON_FAILURE;
827
0
    else
828
0
      rdstls->resultCode = RDSTLS_RESULT_SUCCESS;
829
0
  }
830
0
  rc = TRUE;
831
0
fail:
832
0
  return rc;
833
0
}
834
835
WINPR_ATTR_NODISCARD
836
static BOOL rdstls_process_authentication_request_with_cookie(rdpRdstls* rdstls, wStream* s,
837
                                                              uint16_t version)
838
0
{
839
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_1, version))
840
0
    return FALSE;
841
842
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
843
0
    return FALSE;
844
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
845
0
    return FALSE;
846
847
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
848
0
    return FALSE;
849
850
0
  const rdpSettings* settings = rdstls->context->settings;
851
0
  WINPR_ASSERT(settings);
852
853
0
  const uint32_t id = Stream_Get_UINT32(s);
854
0
  const uint32_t expected = freerdp_settings_get_uint32(settings, FreeRDP_RedirectedSessionId);
855
0
  if (id != expected)
856
0
  {
857
0
    WLog_Print(rdstls->log, WLOG_ERROR,
858
0
               "RDSTLS Cookie SessionId does not match RedirectedSessionId. Deny access.");
859
0
    return FALSE;
860
0
  }
861
862
0
  ARC_SC_PRIVATE_PACKET cookie = WINPR_C_ARRAY_INIT;
863
0
  if (!rdstls_read_cookie(rdstls->log, s, &cookie))
864
0
    return FALSE;
865
866
0
  const ARC_SC_PRIVATE_PACKET* expect =
867
0
      freerdp_settings_get_pointer(settings, FreeRDP_ServerAutoReconnectCookie);
868
0
  if (!expect)
869
0
  {
870
0
    WLog_Print(rdstls->log, WLOG_ERROR, "No RDSTLS Cookie provided by server. Deny access.");
871
0
    return FALSE;
872
0
  }
873
874
0
  if (memcmp(expect, &cookie, sizeof(ARC_SC_PRIVATE_PACKET)) != 0)
875
0
  {
876
0
    WLog_Print(rdstls->log, WLOG_ERROR, "RDSTLS Cookie does not match. Deny access.");
877
0
    return FALSE;
878
0
  }
879
880
0
  WLog_Print(rdstls->log, WLOG_DEBUG, "RDSTLS Cookie matches. Grant access.");
881
0
  rdstls->resultCode = RDSTLS_RESULT_SUCCESS;
882
0
  return TRUE;
883
0
}
884
885
WINPR_ATTR_NODISCARD
886
static BOOL rdstls_process_authentication_request_with_fedauth_token(rdpRdstls* rdstls, wStream* s,
887
                                                                     uint16_t version)
888
0
{
889
0
  WINPR_ASSERT(rdstls);
890
891
0
  if (!rdstls_version_required(rdstls->log, RDSTLS_VERSION_2, version))
892
0
    return FALSE;
893
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
894
0
    return FALSE;
895
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
896
0
    return FALSE;
897
0
  if ((rdstls->supportedVersions & RDSTLS_VERSION_2) == 0)
898
0
  {
899
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token only supported with RDSTLS_VERSION_2");
900
0
    return FALSE;
901
0
  }
902
903
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
904
0
    return FALSE;
905
0
  const uint16_t wbytes = Stream_Get_UINT16(s);
906
0
  if (wbytes == 0)
907
0
  {
908
0
    WLog_Print(rdstls->log, WLOG_ERROR, "Empty FedAuth token given by client. Deny access");
909
0
    return FALSE;
910
0
  }
911
0
  if ((wbytes % sizeof(WCHAR)) != 0)
912
0
  {
913
0
    WLog_Print(rdstls->log, WLOG_ERROR,
914
0
               "Invalid FedAuth token length %" PRIu16 "given by client. Must be even", wbytes);
915
0
    return FALSE;
916
0
  }
917
0
  const size_t wcharlen = wbytes / sizeof(WCHAR);
918
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, wbytes))
919
0
    return FALSE;
920
921
0
  const rdpSettings* settings = rdstls->context->settings;
922
0
  WINPR_ASSERT(settings);
923
924
0
  size_t len = 0;
925
0
  WCHAR* token =
926
0
      freerdp_settings_get_string_as_utf16(settings, FreeRDP_EndpointFedAuthToken, &len);
927
0
  if (!token || (len == 0))
928
0
  {
929
0
    free(token);
930
0
    WLog_Print(rdstls->log, WLOG_ERROR,
931
0
               "No FedAuth token provided by server to compare. Deny access");
932
0
    return FALSE;
933
0
  }
934
935
0
  if (len != wcharlen)
936
0
  {
937
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token length does not match. Deny access");
938
0
    free(token);
939
0
    return FALSE;
940
0
  }
941
942
0
  const int rc = memcmp(token, Stream_Pointer(s), len * sizeof(WCHAR));
943
0
  free(token);
944
0
  if (rc != 0)
945
0
  {
946
0
    WLog_Print(rdstls->log, WLOG_ERROR, "FedAuth token does not match. Deny access");
947
0
    return FALSE;
948
0
  }
949
950
0
  WLog_Print(rdstls->log, WLOG_INFO, "FedAuth token does match. Grant access");
951
0
  return TRUE;
952
0
}
953
954
WINPR_ATTR_NODISCARD
955
static BOOL rdstls_process_authentication_request(rdpRdstls* rdstls, wStream* s, uint16_t version)
956
0
{
957
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
958
0
    return FALSE;
959
960
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
961
0
    return FALSE;
962
963
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 2))
964
0
    return FALSE;
965
966
0
  const UINT16 dataType = Stream_Get_UINT16(s);
967
0
  switch (dataType)
968
0
  {
969
0
    case RDSTLS_DATA_PASSWORD_CREDS:
970
0
      if (!rdstls_process_authentication_request_with_password(rdstls, s, version))
971
0
        return FALSE;
972
0
      break;
973
0
    case RDSTLS_DATA_AUTORECONNECT_COOKIE:
974
0
      if (!rdstls_process_authentication_request_with_cookie(rdstls, s, version))
975
0
        return FALSE;
976
0
      break;
977
0
    case RDSTLS_DATA_FEDAUTH_TOKEN:
978
0
      if (!rdstls_process_authentication_request_with_fedauth_token(rdstls, s, version))
979
0
        return FALSE;
980
0
      break;
981
0
    default:
982
0
      WLog_Print(rdstls->log, WLOG_ERROR,
983
0
                 "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32
984
0
                 " or 0x%04" PRIX32,
985
0
                 dataType, WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_PASSWORD_CREDS),
986
0
                 WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_AUTORECONNECT_COOKIE));
987
0
      return FALSE;
988
0
  }
989
990
0
  return TRUE;
991
0
}
992
993
WINPR_ATTR_NODISCARD
994
static BOOL rdstls_process_authentication_response(rdpRdstls* rdstls, wStream* s)
995
0
{
996
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
997
0
    return FALSE;
998
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
999
0
    return FALSE;
1000
1001
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 6))
1002
0
    return FALSE;
1003
1004
0
  const UINT16 dataType = Stream_Get_UINT16(s);
1005
0
  if (dataType != RDSTLS_DATA_RESULT_CODE)
1006
0
  {
1007
0
    WLog_Print(rdstls->log, WLOG_ERROR,
1008
0
               "received invalid DataType=0x%04" PRIX16 ", expected 0x%04" PRIX32, dataType,
1009
0
               WINPR_CXX_COMPAT_CAST(UINT32, RDSTLS_DATA_RESULT_CODE));
1010
0
    return FALSE;
1011
0
  }
1012
1013
0
  const UINT32 resultCode = Stream_Get_UINT32(s);
1014
0
  if (resultCode != RDSTLS_RESULT_SUCCESS)
1015
0
  {
1016
0
    WLog_Print(rdstls->log, WLOG_ERROR, "resultCode: %s [0x%08" PRIX32 "]",
1017
0
               rdstls_result_code_str(resultCode), resultCode);
1018
1019
0
    UINT32 error = FREERDP_ERROR_CONNECT_UNDEFINED;
1020
0
    switch (resultCode)
1021
0
    {
1022
0
      case RDSTLS_RESULT_ACCESS_DENIED:
1023
0
        error = FREERDP_ERROR_CONNECT_ACCESS_DENIED;
1024
0
        break;
1025
0
      case RDSTLS_RESULT_ACCOUNT_DISABLED:
1026
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED;
1027
0
        break;
1028
0
      case RDSTLS_RESULT_ACCOUNT_LOCKED_OUT:
1029
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT;
1030
0
        break;
1031
0
      case RDSTLS_RESULT_LOGON_FAILURE:
1032
0
        error = FREERDP_ERROR_CONNECT_LOGON_FAILURE;
1033
0
        break;
1034
0
      case RDSTLS_RESULT_INVALID_LOGON_HOURS:
1035
0
        error = FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION;
1036
0
        break;
1037
0
      case RDSTLS_RESULT_PASSWORD_EXPIRED:
1038
0
        error = FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED;
1039
0
        break;
1040
0
      case RDSTLS_RESULT_PASSWORD_MUST_CHANGE:
1041
0
        error = FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE;
1042
0
        break;
1043
0
      default:
1044
0
        WLog_Print(rdstls->log, WLOG_ERROR,
1045
0
                   "Unexpected resultCode: [0x%08" PRIX32 "], NTSTATUS=%s, Win32Error=%s",
1046
0
                   resultCode, GetSecurityStatusString((SECURITY_STATUS)resultCode),
1047
0
                   Win32ErrorCode2Tag(resultCode & 0xFFFF));
1048
0
        error = FREERDP_ERROR_CONNECT_UNDEFINED;
1049
0
        break;
1050
0
    }
1051
1052
0
    freerdp_set_last_error_if_not(rdstls->context, error);
1053
0
    return FALSE;
1054
0
  }
1055
1056
0
  return TRUE;
1057
0
}
1058
1059
WINPR_ATTR_NODISCARD
1060
static BOOL rdstls_send_capabilities(rdpRdstls* rdstls)
1061
0
{
1062
0
  BOOL rc = FALSE;
1063
1064
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1065
0
    return FALSE;
1066
1067
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
1068
0
    return FALSE;
1069
1070
0
  wStream* s = Stream_New(nullptr, 512);
1071
0
  if (!s)
1072
0
    goto fail;
1073
1074
0
  if (!rdstls_write_capabilities(rdstls, s))
1075
0
    goto fail;
1076
0
  if (transport_write(rdstls->transport, s) < 0)
1077
0
    goto fail;
1078
1079
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
1080
0
fail:
1081
0
  Stream_Free(s, TRUE);
1082
0
  return rc;
1083
0
}
1084
1085
WINPR_ATTR_NODISCARD
1086
static BOOL rdstls_recv_authentication_request(rdpRdstls* rdstls, uint16_t* pVersion)
1087
0
{
1088
0
  BOOL rc = FALSE;
1089
0
  WINPR_ASSERT(pVersion);
1090
1091
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1092
0
    return FALSE;
1093
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
1094
0
    return FALSE;
1095
1096
0
  wStream* s = Stream_New(nullptr, 4096);
1097
0
  if (!s)
1098
0
    goto fail;
1099
1100
0
  WINPR_ASSERT(rdstls);
1101
1102
0
  {
1103
0
    const int res = transport_read_pdu(rdstls->transport, s);
1104
0
    if (res < 0)
1105
0
      goto fail;
1106
0
  }
1107
1108
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1109
0
    goto fail;
1110
1111
0
  const UINT16 version = Stream_Get_UINT16(s);
1112
0
  if (!rdstls_is_version_supported(rdstls, version))
1113
0
    goto fail;
1114
0
  *pVersion = version;
1115
1116
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1117
0
  switch (pduType)
1118
0
  {
1119
0
    case RDSTLS_TYPE_AUTHREQ:
1120
0
      if (!rdstls_process_authentication_request(rdstls, s, version))
1121
0
        goto fail;
1122
0
      break;
1123
0
    default:
1124
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1125
0
                 "Invalid RDSTLS PDU type [0x%04" PRIx16 "] while reading AUTHREQ", pduType);
1126
0
      goto fail;
1127
0
  }
1128
1129
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
1130
0
fail:
1131
0
  Stream_Free(s, TRUE);
1132
0
  return rc;
1133
0
}
1134
1135
WINPR_ATTR_NODISCARD
1136
static BOOL rdstls_send_authentication_response(rdpRdstls* rdstls, uint16_t version)
1137
0
{
1138
0
  BOOL rc = FALSE;
1139
1140
0
  if (!rdstls_required_role_is_server(rdstls, TRUE))
1141
0
    return FALSE;
1142
1143
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
1144
0
    return FALSE;
1145
1146
0
  wStream* s = Stream_New(nullptr, 512);
1147
0
  if (!s)
1148
0
    goto fail;
1149
1150
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
1151
0
    goto fail;
1152
1153
0
  Stream_Write_UINT16(s, version);
1154
1155
0
  if (!rdstls_write_authentication_response(rdstls, s))
1156
0
    goto fail;
1157
1158
0
  if (transport_write(rdstls->transport, s) < 0)
1159
0
    goto fail;
1160
1161
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
1162
0
fail:
1163
0
  Stream_Free(s, TRUE);
1164
0
  return rc;
1165
0
}
1166
1167
WINPR_ATTR_NODISCARD
1168
static BOOL rdstls_recv_capabilities(rdpRdstls* rdstls)
1169
0
{
1170
0
  BOOL rc = FALSE;
1171
1172
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1173
0
    return FALSE;
1174
1175
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_CAPABILITIES))
1176
0
    return FALSE;
1177
1178
0
  wStream* s = Stream_New(nullptr, 512);
1179
0
  if (!s)
1180
0
    goto fail;
1181
1182
0
  WINPR_ASSERT(rdstls);
1183
1184
0
  {
1185
0
    const int res = transport_read_pdu(rdstls->transport, s);
1186
0
    if (res < 0)
1187
0
      goto fail;
1188
0
  }
1189
1190
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1191
0
    goto fail;
1192
1193
0
  const UINT16 version = Stream_Get_UINT16(s);
1194
0
  if (!rdstls_is_version_supported(rdstls, version))
1195
0
    goto fail;
1196
1197
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1198
0
  switch (pduType)
1199
0
  {
1200
0
    case RDSTLS_TYPE_CAPABILITIES:
1201
0
      if (!rdstls_process_capabilities(rdstls, s))
1202
0
        goto fail;
1203
0
      break;
1204
0
    default:
1205
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1206
0
                 "Invalid pduType 0x%04" PRIx16 " while reading capability", pduType);
1207
0
      goto fail;
1208
0
  }
1209
1210
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_REQ);
1211
0
fail:
1212
0
  Stream_Free(s, TRUE);
1213
0
  return rc;
1214
0
}
1215
1216
WINPR_ATTR_NODISCARD
1217
static BOOL rdstls_send_authentication_request(rdpRdstls* rdstls, uint16_t* pVersion)
1218
0
{
1219
0
  WINPR_ASSERT(pVersion);
1220
1221
0
  BOOL rc = FALSE;
1222
1223
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1224
0
    return FALSE;
1225
1226
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_REQ))
1227
0
    return FALSE;
1228
1229
0
  wStream* s = Stream_New(nullptr, 4096);
1230
0
  if (!s)
1231
0
    goto fail;
1232
1233
0
  WINPR_ASSERT(rdstls->context);
1234
1235
0
  const rdpSettings* settings = rdstls->context->settings;
1236
0
  WINPR_ASSERT(settings);
1237
1238
0
  if (!Stream_EnsureRemainingCapacity(s, 2))
1239
0
    goto fail;
1240
1241
0
  const RDSTLS_STATE state = rdstls_get_state(rdstls);
1242
0
  const char* fedAuthToken = freerdp_settings_get_string(settings, FreeRDP_EndpointFedAuthToken);
1243
0
  BOOL useFedAuth = (state == RDSTLS_STATE_AUTH_REQ) && !utils_str_is_empty(fedAuthToken);
1244
0
  if ((rdstls->supportedVersions & RDSTLS_VERSION_2) == 0)
1245
0
  {
1246
0
    useFedAuth = FALSE;
1247
0
    WLog_Print(rdstls->log, WLOG_WARN,
1248
0
               "Client has FedAuthToken ready, but server did not announce RDSTLS_VERSION_2.");
1249
0
  }
1250
1251
0
  *pVersion = useFedAuth ? RDSTLS_VERSION_2 : RDSTLS_VERSION_1;
1252
0
  Stream_Write_UINT16(s, *pVersion);
1253
1254
0
  if (useFedAuth)
1255
0
  {
1256
0
    if (!rdstls_write_authentication_request_with_fedauth_token(rdstls, s))
1257
0
      goto fail;
1258
0
  }
1259
0
  else if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED)
1260
0
  {
1261
0
    if (!rdstls_write_authentication_request_with_password(rdstls, s))
1262
0
      goto fail;
1263
0
  }
1264
0
  else if (settings->ServerAutoReconnectCookie != nullptr)
1265
0
  {
1266
0
    if (!rdstls_write_authentication_request_with_cookie(rdstls, s))
1267
0
      goto fail;
1268
0
  }
1269
0
  else
1270
0
  {
1271
0
    WLog_Print(rdstls->log, WLOG_ERROR,
1272
0
               "cannot authenticate with FedAuth token, password or "
1273
0
               "auto-reconnect cookie");
1274
0
    goto fail;
1275
0
  }
1276
1277
0
  WINPR_ASSERT(rdstls);
1278
0
  if (transport_write(rdstls->transport, s) < 0)
1279
0
    goto fail;
1280
1281
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_AUTH_RSP);
1282
0
fail:
1283
0
  Stream_Free(s, TRUE);
1284
0
  return rc;
1285
0
}
1286
1287
WINPR_ATTR_NODISCARD
1288
static BOOL rdstls_recv_authentication_response(rdpRdstls* rdstls, uint16_t expected)
1289
0
{
1290
0
  BOOL rc = FALSE;
1291
1292
0
  WINPR_ASSERT(rdstls);
1293
1294
0
  if (!rdstls_required_role_is_server(rdstls, FALSE))
1295
0
    return FALSE;
1296
1297
0
  if (!rdstls_check_state_requirements(rdstls, RDSTLS_STATE_AUTH_RSP))
1298
0
    return FALSE;
1299
1300
0
  wStream* s = Stream_New(nullptr, 512);
1301
0
  if (!s)
1302
0
    goto fail;
1303
1304
0
  {
1305
0
    const int res = transport_read_pdu(rdstls->transport, s);
1306
0
    if (res < 0)
1307
0
      goto fail;
1308
0
  }
1309
1310
0
  if (!Stream_CheckAndLogRequiredLengthWLog(rdstls->log, s, 4))
1311
0
    goto fail;
1312
1313
0
  const UINT16 version = Stream_Get_UINT16(s);
1314
0
  if (!rdstls_is_version_supported(rdstls, version))
1315
0
    goto fail;
1316
0
  if (version != expected)
1317
0
    goto fail;
1318
1319
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1320
0
  switch (pduType)
1321
0
  {
1322
0
    case RDSTLS_TYPE_AUTHRSP:
1323
0
      if (!rdstls_process_authentication_response(rdstls, s))
1324
0
        goto fail;
1325
0
      break;
1326
0
    default:
1327
0
      WLog_Print(rdstls->log, WLOG_ERROR,
1328
0
                 "Invalid RDSTLS PDU type [0x%04" PRIx16 "] while reading AUTHRSP", pduType);
1329
0
      goto fail;
1330
0
  }
1331
1332
0
  rc = rdstls_set_state(rdstls, RDSTLS_STATE_FINAL);
1333
0
fail:
1334
0
  Stream_Free(s, TRUE);
1335
0
  return rc;
1336
0
}
1337
1338
WINPR_ATTR_NODISCARD
1339
static int rdstls_server_authenticate(rdpRdstls* rdstls)
1340
0
{
1341
0
  WINPR_ASSERT(rdstls);
1342
0
  uint16_t version = 0;
1343
1344
0
  if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1345
0
    return -1;
1346
1347
0
  if (!rdstls_send_capabilities(rdstls))
1348
0
    return -1;
1349
1350
0
  if (!rdstls_recv_authentication_request(rdstls, &version))
1351
0
    return -1;
1352
1353
0
  if (!rdstls_send_authentication_response(rdstls, version))
1354
0
    return -1;
1355
1356
0
  if (rdstls->resultCode != RDSTLS_RESULT_SUCCESS)
1357
0
    return -1;
1358
1359
0
  return 1;
1360
0
}
1361
1362
WINPR_ATTR_NODISCARD
1363
static int rdstls_client_authenticate(rdpRdstls* rdstls)
1364
0
{
1365
0
  if (!rdstls_set_state(rdstls, RDSTLS_STATE_CAPABILITIES))
1366
0
    return -1;
1367
1368
0
  if (!rdstls_recv_capabilities(rdstls))
1369
0
    return -1;
1370
1371
0
  uint16_t version = 0;
1372
0
  if (!rdstls_send_authentication_request(rdstls, &version))
1373
0
    return -1;
1374
1375
0
  if (!rdstls_recv_authentication_response(rdstls, version))
1376
0
    return -1;
1377
1378
0
  return 1;
1379
0
}
1380
1381
/**
1382
 * Authenticate using RDSTLS.
1383
 * @param rdstls The RDSTLS instance to use
1384
 *
1385
 * @return 1 if authentication is successful
1386
 */
1387
1388
int rdstls_authenticate(rdpRdstls* rdstls)
1389
0
{
1390
0
  WINPR_ASSERT(rdstls);
1391
1392
0
  if (rdstls->server)
1393
0
    return rdstls_server_authenticate(rdstls);
1394
0
  else
1395
0
    return rdstls_client_authenticate(rdstls);
1396
0
}
1397
1398
WINPR_ATTR_NODISCARD
1399
static SSIZE_T rdstls_parse_pdu_data_type(wLog* log, UINT16 dataType, wStream* s)
1400
0
{
1401
0
  size_t pduLength = 0;
1402
1403
0
  switch (dataType)
1404
0
  {
1405
0
    case RDSTLS_DATA_PASSWORD_CREDS:
1406
0
    {
1407
0
      if (Stream_GetRemainingLength(s) < 2)
1408
0
        return 0;
1409
1410
0
      const UINT16 redirGuidLength = Stream_Get_UINT16(s);
1411
1412
0
      if (Stream_GetRemainingLength(s) < redirGuidLength)
1413
0
        return 0;
1414
0
      Stream_Seek(s, redirGuidLength);
1415
1416
0
      if (Stream_GetRemainingLength(s) < 2)
1417
0
        return 0;
1418
1419
0
      const UINT16 usernameLength = Stream_Get_UINT16(s);
1420
1421
0
      if (Stream_GetRemainingLength(s) < usernameLength)
1422
0
        return 0;
1423
0
      Stream_Seek(s, usernameLength);
1424
1425
0
      if (Stream_GetRemainingLength(s) < 2)
1426
0
        return 0;
1427
0
      const UINT16 domainLength = Stream_Get_UINT16(s);
1428
1429
0
      if (Stream_GetRemainingLength(s) < domainLength)
1430
0
        return 0;
1431
0
      Stream_Seek(s, domainLength);
1432
1433
0
      if (Stream_GetRemainingLength(s) < 2)
1434
0
        return 0;
1435
0
      const UINT16 passwordLength = Stream_Get_UINT16(s);
1436
1437
0
      if (passwordLength == 0)
1438
0
      {
1439
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS PASSWORD_CREDS: empty password");
1440
0
        return -1;
1441
0
      }
1442
0
      else if ((redirGuidLength == 0) && (usernameLength == 0) && (domainLength == 0) &&
1443
0
               (passwordLength == 0))
1444
0
      {
1445
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS PASSWORD_CREDS: lengths 0");
1446
0
        return -1;
1447
0
      }
1448
0
      pduLength = Stream_GetPosition(s) + passwordLength;
1449
0
    }
1450
0
    break;
1451
0
    case RDSTLS_DATA_AUTORECONNECT_COOKIE:
1452
0
    {
1453
0
      if (Stream_GetRemainingLength(s) < 6)
1454
0
        return 0;
1455
0
      Stream_Seek(s, 4);
1456
0
      const UINT16 cookieLength = Stream_Get_UINT16(s);
1457
0
      if (cookieLength == 0)
1458
0
      {
1459
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS COOKIE::length");
1460
0
        return -1;
1461
0
      }
1462
0
      pduLength = Stream_GetPosition(s) + cookieLength;
1463
0
    }
1464
0
    break;
1465
0
    case RDSTLS_DATA_FEDAUTH_TOKEN:
1466
0
    {
1467
0
      if (Stream_GetRemainingLength(s) < 6)
1468
0
        return 0;
1469
0
      Stream_Seek(s, 4);
1470
0
      const UINT16 tokenLength = Stream_Get_UINT16(s);
1471
0
      if (tokenLength == 0)
1472
0
      {
1473
0
        WLog_Print(log, WLOG_ERROR, "invalid RDSLTS FEDAUTH_TOKEN::length");
1474
0
        return -1;
1475
0
      }
1476
0
      pduLength = Stream_GetPosition(s) + tokenLength;
1477
0
    }
1478
0
    break;
1479
0
    default:
1480
0
      WLog_Print(log, WLOG_ERROR, "invalid RDSLTS dataType");
1481
0
      return -1;
1482
0
  }
1483
1484
0
  if (pduLength > SSIZE_MAX)
1485
0
    return 0;
1486
0
  return (SSIZE_T)pduLength;
1487
0
}
1488
1489
SSIZE_T rdstls_parse_pdu(wLog* log, wStream* stream)
1490
0
{
1491
0
  SSIZE_T pduLength = -1;
1492
0
  wStream sbuffer = WINPR_C_ARRAY_INIT;
1493
0
  wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Buffer(stream), Stream_Length(stream));
1494
1495
0
  if (Stream_GetRemainingLength(s) < 2)
1496
0
    return 0;
1497
1498
0
  const UINT16 version = Stream_Get_UINT16(s);
1499
0
  if (!rdstls_are_some_versions_supported(log, version, FALSE))
1500
0
    return -1;
1501
1502
0
  if (Stream_GetRemainingLength(s) < 2)
1503
0
    return 0;
1504
1505
0
  const UINT16 pduType = Stream_Get_UINT16(s);
1506
0
  switch (pduType)
1507
0
  {
1508
0
    case RDSTLS_TYPE_CAPABILITIES:
1509
0
      pduLength = 8;
1510
0
      break;
1511
0
    case RDSTLS_TYPE_AUTHREQ:
1512
0
    {
1513
0
      if (Stream_GetRemainingLength(s) < 2)
1514
0
        return 0;
1515
1516
0
      const UINT16 dataType = Stream_Get_UINT16(s);
1517
0
      pduLength = rdstls_parse_pdu_data_type(log, dataType, s);
1518
0
    }
1519
0
    break;
1520
0
    case RDSTLS_TYPE_AUTHRSP:
1521
0
      pduLength = 10;
1522
0
      break;
1523
0
    default:
1524
0
      WLog_Print(log, WLOG_ERROR, "invalid RDSTLS PDU type");
1525
0
      return -1;
1526
0
  }
1527
1528
0
  return pduLength;
1529
0
}