0db849fc5c
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.
511 lines
13 KiB
C
511 lines
13 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2004-2013
|
|
*
|
|
* 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 sesrun.c
|
|
* @brief An utility to start a session
|
|
* @author Jay Sorg, Simone Fedele
|
|
*
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
|
|
#include "parse.h"
|
|
#include "trans.h"
|
|
#include "os_calls.h"
|
|
#include "config.h"
|
|
#include "log.h"
|
|
#include "string_calls.h"
|
|
#include "guid.h"
|
|
|
|
#include "tools_common.h"
|
|
|
|
#if !defined(PACKAGE_VERSION)
|
|
#define PACKAGE_VERSION "???"
|
|
#endif
|
|
|
|
#ifndef MAX_PASSWORD_LEN
|
|
# define MAX_PASSWORD_LEN 512
|
|
#endif
|
|
|
|
#ifndef DEFAULT_WIDTH
|
|
# define DEFAULT_WIDTH 1280
|
|
#endif
|
|
|
|
#ifndef DEFAULT_HEIGHT
|
|
# define DEFAULT_HEIGHT 1024
|
|
#endif
|
|
|
|
/* Default setting used by Windows 10 mstsc.exe */
|
|
#ifndef DEFAULT_BPP
|
|
# define DEFAULT_BPP 32
|
|
#endif
|
|
|
|
#ifndef DEFAULT_TYPE
|
|
# define DEFAULT_TYPE "Xorg"
|
|
#endif
|
|
|
|
/**
|
|
* Maps session type strings session type codes
|
|
*/
|
|
static struct
|
|
{
|
|
const char *name;
|
|
enum scp_session_type type;
|
|
} type_map[] =
|
|
{
|
|
{ "Xvnc", SCP_SESSION_TYPE_XVNC},
|
|
{ "X11rdp", SCP_SESSION_TYPE_XRDP},
|
|
{ "Xorg", SCP_SESSION_TYPE_XORG},
|
|
{ NULL, (enum scp_session_type) - 1}
|
|
};
|
|
|
|
/**
|
|
* Parameters needed for a session
|
|
*/
|
|
struct session_params
|
|
{
|
|
int width;
|
|
int height;
|
|
int bpp;
|
|
enum scp_session_type session_type;
|
|
|
|
const char *directory;
|
|
const char *shell;
|
|
const char *connection_description;
|
|
|
|
const char *username;
|
|
char password[MAX_PASSWORD_LEN + 1];
|
|
};
|
|
|
|
/**************************************************************************//**
|
|
* Maps a string to a session type value
|
|
*
|
|
* @param string session type string
|
|
* @param[out] value session type value
|
|
* @return 0 for success or != 0 if not found
|
|
*/
|
|
static
|
|
int string_to_session_type(const char *t, enum scp_session_type *value)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0 ; type_map[i].name != NULL; ++i)
|
|
{
|
|
if (g_strcasecmp(type_map[i].name, t) == 0)
|
|
{
|
|
*value = type_map[i].type;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Returns a list of supported session types
|
|
*
|
|
* Caller supplies a buffer. Buffer handling and buffer overflow detection are
|
|
* the same as snprint()
|
|
*
|
|
* @param buff area for result
|
|
* @param bufflen Size of result
|
|
* @return number of characters for the output string
|
|
*/
|
|
static
|
|
unsigned int get_session_type_list(char *buff, unsigned int bufflen)
|
|
{
|
|
unsigned int i;
|
|
unsigned int ret = 0;
|
|
const char *sep = "";
|
|
|
|
for (i = 0 ; type_map[i].name != NULL; ++i)
|
|
{
|
|
if (ret < bufflen)
|
|
{
|
|
ret += g_snprintf(buff + ret, bufflen - ret,
|
|
"%s%s", sep, type_map[i].name);
|
|
sep = ", ";
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Prints a brief summary of options and defaults
|
|
*/
|
|
static void
|
|
usage(void)
|
|
{
|
|
char sesstype_list[64];
|
|
|
|
(void)get_session_type_list(sesstype_list, sizeof(sesstype_list));
|
|
|
|
g_printf("xrdp session starter v" PACKAGE_VERSION "\n");
|
|
g_printf("\nusage:\n");
|
|
g_printf("sesrun [options] username\n\n");
|
|
g_printf("options:\n");
|
|
g_printf(" -g <geometry> Default:%dx%d\n",
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
g_printf(" -b <bits-per-pixel> Default:%d\n", DEFAULT_BPP);
|
|
g_printf(" -t <type> Default:%s\n", DEFAULT_TYPE);
|
|
g_printf(" -D <directory> Default: $HOME\n"
|
|
" -S <shell> Default: Defined window manager\n"
|
|
" -p <password> TESTING ONLY - DO NOT USE IN PRODUCTION\n"
|
|
" -F <file-descriptor> Read password from this file descriptor\n"
|
|
" -c <sesman_ini> Alternative sesman.ini file\n");
|
|
g_printf("Supported types are %s\n",
|
|
sesstype_list);
|
|
g_printf("Password is prompted if -p or -F are not specified\n");
|
|
}
|
|
|
|
|
|
/**************************************************************************//**
|
|
* Parses a string <width>x<height>
|
|
*
|
|
* @param geom_str Input string
|
|
* @param sp Session parameter structure for resulting width and height
|
|
* @return !=0 for success
|
|
*/
|
|
static int
|
|
parse_geometry_string(const char *geom_str, struct session_params *sp)
|
|
{
|
|
int result = 0;
|
|
unsigned int sep_count = 0; /* Count of 'x' separators */
|
|
unsigned int other_count = 0; /* Count of non-digits and non separators */
|
|
const char *sepp = NULL; /* Pointer to the 'x' */
|
|
const char *p = geom_str;
|
|
|
|
while (*p != '\0')
|
|
{
|
|
if (!isdigit(*p))
|
|
{
|
|
if (*p == 'x' || *p == 'X')
|
|
{
|
|
++sep_count;
|
|
sepp = p;
|
|
}
|
|
else
|
|
{
|
|
++other_count;
|
|
}
|
|
}
|
|
++p;
|
|
}
|
|
|
|
if (sep_count != 1 || other_count > 0 ||
|
|
sepp == geom_str || /* Separator at start of string */
|
|
sepp == (p - 1) ) /* Separator at end of string */
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Invalid geometry string '%s'", geom_str);
|
|
}
|
|
else
|
|
{
|
|
sp->width = atoi(geom_str);
|
|
sp->height = atoi(sepp + 1);
|
|
result = 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/**************************************************************************//**
|
|
* Read a password from a file descriptor
|
|
*
|
|
* @param fd_str string representing file descriptor
|
|
* @param sp Session parameter structure for resulting password
|
|
* @return !=0 for success
|
|
*/
|
|
static int
|
|
read_password_from_fd(const char *fd_str, struct session_params *sp)
|
|
{
|
|
int result = 0;
|
|
int s = g_file_read(atoi(fd_str), sp->password, sizeof (sp->password) - 1);
|
|
if (s < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't read password from fd %s - %s",
|
|
fd_str, g_get_strerror());
|
|
sp->password[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
sp->password[s] = '\0';
|
|
if (s > 0 && sp->password[s - 1] == '\n')
|
|
{
|
|
sp->password[s - 1] = '\0';
|
|
}
|
|
result = 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Parses the program args
|
|
*
|
|
* @param argc Passed to main
|
|
* @param @argv Passed to main
|
|
* @param sp Session parameter structure for resulting values
|
|
* @param sesman_ini Pointer to an alternative config file if one is specified
|
|
* @return !=0 for success
|
|
*/
|
|
static int
|
|
parse_program_args(int argc, char *argv[], struct session_params *sp,
|
|
const char **sesman_ini)
|
|
{
|
|
int params_ok = 1;
|
|
int opt;
|
|
bool_t password_set = 0;
|
|
|
|
sp->width = DEFAULT_WIDTH;
|
|
sp->height = DEFAULT_HEIGHT;
|
|
sp->bpp = DEFAULT_BPP;
|
|
(void)string_to_session_type(DEFAULT_TYPE, &sp->session_type);
|
|
|
|
sp->directory = "";
|
|
sp->shell = "";
|
|
sp->connection_description = "";
|
|
|
|
sp->username = NULL;
|
|
sp->password[0] = '\0';
|
|
|
|
while ((opt = getopt(argc, argv, "g:b:s:t:D:S:p:F:c:")) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'g':
|
|
if (!parse_geometry_string(optarg, sp))
|
|
{
|
|
params_ok = 0;
|
|
}
|
|
break;
|
|
|
|
case 'b':
|
|
sp->bpp = atoi(optarg);
|
|
break;
|
|
|
|
case 't':
|
|
if (string_to_session_type(optarg, &sp->session_type) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Unrecognised session type '%s'",
|
|
optarg);
|
|
params_ok = 0;
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
sp->directory = optarg;
|
|
break;
|
|
|
|
case 'S':
|
|
sp->shell = optarg;
|
|
break;
|
|
|
|
case 'p':
|
|
if (password_set)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Ignoring option '%c' - password already set ",
|
|
(char)opt);
|
|
}
|
|
else
|
|
{
|
|
g_strncpy(sp->password, optarg, sizeof(sp->password) - 1);
|
|
password_set = 1;
|
|
}
|
|
break;
|
|
|
|
case 'F':
|
|
if (password_set)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Ignoring option '%c' - password already set ",
|
|
(char)opt);
|
|
}
|
|
else
|
|
{
|
|
if (read_password_from_fd(optarg, sp))
|
|
{
|
|
password_set = 1;
|
|
}
|
|
else
|
|
{
|
|
params_ok = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
*sesman_ini = optarg;
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Unrecognised switch '%c'", (char)opt);
|
|
params_ok = 0;
|
|
}
|
|
}
|
|
|
|
if (argc <= optind)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "No user name speciified");
|
|
params_ok = 0;
|
|
}
|
|
else if ((argc - optind) > 1)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Unexpected arguments after username");
|
|
params_ok = 0;
|
|
}
|
|
else
|
|
{
|
|
sp->username = argv[optind];
|
|
}
|
|
|
|
if (params_ok && !password_set)
|
|
{
|
|
const char *p = getpass("Password: ");
|
|
if (p != NULL)
|
|
{
|
|
g_strcpy(sp->password, p);
|
|
}
|
|
}
|
|
|
|
return params_ok;
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Sends an SCP create session request
|
|
*
|
|
* @param t SCP connection
|
|
* @param sp Data for request
|
|
*/
|
|
static int
|
|
send_create_session_request(struct trans *t, const struct session_params *sp)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"width:%d height:%d bpp:%d code:%d\n"
|
|
"directory:\"%s\"\n"
|
|
"shell:\"%s\" connection_description:\"%s\"",
|
|
sp->width, sp->height, sp->bpp, sp->session_type,
|
|
sp->directory,
|
|
sp->shell, sp->connection_description);
|
|
/* Only log the password in development builds */
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "password:\"%s\"", sp->password);
|
|
|
|
return scp_send_create_session_request(
|
|
t, sp->username, sp->password, sp->session_type,
|
|
sp->width, sp->height, sp->bpp, sp->shell, sp->directory,
|
|
sp->connection_description);
|
|
}
|
|
|
|
/**************************************************************************//**
|
|
* Receives an SCP create session response
|
|
*
|
|
* @param t SCP transport to receive reply on
|
|
* @return 0 for success
|
|
*/
|
|
static int
|
|
handle_create_session_response(struct trans *t)
|
|
{
|
|
int auth_result;
|
|
int display;
|
|
struct guid guid;
|
|
|
|
int rv = wait_for_sesman_reply(t, E_SCP_CREATE_SESSION_RESPONSE);
|
|
if (rv == 0)
|
|
{
|
|
rv = scp_get_create_session_response(t, &auth_result,
|
|
&display, &guid);
|
|
|
|
if (rv == 0)
|
|
{
|
|
if (auth_result != 0)
|
|
{
|
|
g_printf("Connection denied (authentication error)\n");
|
|
}
|
|
else
|
|
{
|
|
char guid_str[GUID_STR_SIZE];
|
|
g_printf("ok display=:%d GUID=%s\n",
|
|
display,
|
|
guid_to_str(&guid, guid_str));
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *sesman_ini = XRDP_CFG_PATH "/sesman.ini";
|
|
struct config_sesman *cfg = NULL;
|
|
|
|
struct trans *t = NULL;
|
|
struct session_params sp;
|
|
|
|
struct log_config *logging;
|
|
int rv = 1;
|
|
|
|
logging = log_config_init_for_console(LOG_LEVEL_WARNING,
|
|
g_getenv("SESRUN_LOG_LEVEL"));
|
|
log_start_from_param(logging);
|
|
log_config_free(logging);
|
|
|
|
if (!parse_program_args(argc, argv, &sp, &sesman_ini))
|
|
{
|
|
usage();
|
|
}
|
|
else if ((cfg = config_read(sesman_ini)) == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "error reading config file %s : %s",
|
|
sesman_ini, g_get_strerror());
|
|
}
|
|
else if (!(t = scp_connect(cfg->listen_port, NULL)))
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "connect error - %s", g_get_strerror());
|
|
}
|
|
else
|
|
{
|
|
rv = send_create_session_request(t, &sp);
|
|
if (rv != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Error sending create session to sesman");
|
|
}
|
|
else
|
|
{
|
|
rv = handle_create_session_response(t);
|
|
}
|
|
trans_delete(t);
|
|
}
|
|
|
|
g_memset(sp.password, '\0', sizeof(sp.password));
|
|
config_free(cfg);
|
|
log_end();
|
|
|
|
return rv;
|
|
}
|