Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/libfreerdp/core/aad.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Network Level Authentication (NLA)
4
 *
5
 * Copyright 2023 Isaac Klein <fifthdegree@protonmail.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 <stdio.h>
23
#include <string.h>
24
25
#include <freerdp/crypto/crypto.h>
26
#include <freerdp/crypto/privatekey.h>
27
#include "../crypto/privatekey.h"
28
#include <freerdp/utils/http.h>
29
#include <freerdp/utils/aad.h>
30
31
#include <winpr/crypto.h>
32
#include <winpr/json.h>
33
34
#include "transport.h"
35
36
#include "aad.h"
37
38
struct rdp_aad
39
{
40
  AAD_STATE state;
41
  rdpContext* rdpcontext;
42
  rdpTransport* transport;
43
  char* access_token;
44
  rdpPrivateKey* key;
45
  char* kid;
46
  char* nonce;
47
  char* hostname;
48
  char* scope;
49
  wLog* log;
50
};
51
52
#ifdef WITH_AAD
53
54
static BOOL get_encoded_rsa_params(wLog* wlog, rdpPrivateKey* key, char** e, char** n);
55
static BOOL generate_pop_key(rdpAad* aad);
56
57
WINPR_ATTR_FORMAT_ARG(2, 3)
58
static SSIZE_T stream_sprintf(wStream* s, WINPR_FORMAT_ARG const char* fmt, ...)
59
{
60
  va_list ap;
61
  va_start(ap, fmt);
62
  const int rc = vsnprintf(NULL, 0, fmt, ap);
63
  va_end(ap);
64
65
  if (rc < 0)
66
    return rc;
67
68
  if (!Stream_EnsureRemainingCapacity(s, (size_t)rc + 1))
69
    return -1;
70
71
  char* ptr = Stream_PointerAs(s, char);
72
  va_start(ap, fmt);
73
  const int rc2 = vsnprintf(ptr, rc + 1, fmt, ap);
74
  va_end(ap);
75
  if (rc != rc2)
76
    return -23;
77
  if (!Stream_SafeSeek(s, (size_t)rc2))
78
    return -3;
79
  return rc2;
80
}
81
82
static BOOL json_get_object(wLog* wlog, WINPR_JSON* json, const char* key, WINPR_JSON** obj)
83
{
84
  WINPR_ASSERT(json);
85
  WINPR_ASSERT(key);
86
87
  if (!WINPR_JSON_HasObjectItem(json, key))
88
  {
89
    WLog_Print(wlog, WLOG_ERROR, "[json] does not contain a key '%s'", key);
90
    return FALSE;
91
  }
92
93
  WINPR_JSON* prop = WINPR_JSON_GetObjectItem(json, key);
94
  if (!prop)
95
  {
96
    WLog_Print(wlog, WLOG_ERROR, "[json] object for key '%s' is NULL", key);
97
    return FALSE;
98
  }
99
  *obj = prop;
100
  return TRUE;
101
}
102
103
static BOOL json_get_number(wLog* wlog, WINPR_JSON* json, const char* key, double* result)
104
{
105
  BOOL rc = FALSE;
106
  WINPR_JSON* prop = NULL;
107
  if (!json_get_object(wlog, json, key, &prop))
108
    return FALSE;
109
110
  if (!WINPR_JSON_IsNumber(prop))
111
  {
112
    WLog_Print(wlog, WLOG_ERROR, "[json] object for key '%s' is NOT a NUMBER", key);
113
    goto fail;
114
  }
115
116
  *result = WINPR_JSON_GetNumberValue(prop);
117
118
  rc = TRUE;
119
fail:
120
  return rc;
121
}
122
123
static BOOL json_get_const_string(wLog* wlog, WINPR_JSON* json, const char* key,
124
                                  const char** result)
125
{
126
  BOOL rc = FALSE;
127
  WINPR_ASSERT(result);
128
129
  *result = NULL;
130
131
  WINPR_JSON* prop = NULL;
132
  if (!json_get_object(wlog, json, key, &prop))
133
    return FALSE;
134
135
  if (!WINPR_JSON_IsString(prop))
136
  {
137
    WLog_Print(wlog, WLOG_ERROR, "[json] object for key '%s' is NOT a STRING", key);
138
    goto fail;
139
  }
140
141
  const char* str = WINPR_JSON_GetStringValue(prop);
142
  if (!str)
143
    WLog_Print(wlog, WLOG_ERROR, "[json] object for key '%s' is NULL", key);
144
  *result = str;
145
  rc = str != NULL;
146
147
fail:
148
  return rc;
149
}
150
151
static BOOL json_get_string_alloc(wLog* wlog, WINPR_JSON* json, const char* key, char** result)
152
{
153
  const char* str = NULL;
154
  if (!json_get_const_string(wlog, json, key, &str))
155
    return FALSE;
156
  free(*result);
157
  *result = _strdup(str);
158
  if (!*result)
159
    WLog_Print(wlog, WLOG_ERROR, "[json] object for key '%s' strdup is NULL", key);
160
  return *result != NULL;
161
}
162
163
static INLINE const char* aad_auth_result_to_string(DWORD code)
164
{
165
#define ERROR_CASE(cd, x)   \
166
  if ((cd) == (DWORD)(x)) \
167
    return #x;
168
169
  ERROR_CASE(code, S_OK)
170
  ERROR_CASE(code, SEC_E_INVALID_TOKEN)
171
  ERROR_CASE(code, E_ACCESSDENIED)
172
  ERROR_CASE(code, STATUS_LOGON_FAILURE)
173
  ERROR_CASE(code, STATUS_NO_LOGON_SERVERS)
174
  ERROR_CASE(code, STATUS_INVALID_LOGON_HOURS)
175
  ERROR_CASE(code, STATUS_INVALID_WORKSTATION)
176
  ERROR_CASE(code, STATUS_PASSWORD_EXPIRED)
177
  ERROR_CASE(code, STATUS_ACCOUNT_DISABLED)
178
  return "Unknown error";
179
}
180
181
static BOOL aad_get_nonce(rdpAad* aad)
182
{
183
  BOOL ret = FALSE;
184
  BYTE* response = NULL;
185
  long resp_code = 0;
186
  size_t response_length = 0;
187
  WINPR_JSON* json = NULL;
188
189
  if (!freerdp_http_request("https://login.microsoftonline.com/common/oauth2/v2.0/token",
190
                            "grant_type=srv_challenge", &resp_code, &response, &response_length))
191
  {
192
    WLog_Print(aad->log, WLOG_ERROR, "nonce request failed");
193
    goto fail;
194
  }
195
196
  if (resp_code != HTTP_STATUS_OK)
197
  {
198
    WLog_Print(aad->log, WLOG_ERROR,
199
               "Server unwilling to provide nonce; returned status code %li", resp_code);
200
    if (response_length > 0)
201
      WLog_Print(aad->log, WLOG_ERROR, "[status message] %s", response);
202
    goto fail;
203
  }
204
205
  json = WINPR_JSON_ParseWithLength((const char*)response, response_length);
206
  if (!json)
207
  {
208
    WLog_Print(aad->log, WLOG_ERROR, "Failed to parse nonce response");
209
    goto fail;
210
  }
211
212
  if (!json_get_string_alloc(aad->log, json, "Nonce", &aad->nonce))
213
    goto fail;
214
215
  ret = TRUE;
216
217
fail:
218
  free(response);
219
  WINPR_JSON_Delete(json);
220
  return ret;
221
}
222
223
int aad_client_begin(rdpAad* aad)
224
{
225
  size_t size = 0;
226
227
  WINPR_ASSERT(aad);
228
  WINPR_ASSERT(aad->rdpcontext);
229
230
  rdpSettings* settings = aad->rdpcontext->settings;
231
  WINPR_ASSERT(settings);
232
233
  freerdp* instance = aad->rdpcontext->instance;
234
  WINPR_ASSERT(instance);
235
236
  /* Get the host part of the hostname */
237
  const char* hostname = freerdp_settings_get_string(settings, FreeRDP_AadServerHostname);
238
  if (!hostname)
239
    hostname = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
240
  if (!hostname)
241
  {
242
    WLog_Print(aad->log, WLOG_ERROR, "FreeRDP_ServerHostname == NULL");
243
    return -1;
244
  }
245
246
  aad->hostname = _strdup(hostname);
247
  if (!aad->hostname)
248
  {
249
    WLog_Print(aad->log, WLOG_ERROR, "_strdup(FreeRDP_ServerHostname) == NULL");
250
    return -1;
251
  }
252
253
  char* p = strchr(aad->hostname, '.');
254
  if (p)
255
    *p = '\0';
256
257
  if (winpr_asprintf(&aad->scope, &size,
258
                     "ms-device-service%%3A%%2F%%2Ftermsrv.wvd.microsoft.com%%2Fname%%2F%s%%"
259
                     "2Fuser_impersonation",
260
                     aad->hostname) <= 0)
261
    return -1;
262
263
  if (!generate_pop_key(aad))
264
    return -1;
265
266
  /* Obtain an oauth authorization code */
267
  if (!instance->GetAccessToken)
268
  {
269
    WLog_Print(aad->log, WLOG_ERROR, "instance->GetAccessToken == NULL");
270
    return -1;
271
  }
272
  const BOOL arc = instance->GetAccessToken(instance, ACCESS_TOKEN_TYPE_AAD, &aad->access_token,
273
                                            2, aad->scope, aad->kid);
274
  if (!arc)
275
  {
276
    WLog_Print(aad->log, WLOG_ERROR, "Unable to obtain access token");
277
    return -1;
278
  }
279
280
  /* Send the nonce request message */
281
  if (!aad_get_nonce(aad))
282
  {
283
    WLog_Print(aad->log, WLOG_ERROR, "Unable to obtain nonce");
284
    return -1;
285
  }
286
287
  return 1;
288
}
289
290
static char* aad_create_jws_header(rdpAad* aad)
291
{
292
  WINPR_ASSERT(aad);
293
294
  /* Construct the base64url encoded JWS header */
295
  char* buffer = NULL;
296
  size_t bufferlen = 0;
297
  const int length =
298
      winpr_asprintf(&buffer, &bufferlen, "{\"alg\":\"RS256\",\"kid\":\"%s\"}", aad->kid);
299
  if (length < 0)
300
    return NULL;
301
302
  char* jws_header = crypto_base64url_encode((const BYTE*)buffer, bufferlen);
303
  free(buffer);
304
  return jws_header;
305
}
306
307
static char* aad_create_jws_payload(rdpAad* aad, const char* ts_nonce)
308
{
309
  const time_t ts = time(NULL);
310
311
  WINPR_ASSERT(aad);
312
313
  char* e = NULL;
314
  char* n = NULL;
315
  if (!get_encoded_rsa_params(aad->log, aad->key, &e, &n))
316
    return NULL;
317
318
  /* Construct the base64url encoded JWS payload */
319
  char* buffer = NULL;
320
  size_t bufferlen = 0;
321
  const int length =
322
      winpr_asprintf(&buffer, &bufferlen,
323
                     "{"
324
                     "\"ts\":\"%li\","
325
                     "\"at\":\"%s\","
326
                     "\"u\":\"ms-device-service://termsrv.wvd.microsoft.com/name/%s\","
327
                     "\"nonce\":\"%s\","
328
                     "\"cnf\":{\"jwk\":{\"kty\":\"RSA\",\"e\":\"%s\",\"n\":\"%s\"}},"
329
                     "\"client_claims\":\"{\\\"aad_nonce\\\":\\\"%s\\\"}\""
330
                     "}",
331
                     ts, aad->access_token, aad->hostname, ts_nonce, e, n, aad->nonce);
332
  free(e);
333
  free(n);
334
335
  if (length < 0)
336
    return NULL;
337
338
  char* jws_payload = crypto_base64url_encode((BYTE*)buffer, bufferlen);
339
  free(buffer);
340
  return jws_payload;
341
}
342
343
static BOOL aad_update_digest(rdpAad* aad, WINPR_DIGEST_CTX* ctx, const char* what)
344
{
345
  WINPR_ASSERT(aad);
346
  WINPR_ASSERT(ctx);
347
  WINPR_ASSERT(what);
348
349
  const BOOL dsu1 = winpr_DigestSign_Update(ctx, what, strlen(what));
350
  if (!dsu1)
351
  {
352
    WLog_Print(aad->log, WLOG_ERROR, "winpr_DigestSign_Update [%s] failed", what);
353
    return FALSE;
354
  }
355
  return TRUE;
356
}
357
358
static char* aad_final_digest(rdpAad* aad, WINPR_DIGEST_CTX* ctx)
359
{
360
  char* jws_signature = NULL;
361
362
  WINPR_ASSERT(aad);
363
  WINPR_ASSERT(ctx);
364
365
  size_t siglen = 0;
366
  const int dsf = winpr_DigestSign_Final(ctx, NULL, &siglen);
367
  if (dsf <= 0)
368
  {
369
    WLog_Print(aad->log, WLOG_ERROR, "winpr_DigestSign_Final failed with %d", dsf);
370
    return FALSE;
371
  }
372
373
  char* buffer = calloc(siglen + 1, sizeof(char));
374
  if (!buffer)
375
  {
376
    WLog_Print(aad->log, WLOG_ERROR, "calloc %" PRIuz " bytes failed", siglen + 1);
377
    goto fail;
378
  }
379
380
  size_t fsiglen = siglen;
381
  const int dsf2 = winpr_DigestSign_Final(ctx, (BYTE*)buffer, &fsiglen);
382
  if (dsf2 <= 0)
383
  {
384
    WLog_Print(aad->log, WLOG_ERROR, "winpr_DigestSign_Final failed with %d", dsf2);
385
    goto fail;
386
  }
387
388
  if (siglen != fsiglen)
389
  {
390
    WLog_Print(aad->log, WLOG_ERROR,
391
               "winpr_DigestSignFinal returned different sizes, first %" PRIuz " then %" PRIuz,
392
               siglen, fsiglen);
393
    goto fail;
394
  }
395
  jws_signature = crypto_base64url_encode((const BYTE*)buffer, fsiglen);
396
fail:
397
  free(buffer);
398
  return jws_signature;
399
}
400
401
static char* aad_create_jws_signature(rdpAad* aad, const char* jws_header, const char* jws_payload)
402
{
403
  char* jws_signature = NULL;
404
405
  WINPR_ASSERT(aad);
406
407
  WINPR_DIGEST_CTX* md_ctx = freerdp_key_digest_sign(aad->key, WINPR_MD_SHA256);
408
  if (!md_ctx)
409
  {
410
    WLog_Print(aad->log, WLOG_ERROR, "winpr_Digest_New failed");
411
    goto fail;
412
  }
413
414
  if (!aad_update_digest(aad, md_ctx, jws_header))
415
    goto fail;
416
  if (!aad_update_digest(aad, md_ctx, "."))
417
    goto fail;
418
  if (!aad_update_digest(aad, md_ctx, jws_payload))
419
    goto fail;
420
421
  jws_signature = aad_final_digest(aad, md_ctx);
422
fail:
423
  winpr_Digest_Free(md_ctx);
424
  return jws_signature;
425
}
426
427
static int aad_send_auth_request(rdpAad* aad, const char* ts_nonce)
428
{
429
  int ret = -1;
430
  char* jws_header = NULL;
431
  char* jws_payload = NULL;
432
  char* jws_signature = NULL;
433
434
  WINPR_ASSERT(aad);
435
  WINPR_ASSERT(ts_nonce);
436
437
  wStream* s = Stream_New(NULL, 1024);
438
  if (!s)
439
    goto fail;
440
441
  /* Construct the base64url encoded JWS header */
442
  jws_header = aad_create_jws_header(aad);
443
  if (!jws_header)
444
    goto fail;
445
446
  /* Construct the base64url encoded JWS payload */
447
  jws_payload = aad_create_jws_payload(aad, ts_nonce);
448
  if (!jws_payload)
449
    goto fail;
450
451
  /* Sign the JWS with the pop key */
452
  jws_signature = aad_create_jws_signature(aad, jws_header, jws_payload);
453
  if (!jws_signature)
454
    goto fail;
455
456
  /* Construct the Authentication Request PDU with the JWS as the RDP Assertion */
457
  if (stream_sprintf(s, "{\"rdp_assertion\":\"%s.%s.%s\"}", jws_header, jws_payload,
458
                     jws_signature) < 0)
459
    goto fail;
460
461
  /* Include null terminator in PDU */
462
  Stream_Write_UINT8(s, 0);
463
464
  Stream_SealLength(s);
465
466
  if (transport_write(aad->transport, s) < 0)
467
  {
468
    WLog_Print(aad->log, WLOG_ERROR, "transport_write [%" PRIdz " bytes] failed",
469
               Stream_Length(s));
470
  }
471
  else
472
  {
473
    ret = 1;
474
    aad->state = AAD_STATE_AUTH;
475
  }
476
fail:
477
  Stream_Free(s, TRUE);
478
  free(jws_header);
479
  free(jws_payload);
480
  free(jws_signature);
481
482
  return ret;
483
}
484
485
static int aad_parse_state_initial(rdpAad* aad, wStream* s)
486
{
487
  const char* jstr = Stream_PointerAs(s, char);
488
  const size_t jlen = Stream_GetRemainingLength(s);
489
  const char* ts_nonce = NULL;
490
  int ret = -1;
491
  WINPR_JSON* json = NULL;
492
493
  if (!Stream_SafeSeek(s, jlen))
494
    goto fail;
495
496
  json = WINPR_JSON_ParseWithLength(jstr, jlen);
497
  if (!json)
498
    goto fail;
499
500
  if (!json_get_const_string(aad->log, json, "ts_nonce", &ts_nonce))
501
    goto fail;
502
503
  ret = aad_send_auth_request(aad, ts_nonce);
504
fail:
505
  WINPR_JSON_Delete(json);
506
  return ret;
507
}
508
509
static int aad_parse_state_auth(rdpAad* aad, wStream* s)
510
{
511
  int rc = -1;
512
  double result = 0;
513
  DWORD error_code = 0;
514
  WINPR_JSON* json = NULL;
515
  const char* jstr = Stream_PointerAs(s, char);
516
  const size_t jlength = Stream_GetRemainingLength(s);
517
518
  if (!Stream_SafeSeek(s, jlength))
519
    goto fail;
520
521
  json = WINPR_JSON_ParseWithLength(jstr, jlength);
522
  if (!json)
523
    goto fail;
524
525
  if (!json_get_number(aad->log, json, "authentication_result", &result))
526
    goto fail;
527
  error_code = (DWORD)result;
528
529
  if (error_code != S_OK)
530
  {
531
    WLog_Print(aad->log, WLOG_ERROR, "Authentication result: %s (0x%08" PRIx32 ")",
532
               aad_auth_result_to_string(error_code), error_code);
533
    goto fail;
534
  }
535
  aad->state = AAD_STATE_FINAL;
536
  rc = 1;
537
fail:
538
  WINPR_JSON_Delete(json);
539
  return rc;
540
}
541
542
int aad_recv(rdpAad* aad, wStream* s)
543
{
544
  WINPR_ASSERT(aad);
545
  WINPR_ASSERT(s);
546
547
  switch (aad->state)
548
  {
549
    case AAD_STATE_INITIAL:
550
      return aad_parse_state_initial(aad, s);
551
    case AAD_STATE_AUTH:
552
      return aad_parse_state_auth(aad, s);
553
    case AAD_STATE_FINAL:
554
    default:
555
      WLog_Print(aad->log, WLOG_ERROR, "Invalid AAD_STATE %d", aad->state);
556
      return -1;
557
  }
558
}
559
560
static BOOL generate_rsa_2048(rdpAad* aad)
561
{
562
  WINPR_ASSERT(aad);
563
  return freerdp_key_generate(aad->key, 2048);
564
}
565
566
static char* generate_rsa_digest_base64_str(rdpAad* aad, const char* input, size_t ilen)
567
{
568
  char* b64 = NULL;
569
  WINPR_DIGEST_CTX* digest = winpr_Digest_New();
570
  if (!digest)
571
  {
572
    WLog_Print(aad->log, WLOG_ERROR, "winpr_Digest_New failed");
573
    goto fail;
574
  }
575
576
  if (!winpr_Digest_Init(digest, WINPR_MD_SHA256))
577
  {
578
    WLog_Print(aad->log, WLOG_ERROR, "winpr_Digest_Init(WINPR_MD_SHA256) failed");
579
    goto fail;
580
  }
581
582
  if (!winpr_Digest_Update(digest, (const BYTE*)input, ilen))
583
  {
584
    WLog_Print(aad->log, WLOG_ERROR, "winpr_Digest_Update(%" PRIuz ") failed", ilen);
585
    goto fail;
586
  }
587
588
  BYTE hash[WINPR_SHA256_DIGEST_LENGTH] = { 0 };
589
  if (!winpr_Digest_Final(digest, hash, sizeof(hash)))
590
  {
591
    WLog_Print(aad->log, WLOG_ERROR, "winpr_Digest_Final(%" PRIuz ") failed", sizeof(hash));
592
    goto fail;
593
  }
594
595
  /* Base64url encode the hash */
596
  b64 = crypto_base64url_encode(hash, sizeof(hash));
597
598
fail:
599
  winpr_Digest_Free(digest);
600
  return b64;
601
}
602
603
static BOOL generate_json_base64_str(rdpAad* aad, const char* b64_hash)
604
{
605
  WINPR_ASSERT(aad);
606
607
  char* buffer = NULL;
608
  size_t blen = 0;
609
  const int length = winpr_asprintf(&buffer, &blen, "{\"kid\":\"%s\"}", b64_hash);
610
  if (length < 0)
611
    return FALSE;
612
613
  /* Finally, base64url encode the JSON text to form the kid */
614
  free(aad->kid);
615
  aad->kid = crypto_base64url_encode((const BYTE*)buffer, (size_t)length);
616
  free(buffer);
617
618
  if (!aad->kid)
619
  {
620
    return FALSE;
621
  }
622
  return TRUE;
623
}
624
625
BOOL generate_pop_key(rdpAad* aad)
626
{
627
  BOOL ret = FALSE;
628
  char* buffer = NULL;
629
  char* b64_hash = NULL;
630
  char* e = NULL;
631
  char* n = NULL;
632
633
  WINPR_ASSERT(aad);
634
635
  /* Generate a 2048-bit RSA key pair */
636
  if (!generate_rsa_2048(aad))
637
    goto fail;
638
639
  /* Encode the public key as a JWK */
640
  if (!get_encoded_rsa_params(aad->log, aad->key, &e, &n))
641
    goto fail;
642
643
  size_t blen = 0;
644
  const int alen =
645
      winpr_asprintf(&buffer, &blen, "{\"e\":\"%s\",\"kty\":\"RSA\",\"n\":\"%s\"}", e, n);
646
  if (alen < 0)
647
    goto fail;
648
649
  /* Hash the encoded public key */
650
  b64_hash = generate_rsa_digest_base64_str(aad, buffer, blen);
651
  if (!b64_hash)
652
    goto fail;
653
654
  /* Encode a JSON object with a single property "kid" whose value is the encoded hash */
655
  ret = generate_json_base64_str(aad, b64_hash);
656
657
fail:
658
  free(b64_hash);
659
  free(buffer);
660
  free(e);
661
  free(n);
662
  return ret;
663
}
664
665
static char* bn_to_base64_url(wLog* wlog, rdpPrivateKey* key, enum FREERDP_KEY_PARAM param)
666
{
667
  WINPR_ASSERT(wlog);
668
  WINPR_ASSERT(key);
669
670
  size_t len = 0;
671
  BYTE* bn = freerdp_key_get_param(key, param, &len);
672
  if (!bn)
673
    return NULL;
674
675
  char* b64 = crypto_base64url_encode(bn, len);
676
  free(bn);
677
678
  if (!b64)
679
    WLog_Print(wlog, WLOG_ERROR, "failed  base64 url encode BIGNUM");
680
681
  return b64;
682
}
683
684
BOOL get_encoded_rsa_params(wLog* wlog, rdpPrivateKey* key, char** pe, char** pn)
685
{
686
  BOOL rc = FALSE;
687
  char* e = NULL;
688
  char* n = NULL;
689
690
  WINPR_ASSERT(wlog);
691
  WINPR_ASSERT(key);
692
  WINPR_ASSERT(pe);
693
  WINPR_ASSERT(pn);
694
695
  *pe = NULL;
696
  *pn = NULL;
697
698
  e = bn_to_base64_url(wlog, key, FREERDP_KEY_PARAM_RSA_E);
699
  if (!e)
700
  {
701
    WLog_Print(wlog, WLOG_ERROR, "failed  base64 url encode RSA E");
702
    goto fail;
703
  }
704
705
  n = bn_to_base64_url(wlog, key, FREERDP_KEY_PARAM_RSA_N);
706
  if (!n)
707
  {
708
    WLog_Print(wlog, WLOG_ERROR, "failed  base64 url encode RSA N");
709
    goto fail;
710
  }
711
712
  rc = TRUE;
713
fail:
714
  if (!rc)
715
  {
716
    free(e);
717
    free(n);
718
  }
719
  else
720
  {
721
    *pe = e;
722
    *pn = n;
723
  }
724
  return rc;
725
}
726
#else
727
int aad_client_begin(rdpAad* aad)
728
0
{
729
0
  WINPR_ASSERT(aad);
730
0
  WLog_Print(aad->log, WLOG_ERROR, "AAD security not compiled in, aborting!");
731
0
  return -1;
732
0
}
733
int aad_recv(rdpAad* aad, wStream* s)
734
0
{
735
0
  WINPR_ASSERT(aad);
736
0
  WLog_Print(aad->log, WLOG_ERROR, "AAD security not compiled in, aborting!");
737
0
  return -1;
738
0
}
739
#endif
740
741
rdpAad* aad_new(rdpContext* context, rdpTransport* transport)
742
14.9k
{
743
14.9k
  WINPR_ASSERT(transport);
744
14.9k
  WINPR_ASSERT(context);
745
746
14.9k
  rdpAad* aad = (rdpAad*)calloc(1, sizeof(rdpAad));
747
748
14.9k
  if (!aad)
749
0
    return NULL;
750
751
14.9k
  aad->log = WLog_Get(FREERDP_TAG("aad"));
752
14.9k
  aad->key = freerdp_key_new();
753
14.9k
  if (!aad->key)
754
0
    goto fail;
755
14.9k
  aad->rdpcontext = context;
756
14.9k
  aad->transport = transport;
757
758
14.9k
  return aad;
759
0
fail:
760
0
  WINPR_PRAGMA_DIAG_PUSH
761
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
762
0
  aad_free(aad);
763
0
  WINPR_PRAGMA_DIAG_POP
764
0
  return NULL;
765
14.9k
}
766
767
void aad_free(rdpAad* aad)
768
14.9k
{
769
14.9k
  if (!aad)
770
0
    return;
771
772
14.9k
  free(aad->hostname);
773
14.9k
  free(aad->scope);
774
14.9k
  free(aad->nonce);
775
14.9k
  free(aad->access_token);
776
14.9k
  free(aad->kid);
777
14.9k
  freerdp_key_free(aad->key);
778
779
14.9k
  free(aad);
780
14.9k
}
781
782
AAD_STATE aad_get_state(rdpAad* aad)
783
0
{
784
0
  WINPR_ASSERT(aad);
785
0
  return aad->state;
786
0
}
787
788
BOOL aad_is_supported(void)
789
0
{
790
#ifdef WITH_AAD
791
  return TRUE;
792
#else
793
0
  return FALSE;
794
0
#endif
795
0
}
796
797
#ifdef WITH_AAD
798
char* freerdp_utils_aad_get_access_token(wLog* log, const char* data, size_t length)
799
{
800
  char* token = NULL;
801
  WINPR_JSON* access_token_prop = NULL;
802
  const char* access_token_str = NULL;
803
804
  WINPR_JSON* json = WINPR_JSON_ParseWithLength(data, length);
805
  if (!json)
806
  {
807
    WLog_Print(log, WLOG_ERROR, "Failed to parse access token response [got %" PRIuz " bytes",
808
               length);
809
    goto cleanup;
810
  }
811
812
  access_token_prop = WINPR_JSON_GetObjectItem(json, "access_token");
813
  if (!access_token_prop)
814
  {
815
    WLog_Print(log, WLOG_ERROR, "Response has no \"access_token\" property");
816
    goto cleanup;
817
  }
818
819
  access_token_str = WINPR_JSON_GetStringValue(access_token_prop);
820
  if (!access_token_str)
821
  {
822
    WLog_Print(log, WLOG_ERROR, "Invalid value for \"access_token\"");
823
    goto cleanup;
824
  }
825
826
  token = _strdup(access_token_str);
827
828
cleanup:
829
  WINPR_JSON_Delete(json);
830
  return token;
831
}
832
#endif