FreeRDP
aad.c
1 
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 = { 0 };
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 {
729  WINPR_ASSERT(aad);
730  WLog_Print(aad->log, WLOG_ERROR, "AAD security not compiled in, aborting!");
731  return -1;
732 }
733 int aad_recv(rdpAad* aad, wStream* s)
734 {
735  WINPR_ASSERT(aad);
736  WLog_Print(aad->log, WLOG_ERROR, "AAD security not compiled in, aborting!");
737  return -1;
738 }
739 #endif
740 
741 rdpAad* aad_new(rdpContext* context, rdpTransport* transport)
742 {
743  WINPR_ASSERT(transport);
744  WINPR_ASSERT(context);
745 
746  rdpAad* aad = (rdpAad*)calloc(1, sizeof(rdpAad));
747 
748  if (!aad)
749  return NULL;
750 
751  aad->log = WLog_Get(FREERDP_TAG("aad"));
752  aad->key = freerdp_key_new();
753  if (!aad->key)
754  goto fail;
755  aad->rdpcontext = context;
756  aad->transport = transport;
757 
758  return aad;
759 fail:
760  WINPR_PRAGMA_DIAG_PUSH
761  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
762  aad_free(aad);
763  WINPR_PRAGMA_DIAG_POP
764  return NULL;
765 }
766 
767 void aad_free(rdpAad* aad)
768 {
769  if (!aad)
770  return;
771 
772  free(aad->hostname);
773  free(aad->scope);
774  free(aad->nonce);
775  free(aad->access_token);
776  free(aad->kid);
777  freerdp_key_free(aad->key);
778 
779  free(aad);
780 }
781 
782 AAD_STATE aad_get_state(rdpAad* aad)
783 {
784  WINPR_ASSERT(aad);
785  return aad->state;
786 }
787 
788 BOOL aad_is_supported(void)
789 {
790 #ifdef WITH_AAD
791  return TRUE;
792 #else
793  return FALSE;
794 #endif
795 }
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
WINPR_API BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON *object, const char *string)
Check if JSON has an object matching the name.
Definition: json.c:210
WINPR_API WINPR_JSON * WINPR_JSON_ParseWithLength(const char *value, size_t buffer_length)
Parse a JSON string.
Definition: json.c:125
WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
Definition: json.c:349
WINPR_API double WINPR_JSON_GetNumberValue(const WINPR_JSON *item)
Return the Number value of a JSON item.
Definition: json.c:246
WINPR_API BOOL WINPR_JSON_IsNumber(const WINPR_JSON *item)
Check if JSON item is of type Number.
Definition: json.c:336
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition: json.c:144
WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItem(const WINPR_JSON *object, const char *string)
Return a pointer to an JSON object item.
Definition: json.c:184
WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
Definition: json.c:234
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.