/* * 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 "error.h" #include #include #include #include #include /** * 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 (no error at all) */ case FREERDP_ERROR_NONE: case FREERDP_ERROR_SUCCESS: *status = GUAC_PROTOCOL_STATUS_SUCCESS; *message = "Disconnected."; break; /* * General credentials expired (password has expired, password must be * reset before it can be used for the first time, etc.) */ #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED: #endif #ifdef FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE: #endif case FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED: case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED: case FREERDP_ERROR_SERVER_FRESH_CREDENTIALS_REQUIRED: *status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; *message = "Credentials expired."; break; /* * Security negotiation failed (the server is refusing the connection * because the security negotiation process failed) */ case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED: *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; *message = "Security negotiation failed (wrong security type?)"; break; /* * General access denied/revoked (regardless of any credentials * provided, the server is denying the requested access by this * account) */ #ifdef FREERDP_ERROR_CONNECT_ACCESS_DENIED case FREERDP_ERROR_CONNECT_ACCESS_DENIED: #endif #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED: #endif #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT: #endif #ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION: #endif #ifdef FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED case FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED: #endif case FREERDP_ERROR_CONNECT_CLIENT_REVOKED: case FREERDP_ERROR_INSUFFICIENT_PRIVILEGES: case FREERDP_ERROR_SERVER_DENIED_CONNECTION: case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES: *status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; *message = "Access denied by server (account locked/disabled?)"; break; /* * General authentication failure (no credentials provided or wrong * credentials provided) */ #ifdef FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS: #endif #ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE case FREERDP_ERROR_CONNECT_LOGON_FAILURE: #endif #ifdef FREERDP_ERROR_CONNECT_WRONG_PASSWORD case FREERDP_ERROR_CONNECT_WRONG_PASSWORD: #endif case FREERDP_ERROR_AUTHENTICATION_FAILED: *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; *message = "Authentication failure (invalid credentials?)"; break; /* * SSL/TLS connection failed (the server's certificate is not trusted) */ 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 (hostname resolution failed or invalid IP address) */ 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; /* * Connection refused (the server is outright refusing to handle the * inbound connection, typically due to the client requesting a * security type that is not allowed) */ case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED: *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; *message = "Server refused connection (wrong security type?)"; break; /* * Connection failed (the network connection to the server did not * succeed) */ case FREERDP_ERROR_CONNECT_CANCELLED: case FREERDP_ERROR_CONNECT_FAILED: case FREERDP_ERROR_CONNECT_KDC_UNREACHABLE: case FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR: *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; *message = "Connection failed (server unreachable?)"; break; /* * All other (unknown) 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 * [MS-RDPBCGR], section 2.2.5.1.1: "Set Error Info PDU Data", in the * description of the "errorInfo" field. * * https://msdn.microsoft.com/en-us/library/cc240544.aspx */ guac_protocol_status status; const char* message; /* Read disconnect reason code from connection */ int error_info = freerdp_error_info(rdp_inst); /* Translate reason code into Guacamole protocol status */ switch (error_info) { /* Possibly-normal disconnect, depending on freerdp_get_last_error() */ case 0x0: /* ERRINFO_SUCCESS */ guac_rdp_translate_last_error(rdp_inst, &status, &message); break; /* Forced disconnect (possibly by admin) */ case 0x1: /* ERRINFO_RPC_INITIATED_DISCONNECT */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Forcibly disconnected."; break; /* The user was logged off (possibly by admin) */ case 0x2: /* ERRINFO_RPC_INITIATED_LOGOFF */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Logged off."; break; /* The user was idle long enough that the RDP server disconnected */ case 0x3: /* ERRINFO_IDLE_TIMEOUT */ status = GUAC_PROTOCOL_STATUS_SESSION_TIMEOUT; message = "Idle session time limit exceeded."; break; /* The user's session has been active for too long */ case 0x4: /* ERRINFO_LOGON_TIMEOUT */ status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; message = "Active session time limit exceeded."; break; /* Another user logged on, disconnecting this user */ case 0x5: /* ERRINFO_DISCONNECTED_BY_OTHER_CONNECTION */ status = GUAC_PROTOCOL_STATUS_SESSION_CONFLICT; message = "Disconnected by other connection."; break; /* The RDP server is refusing to service the connection */ case 0x6: /* ERRINFO_OUT_OF_MEMORY */ case 0x7: /* ERRINFO_SERVER_DENIED_CONNECTION */ status = GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE; message = "Server refused connection."; break; /* The user does not have permission to connect */ case 0x9: /* ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES */ status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; message = "Insufficient privileges."; break; /* The user's credentials have expired */ case 0xA: /* ERRINFO_SERVER_FRESH_CREDENTIALS_REQUIRED */ status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; message = "Credentials expired."; break; /* The user manually disconnected using an administrative tool within * the session */ case 0xB: /* ERRINFO_RPC_INITIATED_DISCONNECT_BYUSER */ status = GUAC_PROTOCOL_STATUS_SUCCESS; message = "Manually disconnected."; break; /* The user manually logged off */ case 0xC: /* ERRINFO_LOGOFF_BY_USER */ status = GUAC_PROTOCOL_STATUS_SUCCESS; message = "Manually logged off."; break; /* Unimplemented/unknown disconnect reason code */ default: status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; message = "Upstream error."; } /* Send error code if an error occurred */ if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { guac_protocol_send_error(client->socket, message, status); guac_socket_flush(client->socket); } /* Log human-readable description of disconnect at info level */ 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) guac_client_log(client, GUAC_LOG_DEBUG, "Disconnect reason " "code: 0x%X.", error_info); /* Abort connection */ guac_client_stop(client); }