xrdp/libipm/scp.c
matt335672 0db849fc5c Move SCP to a Unix Domain Socket
The TCP socket implementation of sesman has a number of limitations,
namely that it is affected by firewalls, and also that determining the
user on the other end requires a full authentication process.

The advantage of the TCP socket is that sesman and xrdp can be run on
separate machines. This is however not supported by the xorgxrdp
backend (shared memory), and is insecure, in that passwords are sent
in-the-clear, and the connection is susceptible to MitM attacks. This
architecture has been deprecated in release notes since xrdp v0.9.17,
and although it will continue to be supported in any further releases
in the x0.9.x series, it will not be supported in the next major
version.
2022-04-18 09:12:35 +01:00

567 lines
16 KiB
C

/**
* 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 <config_ac.h>
#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 *connection_description)
{
int rv;
rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_GATEWAY_REQUEST,
"sss",
username,
password,
connection_description);
/* 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 **connection_description)
{
/* 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,
connection_description);
}
/*****************************************************************************/
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 *connection_description)
{
int rv = libipm_msg_out_simple_send(
trans,
(int)E_SCP_CREATE_SESSION_REQUEST,
"ssyqqysss",
username,
password,
type,
width,
height,
bpp,
shell,
directory,
connection_description);
/* 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 **connection_description)
{
/* 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,
connection_description);
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->connection_description);
}
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_connection_description;
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_connection_description);
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_connection_description) + 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->connection_description =
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->connection_description,
i_connection_description);
}
}
}
*info = p;
}
return rv;
}