Only FreeRDP 2.0.0-rc3 and later support all the constants used within src/protocols/rdp/error.c.
314 lines
11 KiB
C
314 lines
11 KiB
C
/*
|
|
* 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 <freerdp/freerdp.h>
|
|
#include <guacamole/client.h>
|
|
#include <guacamole/protocol.h>
|
|
#include <guacamole/socket.h>
|
|
#include <winpr/wtypes.h>
|
|
|
|
/**
|
|
* 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);
|
|
|
|
}
|
|
|