error.c (10920B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "error.h" 21 22#include <freerdp/freerdp.h> 23#include <guacamole/client.h> 24#include <guacamole/protocol.h> 25#include <guacamole/socket.h> 26#include <winpr/wtypes.h> 27 28/** 29 * Translates the error code returned by freerdp_get_last_error() for the given 30 * RDP instance into a Guacamole status code and human-readable message. If no 31 * error was reported, a successful error code and message will be assigned. 32 * 33 * @param rdp_inst 34 * The FreeRDP client instance handling the RDP connection that failed. 35 * 36 * @param status 37 * Pointer to the variable that should receive the guac_protocol_status 38 * value equivalent to the error returned by freerdp_get_last_error(). 39 * 40 * @param message 41 * Pointer to the variable that should receive a static human-readable 42 * message generally describing the error returned by 43 * freerdp_get_last_error(). 44 */ 45static void guac_rdp_translate_last_error(freerdp* rdp_inst, 46 guac_protocol_status* status, const char** message) { 47 48 UINT32 last_error = freerdp_get_last_error(rdp_inst->context); 49 switch (last_error) { 50 51 /* 52 * Normal disconnect (no error at all) 53 */ 54 55 case FREERDP_ERROR_NONE: 56 case FREERDP_ERROR_SUCCESS: 57 *status = GUAC_PROTOCOL_STATUS_SUCCESS; 58 *message = "Disconnected."; 59 break; 60 61 /* 62 * General credentials expired (password has expired, password must be 63 * reset before it can be used for the first time, etc.) 64 */ 65 66#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED 67 case FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED: 68#endif 69 70#ifdef FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE 71 case FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE: 72#endif 73 74 case FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED: 75 case FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED: 76 case FREERDP_ERROR_SERVER_FRESH_CREDENTIALS_REQUIRED: 77 *status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; 78 *message = "Credentials expired."; 79 break; 80 81 /* 82 * Security negotiation failed (the server is refusing the connection 83 * because the security negotiation process failed) 84 */ 85 86 case FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED: 87 *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; 88 *message = "Security negotiation failed (wrong security type?)"; 89 break; 90 91 /* 92 * General access denied/revoked (regardless of any credentials 93 * provided, the server is denying the requested access by this 94 * account) 95 */ 96 97#ifdef FREERDP_ERROR_CONNECT_ACCESS_DENIED 98 case FREERDP_ERROR_CONNECT_ACCESS_DENIED: 99#endif 100 101#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED 102 case FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED: 103#endif 104 105#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT 106 case FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT: 107#endif 108 109#ifdef FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION 110 case FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION: 111#endif 112 113#ifdef FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED 114 case FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED: 115#endif 116 117 case FREERDP_ERROR_CONNECT_CLIENT_REVOKED: 118 case FREERDP_ERROR_INSUFFICIENT_PRIVILEGES: 119 case FREERDP_ERROR_SERVER_DENIED_CONNECTION: 120 case FREERDP_ERROR_SERVER_INSUFFICIENT_PRIVILEGES: 121 *status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; 122 *message = "Access denied by server (account locked/disabled?)"; 123 break; 124 125 /* 126 * General authentication failure (no credentials provided or wrong 127 * credentials provided) 128 */ 129 130#ifdef FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS 131 case FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS: 132#endif 133 134#ifdef FREERDP_ERROR_CONNECT_LOGON_FAILURE 135 case FREERDP_ERROR_CONNECT_LOGON_FAILURE: 136#endif 137 138#ifdef FREERDP_ERROR_CONNECT_WRONG_PASSWORD 139 case FREERDP_ERROR_CONNECT_WRONG_PASSWORD: 140#endif 141 142 case FREERDP_ERROR_AUTHENTICATION_FAILED: 143 *status = GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED; 144 *message = "Authentication failure (invalid credentials?)"; 145 break; 146 147 /* 148 * SSL/TLS connection failed (the server's certificate is not trusted) 149 */ 150 151 case FREERDP_ERROR_TLS_CONNECT_FAILED: 152 *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; 153 *message = "SSL/TLS connection failed (untrusted/self-signed certificate?)"; 154 break; 155 156 /* 157 * DNS lookup failed (hostname resolution failed or invalid IP address) 158 */ 159 160 case FREERDP_ERROR_DNS_ERROR: 161 case FREERDP_ERROR_DNS_NAME_NOT_FOUND: 162 *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; 163 *message = "DNS lookup failed (incorrect hostname?)"; 164 break; 165 166 /* 167 * Connection refused (the server is outright refusing to handle the 168 * inbound connection, typically due to the client requesting a 169 * security type that is not allowed) 170 */ 171 172 case FREERDP_ERROR_CONNECT_TRANSPORT_FAILED: 173 *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; 174 *message = "Server refused connection (wrong security type?)"; 175 break; 176 177 /* 178 * Connection failed (the network connection to the server did not 179 * succeed) 180 */ 181 182 case FREERDP_ERROR_CONNECT_CANCELLED: 183 case FREERDP_ERROR_CONNECT_FAILED: 184 case FREERDP_ERROR_CONNECT_KDC_UNREACHABLE: 185 case FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR: 186 *status = GUAC_PROTOCOL_STATUS_UPSTREAM_NOT_FOUND; 187 *message = "Connection failed (server unreachable?)"; 188 break; 189 190 /* 191 * All other (unknown) errors 192 */ 193 default: 194 *status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; 195 *message = "Upstream error."; 196 197 } 198 199} 200 201void guac_rdp_client_abort(guac_client* client, freerdp* rdp_inst) { 202 203 /* 204 * NOTE: The RDP status codes translated here are documented within 205 * [MS-RDPBCGR], section 2.2.5.1.1: "Set Error Info PDU Data", in the 206 * description of the "errorInfo" field. 207 * 208 * https://msdn.microsoft.com/en-us/library/cc240544.aspx 209 */ 210 211 guac_protocol_status status; 212 const char* message; 213 214 /* Read disconnect reason code from connection */ 215 int error_info = freerdp_error_info(rdp_inst); 216 217 /* Translate reason code into Guacamole protocol status */ 218 switch (error_info) { 219 220 /* Possibly-normal disconnect, depending on freerdp_get_last_error() */ 221 case 0x0: /* ERRINFO_SUCCESS */ 222 guac_rdp_translate_last_error(rdp_inst, &status, &message); 223 break; 224 225 /* Forced disconnect (possibly by admin) */ 226 case 0x1: /* ERRINFO_RPC_INITIATED_DISCONNECT */ 227 status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; 228 message = "Forcibly disconnected."; 229 break; 230 231 /* The user was logged off (possibly by admin) */ 232 case 0x2: /* ERRINFO_RPC_INITIATED_LOGOFF */ 233 status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; 234 message = "Logged off."; 235 break; 236 237 /* The user was idle long enough that the RDP server disconnected */ 238 case 0x3: /* ERRINFO_IDLE_TIMEOUT */ 239 status = GUAC_PROTOCOL_STATUS_SESSION_TIMEOUT; 240 message = "Idle session time limit exceeded."; 241 break; 242 243 /* The user's session has been active for too long */ 244 case 0x4: /* ERRINFO_LOGON_TIMEOUT */ 245 status = GUAC_PROTOCOL_STATUS_SESSION_CLOSED; 246 message = "Active session time limit exceeded."; 247 break; 248 249 /* Another user logged on, disconnecting this user */ 250 case 0x5: /* ERRINFO_DISCONNECTED_BY_OTHER_CONNECTION */ 251 status = GUAC_PROTOCOL_STATUS_SESSION_CONFLICT; 252 message = "Disconnected by other connection."; 253 break; 254 255 /* The RDP server is refusing to service the connection */ 256 case 0x6: /* ERRINFO_OUT_OF_MEMORY */ 257 case 0x7: /* ERRINFO_SERVER_DENIED_CONNECTION */ 258 status = GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE; 259 message = "Server refused connection."; 260 break; 261 262 /* The user does not have permission to connect */ 263 case 0x9: /* ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES */ 264 status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; 265 message = "Insufficient privileges."; 266 break; 267 268 /* The user's credentials have expired */ 269 case 0xA: /* ERRINFO_SERVER_FRESH_CREDENTIALS_REQUIRED */ 270 status = GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; 271 message = "Credentials expired."; 272 break; 273 274 /* The user manually disconnected using an administrative tool within 275 * the session */ 276 case 0xB: /* ERRINFO_RPC_INITIATED_DISCONNECT_BYUSER */ 277 status = GUAC_PROTOCOL_STATUS_SUCCESS; 278 message = "Manually disconnected."; 279 break; 280 281 /* The user manually logged off */ 282 case 0xC: /* ERRINFO_LOGOFF_BY_USER */ 283 status = GUAC_PROTOCOL_STATUS_SUCCESS; 284 message = "Manually logged off."; 285 break; 286 287 /* Unimplemented/unknown disconnect reason code */ 288 default: 289 status = GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; 290 message = "Upstream error."; 291 292 } 293 294 /* Send error code if an error occurred */ 295 if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { 296 guac_protocol_send_error(client->socket, message, status); 297 guac_socket_flush(client->socket); 298 } 299 300 /* Log human-readable description of disconnect at info level */ 301 guac_client_log(client, GUAC_LOG_INFO, "RDP server closed/refused " 302 "connection: %s", message); 303 304 /* Log internal disconnect reason code at debug level */ 305 if (error_info) 306 guac_client_log(client, GUAC_LOG_DEBUG, "Disconnect reason " 307 "code: 0x%X.", error_info); 308 309 /* Abort connection */ 310 guac_client_stop(client); 311 312} 313