Merge pull request #1741 from matt335672/sesrun-improvements

sesrun improvements and doc fixes
This commit is contained in:
metalefty 2020-12-21 13:45:57 +09:00 committed by GitHub
commit 835536b406
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 637 additions and 132 deletions

View File

@ -572,7 +572,7 @@ internal_log_location_overrides_level(const char *function_name,
*/
struct log_config *
log_config_init_for_console(enum logLevels lvl)
log_config_init_for_console(enum logLevels lvl, const char *override_name)
{
struct log_config *config = internalInitAndAllocStruct();
@ -580,7 +580,14 @@ log_config_init_for_console(enum logLevels lvl)
{
config->program_name = "<null>";
config->enable_console = 1;
config->console_level = lvl;
if (override_name != NULL && override_name[0] != '\0')
{
config->console_level = internal_log_text2level(override_name);
}
else
{
config->console_level = lvl;
}
config->dump_on_start = 0; /* Don't need dump for console only */
}
return config;

View File

@ -275,9 +275,16 @@ log_start_from_param(const struct log_config *src_log_config);
*
* The config can be customised by the caller before calling
* log_start_from_param()
*
* @param Default log level
* @param Log level name, or NULL. This can be used to provide an
* override to the default log level, by environment variable or
* argument.
*
* @return pointer to struct log_config.
*/
struct log_config*
log_config_init_for_console(enum logLevels lvl);
log_config_init_for_console(enum logLevels lvl, const char *override_name);
/**
* Read configuration from a file and store the values in the returned

View File

@ -1,42 +1,80 @@
.TH "xrdp\-sesrun" "8" "@PACKAGE_VERSION@" "xrdp team" ""
.SH "NAME"
xrdp\-sesrun \- \fBxrdp-sesman\fR(8) session launcher
\fBxrdp\-sesrun\fR \- \fBxrdp\-sesman\fR(8) session launcher
.SH "SYNTAX"
.B xrdp\-sesrun
.I [ -C /path/to/sesman.ini ] server username password width height bpp code
.I [ options ] username
.SH "DESCRIPTION"
\fBxrdp\-sesrun\fR starts a session using \fBxrdp\-sesman\fR(8).
.br
This is a tool useful for testing, it simply behaves like xrdp when some user logs in a new session and authenticates, thus starting a new session.
This is a tool useful for testing, it simply behaves like xrdp when some
user logs in a new session and authenticates, thus starting a new session.
Default values for the options are set at compile-time. Run the utility without
a username to see what the defaults are for your installation.
The utility prompts for a password if neither \fB-p\fR or \fB-F\fR is used.
.SH "OPTIONS"
.TP
.I \-\-config
(Optional) Specify a path to a different \fIsesman.ini\fR file.
.B -g <width>x<height>
Set session geometry.
.br
Note that most configurations will resize the session on connection, so this
option may not do what you expect.
.TP
.B -b <bits-per-pixel>
Set session bits-per-pixel (colour depth). Some session types (i.e. Xorg)
will ignore this setting.
.TP
.B -s <server>
Server on which sesman is running (probably 'localhost').
.br
Use of this option is discouraged as it will be removed in the future.
.TP
.B -t <session-type>
Session type - one of Xorg, Xvnc or X11rdp. Alternatively, for testing
only, use the numeric session code.
.TP
.B -D <directory>
Directory to run the new session in. Defaults to $HOME for the specified user.
.TP
.B -S <shell>
Specify an alternate shell to run, instead of the default window manager.
.TP
.B -p <password>
Password for user. USE FOR TESTING ONLY - the password will be visible
in the output of the \fBps\fR command.
.TP
.B -F <file-descriptor>
Specify a file descriptor (normally 0) to read the password in from. This
is a secure way to pass the password in to the utility.
.TP
.B -c <sesman-ini>
Specify a different sesman.ini file. This file is used to find out how to
connect to \fBxrdp\-sesman\fR.
.SH "ENVIRONMENT"
.TP
.I server
Server on which sesman is running
.I SESRUN_LOG_LEVEL
Override the default logging level. One of "error", "warn", "info",
"debug", "trace" or a number 1-5.
.SH "EXAMPLES"
.TP
.I username
user name of the session being started
.B
xrdp-sesrun -F 0 user1 <passwd.txt
Create a default session for user \fBuser1\fR with a password from
a file
.TP
.I password
user password
.TP
.I width
Screen width
.TP
.I height
Screen height
.TP
.I bpp
Session color depth
.TP
.I code
Session type (0 for Xvnc, 10 for X11RDP, 20 for Xorg)
.B
xrdp-sesrun -t Xvnc -S /usr/bin/xterm user1
Create an extremely minimal Xvnc session for user \fBuser1\fR. This
could be useful for debugging why the standard session is not starting
properly. Note you would need to install the \fBxterm\fR utility
first. The \fBgnome\-terminal\fR utility probably won't work here.
.SH "FILES"
@bindir@/xrdp\-sesman

View File

@ -471,7 +471,7 @@ config_read(const char *sesman_ini)
if ((cfg->sesman_ini = g_strdup(sesman_ini)) != NULL)
{
int fd;
if ((fd = g_file_open(cfg->sesman_ini)) != -1)
if ((fd = g_file_open_ex(cfg->sesman_ini, 1, 0, 0, 0)) != -1)
{
struct list *sec;
struct list *param_n;

View File

@ -62,7 +62,7 @@ int main(int argc, char **argv)
serv[0] = '\0';
port[0] = '\0';
logging = log_config_init_for_console(LOG_LEVEL_INFO);
logging = log_config_init_for_console(LOG_LEVEL_INFO, NULL);
log_start_from_param(logging);
log_config_free(logging);

View File

@ -28,139 +28,587 @@
#include <config_ac.h>
#endif
#include "sesman.h"
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include "parse.h"
#include "os_calls.h"
#include "config.h"
#include "log.h"
#include "tcp.h"
#if !defined(PACKAGE_VERSION)
#define PACKAGE_VERSION "???"
#endif
struct config_sesman *g_cfg; /* config.h */
#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_SERVER
# define DEFAULT_SERVER "localhost"
#endif
#ifndef DEFAULT_TYPE
# define DEFAULT_TYPE "Xorg"
#endif
/**
* Maps session type strings to internal code numbers
*/
static struct
{
const char *name;
int code;
} type_map[] =
{
{ "Xvnc", 0},
{ "X11rdp", 10},
{ "Xorg", 20},
{ NULL, -1}
};
/**
* Parameters needed for a session
*/
struct session_params
{
int width;
int height;
int bpp;
int session_code;
const char *server;
const char *domain; /* Currently unused by sesman */
const char *directory;
const char *shell;
const char *client_ip;
const char *username;
char password[MAX_PASSWORD_LEN + 1];
};
/**************************************************************************//**
* Maps a string to a session code
*
* @param t session type
* @return session code, or -1 if not found
*/
static
int get_session_type_code(const char *t)
{
unsigned int i;
for (i = 0 ; type_map[i].name != NULL; ++i)
{
if (g_strcasecmp(type_map[i].name, t) == 0)
{
return type_map[i].code;
}
}
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);
/* Don't encourage use of this one - we need to move to local sockets */
g_printf(" -s <server> Default:%s (Deprecated)\n",
DEFAULT_SERVER);
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 or use int for internal code\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;
sp->session_code = get_session_type_code(DEFAULT_TYPE);
sp->server = DEFAULT_SERVER;
sp->domain = "";
sp->directory = "";
sp->shell = "";
sp->client_ip = "";
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 's':
LOG(LOG_LEVEL_WARNING, "Using deprecated option '-s'");
sp->server = optarg;
break;
case 't':
if (isdigit(optarg[0]))
{
sp->session_code = atoi(optarg);
}
else
{
sp->session_code = get_session_type_code(optarg);
if (sp->session_code < 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;
}
/**************************************************************************//**
* Helper function for send_scpv0_auth_request()
*
* @param s Output string
* @param str String to write to s
*/
static void
out_string16(struct stream *s, const char *str)
{
int i = g_strlen(str);
out_uint16_be(s, i);
out_uint8a(s, str, i);
}
/**************************************************************************//**
* Sends an SCP V0 authorization request
*
* @param sck file descriptor to send request on
* @param sp Data for request
*
* @todo This code duplicates functionality in the XRDP function
* xrdp_mm_send_login(). When SCP is reworked, a common library
* function should be used
*/
static void
send_scpv0_auth_request(int sck, const struct session_params *sp)
{
struct stream *out_s;
LOG(LOG_LEVEL_DEBUG,
"width:%d height:%d bpp:%d code:%d\n"
"server:\"%s\" domain:\"%s\" directory:\"%s\"\n"
"shell:\"%s\" client_ip:\"%s\"",
sp->width, sp->height, sp->bpp, sp->session_code,
sp->server, sp->domain, sp->directory,
sp->shell, sp->client_ip);
/* Only log the password in development builds */
LOG_DEVEL(LOG_LEVEL_DEBUG, "password:\"%s\"", sp->password);
make_stream(out_s);
init_stream(out_s, 8192);
s_push_layer(out_s, channel_hdr, 8);
out_uint16_be(out_s, sp->session_code);
out_string16(out_s, sp->username);
out_string16(out_s, sp->password);
out_uint16_be(out_s, sp->width);
out_uint16_be(out_s, sp->height);
out_uint16_be(out_s, sp->bpp);
out_string16(out_s, sp->domain);
out_string16(out_s, sp->shell);
out_string16(out_s, sp->directory);
out_string16(out_s, sp->client_ip);
s_mark_end(out_s);
s_pop_layer(out_s, channel_hdr);
out_uint32_be(out_s, 0); /* version */
out_uint32_be(out_s, out_s->end - out_s->data); /* size */
tcp_force_send(sck, out_s->data, out_s->end - out_s->data);
free_stream(out_s);
}
/**************************************************************************//**
* Receives an SCP V0 authorization reply
*
* @param sck file descriptor to receive reply on
*
* @todo This code duplicates functionality in the XRDP function
* xrdp_mm_process_login_response(). When SCP is reworked, a
* common library function should be used
*/
static int
handle_scpv0_auth_reply(int sck)
{
int result = 1;
int packet_ok = 0;
struct stream *in_s;
make_stream(in_s);
init_stream(in_s, 8192);
if (tcp_force_recv(sck, in_s->data, 8) == 0)
{
int version;
int size;
int code;
int data;
int display;
in_uint32_be(in_s, version);
in_uint32_be(in_s, size);
if (version == 0 && size >= 14)
{
init_stream(in_s, 8192);
if (tcp_force_recv(sck, in_s->data, size - 8) == 0)
{
in_s->end = in_s->data + (size - 8);
in_uint16_be(in_s, code);
in_uint16_be(in_s, data);
in_uint16_be(in_s, display);
if (code == 3)
{
packet_ok = 1;
if (data == 0)
{
g_printf("Connection denied (authentication error)\n");
}
else
{
char guid[16];
char guid_str[64];
if (s_check_rem(in_s, 16) != 0)
{
in_uint8a(in_s, guid, 16);
g_bytes_to_hexstr(guid, 16, guid_str, 64);
}
else
{
g_strcpy(guid_str, "<none>");
}
g_printf("ok data=%d display=:%d GUID=%s\n",
(int)data, display, guid_str);
result = 0;
}
}
}
}
}
if (!packet_ok)
{
LOG(LOG_LEVEL_ERROR, "Corrupt reply packet");
}
free_stream(in_s);
return result;
}
/******************************************************************************/
int
main(int argc, char **argv)
{
const char *sesman_ini = XRDP_CFG_PATH "/sesman.ini";
struct config_sesman *cfg = NULL;
int sck = -1;
int code;
int i;
int size;
int version;
int width;
int height;
int bpp;
int display;
int session_code;
struct stream *in_s;
struct stream *out_s;
char *username;
char *password;
long data;
const char *sesman_ini;
char default_sesman_ini[256];
struct session_params sp;
struct log_config *logging;
int status = 1;
/* User specified a different config file? */
if (argc > 2 && (g_strcmp(argv[1], "-C") == 0))
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))
{
sesman_ini = argv[2];
argv += 2;
argc -= 2;
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
{
g_snprintf(default_sesman_ini, 255, "%s/sesman.ini", XRDP_CFG_PATH);
sesman_ini = default_sesman_ini;
}
if (argc != 8)
{
g_printf("xrdp session starter v" PACKAGE_VERSION "\n");
g_printf("\nusage:\n");
g_printf("xrdp-sesrun [-C /path/to/sesman.ini] <server> "
"<username> <password> "
"<width> <height> <bpp> <session_code>\n");
g_printf("session code 0 for Xvnc, 10 for X11RDP, 20 for Xorg\n");
}
else if ((g_cfg = config_read(sesman_ini)) == NULL)
{
g_printf("xrdp-sesrun: error reading config %s. quitting.\n",
sesman_ini);
}
else
{
username = argv[2];
password = argv[3];
width = g_atoi(argv[4]);
height = g_atoi(argv[5]);
bpp = g_atoi(argv[6]);
session_code = g_atoi(argv[7]);
make_stream(in_s);
init_stream(in_s, 8192);
make_stream(out_s);
init_stream(out_s, 8192);
sck = g_tcp_socket();
if (sck < 0)
{
g_printf("socket error\n");
LOG(LOG_LEVEL_ERROR, "socket error - %s", g_get_strerror());
}
else if (g_tcp_connect(sck, argv[1], g_cfg->listen_port) != 0)
else if (g_tcp_connect(sck, sp.server, cfg->listen_port) != 0)
{
g_printf("connect error\n");
LOG(LOG_LEVEL_ERROR, "connect error - %s", g_get_strerror());
}
else
{
s_push_layer(out_s, channel_hdr, 8);
out_uint16_be(out_s, session_code); /* code */
i = g_strlen(username);
out_uint16_be(out_s, i);
out_uint8a(out_s, username, i);
i = g_strlen(password);
out_uint16_be(out_s, i);
out_uint8a(out_s, password, i);
out_uint16_be(out_s, width);
out_uint16_be(out_s, height);
out_uint16_be(out_s, bpp);
s_mark_end(out_s);
s_pop_layer(out_s, channel_hdr);
out_uint32_be(out_s, 0); /* version */
out_uint32_be(out_s, out_s->end - out_s->data); /* size */
tcp_force_send(sck, out_s->data, out_s->end - out_s->data);
if (tcp_force_recv(sck, in_s->data, 8) == 0)
{
in_uint32_be(in_s, version);
in_uint32_be(in_s, size);
init_stream(in_s, 8192);
if (tcp_force_recv(sck, in_s->data, size - 8) == 0)
{
if (version == 0)
{
in_uint16_be(in_s, code);
if (code == 3)
{
in_uint16_be(in_s, data);
in_uint16_be(in_s, display);
g_printf("ok %d display %d\n", (int)data, display);
status = 0;
}
}
}
}
send_scpv0_auth_request(sck, &sp);
status = handle_scpv0_auth_reply(sck);
}
if (sck >= 0)
{
g_tcp_close(sck);
}
free_stream(in_s);
free_stream(out_s);
}
config_free(g_cfg);
if (sck >= 0)
{
g_tcp_close(sck);
}
g_memset(sp.password, '\0', sizeof(sp.password));
config_free(cfg);
log_end();
return status;
}

View File

@ -161,6 +161,9 @@ xrdp_mm_delete(struct xrdp_mm *self)
/*****************************************************************************/
/* Send login information to sesman */
/* FIXME : This code duplicates functionality in the sesman tools sesrun.c.
* When SCP is reworked, a common library function should be used */
static int
xrdp_mm_send_login(struct xrdp_mm *self)
{
@ -1553,6 +1556,8 @@ xrdp_mm_update_allowed_channels(struct xrdp_mm *self)
}
/*****************************************************************************/
/* FIXME : This code duplicates functionality in the sesman tools sesrun.c.
* When SCP is reworked, a common library function should be used */
static int
xrdp_mm_process_login_response(struct xrdp_mm *self, struct stream *s)
{