FreeRDP
Loading...
Searching...
No Matches
sso_mib_tokens.c
1/*
2 * SPDX-License-Identifier: Apache-2.0
3 * SPDX-FileCopyrightText: Copyright 2025 Siemens
4 */
5
6#include <sso-mib/sso-mib.h>
7#include <freerdp/crypto/crypto.h>
8#include <winpr/json.h>
9
10#include "sso_mib_tokens.h"
11
12#include <freerdp/log.h>
13#define TAG CLIENT_TAG("common.sso")
14
15enum sso_mib_state
16{
17 SSO_MIB_STATE_INIT = 0,
18 SSO_MIB_STATE_FAILED = 1,
19 SSO_MIB_STATE_SUCCESS = 2,
20};
21
22struct MIBClientWrapper
23{
24 MIBClientApp* app;
25 enum sso_mib_state state;
26 pGetCommonAccessToken GetCommonAccessToken;
27};
28
29static BOOL sso_mib_get_avd_access_token(rdpClientContext* client_context, char** token)
30{
31 WINPR_ASSERT(client_context);
32 WINPR_ASSERT(client_context->mibClientWrapper);
33 WINPR_ASSERT(client_context->mibClientWrapper->app);
34 WINPR_ASSERT(token);
35
36 MIBAccount* account = nullptr;
37 GSList* scopes = nullptr;
38
39 BOOL rc = FALSE;
40 *token = nullptr;
41
42 account = mib_client_app_get_account_by_upn(client_context->mibClientWrapper->app, nullptr);
43 if (!account)
44 {
45 goto cleanup;
46 }
47
48 scopes = g_slist_append(scopes, g_strdup("https://www.wvd.microsoft.com/.default"));
49
50 MIBPrt* prt = mib_client_app_acquire_token_silent(client_context->mibClientWrapper->app,
51 account, scopes, nullptr, nullptr, nullptr);
52 if (prt)
53 {
54 const char* access_token = mib_prt_get_access_token(prt);
55 if (access_token)
56 {
57 *token = strdup(access_token);
58 }
59 g_object_unref(prt);
60 }
61
62 rc = TRUE && *token != nullptr;
63cleanup:
64 if (account)
65 g_object_unref(account);
66 g_slist_free_full(scopes, g_free);
67 return rc;
68}
69
70static BOOL sso_mib_get_rdsaad_access_token(rdpClientContext* client_context, const char* scope,
71 const char* req_cnf, char** token)
72{
73 WINPR_ASSERT(client_context);
74 WINPR_ASSERT(client_context->mibClientWrapper);
75 WINPR_ASSERT(client_context->mibClientWrapper->app);
76 WINPR_ASSERT(scope);
77 WINPR_ASSERT(token);
78 WINPR_ASSERT(req_cnf);
79
80 GSList* scopes = nullptr;
81 WINPR_JSON* json = nullptr;
82 MIBPopParams* params = nullptr;
83
84 BOOL rc = FALSE;
85 *token = nullptr;
86 BYTE* req_cnf_dec = nullptr;
87 size_t req_cnf_dec_len = 0;
88
89 scopes = g_slist_append(scopes, g_strdup(scope));
90
91 // Parse the "kid" element from req_cnf
92 crypto_base64_decode(req_cnf, strlen(req_cnf) + 1, &req_cnf_dec, &req_cnf_dec_len);
93 if (!req_cnf_dec)
94 {
95 goto cleanup;
96 }
97
98 json = WINPR_JSON_Parse((const char*)req_cnf_dec);
99 if (!json)
100 {
101 goto cleanup;
102 }
103 WINPR_JSON* prop = WINPR_JSON_GetObjectItemCaseSensitive(json, "kid");
104 if (!prop)
105 {
106 goto cleanup;
107 }
108 const char* kid = WINPR_JSON_GetStringValue(prop);
109 if (!kid)
110 {
111 goto cleanup;
112 }
113
114 params = mib_pop_params_new(MIB_AUTH_SCHEME_POP, MIB_REQUEST_METHOD_GET, "");
115 mib_pop_params_set_kid(params, kid);
116 MIBPrt* prt = mib_client_app_acquire_token_interactive(client_context->mibClientWrapper->app,
117 scopes, MIB_PROMPT_NONE, nullptr,
118 nullptr, nullptr, params);
119 if (prt)
120 {
121 *token = strdup(mib_prt_get_access_token(prt));
122 rc = TRUE;
123 g_object_unref(prt);
124 }
125
126cleanup:
127 if (params)
128 g_object_unref(params);
129 WINPR_JSON_Delete(json);
130 free(req_cnf_dec);
131 g_slist_free_full(scopes, g_free);
132 return rc;
133}
134
135static BOOL sso_mib_get_access_token(rdpContext* context, AccessTokenType tokenType, char** token,
136 size_t count, ...)
137{
138 BOOL rc = FALSE;
139 rdpClientContext* client_context = (rdpClientContext*)context;
140 WINPR_ASSERT(client_context);
141 WINPR_ASSERT(client_context->mibClientWrapper);
142
143 if (!client_context->mibClientWrapper->app)
144 {
145 const char* client_id =
146 freerdp_settings_get_string(context->settings, FreeRDP_GatewayAvdClientID);
147 client_context->mibClientWrapper->app =
148 mib_public_client_app_new(client_id, MIB_AUTHORITY_COMMON, nullptr, nullptr);
149 }
150
151 if (!client_context->mibClientWrapper->app)
152 return FALSE;
153
154 const char* scope = nullptr;
155 const char* req_cnf = nullptr;
156
157 va_list ap = WINPR_C_ARRAY_INIT;
158 va_start(ap, count);
159
160 if (tokenType == ACCESS_TOKEN_TYPE_AAD)
161 {
162 scope = va_arg(ap, const char*);
163 req_cnf = va_arg(ap, const char*);
164 }
165
166 if ((client_context->mibClientWrapper->state == SSO_MIB_STATE_INIT) ||
167 (client_context->mibClientWrapper->state == SSO_MIB_STATE_SUCCESS))
168 {
169 switch (tokenType)
170 {
171 case ACCESS_TOKEN_TYPE_AVD:
172 {
173 rc = sso_mib_get_avd_access_token(client_context, token);
174 if (rc)
175 client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
176 else
177 {
178 WLog_WARN(TAG, "Getting AVD token from identity broker failed, falling back to "
179 "browser-based authentication.");
180 client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
181 }
182 }
183 break;
184 case ACCESS_TOKEN_TYPE_AAD:
185 {
186 // Setup scope without URL encoding for sso-mib
187 char* scope_copy = winpr_str_url_decode(scope, strlen(scope));
188 if (!scope_copy)
189 WLog_ERR(TAG, "Failed to decode scope");
190 else
191 {
192 rc =
193 sso_mib_get_rdsaad_access_token(client_context, scope_copy, req_cnf, token);
194 free(scope_copy);
195 if (rc)
196 client_context->mibClientWrapper->state = SSO_MIB_STATE_SUCCESS;
197 else
198 {
199 WLog_WARN(TAG,
200 "Getting RDS token from identity broker failed, falling back to "
201 "browser-based authentication.");
202 client_context->mibClientWrapper->state = SSO_MIB_STATE_FAILED;
203 }
204 }
205 }
206 break;
207 default:
208 break;
209 }
210 }
211 if (!rc && client_context->mibClientWrapper->GetCommonAccessToken)
212 rc = client_context->mibClientWrapper->GetCommonAccessToken(context, tokenType, token,
213 count, scope, req_cnf);
214 va_end(ap);
215
216 return rc;
217}
218
219MIBClientWrapper* sso_mib_new(rdpContext* context)
220{
221
222 MIBClientWrapper* mibClientWrapper = (MIBClientWrapper*)calloc(1, sizeof(MIBClientWrapper));
223 if (!mibClientWrapper)
224 return nullptr;
225
226 mibClientWrapper->GetCommonAccessToken = freerdp_get_common_access_token(context);
227 if (!freerdp_set_common_access_token(context, sso_mib_get_access_token))
228 {
229 sso_mib_free(mibClientWrapper);
230 return nullptr;
231 }
232 mibClientWrapper->state = SSO_MIB_STATE_INIT;
233 return mibClientWrapper;
234}
235
236void sso_mib_free(MIBClientWrapper* sso)
237{
238 if (!sso)
239 return;
240
241 if (sso->app)
242 g_object_unref(sso->app);
243
244 free(sso);
245}
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_Parse(const char *value)
Parse a '\0' terminated JSON string.
Definition c-json.c:93
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 const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
Definition c-json.c:142
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103
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.