/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors * * Licensed 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. */ /** * * @file libipm/scp.c * @brief scp definitions * @author Simone Fedele/ Matt Burt */ #if defined(HAVE_CONFIG_H) #include #endif #include "scp.h" #include "libipm.h" #include "guid.h" #include "trans.h" #include "os_calls.h" #include "string_calls.h" #include "xrdp_sockets.h" /*****************************************************************************/ static const char * msgno_to_str(unsigned short n) { return (n == E_SCP_GATEWAY_REQUEST) ? "SCP_GATEWAY_REQUEST" : (n == E_SCP_GATEWAY_RESPONSE) ? "SCP_GATEWAY_RESPONSE" : (n == E_SCP_CREATE_SESSION_REQUEST) ? "SCP_CREATE_SESSION_REQUEST" : (n == E_SCP_CREATE_SESSION_RESPONSE) ? "SCP_CREATE_SESSION_RESPONSE" : (n == E_SCP_LIST_SESSIONS_REQUEST) ? "SCP_LIST_SESSIONS_REQUEST" : (n == E_SCP_LIST_SESSIONS_RESPONSE) ? "SCP_LIST_SESSIONS_RESPONSE" : NULL; } /*****************************************************************************/ const char * scp_msgno_to_str(enum scp_msg_code n, char *buff, unsigned int buff_size) { const char *str = msgno_to_str((unsigned short)n); if (str == NULL) { g_snprintf(buff, buff_size, "[code #%d]", (int)n); } else { g_snprintf(buff, buff_size, "%s", str); } return buff; } /*****************************************************************************/ int scp_port_to_unix_domain_path(const char *port, char *buff, unsigned int bufflen) { /* GOTCHA: Changes to this logic should be mirrored in * scp_port_to_display_string() */ int result; /* Make sure we can safely de-reference 'port' */ if (port == NULL) { port = ""; } if (port[0] == '/') { result = g_snprintf(buff, bufflen, "%s", port); } else { const char *sep; if ((sep = g_strrchr(port, '/')) != NULL && sep != port) { /* We allow the user to specify an absolute path, but not * a relative one with embedded '/' characters */ LOG(LOG_LEVEL_WARNING, "Ignoring path elements of '%s'", port); port = sep + 1; } if (port[0] == '\0') { port = SCP_LISTEN_PORT_BASE_STR; } else if (g_strcmp(port, "3350") == 0) { /* Version v0.9.x and earlier of xrdp used a TCP port * number. If we come across this, we'll ignore it for * compatibility with old config files */ LOG(LOG_LEVEL_WARNING, "Ignoring obsolete SCP port value '%s'", port); port = SCP_LISTEN_PORT_BASE_STR; } result = g_snprintf(buff, bufflen, SESMAN_RUNTIME_PATH "/%s", port); } return result; } /*****************************************************************************/ int scp_port_to_display_string(const char *port, char *buff, unsigned int bufflen) { /* Make sure we can safely de-reference 'port' */ if (port == NULL) { port = ""; } /* Ignore any directories for the display */ const char *sep; if ((sep = g_strrchr(port, '/')) != NULL) { port = sep + 1; } /* Check for a default */ if (port[0] == '\0' || g_strcmp(port, "3350") == 0) { port = SCP_LISTEN_PORT_BASE_STR; } return g_snprintf(buff, bufflen, "%s", port); } /*****************************************************************************/ struct trans * scp_connect(const char *port, int (*term_func)(void)) { char sock_path[256]; struct trans *t; (void)scp_port_to_unix_domain_path(port, sock_path, sizeof(sock_path)); if ((t = trans_create(TRANS_MODE_UNIX, 128, 128)) != NULL) { t->is_term = term_func; if (trans_connect(t, NULL, sock_path, 3000) != 0) { trans_delete(t); t = NULL; } else if (libipm_init_trans(t, LIBIPM_FAC_SCP, msgno_to_str) != E_LI_SUCCESS) { trans_delete(t); t = NULL; } } return t; } /*****************************************************************************/ int scp_init_trans(struct trans *trans) { return libipm_init_trans(trans, LIBIPM_FAC_SCP, msgno_to_str); } /*****************************************************************************/ int scp_msg_in_check_available(struct trans *trans, int *available) { return libipm_msg_in_check_available(trans, available); } /*****************************************************************************/ int scp_msg_in_wait_available(struct trans *trans) { return libipm_msg_in_wait_available(trans); } /*****************************************************************************/ void scp_msg_in_reset(struct trans *trans) { libipm_msg_in_reset(trans); } /*****************************************************************************/ enum scp_msg_code scp_msg_in_start(struct trans *trans) { return (enum scp_msg_code)libipm_msg_in_start(trans); } /*****************************************************************************/ int scp_send_gateway_request(struct trans *trans, const char *username, const char *password, const char *ip_addr) { int rv; rv = libipm_msg_out_simple_send( trans, (int)E_SCP_GATEWAY_REQUEST, "sss", username, password, ip_addr); /* Wipe the output buffer to remove the password */ libipm_msg_out_erase(trans); return rv; } /*****************************************************************************/ int scp_get_gateway_request(struct trans *trans, const char **username, const char **password, const char **ip_addr) { /* Make sure the buffer is cleared after processing this message */ libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); return libipm_msg_in_parse(trans, "sss", username, password, ip_addr); } /*****************************************************************************/ int scp_send_gateway_response(struct trans *trans, int auth_result) { return libipm_msg_out_simple_send( trans, (int)E_SCP_GATEWAY_RESPONSE, "i", auth_result); } /*****************************************************************************/ int scp_get_gateway_response(struct trans *trans, int *auth_result) { int32_t i_auth_result = 0; int rv = libipm_msg_in_parse(trans, "i", &i_auth_result); if (rv == 0) { *auth_result = i_auth_result; } return rv; } /*****************************************************************************/ int scp_send_create_session_request(struct trans *trans, const char *username, const char *password, enum scp_session_type type, unsigned short width, unsigned short height, unsigned char bpp, const char *shell, const char *directory, const char *ip_addr) { int rv = libipm_msg_out_simple_send( trans, (int)E_SCP_CREATE_SESSION_REQUEST, "ssyqqysss", username, password, type, width, height, bpp, shell, directory, ip_addr); /* Wipe the output buffer to remove the password */ libipm_msg_out_erase(trans); return rv; } /*****************************************************************************/ int scp_get_create_session_request(struct trans *trans, const char **username, const char **password, enum scp_session_type *type, unsigned short *width, unsigned short *height, unsigned char *bpp, const char **shell, const char **directory, const char **ip_addr) { /* Intermediate values */ uint8_t i_type; uint16_t i_width; uint16_t i_height; uint8_t i_bpp; /* Make sure the buffer is cleared after processing this message */ libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); int rv = libipm_msg_in_parse( trans, "ssyqqysss", username, password, &i_type, &i_width, &i_height, &i_bpp, shell, directory, ip_addr); if (rv == 0) { *type = (enum scp_session_type)i_type; *width = i_width; *height = i_height; /* bpp is fixed for Xorg session types */ *bpp = (*type == SCP_SESSION_TYPE_XORG) ? 24 : i_bpp; } return rv; } /*****************************************************************************/ int scp_send_create_session_response(struct trans *trans, int auth_result, int display, const struct guid *guid) { struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; return libipm_msg_out_simple_send( trans, (int)E_SCP_CREATE_SESSION_RESPONSE, "iiB", auth_result, display, &guid_descriptor); } /*****************************************************************************/ int scp_get_create_session_response(struct trans *trans, int *auth_result, int *display, struct guid *guid) { /* Intermediate values */ int32_t i_auth_result; int32_t i_display; const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; int rv = libipm_msg_in_parse( trans, "iiB", &i_auth_result, &i_display, &guid_descriptor); if (rv == 0) { *auth_result = i_auth_result; *display = i_display; } return rv; } /*****************************************************************************/ int scp_send_list_sessions_request(struct trans *trans, const char *username, const char *password) { int rv; rv = libipm_msg_out_simple_send( trans, (int)E_SCP_LIST_SESSIONS_REQUEST, "ss", username, password); /* Wipe the output buffer to remove the password */ libipm_msg_out_erase(trans); return rv; } /*****************************************************************************/ int scp_get_list_sessions_request(struct trans *trans, const char **username, const char **password) { /* Make sure the buffer is cleared after processing this message */ libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); return libipm_msg_in_parse(trans, "ss", username, password); } /*****************************************************************************/ int scp_send_list_sessions_response( struct trans *trans, enum scp_list_sessions_status status, const struct scp_session_info *info) { int rv; if (status != E_SCP_LS_SESSION_INFO) { rv = libipm_msg_out_simple_send( trans, (int)E_SCP_LIST_SESSIONS_RESPONSE, "i", status); } else { rv = libipm_msg_out_simple_send( trans, (int)E_SCP_LIST_SESSIONS_RESPONSE, "iiuyqqyxss", status, info->sid, info->display, info->type, info->width, info->height, info->bpp, info->start_time, info->username, info->start_ip_addr); } return rv; } /*****************************************************************************/ int scp_get_list_sessions_response( struct trans *trans, enum scp_list_sessions_status *status, struct scp_session_info **info) { int32_t i_status; int rv; if (info == NULL) { LOG_DEVEL(LOG_LEVEL_ERROR, "Bad pointer in %s", __func__); rv = 1; } else if ((rv = libipm_msg_in_parse(trans, "i", &i_status)) == 0) { *status = (enum scp_list_sessions_status)i_status; struct scp_session_info *p = NULL; if (*status == E_SCP_LS_SESSION_INFO) { int32_t i_sid; uint32_t i_display; uint8_t i_type; uint16_t i_width; uint16_t i_height; uint8_t i_bpp; int64_t i_start_time; char *i_username; char *i_start_ip_addr; rv = libipm_msg_in_parse( trans, "iuyqqyxss", &i_sid, &i_display, &i_type, &i_width, &i_height, &i_bpp, &i_start_time, &i_username, &i_start_ip_addr); if (rv == 0) { /* Allocate a block of memory large enough for the * structure result, and the strings it contains */ unsigned int len = sizeof(struct scp_session_info) + g_strlen(i_username) + 1 + g_strlen(i_start_ip_addr) + 1; if ((p = (struct scp_session_info *)g_malloc(len, 1)) == NULL) { *status = E_SCP_LS_NO_MEMORY; } else { /* Set up the string pointers in the block to point * into the memory allocated after the block */ p->username = (char *)p + sizeof(struct scp_session_info); p->start_ip_addr = p->username + g_strlen(i_username) + 1; /* Copy the data over */ p->sid = i_sid; p->display = i_display; p->type = (enum scp_session_type)i_type; p->width = i_width; p->height = i_height; p->bpp = i_bpp; p->start_time = i_start_time; g_strcpy(p->username, i_username); g_strcpy(p->start_ip_addr, i_start_ip_addr); } } } *info = p; } return rv; }