diff --git a/docs/man/xrdp-chansrv.8.in b/docs/man/xrdp-chansrv.8.in index 548a07f0..fc4b31f7 100644 --- a/docs/man/xrdp-chansrv.8.in +++ b/docs/man/xrdp-chansrv.8.in @@ -7,10 +7,11 @@ .SH "DESCRIPTION" \fBxrdp\-chansrv\fR is the \fBxrdp\fR(8) channel server, which manages the Remote Desktop Protocol (RDP) sub-channels. +.PP This program is only forked internally by \fBxrdp\-sesman\fP(8). -.br +.PP Currently \fBxrdp\-chansrv\fP knows about the following channels: -.RE 8 +.RS 8 .TP .B cliprdr Clipboard Redirection @@ -26,18 +27,55 @@ Remote Applications Integrated Locally .TP .B drdynvc Dynamic Virtual Channel -.RS +.RE + +.SH ENVIRONMENT +.TP +.I CHANSRV_LOG_LEVEL +Logging level for chansrv. Case-insensitive. Takes one of these string values: +.RS 8 +.TP +.B LOG_LEVEL_ALWAYS +Only log essential messages +.TP +.B LOG_LEVEL_ERROR +Log errors and above +.TP +.B LOG_LEVEL_WARNING +Log warnings and above +.TP +.B LOG_LEVEL_INFO +Log info and above. This is the default if nothing is specified +.TP +.B LOG_LEVEL_DEBUG +Log debug and above +.TP +.B LOG_LEVEL_TRACE +Log everything +.TP +.RE +.TP +.I CHANSRV_LOG_PATH +Path to the location where the log file is stored. If not specified, +$\fBXDG_DATA_HOME/xrdp\fP or \fB$HOME/.local/share/xrdp\fP is used instead. +.TP +.I DISPLAY +X11 display number. Must be specified. .SH FILES .TP +.I @sysconfdir@/xrdp/sesman.ini +Contains some settings for this program. +.TP .I @socketdir@/xrdp_chansrv_socket_* UNIX socket used by external programs to implement channels. .TP .I @socketdir@/xrdp_api_* UNIX socket used by \fBxrdp\-chansrv\fP to communicate with \fBxrdp\-sesman\fP. .TP -.I $XDG_DATA_HOME/xrdp/xrdp-chansrv.%s.log -Log file used by \fBxrdp\-chansrv\fP(8). \fB%s\fP is display number. +.I xrdp-chansrv.%s.log +Log file used by \fBxrdp\-chansrv\fP(8). \fB%s\fP is display number. See the +description of \fBCHANSRV_LOG_PATH\fP above for the file's location. .SH "SEE ALSO" .BR xrdp\-sesman (8), diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index cf1d1f96..ceeddc8a 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -51,6 +51,8 @@ xrdp_chansrv_SOURCES = \ chansrv.h \ chansrv_common.c \ chansrv_common.h \ + chansrv_config.c \ + chansrv_config.h \ chansrv_fuse.c \ chansrv_fuse.h \ chansrv_xfs.c \ diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index d7ef1869..a36f5723 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -36,6 +36,7 @@ #include "rail.h" #include "xcommon.h" #include "chansrv_fuse.h" +#include "chansrv_config.h" #include "xrdp_sockets.h" #include "audin.h" @@ -57,17 +58,16 @@ static int g_rail_index = -1; static tbus g_term_event = 0; static tbus g_thread_done_event = 0; -static int g_use_unix_socket = 0; +struct config_chansrv *g_cfg = NULL; int g_display_num = 0; int g_cliprdr_chan_id = -1; /* cliprdr */ int g_rdpsnd_chan_id = -1; /* rdpsnd */ int g_rdpdr_chan_id = -1; /* rdpdr */ int g_rail_chan_id = -1; /* rail */ -int g_restrict_outbound_clipboard = 0; char *g_exec_name; -tbus g_exec_event; +tbus g_exec_event = 0; tbus g_exec_mutex; tbus g_exec_sem; int g_exec_pid = 0; @@ -1244,7 +1244,7 @@ setup_listen(void) trans_delete(g_lis_trans); } - if (g_use_unix_socket) + if (g_cfg->use_unix_socket) { g_lis_trans = trans_create(TRANS_MODE_UNIX, 8192, 8192); g_lis_trans->is_term = g_is_term; @@ -1623,52 +1623,23 @@ get_display_num_from_display(char *display_text) int main_cleanup(void) { - g_delete_wait_obj(g_term_event); - g_delete_wait_obj(g_thread_done_event); - g_delete_wait_obj(g_exec_event); - tc_mutex_delete(g_exec_mutex); - g_deinit(); /* os_calls */ - return 0; -} - -/*****************************************************************************/ -static int -read_ini(void) -{ - char filename[256]; - struct list *names; - struct list *values; - char *name; - char *value; - int index; - - g_memset(filename, 0, (sizeof(char) * 256)); - names = list_create(); - names->auto_free = 1; - values = list_create(); - values->auto_free = 1; - g_use_unix_socket = 0; - g_snprintf(filename, 255, "%s/sesman.ini", XRDP_CFG_PATH); - - if (file_by_name_read_section(filename, "Globals", names, values) == 0) + if (g_term_event != 0) { - for (index = 0; index < names->count; index++) - { - name = (char *)list_get_item(names, index); - value = (char *)list_get_item(values, index); - - if (g_strcasecmp(name, "ListenAddress") == 0) - { - if (g_strcasecmp(value, "127.0.0.1") == 0) - { - g_use_unix_socket = 1; - } - } - } + g_delete_wait_obj(g_term_event); } - - list_delete(names); - list_delete(values); + if (g_thread_done_event != 0) + { + g_delete_wait_obj(g_thread_done_event); + } + if (g_exec_event != 0) + { + g_delete_wait_obj(g_exec_event); + tc_mutex_delete(g_exec_mutex); + tc_sem_delete(g_exec_sem); + } + log_end(); + config_free(g_cfg); + g_deinit(); /* os_calls */ return 0; } @@ -1792,32 +1763,33 @@ main(int argc, char **argv) enum logReturns error; struct log_config logconfig; enum logLevels log_level; - char *restrict_outbound_clipboard_env; g_init("xrdp-chansrv"); /* os_calls */ + log_path[255] = 0; if (get_log_path(log_path, 255) != 0) { g_writeln("error reading CHANSRV_LOG_PATH and HOME environment variable"); - g_deinit(); + main_cleanup(); return 1; } - restrict_outbound_clipboard_env = g_getenv("CHANSRV_RESTRICT_OUTBOUND_CLIPBOARD"); - if (restrict_outbound_clipboard_env != 0) + /* + * The user is unable at present to override the sysadmin-provided + * sesman.ini location */ + if ((g_cfg = config_read(0, XRDP_CFG_PATH "/sesman.ini")) == NULL) { - if (g_strcmp(restrict_outbound_clipboard_env, "1") == 0) - { - g_restrict_outbound_clipboard = 1; - } + main_cleanup(); + return 1; } + config_dump(g_cfg); - read_ini(); pid = g_getpid(); display_text = g_getenv("DISPLAY"); - - if (display_text) + if (display_text != NULL) + { get_display_num_from_display(display_text); + } log_level = get_log_level(g_getenv("CHANSRV_LOG_LEVEL"), LOG_LEVEL_INFO); @@ -1857,7 +1829,7 @@ main(int argc, char **argv) break; } - g_deinit(); + main_cleanup(); return 1; } @@ -1877,7 +1849,7 @@ main(int argc, char **argv) if (g_display_num == 0) { LOGM((LOG_LEVEL_ERROR, "main: error, display is zero")); - g_deinit(); + main_cleanup(); return 1; } @@ -1928,7 +1900,6 @@ main(int argc, char **argv) /* cleanup */ main_cleanup(); LOGM((LOG_LEVEL_INFO, "main: app exiting pid %d(0x%8.8x)", pid, pid)); - g_deinit(); return 0; } diff --git a/sesman/chansrv/chansrv_config.c b/sesman/chansrv/chansrv_config.c new file mode 100644 index 00000000..c265ff02 --- /dev/null +++ b/sesman/chansrv/chansrv_config.c @@ -0,0 +1,294 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * 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. + * + * This file implements the interface in chansrv_config.h + */ +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include +#include + +#include "arch.h" + +#include "list.h" +#include "log.h" +#include "file.h" +#include "os_calls.h" + +#include "chansrv_config.h" + +/* Default settings */ +#define DEFAULT_USE_UNIX_SOCKET 0 +#define DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD 0 +#define DEFAULT_FUSE_MOUNT_NAME "xrdp-client" +#define DEFAULT_FILE_UMASK 077 + +/** + * Type used for passing a logging function about + */ +typedef +printflike(2, 3) +enum logReturns (*log_func_t)(const enum logLevels lvl, + const char *msg, ...); + +/***************************************************************************//** + * @brief Error logging function to use to log to stdout + * + * Has the same signature as the log_message() function + */ +static enum logReturns +log_to_stdout(const enum logLevels lvl, const char *msg, ...) +{ + char buff[256]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buff, sizeof(buff), msg, ap); + va_end(ap); + g_writeln("%s", buff); + + return LOG_STARTUP_OK; +} + +/***************************************************************************//** + * Reads the config values we need from the [Globals] section + * + * @param logmsg Function to use to log messages + * @param names List of definitions in the section + * @params values List of corresponding values for the names + * @params cfg Pointer to structure we're filling in + * + * @return 0 for success + */ +static int +read_config_globals(log_func_t logmsg, + struct list *names, struct list *values, + struct config_chansrv *cfg) +{ + int error = 0; + int index; + + for (index = 0; index < names->count; ++index) + { + const char *name = (const char *)list_get_item(names, index); + const char *value = (const char *)list_get_item(values, index); + + if (g_strcasecmp(name, "ListenAddress") == 0) + { + if (g_strcasecmp(value, "127.0.0.1") == 0) + { + cfg->use_unix_socket = 1; + } + } + } + + return error; +} + +/***************************************************************************//** + * Reads the config values we need from the [Security] section + * + * @param logmsg Function to use to log messages + * @param names List of definitions in the section + * @params values List of corresponding values for the names + * @params cfg Pointer to structure we're filling in + * + * @return 0 for success + */ +static int +read_config_security(log_func_t logmsg, + struct list *names, struct list *values, + struct config_chansrv *cfg) +{ + int error = 0; + int index; + + for (index = 0; index < names->count; ++index) + { + const char *name = (const char *)list_get_item(names, index); + const char *value = (const char *)list_get_item(values, index); + + if (g_strcasecmp(name, "RestrictOutboundClipboard") == 0) + { + cfg->restrict_outbound_clipboard = g_text2bool(value); + } + } + + return error; +} + +/***************************************************************************//** + * Reads the config values we need from the [Chansrv] section + * + * @param logmsg Function to use to log messages + * @param names List of definitions in the section + * @params values List of corresponding values for the names + * @params cfg Pointer to structure we're filling in + * + * @return 0 for success + */ +static int +read_config_chansrv(log_func_t logmsg, + struct list *names, struct list *values, + struct config_chansrv *cfg) +{ + int error = 0; + int index; + + for (index = 0; index < names->count; ++index) + { + const char *name = (const char *)list_get_item(names, index); + const char *value = (const char *)list_get_item(values, index); + + if (g_strcasecmp(name, "FuseMountName") == 0) + { + g_free(cfg->fuse_mount_name); + cfg->fuse_mount_name = g_strdup(value); + if (cfg->fuse_mount_name == NULL) + { + logmsg(LOG_LEVEL_ERROR, "Can't alloc FuseMountName"); + error = 1; + break; + } + } + else if (g_strcasecmp(name, "FileUmask") == 0) + { + cfg->file_umask = strtol(value, NULL, 0); + } + } + + return error; +} + +/***************************************************************************//** + * @brief returns a config block with default values + * + * @return Block, or NULL for no memory + */ +static struct config_chansrv * +new_config(void) +{ + /* Do all the allocations at the beginning, then check them together */ + struct config_chansrv *cfg = g_new0(struct config_chansrv, 1); + char *fuse_mount_name = g_strdup(DEFAULT_FUSE_MOUNT_NAME); + if (cfg == NULL || fuse_mount_name == NULL) + { + /* At least one memory allocation failed */ + g_free(fuse_mount_name); + g_free(cfg); + cfg = NULL; + } + else + { + cfg->use_unix_socket = DEFAULT_USE_UNIX_SOCKET; + cfg->restrict_outbound_clipboard = DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD; + cfg->fuse_mount_name = fuse_mount_name; + cfg->file_umask = DEFAULT_FILE_UMASK; + } + + return cfg; +} + +/******************************************************************************/ +struct config_chansrv * +config_read(int use_logger, const char *sesman_ini) +{ + int error = 0; + struct config_chansrv *cfg = NULL; + log_func_t logmsg = (use_logger) ? log_message : log_to_stdout; + int fd; + + fd = g_file_open_ex(sesman_ini, 1, 0, 0, 0); + if (fd < 0) + { + logmsg(LOG_LEVEL_ERROR, "Can't open config file %s", sesman_ini); + error = 1; + } + else + { + if ((cfg = new_config()) == NULL) + { + logmsg(LOG_LEVEL_ERROR, "Can't alloc config block"); + error = 1; + } + else + { + struct list *names = list_create(); + struct list *values = list_create(); + + names->auto_free = 1; + values->auto_free = 1; + + if (!error && file_read_section(fd, "Globals", names, values) == 0) + { + error = read_config_globals(logmsg, names, values, cfg); + } + + + if (!error && file_read_section(fd, "Security", names, values) == 0) + { + error = read_config_security(logmsg, names, values, cfg); + } + + if (!error && file_read_section(fd, "Chansrv", names, values) == 0) + { + error = read_config_chansrv(logmsg, names, values, cfg); + } + + list_delete(names); + list_delete(values); + } + + g_file_close(fd); + } + + if (error) + { + config_free(cfg); + cfg = NULL; + } + + return cfg; +} + +/******************************************************************************/ +void +config_dump(struct config_chansrv *config) +{ + g_writeln("Global configuration:"); + g_writeln(" UseUnixSocket (derived): %d", config->use_unix_socket); + + g_writeln("\nSecurity configuration:"); + g_writeln(" RestrictOutboundClipboard: %d", + config->restrict_outbound_clipboard); + + g_writeln("\nChansrv configuration:"); + g_writeln(" FuseMountName: %s", config->fuse_mount_name); + g_writeln(" FileMask: 0%o", config->file_umask); +} + +/******************************************************************************/ +void +config_free(struct config_chansrv *cc) +{ + if (cc != NULL) + { + g_free(cc->fuse_mount_name); + g_free(cc); + } +} diff --git a/sesman/chansrv/chansrv_config.h b/sesman/chansrv/chansrv_config.h new file mode 100644 index 00000000..72d301cd --- /dev/null +++ b/sesman/chansrv/chansrv_config.h @@ -0,0 +1,72 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * 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. + * + * This file contains the chansrv configuration parameters from sesman.ini + */ + +#ifndef _CHANSRV_CONFIG +#define _CHANSRV_CONFIG + +#include + +struct config_chansrv +{ + /** Whether to use a UNIX socket for chansrv */ + int use_unix_socket; + + /** RestrictOutboundClipboard setting from sesman.ini */ + int restrict_outbound_clipboard; + + /** * FuseMountName from sesman.ini */ + char *fuse_mount_name; + /** FileUmask from sesman.ini */ + mode_t file_umask; +}; + + +/** + * + * @brief Reads sesman configuration + * @param use_logger Use logger to log errors (otherwise stdout) + * @param sesman_ini Name of configuration file to read + * + * @return configuration on success, NULL on failure + * + * @pre logging is assumed to be active + * @post pass return value to config_free() to prevent memory leaks + * + */ +struct config_chansrv * +config_read(int use_logger, const char *sesman_ini); + +/** + * + * @brief Dumps configuration to stdout + * @param pointer to a config_chansrv struct + * + */ +void +config_dump(struct config_chansrv *config); + +/** + * + * @brief Frees configuration allocated by config_read() + * @param pointer to a config_chansrv struct (may be NULL) + * + */ +void +config_free(struct config_chansrv *cs); + +#endif /* _CHANSRV_CONFIG */ diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index 62ee050a..6c62d5cc 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -121,6 +121,7 @@ void xfuse_devredir_cb_file_close(struct state_close *fip) #include "clipboard_file.h" #include "chansrv_fuse.h" #include "chansrv_xfs.h" +#include "chansrv_config.h" #include "devredir.h" #include "list.h" #include "file.h" @@ -310,8 +311,7 @@ struct req_list_item int size; }; -static char g_fuse_mount_name[256] = "xrdp_client"; -static mode_t g_umask = 077; /* Umask for files in fs */ +extern struct config_chansrv *g_cfg; /* in chansrv.c */ static struct list *g_req_list = 0; static struct xfs_fs *g_xfs; /* an inst of xrdp file system */ @@ -404,35 +404,6 @@ static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name); int load_fuse_config(void) { - int index; - char cfg_file[256]; - struct list *items; - struct list *values; - char *item; - char *value; - - items = list_create(); - items->auto_free = 1; - values = list_create(); - values->auto_free = 1; - g_snprintf(cfg_file, 255, "%s/sesman.ini", XRDP_CFG_PATH); - file_by_name_read_section(cfg_file, "Chansrv", items, values); - for (index = 0; index < items->count; index++) - { - item = (char *)list_get_item(items, index); - value = (char *)list_get_item(values, index); - if (g_strcasecmp(item, "FuseMountName") == 0) - { - g_strncpy(g_fuse_mount_name, value, 255); - } - else if (g_strcasecmp(item, "FileUmask") == 0) - { - g_umask = strtol(value, NULL, 0); - log_info("g_umask set to 0%o", g_umask); - } - } - list_delete(items); - list_delete(values); return 0; } @@ -469,7 +440,7 @@ xfuse_init(void) load_fuse_config(); /* define FUSE mount point to ~/xrdp_client, ~/thinclient_drives */ - g_snprintf(g_fuse_root_path, 255, "%s/%s", g_getenv("HOME"), g_fuse_mount_name); + g_snprintf(g_fuse_root_path, 255, "%s/%s", g_getenv("HOME"), g_cfg->fuse_mount_name); g_snprintf(g_fuse_clipboard_path, 255, "%s/.clipboard", g_fuse_root_path); /* if FUSE mount point does not exist, create it */ @@ -1561,7 +1532,7 @@ static int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b, memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_ino = xinode->inum; - stbuf.st_mode = xinode->mode & ~g_umask; + stbuf.st_mode = xinode->mode & ~g_cfg->file_umask; /* * Try to add the entry. From the docs for fuse_add_direntry():- @@ -2453,7 +2424,7 @@ static void xfs_inode_to_fuse_entry_param(const XFS_INODE *xinode, e->attr_timeout = XFUSE_ATTR_TIMEOUT; e->entry_timeout = XFUSE_ENTRY_TIMEOUT; e->attr.st_ino = xinode->inum; - e->attr.st_mode = xinode->mode & ~g_umask; + e->attr.st_mode = xinode->mode & ~g_cfg->file_umask; e->attr.st_nlink = 1; e->attr.st_uid = xinode->uid; e->attr.st_gid = xinode->gid; @@ -2477,7 +2448,7 @@ static void make_fuse_attr_reply(fuse_req_t req, const XFS_INODE *xinode) memset(&st, 0, sizeof(st)); st.st_ino = xinode->inum; - st.st_mode = xinode->mode & ~g_umask; + st.st_mode = xinode->mode & ~g_cfg->file_umask; st.st_nlink = 1; st.st_uid = xinode->uid; st.st_gid = xinode->gid; diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index 834fe051..bc1864be 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -170,6 +170,7 @@ x-special/gnome-copied-files #include "parse.h" #include "os_calls.h" #include "chansrv.h" +#include "chansrv_config.h" #include "clipboard.h" #include "clipboard_file.h" #include "clipboard_common.h" @@ -235,7 +236,7 @@ extern tbus g_x_wait_obj; /* in xcommon.c */ extern Screen *g_screen; /* in xcommon.c */ extern int g_screen_num; /* in xcommon.c */ -extern int g_restrict_outbound_clipboard; /* in chansrv.c */ +extern struct config_chansrv *g_cfg; /* in chansrv.c */ int g_clip_up = 0; @@ -2499,7 +2500,7 @@ clipboard_xevent(void *xevent) switch (lxevent->type) { case SelectionNotify: - if (g_restrict_outbound_clipboard == 0) + if (g_cfg->restrict_outbound_clipboard == 0) { clipboard_event_selection_notify(lxevent); } diff --git a/sesman/session.c b/sesman/session.c index f85616c1..3732bbc9 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -371,11 +371,6 @@ session_start_chansrv(char *username, int display) g_cfg->env_names, g_cfg->env_values); - if (g_cfg->sec.restrict_outbound_clipboard == 1) - { - g_setenv("CHANSRV_RESTRICT_OUTBOUND_CLIPBOARD", "1", 1); - } - /* executing chansrv */ g_execvp(exe_path, (char **) (chansrv_params->items)); /* should not get here */