xrdp/sesman/tools/sesrun.c

570 lines
15 KiB
C
Raw Normal View History

/**
* xrdp: A Remote Desktop Protocol server.
*
2013-06-08 21:51:53 +04:00
* 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
2020-12-03 14:54:22 +03:00
#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"
#include "string_calls.h"
#include "guid.h"
#include "libscp_connection.h"
// cppcheck doesn't always set this macro to something in double-quotes
#if defined(__cppcheck__)
#undef PACKAGE_VERSION
#endif
#if !defined(PACKAGE_VERSION)
#define PACKAGE_VERSION "???"
#endif
2020-12-03 14:54:22 +03:00
#ifndef MAX_PASSWORD_LEN
# define MAX_PASSWORD_LEN 512
#endif
2020-12-03 14:54:22 +03:00
#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
{
2020-12-03 14:54:22 +03:00
const char *name;
int code;
2020-12-03 14:54:22 +03:00
} 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;
2020-12-03 14:54:22 +03:00
const char *server;
2020-12-03 14:54:22 +03:00
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)
{
2020-12-03 14:54:22 +03:00
if (g_strcasecmp(type_map[i].name, t) == 0)
{
return type_map[i].code;
}
2020-06-22 12:27:54 +03:00
}
2020-12-03 14:54:22 +03:00
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)
2020-06-22 12:27:54 +03:00
{
2020-12-03 14:54:22 +03:00
if (ret < bufflen)
{
ret += g_snprintf(buff + ret, bufflen - ret,
"%s%s", sep, type_map[i].name);
sep = ", ";
}
}
2020-12-03 14:54:22 +03:00
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')
{
2020-12-03 14:54:22 +03:00
if (!isdigit(*p))
{
if (*p == 'x' || *p == 'X')
{
++sep_count;
sepp = p;
}
else
{
++other_count;
}
}
++p;
}
2020-12-03 14:54:22 +03:00
if (sep_count != 1 || other_count > 0 ||
2021-05-08 19:58:11 +03:00
sepp == geom_str || /* Separator at start of string */
sepp == (p - 1) ) /* Separator at end of string */
2020-06-22 12:27:54 +03:00
{
2020-12-03 14:54:22 +03:00
LOG(LOG_LEVEL_ERROR, "Invalid geometry string '%s'", geom_str);
2020-06-22 12:27:54 +03:00
}
else
{
2020-12-03 14:54:22 +03:00
sp->width = atoi(geom_str);
sp->height = atoi(sepp + 1);
result = 1;
}
2020-12-03 14:54:22 +03:00
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')
{
2020-12-03 14:54:22 +03:00
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;
}
2020-12-03 14:54:22 +03:00
}
if (argc <= optind)
{
LOG(LOG_LEVEL_ERROR, "No user name specified");
2020-12-03 14:54:22 +03:00
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)
2020-06-22 12:27:54 +03:00
{
2020-12-03 14:54:22 +03:00
g_strcpy(sp->password, p);
2020-06-22 12:27:54 +03:00
}
2020-12-03 14:54:22 +03:00
}
return params_ok;
}
/**************************************************************************//**
* 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 enum SCP_CLIENT_STATES_E
send_scpv0_auth_request(struct trans *t, const struct session_params *sp)
2020-12-03 14:54:22 +03:00
{
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);
return scp_v0c_create_session_request(
t, sp->username, sp->password, sp->session_code, sp->width, sp->height,
sp->bpp, sp->domain, sp->shell, sp->directory, sp->client_ip);
2020-12-03 14:54:22 +03:00
}
/**************************************************************************//**
* Receives an SCP V0 authorization reply
*
* @param t SCP transport to receive reply on
* @return 0 for success
2020-12-03 14:54:22 +03:00
*/
static int
handle_scpv0_auth_reply(struct trans *t)
2020-12-03 14:54:22 +03:00
{
tbus wobj[1];
int ocnt = 0;
2020-12-03 14:54:22 +03:00
int rv = 1;
2020-12-03 14:54:22 +03:00
if (trans_get_wait_objs(t, wobj, &ocnt) != 0)
2020-12-03 14:54:22 +03:00
{
LOG(LOG_LEVEL_ERROR, "Can't get wait object for sesman transport");
}
else
{
while (t->status == TRANS_STATUS_UP)
{
g_obj_wait(wobj, ocnt, NULL, 0, -1);
if (trans_check_wait_objs(t) != 0)
{
LOG(LOG_LEVEL_ERROR, "sesman transport down");
break;
}
2020-12-03 14:54:22 +03:00
if (scp_v0c_reply_available(t))
{
struct scp_v0_reply_type msg;
enum SCP_CLIENT_STATES_E e = scp_v0c_get_reply(t, &msg);
if (e != SCP_CLIENT_STATE_OK)
{
LOG(LOG_LEVEL_ERROR,
"Error reading response from sesman [%s]",
scp_client_state_to_str(e));
}
else
{
if (msg.auth_result == 0)
2020-12-03 14:54:22 +03:00
{
g_printf("Connection denied (authentication error)\n");
}
else
{
char guid_str[GUID_STR_SIZE];
2020-12-03 14:54:22 +03:00
g_printf("ok data=%d display=:%d GUID=%s\n",
msg.auth_result,
msg.display,
guid_to_str(&msg.guid, guid_str));
}
rv = 0;
}
break;
}
}
2020-12-03 14:54:22 +03:00
}
return rv;
2020-12-03 14:54:22 +03:00
}
/******************************************************************************/
int
main(int argc, char **argv)
{
const char *sesman_ini = XRDP_CFG_PATH "/sesman.ini";
struct config_sesman *cfg = NULL;
2020-06-22 12:27:54 +03:00
struct trans *t = NULL;
enum SCP_CLIENT_STATES_E e;
2020-12-03 14:54:22 +03:00
struct session_params sp;
struct log_config *logging;
int rv = 1;
2020-12-03 14:54:22 +03:00
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(sp.server, cfg->listen_port, NULL, NULL, NULL)))
{
LOG(LOG_LEVEL_ERROR, "connect error - %s", g_get_strerror());
}
2020-12-03 14:54:22 +03:00
else
{
e = send_scpv0_auth_request(t, &sp);
if (e != SCP_CLIENT_STATE_OK)
2020-12-03 14:54:22 +03:00
{
LOG(LOG_LEVEL_ERROR,
"Error sending create session to sesman [%s]",
scp_client_state_to_str(e));
rv = 1;
}
2020-12-03 14:54:22 +03:00
else
{
rv = handle_scpv0_auth_reply(t);
2020-12-03 14:54:22 +03:00
}
trans_delete(t);
}
2020-12-03 14:54:22 +03:00
g_memset(sp.password, '\0', sizeof(sp.password));
config_free(cfg);
log_end();
2020-06-22 12:27:54 +03:00
return rv;
}