/** * 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 #endif #include #include #include #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}, { "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 *ip_addr; 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 --help\n" "\nor\n" "sesrun [options] [username]\n\n"); g_printf("options:\n"); g_printf(" -g Default:%dx%d\n", DEFAULT_WIDTH, DEFAULT_HEIGHT); g_printf(" -b Default:%d\n", DEFAULT_BPP); g_printf(" -t Default:%s\n", DEFAULT_TYPE); g_printf(" -D Default: $HOME\n" " -S Default: Defined window manager\n" " -p TESTING ONLY - DO NOT USE IN PRODUCTION\n" " -F Read password from this file descriptor\n" " -c Alternative sesman.ini file\n"); g_printf("\nSupported types are %s\n", sesstype_list); g_printf("\nIf username is omitted, the current user is used.\n" "If username is provided, password is needed.\n" " Password is prompted for if -p or -F are not specified\n"); } /**************************************************************************//** * Parses a string x * * @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->ip_addr = ""; 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) { // No username was specified if (password_set) { LOG(LOG_LEVEL_WARNING, "No username - ignoring specified password"); sp->password[0] = '\0'; } sp->username = NULL; } else if ((argc - optind) > 1) { LOG(LOG_LEVEL_ERROR, "Unexpected arguments after username"); params_ok = 0; } else if (params_ok) { // A username is specified sp->username = argv[optind]; if (!password_set) { const char *p = getpass("Password: "); if (p == NULL) { params_ok = 0; } else { g_snprintf(sp->password, sizeof(sp->password), "%s", p); } } } return params_ok; } /**************************************************************************//** * Sends an SCP login request * * A sys login request (i.e. username / password) is used if a username * is specified. Otherwise we use a uds login request for the current user. * * @param t SCP connection * @param sp Data for request */ static int send_login_request(struct trans *t, const struct session_params *sp) { int rv; LOG(LOG_LEVEL_DEBUG, "ip_addr:\"%s\"", sp->ip_addr); if (sp->username != NULL) { /* Only log the password in development builds */ LOG_DEVEL(LOG_LEVEL_DEBUG, "password:\"%s\"", sp->password); rv = scp_send_sys_login_request(t, sp->username, sp->password, sp->ip_addr); } else { rv = scp_send_uds_login_request(t); } return rv; } /**************************************************************************//** * Receives an SCP login response * * @param t SCP transport to receive reply on * @param[out] server_closed != 0 if server has gone away * @return 0 for successful authentication */ static int handle_login_response(struct trans *t, int *server_closed) { enum scp_login_status login_result; int rv = wait_for_sesman_reply(t, E_SCP_LOGIN_RESPONSE); if (rv != 0) { *server_closed = 1; } else { rv = scp_get_login_response(t, &login_result, server_closed); if (rv == 0) { if (login_result != E_SCP_LOGIN_OK) { char msg[256]; scp_login_status_to_str(login_result, msg, sizeof(msg)); g_printf("Login failed; %s\n", msg); rv = 1; } } scp_msg_in_reset(t); // Done with this message } return rv; } /**************************************************************************//** * 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\" shell:\"%s\"", sp->width, sp->height, sp->bpp, sp->session_type, sp->directory, sp->shell); return scp_send_create_session_request( t, sp->session_type, sp->width, sp->height, sp->bpp, sp->shell, sp->directory); } /**************************************************************************//** * 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) { enum scp_screate_status status; 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, &status, &display, &guid); if (rv == 0) { if (status != E_SCP_SCREATE_OK) { char msg[256]; scp_screate_status_to_str(status, msg, sizeof(msg)); g_printf("Connection failed; %s\n", msg); rv = 1; } else { char guid_str[GUID_STR_SIZE]; g_printf("ok display=:%d GUID=%s\n", display, guid_to_str(&guid, guid_str)); } } scp_msg_in_reset(t); // Done with this message } 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 (argc == 2 && g_strcmp(argv[1], "--help") == 0) { usage(); rv = 0; } else 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, "xrdp-sesrun", NULL))) { LOG(LOG_LEVEL_ERROR, "connect error - %s", g_get_strerror()); } else { int server_closed = 0; while (!server_closed) { rv = send_login_request(t, &sp); if (rv != 0) { LOG(LOG_LEVEL_ERROR, "Error sending login request to sesman"); break; } rv = handle_login_response(t, &server_closed); if (rv == 0) { break; /* Successful authentication */ } if (!server_closed) { const char *p = getpass("Password: "); if (p == NULL) { break; } g_snprintf(sp.password, sizeof(sp.password), "%s", p); } } if (rv == 0) { if ((rv = send_create_session_request(t, &sp)) == 0) { rv = handle_create_session_response(t); } } trans_delete(t); } g_memset(sp.password, '\0', sizeof(sp.password)); config_free(cfg); log_end(); return rv; }