From 1e8d9d92a583137ae77f0cf8d3597f3162192707 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 2 Nov 2020 12:38:06 -0800 Subject: [PATCH 1/2] GUACAMOLE-221: Rely on FreeRDP error code if no RDP disconnect reason is available. --- src/protocols/rdp/error.c | 92 ++++++++++++++++++++++++++++++++++----- src/protocols/rdp/error.h | 13 ++++-- src/protocols/rdp/rdp.c | 5 +-- 3 files changed, 93 insertions(+), 17 deletions(-) diff --git a/src/protocols/rdp/error.c b/src/protocols/rdp/error.c index de0e1cd3..22099fbc 100644 --- a/src/protocols/rdp/error.c +++ b/src/protocols/rdp/error.c @@ -18,14 +18,90 @@ */ #include "error.h" -#include "rdp.h" #include #include #include #include +#include -void guac_rdp_client_abort(guac_client* client) { +/** + * Translates the error code returned by freerdp_get_last_error() for the given + * RDP instance into a Guacamole status code and human-readable message. If no + * error was reported, a successful error code and message will be assigned. + * + * @param rdp_inst + * The FreeRDP client instance handling the RDP connection that failed. + * + * @param status + * Pointer to the variable that should receive the guac_protocol_status + * value equivalent to the error returned by freerdp_get_last_error(). + * + * @param message + * Pointer to the variable that should receive a static human-readable + * message generally describing the error returned by + * freerdp_get_last_error(). + */ +static void guac_rdp_translate_last_error(freerdp* rdp_inst, + guac_protocol_status* status, const char** message) { + + UINT32 last_error = freerdp_get_last_error(rdp_inst->context); + switch (last_error) { + + /* Normal disconnect */ + case FREERDP_ERROR_NONE: + case FREERDP_ERROR_SUCCESS: + *status = GUAC_PROTOCOL_STATUS_SUCCESS; + *message = "Disconnected."; + break; + + /* Authentication failure */ + case FREERDP_ERROR_AUTHENTICATION_FAILED: + case FREERDP_ERROR_CONNECT_ACCESS_DENIED: + case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED: + case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED: + case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT: + case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION: + case FREERDP_ERROR_CONNECT_CLIENT_REVOKED: + case FREERDP_ERROR_CONNECT_LOGON_FAILURE: + case FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED: + case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS: + case FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED: + case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED: + case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE: + case FREERDP_ERROR_CONNECT_WRONG_PASSWORD: + case FREERDP_ERROR_INSUFFICIENT_PRIVILEGES: + case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED: + case FREERDP_ERROR_SERVER_DENIED_CONNECTION: + case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES: + case FREERDP_ERROR_SERVER_FRESH_CREDENTIALS_REQUIRED: + *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; + *message = "Authentication failure."; + break; + + /* Connection failed */ + case FREERDP_ERROR_CONNECT_CANCELLED: + case FREERDP_ERROR_CONNECT_FAILED: + case FREERDP_ERROR_CONNECT_KDC_UNREACHABLE: + case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED: + case FREERDP_ERROR_DNS_ERROR: + case FREERDP_ERROR_DNS_NAME_NOT_FOUND: + case FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR: + case FREERDP_ERROR_TLS_CONNECT_FAILED: + *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; + *message = "Connection failed."; + break; + + /* All other errors */ + default: + *status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; + *message = "Upstream error."; + + } + +} + +void guac_rdp_client_abort(guac_client* client, freerdp* rdp_inst) { /* * NOTE: The RDP status codes translated here are documented within @@ -35,9 +111,6 @@ void guac_rdp_client_abort(guac_client* client) { * https://msdn.microsoft.com/en-us/library/cc240544.aspx */ - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - freerdp* rdp_inst = rdp_client->rdp_inst; - guac_protocol_status status; const char* message; @@ -47,10 +120,9 @@ void guac_rdp_client_abort(guac_client* client) { /* Translate reason code into Guacamole protocol status */ switch (error_info) { - /* Normal disconnect */ + /* Possibly-normal disconnect, depending on freerdp_get_last_error() */ case 0x0: /* ERRINFO_SUCCESS */ - status = GUAC_PROTOCOL_STATUS_SUCCESS; - message = "Disconnected."; + guac_rdp_translate_last_error(rdp_inst, &status, &message); break; /* Forced disconnect (possibly by admin) */ @@ -129,8 +201,8 @@ void guac_rdp_client_abort(guac_client* client) { } /* Log human-readable description of disconnect at info level */ - guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection: %s", - message); + guac_client_log(client, GUAC_LOG_INFO, "RDP server closed/refused " + "connection: %s", message); /* Log internal disconnect reason code at debug level */ if (error_info) diff --git a/src/protocols/rdp/error.h b/src/protocols/rdp/error.h index 469bd4a8..3afe0c4b 100644 --- a/src/protocols/rdp/error.h +++ b/src/protocols/rdp/error.h @@ -20,17 +20,22 @@ #ifndef GUAC_RDP_ERROR_H #define GUAC_RDP_ERROR_H +#include #include /** - * Stops the current connection due to the RDP server disconnecting. If the RDP - * server provided a reason for disconnecting, that reason will be logged, and - * an appropriate error code will be sent to the Guacamole client. + * Stops the current connection due to the RDP server disconnecting or the + * connection attempt failing. If the RDP server or FreeRDP provided a reason + * for for the failure/disconnect, that reason will be logged, and an + * appropriate error code will be sent to the Guacamole client. * * @param client * The Guacamole client to disconnect. + * + * @param rdp_inst + * The FreeRDP client instance handling the RDP connection that failed. */ -void guac_rdp_client_abort(guac_client* client); +void guac_rdp_client_abort(guac_client* client, freerdp* rdp_inst); #endif diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 9444b59a..d7b75892 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -461,8 +461,7 @@ static int guac_rdp_handle_connection(guac_client* client) { /* Connect to RDP server */ if (!freerdp_connect(rdp_inst)) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND, - "Error connecting to RDP server"); + guac_rdp_client_abort(client, rdp_inst); goto fail; } @@ -542,7 +541,7 @@ static int guac_rdp_handle_connection(guac_client* client) { /* Close connection cleanly if server is disconnecting */ if (connection_closing) - guac_rdp_client_abort(client); + guac_rdp_client_abort(client, rdp_inst); /* If a low-level connection error occurred, fail */ else if (wait_result < 0) From 8041585379641d2c31f22235a4bc4180907db9cc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 2 Nov 2020 15:40:29 -0800 Subject: [PATCH 2/2] GUACAMOLE-221: Increase verbosity of logged FreeRDP-related errors. --- src/protocols/rdp/error.c | 67 ++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/protocols/rdp/error.c b/src/protocols/rdp/error.c index 22099fbc..144007a0 100644 --- a/src/protocols/rdp/error.c +++ b/src/protocols/rdp/error.c @@ -55,41 +55,70 @@ static void guac_rdp_translate_last_error(freerdp* rdp_inst, *message = "Disconnected."; break; - /* Authentication failure */ - case FREERDP_ERROR_AUTHENTICATION_FAILED: - case FREERDP_ERROR_CONNECT_ACCESS_DENIED: - case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED: + /* Account expired */ case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED: - case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT: - case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION: - case FREERDP_ERROR_CONNECT_CLIENT_REVOKED: - case FREERDP_ERROR_CONNECT_LOGON_FAILURE: - case FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED: - case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS: case FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED: case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED: case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE: - case FREERDP_ERROR_CONNECT_WRONG_PASSWORD: - case FREERDP_ERROR_INSUFFICIENT_PRIVILEGES: + case FREERDP_ERROR_SERVER_FRESH_CREDENTIALS_REQUIRED: + *status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; + *message = "Credentials expired."; + break; + + /* Security negotiation failed */ case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED: + *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; + *message = "Security negotiation failed (wrong security type?)"; + break; + + /* Access denied */ + case FREERDP_ERROR_CONNECT_ACCESS_DENIED: + case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED: + case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT: + case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION: + case FREERDP_ERROR_CONNECT_CLIENT_REVOKED: + case FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED: + case FREERDP_ERROR_INSUFFICIENT_PRIVILEGES: case FREERDP_ERROR_SERVER_DENIED_CONNECTION: case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES: - case FREERDP_ERROR_SERVER_FRESH_CREDENTIALS_REQUIRED: *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; - *message = "Authentication failure."; + *message = "Access denied by server (account locked/disabled?)"; + break; + + /* General, unspecified authentication failure */ + case FREERDP_ERROR_AUTHENTICATION_FAILED: + case FREERDP_ERROR_CONNECT_LOGON_FAILURE: + case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS: + case FREERDP_ERROR_CONNECT_WRONG_PASSWORD: + *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; + *message = "Authentication failure (invalid credentials?)"; + break; + + /* SSL/TLS connection failed */ + case FREERDP_ERROR_TLS_CONNECT_FAILED: + *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; + *message = "SSL/TLS connection failed (untrusted/self-signed certificate?)"; + break; + + /* DNS lookup failed */ + case FREERDP_ERROR_DNS_ERROR: + case FREERDP_ERROR_DNS_NAME_NOT_FOUND: + *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; + *message = "DNS lookup failed (incorrect hostname?)"; + break; + + case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED: + *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; + *message = "Server refused connection (wrong security type?)"; break; /* Connection failed */ case FREERDP_ERROR_CONNECT_CANCELLED: case FREERDP_ERROR_CONNECT_FAILED: case FREERDP_ERROR_CONNECT_KDC_UNREACHABLE: - case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED: - case FREERDP_ERROR_DNS_ERROR: - case FREERDP_ERROR_DNS_NAME_NOT_FOUND: case FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR: - case FREERDP_ERROR_TLS_CONNECT_FAILED: *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; - *message = "Connection failed."; + *message = "Connection failed (server unreachable?)"; break; /* All other errors */