FreeRDP
Loading...
Searching...
No Matches
arm.c
1
21#include <freerdp/config.h>
22#include <freerdp/version.h>
23
24#include "../settings.h"
25
26#include <winpr/assert.h>
27
28#include <winpr/crt.h>
29#include <winpr/synch.h>
30#include <winpr/print.h>
31#include <winpr/stream.h>
32#include <winpr/winsock.h>
33#include <winpr/cred.h>
34#include <winpr/bcrypt.h>
35
36#include <freerdp/log.h>
37#include <freerdp/error.h>
38#include <freerdp/crypto/certificate.h>
39#include <freerdp/utils/ringbuffer.h>
40#include <freerdp/utils/smartcardlogon.h>
41#include <freerdp/utils/aad.h>
42
43#include "arm.h"
44#include "wst.h"
45#include "websocket.h"
46#include "http.h"
47#include "../credssp_auth.h"
48#include "../proxy.h"
49#include "../rdp.h"
50#include "../../crypto/crypto.h"
51#include "../../crypto/certificate.h"
52#include "../../crypto/opensslcompat.h"
53#include "rpc_fault.h"
54#include "../utils.h"
55#include "../redirection.h"
56
57#include <winpr/json.h>
58
59#include <string.h>
60
61struct rdp_arm
62{
63 rdpContext* context;
64 rdpTls* tls;
65 HttpContext* http;
66
67 UINT32 gateway_retry;
68 wLog* log;
69};
70
71typedef struct rdp_arm rdpArm;
72
73#define TAG FREERDP_TAG("core.gateway.arm")
74
75#ifdef WITH_AAD
76WINPR_ATTR_NODISCARD
77static BOOL arm_tls_connect(rdpArm* arm, rdpTls* tls, UINT32 timeout)
78{
79 WINPR_ASSERT(arm);
80 WINPR_ASSERT(tls);
81 int sockfd = 0;
82 long status = 0;
83 BIO* socketBio = nullptr;
84 BIO* bufferedBio = nullptr;
85 rdpSettings* settings = arm->context->settings;
86 if (!settings)
87 return FALSE;
88
89 const char* peerHostname = freerdp_settings_get_string(settings, FreeRDP_GatewayHostname);
90 if (!peerHostname)
91 return FALSE;
92
93 UINT16 peerPort = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort);
94 const char* proxyUsername = nullptr;
95 const char* proxyPassword = nullptr;
96 BOOL isProxyConnection =
97 proxy_prepare(settings, &peerHostname, &peerPort, &proxyUsername, &proxyPassword);
98
99 sockfd = freerdp_tcp_connect(arm->context, peerHostname, peerPort, timeout);
100
101 WLog_Print(arm->log, WLOG_DEBUG, "connecting to %s %d", peerHostname, peerPort);
102 if (sockfd < 0)
103 return FALSE;
104
105 socketBio = BIO_new(BIO_s_simple_socket());
106
107 if (!socketBio)
108 {
109 closesocket((SOCKET)sockfd);
110 return FALSE;
111 }
112
113 BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
114 bufferedBio = BIO_new(BIO_s_buffered_socket());
115
116 if (!bufferedBio)
117 {
118 BIO_free_all(socketBio);
119 return FALSE;
120 }
121
122 bufferedBio = BIO_push(bufferedBio, socketBio);
123 if (!bufferedBio)
124 return FALSE;
125
126 status = BIO_set_nonblock(bufferedBio, TRUE);
127
128 if (isProxyConnection)
129 {
130 if (!proxy_connect(arm->context, bufferedBio, proxyUsername, proxyPassword,
131 freerdp_settings_get_string(settings, FreeRDP_GatewayHostname),
132 (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort)))
133 {
134 BIO_free_all(bufferedBio);
135 return FALSE;
136 }
137 }
138
139 if (!status)
140 {
141 BIO_free_all(bufferedBio);
142 return FALSE;
143 }
144
145 tls->hostname = freerdp_settings_get_string(settings, FreeRDP_GatewayHostname);
146 tls->port = MIN(UINT16_MAX, WINPR_ASSERTING_INT_CAST(int32_t, settings->GatewayPort));
147 tls->isGatewayTransport = TRUE;
148 status = freerdp_tls_connect(tls, bufferedBio);
149 if (status < 1)
150 {
151 rdpContext* context = arm->context;
152 if (status < 0)
153 freerdp_set_last_error_if_not(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
154 else
155 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED);
156
157 return FALSE;
158 }
159 return (status >= 1);
160}
161
162WINPR_ATTR_NODISCARD
163static BOOL arm_fetch_wellknown(rdpArm* arm)
164{
165 WINPR_ASSERT(arm);
166 WINPR_ASSERT(arm->context);
167 WINPR_ASSERT(arm->context->rdp);
168
169 rdpRdp* rdp = arm->context->rdp;
170 if (rdp->wellknown)
171 return TRUE;
172
173 const char* base =
174 freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayAzureActiveDirectory);
175 const BOOL useTenant =
176 freerdp_settings_get_bool(arm->context->settings, FreeRDP_GatewayAvdUseTenantid);
177 const char* tenantid = "common";
178 if (useTenant)
179 tenantid =
180 freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayAvdAadtenantid);
181
182 rdp->wellknown = freerdp_utils_aad_get_wellknown(arm->log, base, tenantid);
183 return (rdp->wellknown != nullptr);
184}
185
186WINPR_ATTR_MALLOC(Stream_Free, 1)
187static wStream* arm_build_http_request(rdpArm* arm, const char* method,
188 TRANSFER_ENCODING transferEncoding, const char* content_type,
189 size_t content_length)
190{
191 wStream* s = nullptr;
192
193 WINPR_ASSERT(arm);
194 WINPR_ASSERT(method);
195 WINPR_ASSERT(content_type);
196
197 WINPR_ASSERT(arm->context);
198 WINPR_ASSERT(arm->context->rdp);
199
200 const char* uri = http_context_get_uri(arm->http);
201 HttpRequest* request = http_request_new();
202
203 if (!request)
204 return nullptr;
205
206 rdpSettings* settings = arm->context->settings;
207
208 if (!http_request_set_method(request, method) || !http_request_set_uri(request, uri))
209 goto out;
210
211 if (!freerdp_settings_get_string(settings, FreeRDP_GatewayHttpExtAuthBearer))
212 {
213 pGetCommonAccessToken GetCommonAccessToken = freerdp_get_common_access_token(arm->context);
214 if (!GetCommonAccessToken)
215 {
216 WLog_Print(arm->log, WLOG_ERROR, "No authorization token provided");
217 goto out;
218 }
219
220 if (!arm_fetch_wellknown(arm))
221 goto out;
222
223 char* token = nullptr;
224 if (!GetCommonAccessToken(arm->context, ACCESS_TOKEN_TYPE_AVD, &token, 0))
225 {
226 winpr_zfree(token);
227 WLog_Print(arm->log, WLOG_ERROR, "Unable to obtain access token");
228 goto out;
229 }
230
231 const BOOL rc =
232 freerdp_settings_set_string(settings, FreeRDP_GatewayHttpExtAuthBearer, token);
233 winpr_zfree(token);
234 if (!rc)
235 goto out;
236 }
237
238 if (!http_request_set_auth_scheme(request, "Bearer") ||
239 !http_request_set_auth_param(
240 request, freerdp_settings_get_string(settings, FreeRDP_GatewayHttpExtAuthBearer)))
241 goto out;
242
243 if (!http_request_set_transfer_encoding(request, transferEncoding) ||
244 !http_request_set_content_length(request, content_length) ||
245 !http_request_set_content_type(request, content_type))
246 goto out;
247
248 s = http_request_write(arm->http, request);
249out:
250 http_request_free(request);
251
252 if (s)
253 Stream_SealLength(s);
254
255 return s;
256}
257
258WINPR_ATTR_NODISCARD
259static BOOL arm_send_http_request(rdpArm* arm, rdpTls* tls, const char* method,
260 const char* content_type, const char* data, size_t content_length)
261{
262 int status = -1;
263 wStream* s =
264 arm_build_http_request(arm, method, TransferEncodingIdentity, content_type, content_length);
265
266 if (!s)
267 return FALSE;
268
269 const size_t sz = Stream_Length(s);
270 WLog_Print(arm->log, WLOG_TRACE, "header [%" PRIuz "]: %s", sz, Stream_Buffer(s));
271 WLog_Print(arm->log, WLOG_TRACE, "body [%" PRIuz "]: %s", content_length, data);
272 status = freerdp_tls_write_all(tls, Stream_Buffer(s), sz);
273
274 Stream_Free(s, TRUE);
275 if (status >= 0 && content_length > 0 && data)
276 status = freerdp_tls_write_all(tls, (const BYTE*)data, content_length);
277
278 return (status >= 0);
279}
280
281static void arm_free(rdpArm* arm)
282{
283 if (!arm)
284 return;
285
286 freerdp_tls_free(arm->tls);
287 http_context_free(arm->http);
288
289 free(arm);
290}
291
292WINPR_ATTR_MALLOC(arm_free, 1)
293static rdpArm* arm_new(rdpContext* context)
294{
295 WINPR_ASSERT(context);
296
297 rdpArm* arm = (rdpArm*)calloc(1, sizeof(rdpArm));
298 if (!arm)
299 goto fail;
300
301 arm->log = WLog_Get(TAG);
302 arm->context = context;
303 arm->tls = freerdp_tls_new(context);
304 if (!arm->tls)
305 goto fail;
306
307 arm->http = http_context_new();
308
309 if (!arm->http)
310 goto fail;
311
312 return arm;
313
314fail:
315 arm_free(arm);
316 return nullptr;
317}
318
319WINPR_ATTR_MALLOC(arm_free, 1)
320static char* arm_create_request_json(rdpArm* arm, size_t* pLen)
321{
322 WINPR_ASSERT(pLen);
323
324 char* lbi = nullptr;
325 size_t lbiLen = 0;
326 char* message = nullptr;
327
328 WINPR_ASSERT(arm);
329
330 *pLen = 0;
331 WINPR_JSON* json = WINPR_JSON_CreateObject();
332 if (!json)
333 goto arm_create_cleanup;
335 json, "application",
336 freerdp_settings_get_string(arm->context->settings, FreeRDP_RemoteApplicationProgram)))
337 goto arm_create_cleanup;
338
339 const size_t len =
340 freerdp_settings_get_uint32(arm->context->settings, FreeRDP_LoadBalanceInfoLength);
341 lbi = calloc(len + 1ull, sizeof(char));
342 if (!lbi)
343 goto arm_create_cleanup;
344 lbiLen = len;
345
346 memcpy(lbi, freerdp_settings_get_pointer(arm->context->settings, FreeRDP_LoadBalanceInfo), len);
347
348 if (!WINPR_JSON_AddStringToObject(json, "loadBalanceInfo", lbi))
349 goto arm_create_cleanup;
350 if (!WINPR_JSON_AddNullToObject(json, "LogonToken"))
351 goto arm_create_cleanup;
352 if (!WINPR_JSON_AddNullToObject(json, "gatewayLoadBalancerToken"))
353 goto arm_create_cleanup;
354
355 message = WINPR_JSON_PrintUnformatted(json);
356arm_create_cleanup:
357 if (json)
358 WINPR_JSON_Delete(json);
359 winpr_znfree(lbi, lbiLen);
360 if (message)
361 *pLen = strlen(message);
362 return message;
363}
364
379WINPR_ATTR_MALLOC(winpr_Cipher_Free, 1)
380static WINPR_CIPHER_CTX* treatAuthBlob(wLog* log, const BYTE* pbInput, size_t cbInput,
381 size_t* pBlockSize)
382{
383 WINPR_CIPHER_CTX* ret = nullptr;
384 char algoName[100] = WINPR_C_ARRAY_INIT;
385
386 WINPR_ASSERT(pBlockSize);
387 SSIZE_T algoSz = ConvertWCharNToUtf8(WINPR_PACKED_ALIGN_CAST(const WCHAR*, pbInput),
388 cbInput / sizeof(WCHAR), algoName, sizeof(algoName) - 1);
389 if (algoSz <= 0)
390 {
391 WLog_Print(log, WLOG_ERROR, "invalid algoName");
392 return nullptr;
393 }
394
395 if (strcmp(algoName, "AES") != 0)
396 {
397 WLog_Print(log, WLOG_ERROR, "only AES is supported for now");
398 return nullptr;
399 }
400
401 *pBlockSize = WINPR_AES_BLOCK_SIZE;
402 const size_t algoLen = WINPR_ASSERTING_INT_CAST(size_t, (algoSz + 1)) * sizeof(WCHAR);
403 if (cbInput < algoLen)
404 {
405 WLog_Print(log, WLOG_ERROR, "invalid AuthBlob size");
406 return nullptr;
407 }
408
409 cbInput -= algoLen;
410
411 /* BCRYPT_KEY_DATA_BLOB_HEADER */
412 wStream staticStream = WINPR_C_ARRAY_INIT;
413 wStream* s = Stream_StaticConstInit(&staticStream, &pbInput[algoLen], cbInput);
414
415 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 12))
416 return nullptr;
417
418 const UINT32 dwMagic = Stream_Get_UINT32(s);
419 if (dwMagic != BCRYPT_KEY_DATA_BLOB_MAGIC)
420 {
421 WLog_Print(log, WLOG_ERROR, "unsupported authBlob type");
422 return nullptr;
423 }
424
425 const UINT32 dwVersion = Stream_Get_UINT32(s);
426 if (dwVersion != BCRYPT_KEY_DATA_BLOB_VERSION1)
427 {
428 WLog_Print(log, WLOG_ERROR, "unsupported authBlob version %" PRIu32 ", expecting %d",
429 dwVersion, BCRYPT_KEY_DATA_BLOB_VERSION1);
430 return nullptr;
431 }
432
433 const UINT32 cbKeyData = Stream_Get_UINT32(s);
434 if (!Stream_CheckAndLogRequiredLengthWLog(log, s, cbKeyData))
435 {
436 WLog_Print(log, WLOG_ERROR, "invalid authBlob size");
437 return nullptr;
438 }
439
440 WINPR_CIPHER_TYPE cipherType = WINPR_CIPHER_NONE;
441 switch (cbKeyData)
442 {
443 case 16:
444 cipherType = WINPR_CIPHER_AES_128_CBC;
445 break;
446 case 24:
447 cipherType = WINPR_CIPHER_AES_192_CBC;
448 break;
449 case 32:
450 cipherType = WINPR_CIPHER_AES_256_CBC;
451 break;
452 default:
453 WLog_Print(log, WLOG_ERROR, "invalid authBlob cipher size");
454 return nullptr;
455 }
456
457 ret = winpr_Cipher_NewEx(cipherType, WINPR_ENCRYPT, Stream_Pointer(s), cbKeyData, nullptr, 0);
458 if (!ret)
459 {
460 WLog_Print(log, WLOG_ERROR, "error creating cipher");
461 return nullptr;
462 }
463
464 if (!winpr_Cipher_SetPadding(ret, TRUE))
465 {
466 WLog_Print(log, WLOG_ERROR, "unable to enable padding on cipher");
467 winpr_Cipher_Free(ret);
468 return nullptr;
469 }
470
471 return ret;
472}
473
474WINPR_ATTR_NODISCARD
475static BOOL arm_stringEncodeW(const BYTE* pin, size_t cbIn, BYTE** ppOut, size_t* pcbOut)
476{
477 *ppOut = nullptr;
478 *pcbOut = 0;
479
480 /* encode to base64 with crlf */
481 size_t b64len = 0;
482 char* b64encoded = crypto_base64_encode_ex_len(pin, cbIn, TRUE, &b64len);
483 if (!b64encoded)
484 return FALSE;
485
486 /* and then convert to Unicode */
487 size_t outSz = 0;
488 *ppOut = (BYTE*)ConvertUtf8NToWCharAlloc(b64encoded, b64len, &outSz);
489 free(b64encoded);
490
491 if (!*ppOut)
492 return FALSE;
493
494 *pcbOut = (outSz + 1) * sizeof(WCHAR);
495 return TRUE;
496}
497
498WINPR_ATTR_NODISCARD
499static BOOL arm_encodeRedirectPasswd(wLog* log, rdpSettings* settings, const rdpCertificate* cert,
500 WINPR_CIPHER_CTX* cipher, size_t blockSize)
501{
502 BOOL ret = FALSE;
503 BYTE* output = nullptr;
504 BYTE* finalOutput = nullptr;
505
506 /* let's prepare the encrypted password, first we do a
507 * cipheredPass = AES(redirectedAuthBlob, toUtf16(passwd))
508 */
509
510 size_t wpasswdLen = 0;
511 WCHAR* wpasswd = freerdp_settings_get_string_as_utf16(settings, FreeRDP_Password, &wpasswdLen);
512 if (!wpasswd)
513 {
514 WLog_Print(log, WLOG_ERROR, "error when converting password to UTF16");
515 return FALSE;
516 }
517
518 const size_t wpasswdBytes = (wpasswdLen + 1) * sizeof(WCHAR);
519 BYTE* encryptedPass = calloc(1, wpasswdBytes + blockSize); /* 16: block size of AES (padding) */
520
521 if (!encryptedPass)
522 goto out;
523
524 {
525 size_t encryptedPassLen = 0;
526 if (!winpr_Cipher_Update(cipher, wpasswd, wpasswdBytes, encryptedPass, &encryptedPassLen))
527 goto out;
528
529 if (encryptedPassLen > wpasswdBytes)
530 goto out;
531
532 {
533 size_t finalLen = 0;
534 if (!winpr_Cipher_Final(cipher, &encryptedPass[encryptedPassLen], &finalLen))
535 {
536 WLog_Print(log, WLOG_ERROR, "error when ciphering password");
537 goto out;
538 }
539 encryptedPassLen += finalLen;
540 }
541
542 /* then encrypt(cipheredPass, publicKey(redirectedServerCert) */
543 {
544 size_t output_length = 0;
545 if (!freerdp_certificate_publickey_encrypt(cert, encryptedPass, encryptedPassLen,
546 &output, &output_length))
547 {
548 WLog_Print(log, WLOG_ERROR, "unable to encrypt with the server's public key");
549 goto out;
550 }
551
552 {
553 size_t finalOutputLen = 0;
554 if (!arm_stringEncodeW(output, output_length, &finalOutput, &finalOutputLen))
555 {
556 WLog_Print(log, WLOG_ERROR, "unable to base64+utf16 final blob");
557 goto out;
558 }
559
560 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionPassword,
561 finalOutput, finalOutputLen))
562 {
563 WLog_Print(log, WLOG_ERROR,
564 "unable to set the redirection password in settings");
565 goto out;
566 }
567 }
568 }
569 }
570
571 if (!freerdp_settings_set_bool(settings, FreeRDP_RdstlsSecurity, TRUE))
572 goto out;
573 if (!freerdp_settings_set_bool(settings, FreeRDP_AadSecurity, FALSE))
574 goto out;
575 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
576 goto out;
577 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
578 goto out;
579 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
580 goto out;
581 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
582 goto out;
583 if (!freerdp_settings_set_uint32(settings, FreeRDP_RedirectionFlags,
584 LB_PASSWORD_IS_PK_ENCRYPTED))
585 goto out;
586 ret = TRUE;
587out:
588 free(finalOutput);
589 free(output);
590 free(encryptedPass);
591 free(wpasswd);
592 return ret;
593}
594
599WINPR_ATTR_NODISCARD
600static BOOL arm_pick_base64Utf16Field(wLog* log, const WINPR_JSON* json, const char* name,
601 BYTE** poutput, size_t* plen)
602{
603 *poutput = nullptr;
604 *plen = 0;
605
606 WINPR_JSON* node = WINPR_JSON_GetObjectItemCaseSensitive(json, name);
607 if (!node || !WINPR_JSON_IsString(node))
608 return TRUE;
609
610 const char* nodeValue = WINPR_JSON_GetStringValue(node);
611 if (!nodeValue)
612 return TRUE;
613
614 BYTE* output1 = nullptr;
615 size_t len1 = 0;
616 crypto_base64_decode(nodeValue, strlen(nodeValue), &output1, &len1);
617 if (!output1 || !len1)
618 {
619 WLog_Print(log, WLOG_ERROR, "error when first unbase64 for %s", name);
620 free(output1);
621 return FALSE;
622 }
623
624 size_t len2 = 0;
625 char* output2 = ConvertWCharNToUtf8Alloc(WINPR_PACKED_ALIGN_CAST(WCHAR*, output1),
626 len1 / sizeof(WCHAR), &len2);
627 free(output1);
628 if (!output2 || !len2)
629 {
630 WLog_Print(log, WLOG_ERROR, "error when decode('utf-16') for %s", name);
631 free(output2);
632 return FALSE;
633 }
634
635 BYTE* output = nullptr;
636 crypto_base64_decode(output2, len2, &output, plen);
637 free(output2);
638 if (!output || !*plen)
639 {
640 WLog_Print(log, WLOG_ERROR, "error when second unbase64 for %s", name);
641 free(output);
642 return FALSE;
643 }
644
645 *poutput = output;
646 return TRUE;
647}
648
668WINPR_ATTR_NODISCARD
669static size_t arm_parse_ipvx_count(WINPR_JSON* ipvX)
670{
671 WINPR_ASSERT(ipvX);
672 WINPR_JSON* ipAddress = WINPR_JSON_GetObjectItemCaseSensitive(ipvX, "ipAddress");
673 if (!ipAddress || !WINPR_JSON_IsArray(ipAddress))
674 return 0;
675
676 /* Each entry might contain a public and a private address */
677 return WINPR_JSON_GetArraySize(ipAddress) * 2;
678}
679
680WINPR_ATTR_NODISCARD
681static BOOL arm_parse_ipv6(rdpSettings* settings, WINPR_JSON* ipv6, size_t* pAddressIdx)
682{
683 WINPR_ASSERT(settings);
684 WINPR_ASSERT(ipv6);
685 WINPR_ASSERT(pAddressIdx);
686
687 if (!freerdp_settings_get_bool(settings, FreeRDP_IPv6Enabled))
688 return TRUE;
689
690 WINPR_JSON* ipAddress = WINPR_JSON_GetObjectItemCaseSensitive(ipv6, "ipAddress");
691 if (!ipAddress || !WINPR_JSON_IsArray(ipAddress))
692 return TRUE;
693 const size_t naddresses = WINPR_JSON_GetArraySize(ipAddress);
694 const uint32_t count = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount);
695 for (size_t j = 0; j < naddresses; j++)
696 {
697 WINPR_JSON* adressN = WINPR_JSON_GetArrayItem(ipAddress, j);
698 if (!adressN || !WINPR_JSON_IsString(adressN))
699 continue;
700
701 const char* addr = WINPR_JSON_GetStringValue(adressN);
702 if (utils_str_is_empty(addr))
703 continue;
704
705 if (*pAddressIdx >= count)
706 {
707 WLog_ERR(TAG, "Exceeded TargetNetAddresses, parsing failed");
708 return FALSE;
709 }
710
711 if (!winpr_str_is_valid_url(addr))
712 {
713 WLog_ERR(TAG, "TargetNetAddress[%" PRIuz "]: %s not an URL or IP", j, addr);
714 return FALSE;
715 }
716
717 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
718 (*pAddressIdx)++, addr))
719 return FALSE;
720 }
721 return TRUE;
722}
723
724WINPR_ATTR_NODISCARD
725static BOOL arm_parse_ipv4(rdpSettings* settings, WINPR_JSON* ipv4, size_t* pAddressIdx)
726{
727 WINPR_ASSERT(settings);
728 WINPR_ASSERT(ipv4);
729 WINPR_ASSERT(pAddressIdx);
730
731 WINPR_JSON* ipAddress = WINPR_JSON_GetObjectItemCaseSensitive(ipv4, "ipAddress");
732 if (!ipAddress || !WINPR_JSON_IsArray(ipAddress))
733 return TRUE;
734
735 const size_t naddresses = WINPR_JSON_GetArraySize(ipAddress);
736 const uint32_t count = freerdp_settings_get_uint32(settings, FreeRDP_TargetNetAddressCount);
737 for (size_t j = 0; j < naddresses; j++)
738 {
739 WINPR_JSON* adressN = WINPR_JSON_GetArrayItem(ipAddress, j);
740 if (!adressN)
741 continue;
742
743 WINPR_JSON* publicIpNode =
744 WINPR_JSON_GetObjectItemCaseSensitive(adressN, "publicIpAddress");
745 if (publicIpNode && WINPR_JSON_IsString(publicIpNode))
746 {
747 const char* publicIp = WINPR_JSON_GetStringValue(publicIpNode);
748 if (!utils_str_is_empty(publicIp))
749 {
750 if (*pAddressIdx >= count)
751 {
752 WLog_ERR(TAG, "Exceeded TargetNetAddresses, parsing failed");
753 return FALSE;
754 }
755
756 if (!utils_is_valid_ip(publicIp))
757 {
758 WLog_ERR(TAG, "publicIpAddress value %s: not a valid IP", publicIp);
759 return FALSE;
760 }
761
762 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
763 (*pAddressIdx)++, publicIp))
764 return FALSE;
765 }
766 }
767
768 WINPR_JSON* privateIpNode =
769 WINPR_JSON_GetObjectItemCaseSensitive(adressN, "privateIpAddress");
770 if (privateIpNode && WINPR_JSON_IsString(privateIpNode))
771 {
772 const char* privateIp = WINPR_JSON_GetStringValue(privateIpNode);
773 if (!utils_str_is_empty(privateIp))
774 {
775 if (*pAddressIdx >= count)
776 {
777 WLog_ERR(TAG, "Exceeded TargetNetAddresses, parsing failed");
778 return FALSE;
779 }
780 if (!utils_is_valid_ip(privateIp))
781 {
782 WLog_ERR(TAG, "privateIpAddress value %s: not a valid IP", privateIp);
783 return FALSE;
784 }
785
786 if (!freerdp_settings_set_pointer_array(settings, FreeRDP_TargetNetAddresses,
787 (*pAddressIdx)++, privateIp))
788 return FALSE;
789 }
790 }
791 }
792 return TRUE;
793}
794
795WINPR_ATTR_NODISCARD
796static BOOL arm_treat_azureInstanceNetworkMetadata(wLog* log, const char* metadata,
797 rdpSettings* settings)
798{
799 BOOL ret = FALSE;
800
801 WINPR_ASSERT(settings);
802
803 if (!freerdp_target_net_adresses_reset(settings, 0))
804 return FALSE;
805
806 WINPR_JSON* json = WINPR_JSON_Parse(metadata);
807 if (!json)
808 {
809 WLog_Print(log, WLOG_ERROR, "invalid azureInstanceNetworkMetadata");
810 return FALSE;
811 }
812
813 WINPR_JSON* iface = WINPR_JSON_GetObjectItemCaseSensitive(json, "interface");
814 if (!iface)
815 {
816 ret = TRUE;
817 goto out;
818 }
819
820 if (!WINPR_JSON_IsArray(iface))
821 {
822 WLog_Print(log, WLOG_ERROR, "expecting interface to be an Array");
823 goto out;
824 }
825
826 {
827 const size_t interfaceSz = WINPR_JSON_GetArraySize(iface);
828 if (interfaceSz == 0)
829 {
830 WLog_WARN(TAG, "no addresses in azure instance metadata");
831 ret = TRUE;
832 goto out;
833 }
834
835 {
836 size_t count = 0;
837 for (size_t i = 0; i < interfaceSz; i++)
838 {
839 WINPR_JSON* interN = WINPR_JSON_GetArrayItem(iface, i);
840 if (!interN)
841 continue;
842
843 WINPR_JSON* ipv6 = WINPR_JSON_GetObjectItemCaseSensitive(interN, "ipv6");
844 if (ipv6)
845 count += arm_parse_ipvx_count(ipv6);
846
847 WINPR_JSON* ipv4 = WINPR_JSON_GetObjectItemCaseSensitive(interN, "ipv4");
848 if (ipv4)
849 count += arm_parse_ipvx_count(ipv4);
850 }
851
852 if (!freerdp_target_net_adresses_reset(settings, count))
853 return FALSE;
854 }
855
856 {
857 size_t addressIdx = 0;
858 for (size_t i = 0; i < interfaceSz; i++)
859 {
860 WINPR_JSON* interN = WINPR_JSON_GetArrayItem(iface, i);
861 if (!interN)
862 continue;
863
864 WINPR_JSON* ipv6 = WINPR_JSON_GetObjectItemCaseSensitive(interN, "ipv6");
865 if (ipv6)
866 {
867 if (!arm_parse_ipv6(settings, ipv6, &addressIdx))
868 goto out;
869 }
870
871 WINPR_JSON* ipv4 = WINPR_JSON_GetObjectItemCaseSensitive(interN, "ipv4");
872 if (ipv4)
873 {
874 if (!arm_parse_ipv4(settings, ipv4, &addressIdx))
875 goto out;
876 }
877 }
878 if (addressIdx > UINT32_MAX)
879 goto out;
880
881 if (!freerdp_settings_set_uint32(settings, FreeRDP_TargetNetAddressCount,
882 (UINT32)addressIdx))
883 goto out;
884
885 ret = addressIdx > 0;
886 }
887 }
888
889out:
890 WINPR_JSON_Delete(json);
891 return ret;
892}
893
894WINPR_ATTR_NODISCARD
895static BOOL arm_fill_rdstls(rdpArm* arm, rdpSettings* settings, const WINPR_JSON* json,
896 const rdpCertificate* redirectedServerCert)
897{
898 WINPR_ASSERT(arm);
899 WINPR_ASSERT(settings);
900 WINPR_ASSERT(json);
901
902 if (!redirectedServerCert)
903 return FALSE;
904
905 BOOL ret = FALSE;
906 BYTE* authBlob = nullptr;
907 WCHAR* wGUID = nullptr;
908
909 const char* redirUser = freerdp_settings_get_string(settings, FreeRDP_RedirectionUsername);
910 if (redirUser)
911 {
912 if (!freerdp_settings_set_string(settings, FreeRDP_Username, redirUser))
913 goto end;
914 }
915
916 /* Azure/Entra requires the domain field to be set to 'AzureAD' in most cases.
917 * Some setups have been reported to require a different one, so only supply the suggested
918 * default if there was no other domain provided.
919 */
920 {
921 const char* redirDomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
922 if (!redirDomain)
923 {
924 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, "AzureAD"))
925 goto end;
926 }
927 }
928
929 {
930 const char* duser = freerdp_settings_get_string(settings, FreeRDP_Username);
931 const char* ddomain = freerdp_settings_get_string(settings, FreeRDP_Domain);
932 const char* dpwd = freerdp_settings_get_string(settings, FreeRDP_Password);
933 if (!duser || !dpwd)
934 {
935 WINPR_ASSERT(arm->context);
936 WINPR_ASSERT(arm->context->instance);
937
938 char* username = nullptr;
939 char* password = nullptr;
940 char* domain = nullptr;
941
942 if (ddomain)
943 domain = _strdup(ddomain);
944 if (duser)
945 username = _strdup(duser);
946
947 const BOOL rc =
948 IFCALLRESULT(FALSE, arm->context->instance->AuthenticateEx, arm->context->instance,
949 &username, &password, &domain, AUTH_RDSTLS);
950
951 const BOOL rc1 = freerdp_settings_set_string(settings, FreeRDP_Username, username);
952 const BOOL rc2 = freerdp_settings_set_string(settings, FreeRDP_Password, password);
953 const BOOL rc3 = freerdp_settings_set_string(settings, FreeRDP_Domain, domain);
954 winpr_zfree(username);
955 winpr_zfree(password);
956 winpr_zfree(domain);
957 if (!rc || !rc1 || !rc2 || !rc3)
958 goto end;
959 }
960 }
961
962 /* redirectedAuthGuid */
963 {
964 WINPR_JSON* redirectedAuthGuidNode =
965 WINPR_JSON_GetObjectItemCaseSensitive(json, "redirectedAuthGuid");
966 if (!redirectedAuthGuidNode || !WINPR_JSON_IsString(redirectedAuthGuidNode))
967 goto end;
968
969 {
970 const char* redirectedAuthGuid = WINPR_JSON_GetStringValue(redirectedAuthGuidNode);
971 if (!redirectedAuthGuid)
972 goto end;
973
974 {
975 size_t wGUID_len = 0;
976 wGUID = ConvertUtf8ToWCharAlloc(redirectedAuthGuid, &wGUID_len);
977 if (!wGUID || (wGUID_len == 0))
978 {
979 WLog_Print(arm->log, WLOG_ERROR,
980 "unable to allocate space for redirectedAuthGuid");
981 goto end;
982 }
983
984 {
985 const BOOL status = freerdp_settings_set_pointer_len(
986 settings, FreeRDP_RedirectionGuid, wGUID, (wGUID_len + 1) * sizeof(WCHAR));
987
988 if (!status)
989 {
990 WLog_Print(arm->log, WLOG_ERROR, "unable to set RedirectionGuid");
991 goto end;
992 }
993 }
994 }
995 }
996 }
997
998 /* redirectedAuthBlob */
999 {
1000 size_t authBlobLen = 0;
1001 if (!arm_pick_base64Utf16Field(arm->log, json, "redirectedAuthBlob", &authBlob,
1002 &authBlobLen))
1003 goto end;
1004
1005 {
1006 size_t blockSize = 0;
1007 WINPR_CIPHER_CTX* cipher = treatAuthBlob(arm->log, authBlob, authBlobLen, &blockSize);
1008 if (!cipher)
1009 goto end;
1010
1011 {
1012 const BOOL rerp = arm_encodeRedirectPasswd(arm->log, settings, redirectedServerCert,
1013 cipher, blockSize);
1014 winpr_Cipher_Free(cipher);
1015 if (!rerp)
1016 goto end;
1017 }
1018 }
1019 }
1020
1021 ret = TRUE;
1022
1023end:
1024 free(wGUID);
1025 free(authBlob);
1026 return ret;
1027}
1028
1029WINPR_ATTR_NODISCARD
1030static BOOL arm_fill_gateway_parameters(rdpArm* arm, const char* message, size_t len)
1031{
1032 WINPR_ASSERT(arm);
1033 WINPR_ASSERT(arm->context);
1034 WINPR_ASSERT(message);
1035
1036 rdpCertificate* redirectedServerCert = nullptr;
1037 WINPR_JSON* json = WINPR_JSON_ParseWithLength(message, len);
1038 BOOL status = FALSE;
1039 if (!json)
1040 {
1041 WLog_Print(arm->log, WLOG_ERROR, "Response data is not valid JSON: %s",
1043 return FALSE;
1044 }
1045
1046 if (WLog_IsLevelActive(arm->log, WLOG_DEBUG))
1047 {
1048 char* str = WINPR_JSON_PrintUnformatted(json);
1049 WLog_Print(arm->log, WLOG_DEBUG, "Got HTTP Response data: %s", str);
1050 free(str);
1051 }
1052
1053 rdpSettings* settings = arm->context->settings;
1054 WINPR_JSON* gwurl = WINPR_JSON_GetObjectItemCaseSensitive(json, "gatewayLocationPreWebSocket");
1055 if (!gwurl)
1056 gwurl = WINPR_JSON_GetObjectItemCaseSensitive(json, "gatewayLocation");
1057 const char* gwurlstr = WINPR_JSON_GetStringValue(gwurl);
1058 if (gwurlstr != nullptr)
1059 {
1060 WLog_Print(arm->log, WLOG_DEBUG, "extracted target url %s", gwurlstr);
1061 if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUrl, gwurlstr))
1062 goto fail;
1063 }
1064
1065 {
1066 WINPR_JSON* serverNameNode =
1067 WINPR_JSON_GetObjectItemCaseSensitive(json, "redirectedServerName");
1068 if (serverNameNode)
1069 {
1070 const char* serverName = WINPR_JSON_GetStringValue(serverNameNode);
1071 if (serverName)
1072 {
1073 if (!freerdp_settings_set_string(settings, FreeRDP_ServerHostname, serverName))
1074 goto fail;
1075 }
1076 }
1077 }
1078
1079 {
1080 const char key[] = "redirectedUsername";
1081 if (WINPR_JSON_HasObjectItem(json, key))
1082 {
1083 const char* userName = nullptr;
1084 WINPR_JSON* userNameNode = WINPR_JSON_GetObjectItemCaseSensitive(json, key);
1085 if (userNameNode)
1086 userName = WINPR_JSON_GetStringValue(userNameNode);
1087 if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionUsername, userName))
1088 goto fail;
1089 }
1090 }
1091
1092 {
1093 WINPR_JSON* azureMeta =
1094 WINPR_JSON_GetObjectItemCaseSensitive(json, "azureInstanceNetworkMetadata");
1095 if (azureMeta && WINPR_JSON_IsString(azureMeta))
1096 {
1097 if (!arm_treat_azureInstanceNetworkMetadata(
1098 arm->log, WINPR_JSON_GetStringValue(azureMeta), settings))
1099 {
1100 WLog_Print(arm->log, WLOG_ERROR,
1101 "error when treating azureInstanceNetworkMetadata");
1102 goto fail;
1103 }
1104 }
1105 }
1106
1107 /* redirectedServerCert */
1108 {
1109 size_t certLen = 0;
1110 BYTE* cert = nullptr;
1111 if (arm_pick_base64Utf16Field(arm->log, json, "redirectedServerCert", &cert, &certLen))
1112 {
1113 const BOOL rc = rdp_redirection_read_target_cert(&redirectedServerCert, cert, certLen);
1114 free(cert);
1115 if (!rc)
1116 goto fail;
1117 else if (!rdp_set_target_certificate(settings, redirectedServerCert))
1118 goto fail;
1119 }
1120 }
1121
1122 if (freerdp_settings_get_bool(settings, FreeRDP_AadSecurity))
1123 status = TRUE;
1124 else
1125 status = arm_fill_rdstls(arm, settings, json, redirectedServerCert);
1126
1127fail:
1128 WINPR_JSON_Delete(json);
1129 freerdp_certificate_free(redirectedServerCert);
1130 return status;
1131}
1132
1133WINPR_ATTR_NODISCARD
1134static BOOL arm_handle_request_ok(rdpArm* arm, const HttpResponse* response)
1135{
1136 const size_t len = http_response_get_body_length(response);
1137 const char* msg = http_response_get_body(response);
1138 if ((len == 0) || !msg)
1139 {
1140 WLog_Print(arm->log, WLOG_ERROR, "Got HTTP Response data with empty body");
1141 return FALSE;
1142 }
1143 const size_t alen = strnlen(msg, len + 1);
1144 if (alen > len)
1145 {
1146 WLog_Print(arm->log, WLOG_ERROR, "Got HTTP Response data with invalid termination");
1147 return FALSE;
1148 }
1149
1150 return arm_fill_gateway_parameters(arm, msg, len);
1151}
1152
1153WINPR_ATTR_NODISCARD
1154static BOOL arm_handle_bad_request(rdpArm* arm, const HttpResponse* response, BOOL* retry)
1155{
1156 WINPR_ASSERT(response);
1157 WINPR_ASSERT(retry);
1158
1159 *retry = FALSE;
1160
1161 BOOL rc = FALSE;
1162
1163 http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
1164
1165 const size_t len = http_response_get_body_length(response);
1166 const char* msg = http_response_get_body(response);
1167 if (msg && (strnlen(msg, len + 1) > len))
1168 {
1169 WLog_Print(arm->log, WLOG_ERROR, "Got HTTP Response data, but length is invalid");
1170
1171 return FALSE;
1172 }
1173
1174 WLog_Print(arm->log, WLOG_DEBUG, "Got HTTP Response data: %s", msg);
1175
1176 WINPR_JSON* json = WINPR_JSON_ParseWithLength(msg, len);
1177 if (json == nullptr)
1178 {
1179 const char* error_ptr = WINPR_JSON_GetErrorPtr();
1180 WLog_Print(arm->log, WLOG_ERROR, "WINPR_JSON_ParseWithLength: %s", error_ptr);
1181
1182 return FALSE;
1183 }
1184 else
1185 {
1186 WINPR_JSON* gateway_code_obj = WINPR_JSON_GetObjectItemCaseSensitive(json, "Code");
1187 const char* gw_code_str = WINPR_JSON_GetStringValue(gateway_code_obj);
1188 if (gw_code_str == nullptr)
1189 {
1190 WLog_Print(arm->log, WLOG_ERROR, "Response has no \"Code\" property");
1191 goto fail;
1192 }
1193
1194 if (strcmp(gw_code_str, "E_PROXY_ORCHESTRATION_LB_SESSIONHOST_DEALLOCATED") == 0)
1195 {
1196 *retry = TRUE;
1197 WINPR_JSON* message = WINPR_JSON_GetObjectItemCaseSensitive(json, "Message");
1198 const char* msgstr = WINPR_JSON_GetStringValue(message);
1199 if (!msgstr)
1200 WLog_WARN(TAG, "Starting your VM. It may take up to 5 minutes");
1201 else
1202 WLog_WARN(TAG, "%s", msgstr);
1203 freerdp_set_last_error_if_not(arm->context, FREERDP_ERROR_CONNECT_TARGET_BOOTING);
1204 }
1205 else
1206 {
1207 goto fail;
1208 }
1209 }
1210
1211 rc = TRUE;
1212fail:
1213 WINPR_JSON_Delete(json);
1214 return rc;
1215}
1216
1217WINPR_ATTR_NODISCARD
1218static BOOL arm_handle_request(rdpArm* arm, BOOL* retry, DWORD timeout)
1219{
1220 WINPR_ASSERT(retry);
1221
1222 if (!arm_fetch_wellknown(arm))
1223 {
1224 *retry = TRUE;
1225 return FALSE;
1226 }
1227
1228 *retry = FALSE;
1229
1230 size_t messageLen = 0;
1231 char* message = nullptr;
1232 BOOL rc = FALSE;
1233
1234 HttpResponse* response = nullptr;
1235 long StatusCode = 0;
1236
1237 const char* useragent =
1238 freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpUserAgent);
1239 const char* msuseragent =
1240 freerdp_settings_get_string(arm->context->settings, FreeRDP_GatewayHttpMsUserAgent);
1241 if (!http_context_set_uri(arm->http, "/api/arm/v2/connections") ||
1242 !http_context_set_accept(arm->http, "*/*") ||
1243 !http_context_set_cache_control(arm->http, "no-cache") ||
1244 !http_context_set_pragma(arm->http, "no-cache") ||
1245 !http_context_set_connection(arm->http, "Keep-Alive") ||
1246 !http_context_set_user_agent(arm->http, useragent) ||
1247 !http_context_set_x_ms_user_agent(arm->http, msuseragent) ||
1248 !http_context_set_host(arm->http, freerdp_settings_get_string(arm->context->settings,
1249 FreeRDP_GatewayHostname)))
1250 goto arm_error;
1251
1252 if (!arm_tls_connect(arm, arm->tls, timeout))
1253 goto arm_error;
1254
1255 message = arm_create_request_json(arm, &messageLen);
1256 if (!message)
1257 goto arm_error;
1258
1259 if (!arm_send_http_request(arm, arm->tls, "POST", "application/json", message, messageLen))
1260 goto arm_error;
1261
1262 response = http_response_recv(arm->tls, TRUE);
1263 if (!response)
1264 goto arm_error;
1265
1266 StatusCode = http_response_get_status_code(response);
1267 if (StatusCode == HTTP_STATUS_OK)
1268 {
1269 if (!arm_handle_request_ok(arm, response))
1270 goto arm_error;
1271 }
1272 else if (StatusCode == HTTP_STATUS_BAD_REQUEST)
1273 {
1274 if (!arm_handle_bad_request(arm, response, retry))
1275 goto arm_error;
1276 }
1277 else
1278 {
1279 http_response_log_error_status(WLog_Get(TAG), WLOG_ERROR, response);
1280 goto arm_error;
1281 }
1282
1283 rc = TRUE;
1284arm_error:
1285 http_response_free(response);
1286 winpr_znfree(message, messageLen);
1287 return rc;
1288}
1289
1290#endif
1291
1292BOOL arm_resolve_endpoint(wLog* log, rdpContext* context, DWORD timeout)
1293{
1294#ifndef WITH_AAD
1295 WLog_Print(log, WLOG_ERROR, "arm gateway support not compiled in");
1296 return FALSE;
1297#else
1298
1299 if (!context)
1300 return FALSE;
1301
1302 if (!context->settings)
1303 return FALSE;
1304
1305 if ((freerdp_settings_get_uint32(context->settings, FreeRDP_LoadBalanceInfoLength) == 0) ||
1306 (freerdp_settings_get_string(context->settings, FreeRDP_RemoteApplicationProgram) ==
1307 nullptr))
1308 {
1309 WLog_Print(log, WLOG_ERROR, "loadBalanceInfo and RemoteApplicationProgram needed");
1310 return FALSE;
1311 }
1312
1313 rdpArm* arm = arm_new(context);
1314 if (!arm)
1315 return FALSE;
1316
1317 BOOL retry = FALSE;
1318 BOOL rc = FALSE;
1319 do
1320 {
1321 if (retry && rc)
1322 {
1323 freerdp* instance = context->instance;
1324 WINPR_ASSERT(instance);
1325 SSIZE_T delay = IFCALLRESULT(-1, instance->RetryDialog, instance, "arm-transport",
1326 arm->gateway_retry, arm);
1327 arm->gateway_retry++;
1328 if (delay <= 0)
1329 break; /* error or no retry desired, abort loop */
1330 else
1331 {
1332 WLog_Print(arm->log, WLOG_DEBUG, "Delay for %" PRIdz "ms before next attempt",
1333 delay);
1334 while (delay > 0)
1335 {
1336 DWORD slp = (UINT32)delay;
1337 if (delay > UINT32_MAX)
1338 slp = UINT32_MAX;
1339 Sleep(slp);
1340 delay -= slp;
1341 }
1342 }
1343 }
1344 rc = arm_handle_request(arm, &retry, timeout);
1345
1346 } while (retry && rc);
1347 arm_free(arm);
1348 return rc;
1349#endif
1350}
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_CreateObject(void)
WINPR_JSON_CreateObject.
Definition c-json.c:232
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON *object, const char *string)
Check if JSON has an object matching the name.
Definition c-json.c:132
WINPR_ATTR_NODISCARD WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
Definition c-json.c:114
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON *object, const char *string)
Same as WINPR_JSON_GetObjectItem but with case sensitive matching.
Definition c-json.c:127
WINPR_API WINPR_JSON * WINPR_JSON_ParseWithLength(const char *value, size_t buffer_length)
Parse a JSON string.
Definition c-json.c:98
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetErrorPtr(void)
Return an error string.
Definition c-json.c:137
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
Definition c-json.c:182
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
Definition c-json.c:108
WINPR_API char * WINPR_JSON_PrintUnformatted(WINPR_JSON *item)
Serialize a JSON instance to string without formatting for human readable formatted output see WINPR_...
Definition c-json.c:311
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
Definition c-json.c:187
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddNullToObject(WINPR_JSON *object, const char *name)
WINPR_JSON_AddNullToObject.
Definition c-json.c:237
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddStringToObject(WINPR_JSON *object, const char *name, const char *string)
WINPR_JSON_AddStringToObject.
Definition c-json.c:269
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
Definition c-json.c:142
WINPR_API WINPR_JSON * WINPR_JSON_Parse(const char *value)
Parse a '\0' terminated JSON string.
Definition c-json.c:93
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API WCHAR * freerdp_settings_get_string_as_utf16(const rdpSettings *settings, FreeRDP_Settings_Keys_String id, size_t *pCharLen)
Return an allocated UTF16 string.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.