aleitner commented on code in PR #633:
URL: https://github.com/apache/guacamole-server/pull/633#discussion_r3198301328


##########
src/protocols/rdp/rdp.c:
##########
@@ -327,6 +328,149 @@ static BOOL rdp_freerdp_authenticate(freerdp* instance, 
char** username,
 
 }
 
+#ifdef HAVE_FREERDP_AAD_SUPPORT
+#include "aad.h"
+
+/**
+ * Callback invoked by FreeRDP when an Azure AD access token is required.
+ *
+ * @param instance
+ *     The FreeRDP instance associated with the RDP session.
+ *
+ * @param tokenType
+ *     The type of access token being requested (ACCESS_TOKEN_TYPE_AAD or
+ *     ACCESS_TOKEN_TYPE_AVD).
+ *
+ * @param token
+ *     Pointer to a string which will receive the access token. This function
+ *     must allocate and populate this string.
+ *
+ * @param count
+ *     Number of additional variadic arguments.
+ *
+ * @param ...
+ *     Additional arguments (for AAD: scope and req_cnf)
+ *
+ * @return
+ *     TRUE if an access token was successfully obtained, FALSE otherwise.
+ */
+static BOOL rdp_freerdp_get_access_token(freerdp* instance,
+        AccessTokenType tokenType, char** token, size_t count, ...) {
+
+    rdpContext* context = GUAC_RDP_CONTEXT(instance);
+    guac_client* client = ((rdp_freerdp_context*) context)->client;
+    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+    guac_rdp_settings* settings = rdp_client->settings;
+
+    *token = NULL;
+
+    /* Only handle ACCESS_TOKEN_TYPE_AAD for now */
+    if (tokenType != ACCESS_TOKEN_TYPE_AAD) {
+        guac_client_log(client, GUAC_LOG_ERROR,
+            "AAD: Unsupported token type: %d", tokenType);
+        return FALSE;
+    }
+
+    /* Extract scope and req_cnf from variadic arguments */
+    if (count < 2) {
+        guac_client_log(client, GUAC_LOG_ERROR,
+            "AAD: Expected 2 arguments (scope and req_cnf)");
+        return FALSE;
+    }
+
+    va_list ap;
+    va_start(ap, count);
+    const char* scope = va_arg(ap, const char*);
+    const char* req_cnf = va_arg(ap, const char*);
+    va_end(ap);
+
+    /* Prompt for missing credentials if the client supports it */
+    if (settings->username == NULL || settings->password == NULL) {
+
+        if (!guac_client_owner_supports_required(client)) {
+            guac_client_log(client, GUAC_LOG_ERROR,
+                    "AAD: Username and password are required for AAD "
+                    "authentication, and client does not support prompting");
+            return FALSE;
+        }
+
+        char* required_params[3] = {NULL};
+        int i = 0;
+
+        if (settings->username == NULL) {
+            guac_argv_register(GUAC_RDP_ARGV_USERNAME,
+                    guac_rdp_argv_callback, NULL, 0);
+            required_params[i++] = GUAC_RDP_ARGV_USERNAME;
+        }
+
+        if (settings->password == NULL) {
+            guac_argv_register(GUAC_RDP_ARGV_PASSWORD,
+                    guac_rdp_argv_callback, NULL, 0);
+            required_params[i++] = GUAC_RDP_ARGV_PASSWORD;
+        }
+
+        required_params[i] = NULL;
+
+        guac_client_owner_send_required(client,
+                (const char**) required_params);
+        guac_argv_await((const char**) required_params);
+
+        /* Check that credentials were provided */
+        if (settings->username == NULL || settings->password == NULL) {
+            guac_client_log(client, GUAC_LOG_ERROR,
+                    "AAD: Username and password are required for AAD "
+                    "authentication");
+            return FALSE;
+        }
+    }

Review Comment:
   AAD identifies users by UPN (e.g. [email protected]), so the 
legacy RDP domain field has no role here. Microsoft's login form itself has no 
domain input, identity is derived from the UPN suffix. I also added a 
clarifying comment near the credential prompt



##########
src/protocols/rdp/aad.c:
##########
@@ -0,0 +1,1072 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_FREERDP_AAD_SUPPORT
+
+#include "aad.h"
+
+#include <guacamole/client.h>
+#include <guacamole/mem.h>
+#include <guacamole/string.h>
+
+#include <curl/curl.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Azure AD OAuth2 token endpoint URL format.
+ * The %s placeholder is replaced with the tenant ID.
+ */
+#define GUAC_AAD_TOKEN_ENDPOINT \
+    "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
+
+/**
+ * Azure AD OAuth2 authorization endpoint URL format.
+ * The %s placeholder is replaced with the tenant ID.
+ */
+#define GUAC_AAD_AUTHORIZE_ENDPOINT \
+    "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize";
+
+/**
+ * The native client redirect URI used for the authorization code flow.
+ * This is a special Microsoft-provided redirect URI for non-web applications.
+ */
+#define GUAC_AAD_NATIVE_REDIRECT_URI \
+    "https://login.microsoftonline.com/common/oauth2/nativeclient";
+
+/**
+ * Maximum size for the login page HTML response.
+ */
+#define GUAC_AAD_LOGIN_PAGE_MAX_SIZE (64 * 1024)
+
+/**
+ * HTTP request timeout in seconds.
+ */
+#define GUAC_AAD_HTTP_TIMEOUT_SECONDS 30
+
+/**
+ * User-Agent string sent with all HTTP requests to Microsoft login endpoints.
+ * A browser-like UA is required to avoid "unsupported browser" responses.
+ */
+#define GUAC_AAD_USER_AGENT \
+    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " \
+    "(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
+
+/**
+ * HTTP response structure for AAD requests.
+ */
+typedef struct guac_rdp_aad_response {
+
+    /**
+     * The response body data.
+     */
+    char* data;
+
+    /**
+     * The current size of the response data.
+     */
+    size_t size;
+
+} guac_rdp_aad_response;
+
+/**
+ * Callback function for libcurl to write received HTTP data into a
+ * guac_rdp_aad_response buffer.
+ *
+ * @param contents
+ *     Pointer to the received data.
+ *
+ * @param size
+ *     Size of each data element.
+ *
+ * @param nmemb
+ *     Number of data elements.
+ *
+ * @param userp
+ *     User-provided pointer (guac_rdp_aad_response structure).
+ *
+ * @return
+ *     The number of bytes processed.
+ */
+static size_t guac_rdp_aad_write_callback(void* contents, size_t size,
+        size_t nmemb, void* userp) {
+
+    size_t total_size = size * nmemb;
+    guac_rdp_aad_response* response = (guac_rdp_aad_response*) userp;
+
+    /* Reject responses that exceed the maximum login page size */
+    if (response->size + total_size > GUAC_AAD_LOGIN_PAGE_MAX_SIZE)
+        return 0;
+
+    /* Copy data into response buffer and null-terminate */
+    memcpy(response->data + response->size, contents, total_size);
+    response->size += total_size;
+    response->data[response->size] = '\0';
+
+    return total_size;
+}
+
+/**
+ * URL-encodes a string for use in HTTP POST data or query parameters.
+ *
+ * @param curl
+ *     The CURL handle to use for encoding.
+ *
+ * @param str
+ *     The string to encode.
+ *
+ * @return
+ *     A newly allocated URL-encoded string, or NULL on error. The caller
+ *     must free this string using curl_free().
+ */
+static char* guac_rdp_aad_urlencode(CURL* curl, const char* str) {
+    if (str == NULL)
+        return NULL;

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to