mirror of https://github.com/neutrinolabs/xrdp
660 lines
18 KiB
C
660 lines
18 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 <ctype.h>
|
|
|
|
#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_SET_PEERNAME_REQUEST) ? "SCP_SET_PEERNAME_REQUEST" :
|
|
|
|
(n == E_SCP_SYS_LOGIN_REQUEST) ? "SCP_SYS_LOGIN_REQUEST" :
|
|
(n == E_SCP_UDS_LOGIN_REQUEST) ? "SCP_UDS_LOGIN_REQUEST" :
|
|
(n == E_SCP_LOGIN_RESPONSE) ? "SCP_LOGIN_RESPONSE" :
|
|
|
|
(n == E_SCP_LOGOUT_REQUEST) ? "SCP_LOGOUT_REQUEST" :
|
|
|
|
(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" :
|
|
|
|
(n == E_SCP_CLOSE_CONNECTION_REQUEST) ? "SCP_CLOSE_CONNECTION_REQUEST" :
|
|
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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* Helper function returning 1 if the passed-in string is an integer >= 0
|
|
*/
|
|
static int is_positive_int(const char *s)
|
|
{
|
|
for ( ; *s != '\0' ; ++s)
|
|
{
|
|
if (!isdigit(*s))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
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 (is_positive_int(port))
|
|
{
|
|
/* 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, XRDP_SOCKET_ROOT_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,
|
|
const char *peername,
|
|
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 (scp_init_trans(t) != 0)
|
|
{
|
|
trans_delete(t);
|
|
t = NULL;
|
|
}
|
|
else if (scp_send_set_peername_request(t, peername) != 0)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
struct trans *
|
|
scp_init_trans_from_fd(int fd, int trans_type, int (*term_func)(void))
|
|
{
|
|
struct trans *result;
|
|
if ((result = trans_create(TRANS_MODE_UNIX, 128, 128)) == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't create SCP transport [%s]",
|
|
g_get_strerror());
|
|
}
|
|
else
|
|
{
|
|
result->sck = fd;
|
|
result->type1 = trans_type;
|
|
result->status = TRANS_STATUS_UP;
|
|
result->is_term = term_func;
|
|
|
|
// Make sure child processes don't inherit our FD
|
|
(void)g_file_set_cloexec(result->sck, 1);
|
|
|
|
if (scp_init_trans(result) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "scp_init_trans() call failed");
|
|
trans_delete(result);
|
|
result = NULL;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
enum scp_msg_code
|
|
scp_msg_in_get_msgno(const struct trans *trans)
|
|
{
|
|
return (enum scp_msg_code)libipm_msg_in_get_msgno(trans);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
scp_msg_in_reset(struct trans *trans)
|
|
{
|
|
libipm_msg_in_reset(trans);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
int
|
|
scp_send_set_peername_request(struct trans *trans,
|
|
const char *peername)
|
|
{
|
|
return libipm_msg_out_simple_send(
|
|
trans,
|
|
(int)E_SCP_SET_PEERNAME_REQUEST,
|
|
"s",
|
|
peername);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_get_set_peername_request(struct trans *trans,
|
|
const char **peername)
|
|
{
|
|
return libipm_msg_in_parse( trans, "s", peername);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
int
|
|
scp_send_uds_login_request(struct trans *trans)
|
|
{
|
|
return libipm_msg_out_simple_send(trans,
|
|
(int)E_SCP_UDS_LOGIN_REQUEST,
|
|
NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
int
|
|
scp_send_sys_login_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_SYS_LOGIN_REQUEST,
|
|
"sss",
|
|
username,
|
|
password,
|
|
ip_addr);
|
|
|
|
/* Wipe the output buffer to remove the password */
|
|
libipm_msg_out_erase(trans);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_get_sys_login_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_login_response(struct trans *trans,
|
|
enum scp_login_status login_result,
|
|
int server_closed,
|
|
int uid)
|
|
{
|
|
return libipm_msg_out_simple_send(
|
|
trans,
|
|
(int)E_SCP_LOGIN_RESPONSE,
|
|
"ibi",
|
|
login_result,
|
|
(server_closed != 0), /* Convert to 0/1 */
|
|
uid);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_get_login_response(struct trans *trans,
|
|
enum scp_login_status *login_result,
|
|
int *server_closed,
|
|
int *uid)
|
|
{
|
|
int32_t i_login_result = 0;
|
|
int32_t i_uid = 0;
|
|
int dummy;
|
|
|
|
/* User can pass in NULL for server_closed if they're trying an
|
|
* login method like UDS for which all fails are fatal. Likewise
|
|
* they may be uninterested in the uid */
|
|
if (server_closed == NULL)
|
|
{
|
|
server_closed = &dummy;
|
|
}
|
|
if (uid == NULL)
|
|
{
|
|
uid = &dummy;
|
|
}
|
|
|
|
int rv = libipm_msg_in_parse(trans, "ibi",
|
|
&i_login_result, server_closed, &i_uid);
|
|
if (rv == 0)
|
|
{
|
|
*login_result = (enum scp_login_status)i_login_result;
|
|
*uid = i_uid;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_send_logout_request(struct trans *trans)
|
|
{
|
|
return libipm_msg_out_simple_send( trans, (int)E_SCP_LOGOUT_REQUEST, NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_send_create_session_request(struct trans *trans,
|
|
enum scp_session_type type,
|
|
unsigned short width,
|
|
unsigned short height,
|
|
unsigned char bpp,
|
|
const char *shell,
|
|
const char *directory)
|
|
{
|
|
return libipm_msg_out_simple_send(
|
|
trans,
|
|
(int)E_SCP_CREATE_SESSION_REQUEST,
|
|
"yqqyss",
|
|
type,
|
|
width,
|
|
height,
|
|
bpp,
|
|
shell,
|
|
directory);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_get_create_session_request(struct trans *trans,
|
|
enum scp_session_type *type,
|
|
unsigned short *width,
|
|
unsigned short *height,
|
|
unsigned char *bpp,
|
|
const char **shell,
|
|
const char **directory)
|
|
{
|
|
/* Intermediate values */
|
|
uint8_t i_type;
|
|
uint16_t i_width;
|
|
uint16_t i_height;
|
|
uint8_t i_bpp;
|
|
|
|
int rv = libipm_msg_in_parse(
|
|
trans,
|
|
"yqqyss",
|
|
&i_type,
|
|
&i_width,
|
|
&i_height,
|
|
&i_bpp,
|
|
shell,
|
|
directory);
|
|
|
|
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,
|
|
enum scp_screate_status status,
|
|
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",
|
|
status,
|
|
display,
|
|
&guid_descriptor);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_get_create_session_response(struct trans *trans,
|
|
enum scp_screate_status *status,
|
|
int *display,
|
|
struct guid *guid)
|
|
{
|
|
/* Intermediate values */
|
|
int32_t i_status;
|
|
int32_t i_display;
|
|
|
|
const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) };
|
|
|
|
int rv = libipm_msg_in_parse(
|
|
trans,
|
|
"iiB",
|
|
&i_status,
|
|
&i_display,
|
|
&guid_descriptor);
|
|
if (rv == 0)
|
|
{
|
|
*status = (enum scp_screate_status)i_status;
|
|
*display = i_display;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_send_list_sessions_request(struct trans *trans)
|
|
{
|
|
return libipm_msg_out_simple_send(
|
|
trans,
|
|
(int)E_SCP_LIST_SESSIONS_REQUEST,
|
|
NULL);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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,
|
|
"iiuyqqyxis",
|
|
status,
|
|
info->sid,
|
|
info->display,
|
|
info->type,
|
|
info->width,
|
|
info->height,
|
|
info->bpp,
|
|
info->start_time,
|
|
info->uid,
|
|
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;
|
|
int32_t i_uid;
|
|
char *i_start_ip_addr;
|
|
|
|
rv = libipm_msg_in_parse(
|
|
trans,
|
|
"iuyqqyxis",
|
|
&i_sid,
|
|
&i_display,
|
|
&i_type,
|
|
&i_width,
|
|
&i_height,
|
|
&i_bpp,
|
|
&i_start_time,
|
|
&i_uid,
|
|
&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_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->start_ip_addr =
|
|
(char *)p + sizeof(struct scp_session_info);
|
|
|
|
/* 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;
|
|
p->uid = i_uid;
|
|
g_strcpy(p->start_ip_addr, i_start_ip_addr);
|
|
}
|
|
}
|
|
}
|
|
*info = p;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
int
|
|
scp_send_close_connection_request(struct trans *trans)
|
|
{
|
|
return libipm_msg_out_simple_send(
|
|
trans,
|
|
(int)E_SCP_CLOSE_CONNECTION_REQUEST,
|
|
NULL);
|
|
}
|