From a16e56f711d1109311d1e51b612ef33ed55444c6 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 8 Sep 2022 14:00:36 +0100 Subject: [PATCH 01/11] Add function to get user information by UID Moving to a uid_t to store the user information makes a lot of sense. When doing this, we need a function to get information about a user from the uid_t As well as creating the function g_getuser_info_by_uid() we also rename g_getuser_info() to g_getuser_info_by_name() and make the parameter ordering more usual. --- common/os_calls.c | 76 ++++++++++++++++++++++++++++++++++++++++------- common/os_calls.h | 6 ++-- sesman/access.c | 4 +-- sesman/env.c | 3 +- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/common/os_calls.c b/common/os_calls.c index 095359c1..a30b60f7 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -2797,7 +2797,7 @@ g_initgroups(const char *username) return 0; #else int gid; - int error = g_getuser_info(username, &gid, NULL, NULL, NULL, NULL); + int error = g_getuser_info_by_name(username, NULL, &gid, NULL, NULL, NULL); if (error == 0) { error = initgroups(username, gid); @@ -3060,26 +3060,85 @@ g_sighup(int pid) /* the caller is responsible to free the buffs */ /* does not work in win32 */ int -g_getuser_info(const char *username, int *gid, int *uid, char **shell, - char **dir, char **gecos) +g_getuser_info_by_name(const char *username, int *uid, int *gid, + char **shell, char **dir, char **gecos) +{ + int rv = 1; +#if !defined(_WIN32) + + if (username == NULL) + { + LOG(LOG_LEVEL_ERROR, "g_getuser_info_by_name() called for NULL user"); + } + else + { + struct passwd *pwd_1 = getpwnam(username); + + if (pwd_1 != 0) + { + rv = 0; + + if (uid != 0) + { + *uid = pwd_1->pw_uid; + } + + if (gid != 0) + { + *gid = pwd_1->pw_gid; + } + + if (shell != 0) + { + *shell = g_strdup(pwd_1->pw_shell); + } + + if (dir != 0) + { + *dir = g_strdup(pwd_1->pw_dir); + } + + if (gecos != 0) + { + *gecos = g_strdup(pwd_1->pw_gecos); + } + } + } +#endif + return rv; +} + + +/*****************************************************************************/ +/* returns 0 if ok */ +/* the caller is responsible to free the buffs */ +/* does not work in win32 */ +int +g_getuser_info_by_uid(int uid, char **username, int *gid, + char **shell, char **dir, char **gecos) { #if defined(_WIN32) return 1; #else struct passwd *pwd_1; - pwd_1 = getpwnam(username); + pwd_1 = getpwuid(uid); if (pwd_1 != 0) { + if (username != NULL) + { + *username = g_strdup(pwd_1->pw_name); + } + if (gid != 0) { *gid = pwd_1->pw_gid; } - if (uid != 0) + if (shell != 0) { - *uid = pwd_1->pw_uid; + *shell = g_strdup(pwd_1->pw_shell); } if (dir != 0) @@ -3087,11 +3146,6 @@ g_getuser_info(const char *username, int *gid, int *uid, char **shell, *dir = g_strdup(pwd_1->pw_dir); } - if (shell != 0) - { - *shell = g_strdup(pwd_1->pw_shell); - } - if (gecos != 0) { *gecos = g_strdup(pwd_1->pw_gecos); diff --git a/common/os_calls.h b/common/os_calls.h index df24cd69..b1153571 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -193,8 +193,10 @@ int g_exit(int exit_code); int g_getpid(void); int g_sigterm(int pid); int g_sighup(int pid); -int g_getuser_info(const char *username, int *gid, int *uid, char **shell, - char **dir, char **gecos); +int g_getuser_info_by_name(const char *username, int *uid, int *gid, + char **shell, char **dir, char **gecos); +int g_getuser_info_by_uid(int uid, char **username, int *gid, + char **shell, char **dir, char **gecos); int g_getgroup_info(const char *groupname, int *gid); int g_check_user_in_group(const char *username, int gid, int *ok); int g_time1(void); diff --git a/sesman/access.c b/sesman/access.c index 442d9e7b..6c7eb264 100644 --- a/sesman/access.c +++ b/sesman/access.c @@ -51,7 +51,7 @@ access_login_allowed(const char *user) return 1; } - if (0 != g_getuser_info(user, &gid, 0, 0, 0, 0)) + if (0 != g_getuser_info_by_name(user, 0, &gid, 0, 0, 0)) { LOG(LOG_LEVEL_ERROR, "Cannot read user info! - login denied"); return 0; @@ -100,7 +100,7 @@ access_login_mng_allowed(const char *user) return 1; } - if (0 != g_getuser_info(user, &gid, 0, 0, 0, 0)) + if (0 != g_getuser_info_by_name(user, 0, &gid, 0, 0, 0)) { LOG(LOG_LEVEL_ERROR, "[MNG] Cannot read user info! - login denied"); return 0; diff --git a/sesman/env.c b/sesman/env.c index 34a6a97f..0a4e6220 100644 --- a/sesman/env.c +++ b/sesman/env.c @@ -107,7 +107,8 @@ env_set_user(const char *username, char **passwd_file, int display, pw_shell = 0; pw_dir = 0; - error = g_getuser_info(username, &pw_gid, &pw_uid, &pw_shell, &pw_dir, 0); + error = g_getuser_info_by_name(username, &pw_uid, &pw_gid, &pw_shell, + &pw_dir, 0); if (error == 0) { From 891efed9adf761047dd2158aa773f849c9755379 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:05:51 +0100 Subject: [PATCH 02/11] Remove nested includes from sesman.h The intention is to improve decoupling of the modules making up sesman. --- sesman/access.c | 6 ++++++ sesman/config.c | 3 ++- sesman/env.c | 6 +++++- sesman/sesman.c | 21 +++++++++------------ sesman/sesman.h | 18 +++++++----------- sesman/session.c | 11 ++++++++++- sesman/sig.c | 9 ++++++++- 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/sesman/access.c b/sesman/access.c index 6c7eb264..d3cb98f7 100644 --- a/sesman/access.c +++ b/sesman/access.c @@ -28,7 +28,13 @@ #include #endif +#include "arch.h" + +#include "access.h" +#include "config.h" +#include "log.h" #include "sesman.h" +#include "os_calls.h" #include "string_calls.h" /******************************************************************************/ diff --git a/sesman/config.c b/sesman/config.c index 23e7ff37..5776c13a 100644 --- a/sesman/config.c +++ b/sesman/config.c @@ -29,9 +29,10 @@ #endif #include "arch.h" +#include "config.h" + #include "list.h" #include "file.h" -#include "sesman.h" #include "log.h" #include "string_calls.h" #include "chansrv/chansrv_common.h" diff --git a/sesman/env.c b/sesman/env.c index 0a4e6220..e840de31 100644 --- a/sesman/env.c +++ b/sesman/env.c @@ -30,11 +30,15 @@ #include -#include "xrdp_sockets.h" +#include "env.h" +#include "config.h" #include "list.h" +#include "log.h" +#include "os_calls.h" #include "sesman.h" #include "ssl_calls.h" #include "string_calls.h" +#include "xrdp_sockets.h" /******************************************************************************/ int diff --git a/sesman/sesman.c b/sesman/sesman.c index 0e0c6bcb..86833e51 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -30,13 +30,18 @@ #include +#include "arch.h" #include "sesman.h" -#include "xrdp_configure_options.h" + +#include "config.h" +#include "lock_uds.h" +#include "os_calls.h" +#include "scp.h" +#include "scp_process.h" +#include "sig.h" #include "string_calls.h" #include "trans.h" - -#include "scp_process.h" -#include "lock_uds.h" +#include "xrdp_configure_options.h" /** * Maximum number of short-lived connections to sesman @@ -72,14 +77,6 @@ tintptr g_term_event = 0; tintptr g_sigchld_event = 0; tintptr g_reload_event = 0; -/** - * Items stored on the g_con_list - */ -struct sesman_con -{ - struct trans *t; -}; - static struct trans *g_list_trans; /* Variables used to lock g_list_trans */ diff --git a/sesman/sesman.h b/sesman/sesman.h index 785810d6..38231ec1 100644 --- a/sesman/sesman.h +++ b/sesman/sesman.h @@ -27,17 +27,13 @@ #ifndef SESMAN_H #define SESMAN_H -#include "arch.h" -#include "parse.h" -#include "os_calls.h" -#include "log.h" -#include "env.h" -#include "auth.h" -#include "config.h" -#include "sig.h" -#include "session.h" -#include "access.h" -#include "scp.h" +/** + * Type for managing sesman connections from xrdp (etc) + */ +struct sesman_con +{ + struct trans *t; +}; /* Globals */ extern struct config_sesman *g_cfg; diff --git a/sesman/session.c b/sesman/session.c index e00351cb..70ba972c 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -37,10 +37,19 @@ #include #endif +#include "arch.h" +#include "session.h" + +#include "auth.h" +#include "config.h" +#include "env.h" +#include "list.h" +#include "log.h" +#include "os_calls.h" #include "sesman.h" +#include "string_calls.h" #include "xauth.h" #include "xrdp_sockets.h" -#include "string_calls.h" #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 diff --git a/sesman/sig.c b/sesman/sig.c index 65c1d288..4b7b3ec9 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -28,8 +28,15 @@ #include #endif -#include "string_calls.h" +#include "arch.h" +#include "sig.h" + +#include "config.h" +#include "log.h" +#include "os_calls.h" #include "sesman.h" +#include "session.h" +#include "string_calls.h" /******************************************************************************/ void From 4c4bdc9782c0b20f4b1a16b614b0711f6042206b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 19 May 2022 16:27:30 +0100 Subject: [PATCH 03/11] SCP: separate authentication from session creation Messaging changes:- - Implement sys_login request message with username, password and IP address - Implement UDS login message for current user connected to sesman - Implement common login response message for login requests - Implement logout message so gateway authentications can be handled - with login/logout messages - Remove login info from the create session request - Existing gateway request/response messages removed - Add close connection message so that sesman can close terminated connections without displaying ERROR messages in the log. - Add a set_peername message so clients can send a name to sesman for improved logging. Other changes:- - Add status types for logging in and session creation, so that the front-end can supply the user with more informative errors in the event of an error occurring. - Users identities are now carried by UID rather than username, as xrdp and sesman are guaranteed to be on the same machine. --- libipm/Makefile.am | 3 +- libipm/scp.c | 237 +++++++++++++++++++-------------- libipm/scp.h | 222 +++++++++++++++++------------- libipm/scp_application_types.c | 84 ++++++++++++ libipm/scp_application_types.h | 80 ++++++++++- 5 files changed, 427 insertions(+), 199 deletions(-) create mode 100644 libipm/scp_application_types.c diff --git a/libipm/Makefile.am b/libipm/Makefile.am index 2957182b..d4d30987 100644 --- a/libipm/Makefile.am +++ b/libipm/Makefile.am @@ -15,7 +15,8 @@ libipm_la_SOURCES = \ libipm_private.h \ scp.h \ scp.c \ - scp_application_types.h + scp_application_types.h \ + scp_application_types.c libipm_la_LIBADD = \ $(top_builddir)/common/libcommon.la diff --git a/libipm/scp.c b/libipm/scp.c index 45f35e4b..5cca6edc 100644 --- a/libipm/scp.c +++ b/libipm/scp.c @@ -40,12 +40,21 @@ static const char * msgno_to_str(unsigned short n) { return - (n == E_SCP_GATEWAY_REQUEST) ? "SCP_GATEWAY_REQUEST" : - (n == E_SCP_GATEWAY_RESPONSE) ? "SCP_GATEWAY_RESPONSE" : + (n == E_SCP_SET_PEERNAME_REQUEST) ? "SCP_SET_PEERNAME_REQUEST" : + + (n == E_SCP_SYS_LOGIN_REQUEST) ? "SCP_SYS_LOGIN_REQUEST" : + (n == E_SCP_UDS_LOGIN_REQUEST) ? "SCP_UDS_LOGIN_REQUEST" : + (n == E_SCP_LOGIN_RESPONSE) ? "SCP_LOGIN_RESPONSE" : + + (n == E_SCP_LOGOUT_REQUEST) ? "SCP_LOGOUT_REQUEST" : + (n == E_SCP_CREATE_SESSION_REQUEST) ? "SCP_CREATE_SESSION_REQUEST" : (n == E_SCP_CREATE_SESSION_RESPONSE) ? "SCP_CREATE_SESSION_RESPONSE" : + (n == E_SCP_LIST_SESSIONS_REQUEST) ? "SCP_LIST_SESSIONS_REQUEST" : (n == E_SCP_LIST_SESSIONS_RESPONSE) ? "SCP_LIST_SESSIONS_RESPONSE" : + + (n == E_SCP_CLOSE_CONNECTION_REQUEST) ? "SCP_CLOSE_CONNECTION_REQUEST" : NULL; } @@ -147,6 +156,7 @@ scp_port_to_display_string(const char *port, char *buff, unsigned int bufflen) /*****************************************************************************/ struct trans * scp_connect(const char *port, + const char *peername, int (*term_func)(void)) { char sock_path[256]; @@ -162,8 +172,12 @@ scp_connect(const char *port, trans_delete(t); t = NULL; } - else if (libipm_init_trans(t, LIBIPM_FAC_SCP, msgno_to_str) != - E_LI_SUCCESS) + else if (scp_init_trans(t) != 0) + { + trans_delete(t); + t = NULL; + } + else if (scp_send_set_peername_request(t, peername) != 0) { trans_delete(t); t = NULL; @@ -214,16 +228,48 @@ scp_msg_in_start(struct trans *trans) /*****************************************************************************/ int -scp_send_gateway_request(struct trans *trans, - const char *username, - const char *password, - const char *ip_addr) +scp_send_set_peername_request(struct trans *trans, + const char *peername) +{ + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_SET_PEERNAME_REQUEST, + "s", + peername); +} + +/*****************************************************************************/ + +int +scp_get_set_peername_request(struct trans *trans, + const char **peername) +{ + return libipm_msg_in_parse( trans, "s", peername); +} + + +/*****************************************************************************/ +int +scp_send_uds_login_request(struct trans *trans) +{ + return libipm_msg_out_simple_send(trans, + (int)E_SCP_UDS_LOGIN_REQUEST, + NULL); +} + + +/*****************************************************************************/ +int +scp_send_sys_login_request(struct trans *trans, + const char *username, + const char *password, + const char *ip_addr) { int rv; rv = libipm_msg_out_simple_send( trans, - (int)E_SCP_GATEWAY_REQUEST, + (int)E_SCP_SYS_LOGIN_REQUEST, "sss", username, password, @@ -238,93 +284,99 @@ scp_send_gateway_request(struct trans *trans, /*****************************************************************************/ int -scp_get_gateway_request(struct trans *trans, - const char **username, - const char **password, - const char **ip_addr) +scp_get_sys_login_request(struct trans *trans, + const char **username, + const char **password, + const char **ip_addr) { /* Make sure the buffer is cleared after processing this message */ libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); - return libipm_msg_in_parse(trans, "sss", username, password, - ip_addr); + return libipm_msg_in_parse( trans, "sss", + username, password, ip_addr); } /*****************************************************************************/ int -scp_send_gateway_response(struct trans *trans, - int auth_result) +scp_send_login_response(struct trans *trans, + enum scp_login_status login_result, + int server_closed) { return libipm_msg_out_simple_send( trans, - (int)E_SCP_GATEWAY_RESPONSE, - "i", - auth_result); + (int)E_SCP_LOGIN_RESPONSE, + "ib", + login_result, + (server_closed != 0)); /* Convert to 0/1 */ } /*****************************************************************************/ int -scp_get_gateway_response(struct trans *trans, - int *auth_result) +scp_get_login_response(struct trans *trans, + enum scp_login_status *login_result, + int *server_closed) { - int32_t i_auth_result = 0; - int rv = libipm_msg_in_parse(trans, "i", &i_auth_result); + int32_t i_login_result = 0; + int dummy; + /* User can pass in NULL for server_closed if they're trying an + * login method like UDS for which all fails are fatal */ + if (server_closed == NULL) + { + server_closed = &dummy; + } + + int rv = libipm_msg_in_parse(trans, "ib", &i_login_result, server_closed); if (rv == 0) { - *auth_result = i_auth_result; + *login_result = (enum scp_login_status)i_login_result; } return rv; } +/*****************************************************************************/ + +int +scp_send_logout_request(struct trans *trans) +{ + return libipm_msg_out_simple_send( trans, (int)E_SCP_LOGOUT_REQUEST, NULL); +} + + /*****************************************************************************/ int scp_send_create_session_request(struct trans *trans, - const char *username, - const char *password, enum scp_session_type type, unsigned short width, unsigned short height, unsigned char bpp, const char *shell, - const char *directory, - const char *ip_addr) + const char *directory) { - int rv = libipm_msg_out_simple_send( - trans, - (int)E_SCP_CREATE_SESSION_REQUEST, - "ssyqqysss", - username, - password, - type, - width, - height, - bpp, - shell, - directory, - ip_addr); - - /* Wipe the output buffer to remove the password */ - libipm_msg_out_erase(trans); - - return rv; + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_CREATE_SESSION_REQUEST, + "yqqyss", + type, + width, + height, + bpp, + shell, + directory); } /*****************************************************************************/ int scp_get_create_session_request(struct trans *trans, - const char **username, - const char **password, enum scp_session_type *type, unsigned short *width, unsigned short *height, unsigned char *bpp, const char **shell, - const char **directory, - const char **ip_addr) + const char **directory) { /* Intermediate values */ uint8_t i_type; @@ -332,21 +384,15 @@ scp_get_create_session_request(struct trans *trans, uint16_t i_height; uint8_t i_bpp; - /* Make sure the buffer is cleared after processing this message */ - libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); - int rv = libipm_msg_in_parse( trans, - "ssyqqysss", - username, - password, + "yqqyss", &i_type, &i_width, &i_height, &i_bpp, shell, - directory, - ip_addr); + directory); if (rv == 0) { @@ -364,7 +410,7 @@ scp_get_create_session_request(struct trans *trans, int scp_send_create_session_response(struct trans *trans, - int auth_result, + enum scp_screate_status status, int display, const struct guid *guid) { @@ -374,7 +420,7 @@ scp_send_create_session_response(struct trans *trans, trans, (int)E_SCP_CREATE_SESSION_RESPONSE, "iiB", - auth_result, + status, display, &guid_descriptor); } @@ -383,12 +429,12 @@ scp_send_create_session_response(struct trans *trans, int scp_get_create_session_response(struct trans *trans, - int *auth_result, + enum scp_screate_status *status, int *display, struct guid *guid) { /* Intermediate values */ - int32_t i_auth_result; + int32_t i_status; int32_t i_display; const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; @@ -396,12 +442,12 @@ scp_get_create_session_response(struct trans *trans, int rv = libipm_msg_in_parse( trans, "iiB", - &i_auth_result, + &i_status, &i_display, &guid_descriptor); if (rv == 0) { - *auth_result = i_auth_result; + *status = (enum scp_screate_status)i_status; *display = i_display; } @@ -411,36 +457,12 @@ scp_get_create_session_response(struct trans *trans, /*****************************************************************************/ int -scp_send_list_sessions_request(struct trans *trans, - const char *username, - const char *password) +scp_send_list_sessions_request(struct trans *trans) { - int rv; - - rv = libipm_msg_out_simple_send( - trans, - (int)E_SCP_LIST_SESSIONS_REQUEST, - "ss", - username, - password); - - /* Wipe the output buffer to remove the password */ - libipm_msg_out_erase(trans); - - return rv; -} - -/*****************************************************************************/ - -int -scp_get_list_sessions_request(struct trans *trans, - const char **username, - const char **password) -{ - /* Make sure the buffer is cleared after processing this message */ - libipm_set_flags(trans, LIBIPM_E_MSG_IN_ERASE_AFTER_USE); - - return libipm_msg_in_parse(trans, "ss", username, password); + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_LIST_SESSIONS_REQUEST, + NULL); } /*****************************************************************************/ @@ -465,7 +487,7 @@ scp_send_list_sessions_response( rv = libipm_msg_out_simple_send( trans, (int)E_SCP_LIST_SESSIONS_RESPONSE, - "iiuyqqyxss", + "iiuyqqyxis", status, info->sid, info->display, @@ -474,7 +496,7 @@ scp_send_list_sessions_response( info->height, info->bpp, info->start_time, - info->username, + info->uid, info->start_ip_addr); } @@ -511,12 +533,12 @@ scp_get_list_sessions_response( uint16_t i_height; uint8_t i_bpp; int64_t i_start_time; - char *i_username; + int32_t i_uid; char *i_start_ip_addr; rv = libipm_msg_in_parse( trans, - "iuyqqyxss", + "iuyqqyxis", &i_sid, &i_display, &i_type, @@ -524,7 +546,7 @@ scp_get_list_sessions_response( &i_height, &i_bpp, &i_start_time, - &i_username, + &i_uid, &i_start_ip_addr); if (rv == 0) @@ -532,7 +554,6 @@ scp_get_list_sessions_response( /* Allocate a block of memory large enough for the * structure result, and the strings it contains */ unsigned int len = sizeof(struct scp_session_info) + - g_strlen(i_username) + 1 + g_strlen(i_start_ip_addr) + 1; if ((p = (struct scp_session_info *)g_malloc(len, 1)) == NULL) { @@ -542,9 +563,8 @@ scp_get_list_sessions_response( { /* Set up the string pointers in the block to point * into the memory allocated after the block */ - p->username = (char *)p + sizeof(struct scp_session_info); p->start_ip_addr = - p->username + g_strlen(i_username) + 1; + (char *)p + sizeof(struct scp_session_info); /* Copy the data over */ p->sid = i_sid; @@ -554,7 +574,7 @@ scp_get_list_sessions_response( p->height = i_height; p->bpp = i_bpp; p->start_time = i_start_time; - g_strcpy(p->username, i_username); + p->uid = i_uid; g_strcpy(p->start_ip_addr, i_start_ip_addr); } } @@ -563,3 +583,14 @@ scp_get_list_sessions_response( } return rv; } + +/*****************************************************************************/ + +int +scp_send_close_connection_request(struct trans *trans) +{ + return libipm_msg_out_simple_send( + trans, + (int)E_SCP_CLOSE_CONNECTION_REQUEST, + NULL); +} diff --git a/libipm/scp.h b/libipm/scp.h index 27b3b116..ae6d3a97 100644 --- a/libipm/scp.h +++ b/libipm/scp.h @@ -43,12 +43,24 @@ struct trans; /* Message codes */ enum scp_msg_code { - E_SCP_GATEWAY_REQUEST = 1, - E_SCP_GATEWAY_RESPONSE, + E_SCP_SET_PEERNAME_REQUEST = 1, + // No E_SCP_SET_PEERNAME_RESPONSE + + E_SCP_SYS_LOGIN_REQUEST, + E_SCP_UDS_LOGIN_REQUEST, + E_SCP_LOGIN_RESPONSE, /* Shared between login request types */ + + E_SCP_LOGOUT_REQUEST, + // No S_SCP_LOGOUT_RESPONSE + E_SCP_CREATE_SESSION_REQUEST, E_SCP_CREATE_SESSION_RESPONSE, + E_SCP_LIST_SESSIONS_REQUEST, - E_SCP_LIST_SESSIONS_RESPONSE + E_SCP_LIST_SESSIONS_RESPONSE, + + E_SCP_CLOSE_CONNECTION_REQUEST + // No E_SCP_CLOSE_CONNECTION_RESPONSE }; /* Common facilities */ @@ -97,6 +109,7 @@ scp_port_to_display_string(const char *port, char *buff, unsigned int bufflen); * Connect to an SCP server * * @param port Port definition (e.g. from sesman.ini) + * @param peername Name of this program or object (e.g. "xrdp-sesadmin") * @param term_func Function to poll during connection for program * termination, or NULL for none. * @return Initialised SCP transport @@ -104,16 +117,19 @@ scp_port_to_display_string(const char *port, char *buff, unsigned int bufflen); * The returned transport has the is_term member set to term_func. */ struct trans * -scp_connect(const char *port, +scp_connect(const char *port, + const char *peername, int (*term_func)(void)); /** * Converts a standard trans connected to an SCP endpoint to an SCP transport * + * If you are running on a client, you may wish to use + * scp_send_set_peername_request() after the commnect to inform the + * server who you are. + * * @param trans connected endpoint * @return != 0 for error - * - * The returned transport has the is_term member set to term_func. */ int scp_init_trans(struct trans *trans); @@ -170,9 +186,54 @@ scp_msg_in_start(struct trans *trans); void scp_msg_in_reset(struct trans *trans); +/* -------------------- Setup messages-------------------- */ /** - * Send an E_SCP_GATEWAY_REQUEST (SCP client) + * Send an E_SCP_SET_PEERNAME_REQUEST (SCP client) + * + * @param trans SCP transport + * @param peername Peername + * @return != 0 for error + * + * Server does not send a response + * + * This message is sent automatically by scp_connect(), but it can + * be sent at any time. + */ +int +scp_send_set_peername_request(struct trans *trans, + const char *peername); + +/** + * Parse an incoming E_SCP_SET_PEERNAME_REQUEST message (SCP server) + * + * @param trans SCP transport + * @param[out] peername peername + * @return != 0 for error + */ +int +scp_get_set_peername_request(struct trans *trans, + const char **peername); + +/* -------------------- Login messages-------------------- */ + +/** + * Send an E_SCP_UDS_LOGIN_REQUEST (SCP client) + * + * User is logged in using their socket details + * + * @param trans SCP transport + * @return != 0 for error + * + * Server replies with E_SCP_LOGIN_RESPONSE + */ +int +scp_send_uds_login_request(struct trans *trans); + +/** + * Send an E_SCP_SYS_LOGIN_REQUEST (SCP client) + * + * User is logged in using explicit credentials * * @param trans SCP transport * @param username Username @@ -180,16 +241,16 @@ scp_msg_in_reset(struct trans *trans); * @param ip_addr IP address for the client (or "" if not known) * @return != 0 for error * - * Server replies with E_SCP_GATEWAY_RESPONSE + * Server replies with E_SCP_LOGIN_RESPONSE */ int -scp_send_gateway_request(struct trans *trans, - const char *username, - const char *password, - const char *ip_addr); +scp_send_sys_login_request(struct trans *trans, + const char *username, + const char *password, + const char *ip_addr); /** - * Parse an incoming E_SCP_GATEWAY_REQUEST message (SCP server) + * Parse an incoming E_SCP_SYS_LOGIN_REQUEST message (SCP server) * * @param trans SCP transport * @param[out] username Username @@ -198,78 +259,89 @@ scp_send_gateway_request(struct trans *trans, * @return != 0 for error */ int -scp_get_gateway_request(struct trans *trans, - const char **username, - const char **password, - const char **ip_addr); +scp_get_sys_login_request(struct trans *trans, + const char **username, + const char **password, + const char **ip_addr); /** - * Send an E_SCP_GATEWAY_RESPONSE (SCP server) + * Send an E_SCP_LOGIN_RESPONSE (SCP server) * * @param trans SCP transport - * @param auth_result 0 for success, PAM error code otherwise + * @param login_result What happened to the login + * @param server_closed. If login fails, whether server has closed connection. + * If not, a retry can be made. * @return != 0 for error */ int -scp_send_gateway_response(struct trans *trans, - int auth_result); +scp_send_login_response(struct trans *trans, + enum scp_login_status login_result, + int server_closed); /** - * Parses an incoming E_SCP_GATEWAY_RESPONSE (SCP client) + * Parses an incoming E_SCP_LOGIN_RESPONSE (SCP client) * * @param trans SCP transport - * @param[out] auth_result 0 for success, PAM error code otherwise + * @param[out] login_result 0 for success, PAM error code otherwise + * @param[out] server_closed. If login fails, whether server has closed + * connection. If not a retry can be made. * @return != 0 for error */ int -scp_get_gateway_response(struct trans *trans, - int *auth_result); +scp_get_login_response(struct trans *trans, + enum scp_login_status *login_result, + int *server_closed); -/* Session messages */ +/** + * Send an E_SCP_LOGOUT_REQUEST (SCP client) + * + * @param trans SCP transport + * @return != 0 for error + * + * Logs the user out (if they are logged in), so that another + * login request can be sent, maybe with a different user. + * + * A reply is not sent + */ +int +scp_send_logout_request(struct trans *trans); + +/* -------------------- Session messages-------------------- */ /** * Send an E_SCP_CREATE_SESSION_REQUEST (SCP client) * * @param trans SCP transport - * @param username Username of session to create or re-connect to - * @param password Password for user * @param type Session type * @param width Initial session width * @param height Initial session height * @param bpp Session bits-per-pixel (ignored for Xorg sessions) * @param shell User program to run. May be "" * @param directory Directory to run the program in. May be "" - * @param ip_addr IP address for the client (or "" if not known) * @return != 0 for error * * Server replies with E_SCP_CREATE_SESSION_RESPONSE */ int scp_send_create_session_request(struct trans *trans, - const char *username, - const char *password, enum scp_session_type type, unsigned short width, unsigned short height, unsigned char bpp, const char *shell, - const char *directory, - const char *ip_addr); + const char *directory); /** * Parse an incoming E_SCP_CREATE_SESSION_REQUEST (SCP server) * * @param trans SCP transport - * @param[out] username Username of session to create or re-connect to - * @param[out] password Password for user * @param[out] type Session type * @param[out] width Initial session width * @param[out] height Initial session height * @param[out] bpp Session bits-per-pixel (ignored for Xorg sessions) * @param[out] shell User program to run. May be "" * @param[out] directory Directory to run the program in. May be "" - * @param[out] ip_addr IP address for the client. May be "" * @return != 0 for error * * Returned string pointers are valid until scp_msg_in_reset() is @@ -277,29 +349,26 @@ scp_send_create_session_request(struct trans *trans, */ int scp_get_create_session_request(struct trans *trans, - const char **username, - const char **password, enum scp_session_type *type, unsigned short *width, unsigned short *height, unsigned char *bpp, const char **shell, - const char **directory, - const char **ip_addr); + const char **directory); /** * Send an E_SCP_CREATE_SESSION_RESPONSE (SCP server) * * @param trans SCP transport - * @param auth_result 0 for success, PAM error code otherwise - * @param display Should be zero if authentication failed. - * @param guid Guid for session. Should be all zeros if authentication failed + * @param status Status of creation request + * @param display Should be zero if create session failed. + * @param guid Guid for session. Should be all zeros if create session failed * * @return != 0 for error */ int scp_send_create_session_response(struct trans *trans, - int auth_result, + enum scp_screate_status status, int display, const struct guid *guid); @@ -308,73 +377,29 @@ scp_send_create_session_response(struct trans *trans, * Parse an incoming E_SCP_CREATE_SESSION_RESPONSE (SCP client) * * @param trans SCP transport - * @param[out] auth_result 0 for success, PAM error code otherwise - * @param[out] display Should be zero if authentication failed. - * @param[out] guid Guid for session. Should be all zeros if authentication + * @param[out] status Status of creation request + * @param[out] display Should be zero if create session failed. + * @param[out] guid Guid for session. Should be all zeros if create session * failed * * @return != 0 for error */ int scp_get_create_session_response(struct trans *trans, - int *auth_result, + enum scp_screate_status *status, int *display, struct guid *guid); -/** - * Status of an E_SCP_LIST_SESSIONS_RESPONSE message - */ -enum scp_list_sessions_status -{ - /** - * This message contains a valid session, and other messages - * will be sent - */ - E_SCP_LS_SESSION_INFO = 0, - - /** - * This message indicates the end of a list of sessions. No session - * is contained in the message */ - E_SCP_LS_END_OF_LIST, - - /** - * Authentication failed for the user - */ - E_SCP_LS_AUTHENTICATION_FAIL = 100, - - /** - * A client-side error occurred allocating memory for the session - */ - E_SCP_LS_NO_MEMORY -}; - /** * Send an E_LIST_SESSIONS_REQUEST (SCP client) * * @param trans SCP transport - * @param username Username - * @param password Password * @return != 0 for error * * Server replies with one or more E_SCP_LIST_SESSIONS_RESPONSE */ int -scp_send_list_sessions_request(struct trans *trans, - const char *username, - const char *password); - -/** - * Parse an incoming E_LIST_SESSIONS_REQUEST (SCP server) - * - * @param trans SCP transport - * @param[out] username Username - * @param[out] password Password - * @return != 0 for error - */ -int -scp_get_list_sessions_request(struct trans *trans, - const char **username, - const char **password); +scp_send_list_sessions_request(struct trans *trans); /** * Send an E_LIST_SESSIONS_RESPONSE (SCP server) @@ -411,5 +436,16 @@ scp_get_list_sessions_response( enum scp_list_sessions_status *status, struct scp_session_info **info); +/** + * Send an E_CLOSE_CONNECTION_REQUEST (SCP client) + * + * @param trans SCP transport + * @return != 0 for error + * + * Server closes the connection quietly. + */ +int +scp_send_close_connection_request(struct trans *trans); + #endif /* SCP_H */ diff --git a/libipm/scp_application_types.c b/libipm/scp_application_types.c new file mode 100644 index 00000000..08d4353f --- /dev/null +++ b/libipm/scp_application_types.c @@ -0,0 +1,84 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors + * + * 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 libipm/scp_application_types.c + * @brief Support routines for types in scp_application_types.h + * @author Matt Burt + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include "scp_application_types.h" +#include "os_calls.h" + +/*****************************************************************************/ +const char * +scp_login_status_to_str(enum scp_login_status n, + char *buff, unsigned int buff_size) +{ + const char *str = + (n == E_SCP_LOGIN_OK) ? "OK" : + (n == E_SCP_LOGIN_ALREADY_LOGGED_IN) ? "A user is already logged in" : + (n == E_SCP_LOGIN_NO_MEMORY) ? "No memory for login" : + (n == E_SCP_LOGIN_NOT_AUTHENTICATED) ? "User does not exist, or could not be authenticated" : + (n == E_SCP_LOGIN_NOT_AUTHORIZED) ? "User is not authorized" : + (n == E_SCP_LOGIN_GENERAL_ERROR) ? "General login error" : + /* Default */ NULL; + + if (str == NULL) + { + g_snprintf(buff, buff_size, "[login error code #%d]", (int)n); + } + else + { + g_snprintf(buff, buff_size, "%s", str); + } + + return buff; +} + +/*****************************************************************************/ +const char * +scp_screate_status_to_str(enum scp_screate_status n, + char *buff, unsigned int buff_size) +{ + const char *str = + (n == E_SCP_SCREATE_OK) ? "OK" : + (n == E_SCP_SCREATE_NO_MEMORY) ? "No memory for session" : + (n == E_SCP_SCREATE_NOT_LOGGED_IN) ? "Connection is not logged in" : + (n == E_SCP_SCREATE_MAX_REACHED) ? "Max session limit reached" : + (n == E_SCP_SCREATE_NO_DISPLAY) ? "No X displays are avaiable" : + (n == E_SCP_SCREATE_GENERAL_ERROR) ? "General session creation error" : + /* Default */ NULL; + + if (str == NULL) + { + g_snprintf(buff, buff_size, "[session creation error code #%d]", + (int)n); + } + else + { + g_snprintf(buff, buff_size, "%s", str); + } + + return buff; +} diff --git a/libipm/scp_application_types.h b/libipm/scp_application_types.h index d8e7c01a..0f6083bc 100644 --- a/libipm/scp_application_types.h +++ b/libipm/scp_application_types.h @@ -26,7 +26,7 @@ #ifndef SCP_APPLICATION_TYPES_H #define SCP_APPLICATION_TYPES_H -#include +#include /** * Select the desktop application session type @@ -57,9 +57,85 @@ struct scp_session_info unsigned short height; ///< Initial session height unsigned char bpp; ///< Session bits-per-pixel time_t start_time; ///< When session was created - char *username; ///< Username for session + uid_t uid; ///< Username for session char *start_ip_addr; ///< IP address of starting client }; +/** + * Status of a login request + */ +enum scp_login_status +{ + E_SCP_LOGIN_OK = 0, ///< The connection is now loggned in + E_SCP_LOGIN_ALREADY_LOGGED_IN, //< A user is currently logged in + E_SCP_LOGIN_NO_MEMORY, ///< Memory allocation failure + /** + * User couldn't be authenticated, or user doesn't exist */ + E_SCP_LOGIN_NOT_AUTHENTICATED, + E_SCP_LOGIN_NOT_AUTHORIZED, ///< User is authenticated, but not authorized + E_SCP_LOGIN_GENERAL_ERROR ///< An unspecific error has occurred +}; + +/** + * Convert an scp_login_status code to a readable string for output + * @param n Message code + * @param buff to contain string + * @param buff_size length of buff + * @return buff is returned for convenience. + */ +const char * +scp_login_status_to_str(enum scp_login_status n, + char *buff, unsigned int buff_size); + +/** + * Status of a session creation request + */ +enum scp_screate_status +{ + E_SCP_SCREATE_OK = 0, ///< Session created + E_SCP_SCREATE_NO_MEMORY, ///< Memory allocation failure + E_SCP_SCREATE_NOT_LOGGED_IN, ///< Connection is not logged in + E_SCP_SCREATE_MAX_REACHED, ///< Max number of sessions already reached + E_SCP_SCREATE_NO_DISPLAY, ///< No X server display number is available + E_SCP_SCREATE_GENERAL_ERROR ///< An unspecific error has occurred +}; + +/** + * Convert an scp_session creation code to a readable string for output + * @param n Message code + * @param buff to contain string + * @param buff_size length of buff + * @return buff is returned for convenience. + */ +const char * +scp_screate_status_to_str(enum scp_screate_status n, + char *buff, unsigned int buff_size); + +/** + * Status of an list sessions message + */ +enum scp_list_sessions_status +{ + /** + * This message contains a valid session, and other messages + * will be sent + */ + E_SCP_LS_SESSION_INFO = 0, + + /** + * This message indicates the end of a list of sessions. No session + * is contained in the message */ + E_SCP_LS_END_OF_LIST, + + /** + * Client hasn't logged in yet + */ + E_SCP_LS_NOT_LOGGED_IN = 100, + /** + * A client-side error occurred allocating memory for the session + */ + E_SCP_LS_NO_MEMORY +}; + #endif /* SCP_APPLICATION_TYPES_H */ From 2a3cec4125b4338939b52581178042ff2e4af8bd Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 13 Sep 2022 10:55:47 +0100 Subject: [PATCH 04/11] Update PAM auth module for UDS logins An extra method auth_uds() is added to the PAM module to allow a 'struct auth_info' to be created for a UDS login. The PAM stack is used to check the UDS user can be authorized. Also, an error code is returned from the auth module rather than a simple boolean. This allows a more complete status to be communicated to the user. See https://github.com/neutrinolabs/xrdp/discussions/1921 and also #909 and #642 --- sesman/auth.h | 18 ++- sesman/verify_user_pam.c | 233 ++++++++++++++++++++++++++------------- 2 files changed, 169 insertions(+), 82 deletions(-) diff --git a/sesman/auth.h b/sesman/auth.h index c07b5177..9d87d8b1 100644 --- a/sesman/auth.h +++ b/sesman/auth.h @@ -27,11 +27,11 @@ #ifndef AUTH_H #define AUTH_H - /** * Opaque type used to represent an authentication handle */ struct auth_info; +#include "scp_application_types.h" /** * @@ -39,13 +39,25 @@ struct auth_info; * @param user user's login name * @param pass user's password * @param client_ip IP address of connecting client (or ""/NULL if not known) - * @param[out] errorcode from result + * @param[out] Error code for the operation. E_SCP_LOGIN_OK on success. * @return auth handle on success, NULL on failure * */ struct auth_info * auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode); + const char *client_ip, enum scp_login_status *errorcode); + +/** + * + * @brief Gets an auth handle for a UDS login + * + * @param uid User ID + * @param[out] Error code for the operation. E_SCP_LOGIN_OK on success. + * @return auth handle on success, NULL on failure + * + */ +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode); /** * diff --git a/sesman/verify_user_pam.c b/sesman/verify_user_pam.c index b75b71ff..abc28ee9 100644 --- a/sesman/verify_user_pam.c +++ b/sesman/verify_user_pam.c @@ -37,10 +37,9 @@ #include #include -/* Allows the conversation function to find the username and password */ -struct t_user_pass +/* Allows the conversation function to find required items */ +struct conv_func_data { - const char *user; const char *pass; }; @@ -100,7 +99,7 @@ msg_style_to_str(int msg_style, char *buff, unsigned int bufflen) * @param[in] num_msg Count of messages in the msg array * @param[in] msg Messages from the PAM stack to the application * @param[out] resp Message replies from the application to the PAM stack - * @param[in] appdata_ptr Used to pass in a struct t_user_pass pointer + * @param[in] appdata_ptr Used to pass in a struct conv_func_data pointer * * @result PAM_SUCCESS if the messages were all processed successfully. * @@ -120,7 +119,7 @@ verify_pam_conv(int num_msg, const struct pam_message **msg, { int i; struct pam_response *reply = NULL; - struct t_user_pass *user_pass; + struct conv_func_data *conv_func_data; char sb[64]; int rv = PAM_SUCCESS; @@ -144,10 +143,10 @@ verify_pam_conv(int num_msg, const struct pam_message **msg, switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: /* password */ - user_pass = (struct t_user_pass *) appdata_ptr; + conv_func_data = (struct conv_func_data *) appdata_ptr; /* Check this function isn't being called * later than we expected */ - if (user_pass == NULL) + if (conv_func_data == NULL || conv_func_data->pass == NULL) { LOG(LOG_LEVEL_ERROR, "verify_pam_conv: Password unavailable"); @@ -155,7 +154,7 @@ verify_pam_conv(int num_msg, const struct pam_message **msg, } else { - reply[i].resp = g_strdup(user_pass->pass); + reply[i].resp = g_strdup(conv_func_data->pass); } break; @@ -216,73 +215,79 @@ get_service_name(char *service_name) } /******************************************************************************/ -/* returns non-NULL for success - * Detailed error code is in the errorcode variable */ -struct auth_info * -auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode) +/** Performs PAM operations common to login methods + * + * @param auth_info Module auth_info structure + * @param user User name + * @param pass Password, if needed for authentication. + * @param client_ip Client IP if known, or NULL + * @param authentication_required True if user must be authenticated + * + * For a UDS connection, the user can be assumed to be authenticated, + * so in this instance authentication_required can be false. + * + * @return Code describing the success of the operation + */ +static enum scp_login_status +common_pam_login(struct auth_info *auth_info, + const char *user, + const char *pass, + const char *client_ip, + int authentication_required) { - int error; - struct auth_info *auth_info; + int perror; char service_name[256]; - struct t_user_pass user_pass = {user, pass}; - struct pam_conv pamc = {verify_pam_conv, (void *) &user_pass}; + struct conv_func_data conv_func_data; + struct pam_conv pamc; - auth_info = g_new0(struct auth_info, 1); - if (auth_info == NULL) - { - LOG(LOG_LEVEL_ERROR, "auth_userpass: No memory"); - error = PAM_BUF_ERR; - return NULL; - } + /* + * Set up the data required by the conversation function, and the + * structure which allows us to pass this to pam_start() + */ + conv_func_data.pass = (authentication_required) ? pass : NULL; + pamc.conv = verify_pam_conv; + pamc.appdata_ptr = (void *) &conv_func_data; get_service_name(service_name); - error = pam_start(service_name, user, &pamc, &(auth_info->ph)); + perror = pam_start(service_name, user, &pamc, &(auth_info->ph)); - if (error != PAM_SUCCESS) + if (perror != PAM_SUCCESS) { - if (errorcode != NULL) - { - *errorcode = error; - } LOG(LOG_LEVEL_ERROR, "pam_start failed: %s", - pam_strerror(auth_info->ph, error)); - pam_end(auth_info->ph, error); - g_free(auth_info); - return NULL; + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_GENERAL_ERROR; } if (client_ip != NULL && client_ip[0] != '\0') { - error = pam_set_item(auth_info->ph, PAM_RHOST, client_ip); - if (error != PAM_SUCCESS) + perror = pam_set_item(auth_info->ph, PAM_RHOST, client_ip); + if (perror != PAM_SUCCESS) { LOG(LOG_LEVEL_ERROR, "pam_set_item(PAM_RHOST) failed: %s", - pam_strerror(auth_info->ph, error)); + pam_strerror(auth_info->ph, perror)); } } - error = pam_set_item(auth_info->ph, PAM_TTY, service_name); - if (error != PAM_SUCCESS) + perror = pam_set_item(auth_info->ph, PAM_TTY, service_name); + if (perror != PAM_SUCCESS) { LOG(LOG_LEVEL_ERROR, "pam_set_item(PAM_TTY) failed: %s", - pam_strerror(auth_info->ph, error)); + pam_strerror(auth_info->ph, perror)); } - error = pam_authenticate(auth_info->ph, 0); - - if (error != PAM_SUCCESS) + if (authentication_required) { - if (errorcode != NULL) + perror = pam_authenticate(auth_info->ph, 0); + + if (perror != PAM_SUCCESS) { - *errorcode = error; + LOG(LOG_LEVEL_ERROR, "pam_authenticate failed: %s", + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_NOT_AUTHENTICATED; } - LOG(LOG_LEVEL_ERROR, "pam_authenticate failed: %s", - pam_strerror(auth_info->ph, error)); - pam_end(auth_info->ph, error); - g_free(auth_info); - return NULL; } /* From man page: The pam_acct_mgmt function is used to determine if the users account is @@ -290,35 +295,99 @@ auth_userpass(const char *user, const char *pass, verifies access restrictions. It is typically called after the user has been authenticated. */ - error = pam_acct_mgmt(auth_info->ph, 0); + perror = pam_acct_mgmt(auth_info->ph, 0); - if (error != PAM_SUCCESS) + if (perror != PAM_SUCCESS) { - if (errorcode != NULL) - { - *errorcode = error; - } LOG(LOG_LEVEL_ERROR, "pam_acct_mgmt failed: %s", - pam_strerror(auth_info->ph, error)); - pam_end(auth_info->ph, error); - g_free(auth_info); - return NULL; + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_NOT_AUTHORIZED; } /* Set the appdata_ptr passed to the conversation function to * NULL, as the existing value is going out of scope */ pamc.appdata_ptr = NULL; - error = pam_set_item(auth_info->ph, PAM_CONV, &pamc); - if (error != PAM_SUCCESS) + perror = pam_set_item(auth_info->ph, PAM_CONV, &pamc); + if (perror != PAM_SUCCESS) { LOG(LOG_LEVEL_ERROR, "pam_set_item(PAM_CONV) failed: %s", - pam_strerror(auth_info->ph, error)); + pam_strerror(auth_info->ph, perror)); + } + + return E_SCP_LOGIN_OK; +} + + +/******************************************************************************/ +/* returns non-NULL for success + * Detailed error code is in the errorcode variable */ + +struct auth_info * +auth_userpass(const char *user, const char *pass, + const char *client_ip, enum scp_login_status *errorcode) +{ + struct auth_info *auth_info; + enum scp_login_status status; + + auth_info = g_new0(struct auth_info, 1); + if (auth_info == NULL) + { + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + status = common_pam_login(auth_info, user, pass, client_ip, 1); + + if (status != E_SCP_LOGIN_OK) + { + g_free(auth_info); + auth_info = NULL; + } + } + + if (errorcode != NULL) + { + *errorcode = status; } return auth_info; } /******************************************************************************/ + +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode) +{ + struct auth_info *auth_info; + enum scp_login_status status; + + auth_info = g_new0(struct auth_info, 1); + if (auth_info == NULL) + { + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + status = common_pam_login(auth_info, user, NULL, NULL, 0); + + if (status != E_SCP_LOGIN_OK) + { + g_free(auth_info); + auth_info = NULL; + } + } + + if (errorcode != NULL) + { + *errorcode = status; + } + + return auth_info; +} + +/******************************************************************************/ + /* returns error */ int auth_start_session(struct auth_info *auth_info, int display_num) @@ -364,17 +433,31 @@ auth_start_session(struct auth_info *auth_info, int display_num) int auth_stop_session(struct auth_info *auth_info) { + int rv = 0; int error; - error = pam_close_session(auth_info->ph, 0); - if (error != PAM_SUCCESS) + if (auth_info->session_opened) { - LOG(LOG_LEVEL_ERROR, "pam_close_session failed: %s", - pam_strerror(auth_info->ph, error)); - return 1; + error = pam_close_session(auth_info->ph, 0); + if (error != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_close_session failed: %s", + pam_strerror(auth_info->ph, error)); + rv = 1; + } + else + { + auth_info->session_opened = 0; + } } - auth_info->session_opened = 0; - return 0; + + if (auth_info->did_setcred) + { + pam_setcred(auth_info->ph, PAM_DELETE_CRED); + auth_info->did_setcred = 0; + } + + return rv; } /******************************************************************************/ @@ -387,15 +470,7 @@ auth_end(struct auth_info *auth_info) { if (auth_info->ph != 0) { - if (auth_info->session_opened) - { - pam_close_session(auth_info->ph, 0); - } - - if (auth_info->did_setcred) - { - pam_setcred(auth_info->ph, PAM_DELETE_CRED); - } + auth_stop_session(auth_info); pam_end(auth_info->ph, PAM_SUCCESS); auth_info->ph = 0; From d2a5fcdcd8dee4e08dfcafd1b72edb78e73e5877 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:45:25 +0000 Subject: [PATCH 05/11] Update other auth modules to use new interface The previous commit introduced a new interface for the auth modules. This commit simply updates the other auth modules to use the new interface. The basic auth module is also updated so that if a user has a shadow password entry indicated, but the shadow entry cannot be found, an error is logged rather than silently succeeding. The BSD authentication module is also updated to allow it to be compiled on a Linux system for basic testing. --- sesman/verify_user.c | 120 +++++++++--- sesman/verify_user_bsd.c | 66 ++++++- sesman/verify_user_kerberos.c | 33 +++- sesman/verify_user_pam_userpass.c | 308 +++++++++++++++++++++++++----- 4 files changed, 433 insertions(+), 94 deletions(-) diff --git a/sesman/verify_user.c b/sesman/verify_user.c index bf0052b7..ac0e9eaa 100644 --- a/sesman/verify_user.c +++ b/sesman/verify_user.c @@ -63,53 +63,115 @@ struct auth_info /* returns non-NULL for success */ struct auth_info * auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode) + const char *client_ip, enum scp_login_status *errorcode) { - const char *encr; - const char *epass; + const char *encr = NULL; struct passwd *spw; - struct spwd *stp; + /* Need a non-NULL pointer to return to indicate success */ static struct auth_info success = {0}; - spw = getpwnam(user); + /* Most likely codepath return from here is 'not authenticated' */ + enum scp_login_status status = E_SCP_LOGIN_NOT_AUTHENTICATED; - if (spw == 0) + /* Find the encrypted password */ + if ((spw = getpwnam(user)) != NULL) { - return NULL; - } - - if (g_strncmp(spw->pw_passwd, "x", 3) == 0) - { - /* the system is using shadow */ - stp = getspnam(user); - - if (stp == 0) + if (g_strncmp(spw->pw_passwd, "x", 3) == 0) { - return NULL; - } + struct spwd *stp; - if (1 == auth_account_disabled(stp)) + /* the system is using shadow */ + if ((stp = getspnam(user)) == NULL) + { + LOG(LOG_LEVEL_ERROR, "Can't get shadow entry for account %s", + user); + status = E_SCP_LOGIN_GENERAL_ERROR; + } + else + { + if (1 == auth_account_disabled(stp)) + { + LOG(LOG_LEVEL_INFO, "account %s is disabled", user); + status = E_SCP_LOGIN_NOT_AUTHORIZED; + } + else + { + encr = stp->sp_pwdp; + } + } + } + else { - LOG(LOG_LEVEL_INFO, "account %s is disabled", user); - return NULL; + /* old system with only passwd */ + encr = spw->pw_passwd; } + } - encr = stp->sp_pwdp; - } - else + if (encr != NULL) { - /* old system with only passwd */ - encr = spw->pw_passwd; + const char *epass; + if ((epass = crypt(pass, encr)) != NULL && + g_strcmp(encr, epass) == 0) + { + status = E_SCP_LOGIN_OK; + } } - epass = crypt(pass, encr); - if (epass == 0) + + if (errorcode != NULL) { - return NULL; + *errorcode = status; } - return (strcmp(encr, epass) == 0) ? &success : NULL; + + return (status == E_SCP_LOGIN_OK) ? &success : NULL; } +/******************************************************************************/ + +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode) +{ + struct passwd *spw; + + /* Need a non-NULL pointer to return to indicate success */ + static struct auth_info success = {0}; + + enum scp_login_status status = E_SCP_LOGIN_OK; + + /* Try to check for a disabled account */ + if ((spw = getpwnam(user)) != NULL) + { + if (g_strncmp(spw->pw_passwd, "x", 3) == 0) + { + struct spwd *stp; + + /* the system is using shadow */ + if ((stp = getspnam(user)) == NULL) + { + LOG(LOG_LEVEL_ERROR, "Can't get shadow entry for account %s", + user); + status = E_SCP_LOGIN_GENERAL_ERROR; + } + else + { + if (1 == auth_account_disabled(stp)) + { + LOG(LOG_LEVEL_INFO, "account %s is disabled", user); + status = E_SCP_LOGIN_NOT_AUTHORIZED; + } + } + } + } + + if (errorcode != NULL) + { + *errorcode = status; + } + + return (status == E_SCP_LOGIN_OK) ? &success : NULL; +} + + /******************************************************************************/ /* returns error */ int diff --git a/sesman/verify_user_bsd.c b/sesman/verify_user_bsd.c index cb3bd532..3dbbbe6c 100644 --- a/sesman/verify_user_bsd.c +++ b/sesman/verify_user_bsd.c @@ -37,13 +37,26 @@ #include #include #include +#include +#include +#if defined(OpenBSD) #include #include - -#ifndef SECS_PER_DAY -#define SECS_PER_DAY (24L*3600L) +#else +/* + * If OpenBSD isn't defined, add static definitions of OpenBSD-specific + * functions. This won't work, but will let the compiler on other + * systems check that all is correctly defined */ +static int +auth_userokay(char *name, char *style, char *type, char *password) +{ + fprintf(stderr, "auth_userokay() not implmented on this platform!\n"); + abort(); + return 0; +} #endif + /* * Need a complete type for struct auth_info, even though we're * not really using it if this module (BSD authentication) is selected */ @@ -55,18 +68,53 @@ struct auth_info /******************************************************************************/ /* returns non-NULL for success */ struct auth_info * -auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode) +auth_userpass(const char *const_user, const char *const_pass, + const char *client_ip, enum scp_login_status *errorcode) { /* Need a non-NULL pointer to return to indicate success */ static struct auth_info success = {0}; - struct auth_info *ret = NULL; + enum scp_login_status status; - if (auth_userokay(user, NULL, "auth-xrdp", pass)) + // auth_userokay is not const-correct. See usr.sbin/smtpd/smtpd.c in + // the OpenBSD source tree for this workaround + char user[LOGIN_NAME_MAX]; + char pass[LINE_MAX]; + char type[] = "auth-xrdp"; + + snprintf(user, sizeof(user), "%s", const_user); + snprintf(pass, sizeof(pass), "%s", const_pass); + + if (auth_userokay(user, NULL, type, pass)) { - ret = &success; + status = E_SCP_LOGIN_OK; } - return ret; + else + { + status = E_SCP_LOGIN_NOT_AUTHENTICATED; + } + + if (errorcode != NULL) + { + *errorcode = status; + } + + return (status == E_SCP_LOGIN_OK) ? &success : NULL; +} + +/******************************************************************************/ +/* returns non-NULL for success */ +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode) +{ + /* Need a non-NULL pointer to return to indicate success */ + static struct auth_info success = {0}; + + if (errorcode != NULL) + { + *errorcode = E_SCP_LOGIN_OK; + } + + return &success; } /******************************************************************************/ diff --git a/sesman/verify_user_kerberos.c b/sesman/verify_user_kerberos.c index 514bc11c..31c9af4d 100644 --- a/sesman/verify_user_kerberos.c +++ b/sesman/verify_user_kerberos.c @@ -127,12 +127,12 @@ k5_begin(const char *username) /******************************************************************************/ /* returns boolean */ -static int +static enum scp_login_status k5_kinit(struct auth_info *auth_info, const char *password) { + enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR; krb5_creds my_creds; krb5_error_code code = 0; - int rv = 0; code = krb5_get_init_creds_password(auth_info->ctx, &my_creds, auth_info->me, @@ -144,6 +144,7 @@ k5_kinit(struct auth_info *auth_info, const char *password) { log_kerberos_failure(auth_info->ctx, code, "krb5_get_init_creds_password"); + status = E_SCP_LOGIN_NOT_AUTHENTICATED; } else { @@ -162,7 +163,7 @@ k5_kinit(struct auth_info *auth_info, const char *password) } else { - rv = 1; + status = E_SCP_LOGIN_OK; } /* Prevent double-free of the client principal */ @@ -174,20 +175,22 @@ k5_kinit(struct auth_info *auth_info, const char *password) krb5_free_cred_contents(auth_info->ctx, &my_creds); } - return rv; + return status; } /******************************************************************************/ /* returns non-NULL for success */ struct auth_info * auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode) + const char *client_ip, enum scp_login_status *errorcode) { + enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR; struct auth_info *auth_info = k5_begin(user); if (auth_info) { - if (!k5_kinit(auth_info, pass)) + status = k5_kinit(auth_info, pass); + if (status != E_SCP_LOGIN_OK) { auth_end(auth_info); auth_info = NULL; @@ -196,7 +199,23 @@ auth_userpass(const char *user, const char *pass, if (errorcode != NULL) { - *errorcode = (auth_info == NULL); + *errorcode = status; + } + + return auth_info; +} + +/******************************************************************************/ +/* returns non-NULL for success */ +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode) +{ + struct auth_info *auth_info = k5_begin(user); + + if (errorcode != NULL) + { + *errorcode = + (auth_info != NULL) ? E_SCP_LOGIN_OK : E_SCP_LOGIN_GENERAL_ERROR; } return auth_info; diff --git a/sesman/verify_user_pam_userpass.c b/sesman/verify_user_pam_userpass.c index 6b971793..8901bb9c 100644 --- a/sesman/verify_user_pam_userpass.c +++ b/sesman/verify_user_pam_userpass.c @@ -18,8 +18,8 @@ /** * - * @file verify_user_pam_userpass.c - * @brief Authenticate user using pam_userpass module + * @file verify_user_pam.c + * @brief Authenticate user using pam * @author Jay Sorg * */ @@ -29,81 +29,220 @@ #endif #include "arch.h" -#include "auth.h" #include "os_calls.h" +#include "log.h" #include "string_calls.h" +#include "auth.h" #include +#include +#include + #define SERVICE "xrdp" -/* - * Need a complete type for struct auth_info, even though we're - * not really using it if this module (PAM userpass) is selected */ struct auth_info { - char dummy; + pam_userpass_t userpass; + int session_opened; + int did_setcred; + struct pam_conv pamc; + pam_handle_t *ph; }; /******************************************************************************/ -/* returns non-NULL for success */ + +/** Performs PAM operations common to login methods + * + * @param auth_info Module auth_info structure + * @param client_ip Client IP if known, or NULL + * @param need_pam_authenticate True if user must be authenticated as + * well as authorized + * @return Code describing the success of the operation + * + * The username is assumed to be supplied by the caller in + * auth_info->userpass.user + */ +static enum scp_login_status +common_pam_login(struct auth_info *auth_info, + const char *client_ip, + int need_pam_authenticate) +{ + int perror; + char service_name[256]; + + perror = pam_start(SERVICE, auth_info->userpass.user, + &(auth_info->pamc), &(auth_info->ph)); + + if (perror != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_start failed: %s", + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_GENERAL_ERROR; + } + + if (client_ip != NULL && client_ip[0] != '\0') + { + perror = pam_set_item(auth_info->ph, PAM_RHOST, client_ip); + if (perror != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_set_item(PAM_RHOST) failed: %s", + pam_strerror(auth_info->ph, perror)); + } + } + + perror = pam_set_item(auth_info->ph, PAM_TTY, service_name); + if (perror != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_set_item(PAM_TTY) failed: %s", + pam_strerror(auth_info->ph, perror)); + } + + if (need_pam_authenticate) + { + perror = pam_authenticate(auth_info->ph, 0); + + if (perror != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_authenticate failed: %s", + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_NOT_AUTHENTICATED; + } + } + /* From man page: + The pam_acct_mgmt function is used to determine if the users account is + valid. It checks for authentication token and account expiration and + verifies access restrictions. It is typically called after the user has + been authenticated. + */ + perror = pam_acct_mgmt(auth_info->ph, 0); + + if (perror != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_acct_mgmt failed: %s", + pam_strerror(auth_info->ph, perror)); + pam_end(auth_info->ph, perror); + return E_SCP_LOGIN_NOT_AUTHORIZED; + } + + return E_SCP_LOGIN_OK; +} + + +/******************************************************************************/ +/* returns non-NULL for success + * Detailed error code is in the errorcode variable */ + struct auth_info * auth_userpass(const char *user, const char *pass, - const char *client_ip, int *errorcode) + const char *client_ip, enum scp_login_status *errorcode) { - pam_handle_t *pamh; - pam_userpass_t userpass; - struct pam_conv conv = {pam_userpass_conv, &userpass}; - const void *template1; - int status; - /* Need a non-NULL pointer to return to indicate success */ - static struct auth_info success = {0}; + struct auth_info *auth_info; + enum scp_login_status status; - userpass.user = user; - userpass.pass = pass; - - if (pam_start(SERVICE, user, &conv, &pamh) != PAM_SUCCESS) + auth_info = g_new0(struct auth_info, 1); + if (auth_info == NULL) { - return NULL; + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + auth_info->userpass.user = user; + auth_info->userpass.pass = pass; + + auth_info->pamc.conv = &pam_userpass_conv; + auth_info->pamc.appdata_ptr = &(auth_info->userpass); + status = common_pam_login(auth_info, client_ip, 1); + + if (status != E_SCP_LOGIN_OK) + { + g_free(auth_info); + auth_info = NULL; + } } - status = pam_authenticate(pamh, 0); - - if (status != PAM_SUCCESS) + if (errorcode != NULL) { - pam_end(pamh, status); - return NULL; + *errorcode = status; } - status = pam_acct_mgmt(pamh, 0); - - if (status != PAM_SUCCESS) - { - pam_end(pamh, status); - return NULL; - } - - status = pam_get_item(pamh, PAM_USER, &template1); - - if (status != PAM_SUCCESS) - { - pam_end(pamh, status); - return NULL; - } - - if (pam_end(pamh, PAM_SUCCESS) != PAM_SUCCESS) - { - return NULL; - } - - return &success; + return auth_info; } /******************************************************************************/ + +struct auth_info * +auth_uds(const char *user, enum scp_login_status *errorcode) +{ + struct auth_info *auth_info; + enum scp_login_status status; + + auth_info = g_new0(struct auth_info, 1); + if (auth_info == NULL) + { + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + auth_info->userpass.user = user; + status = common_pam_login(auth_info, NULL, 0); + + if (status != E_SCP_LOGIN_OK) + { + g_free(auth_info); + auth_info = NULL; + } + } + + if (errorcode != NULL) + { + *errorcode = status; + } + + return auth_info; +} + +/******************************************************************************/ + /* returns error */ int auth_start_session(struct auth_info *auth_info, int display_num) { + int error; + char display[256]; + + g_sprintf(display, ":%d", display_num); + error = pam_set_item(auth_info->ph, PAM_TTY, display); + + if (error != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_set_item failed: %s", + pam_strerror(auth_info->ph, error)); + return 1; + } + + error = pam_setcred(auth_info->ph, PAM_ESTABLISH_CRED); + + if (error != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_setcred failed: %s", + pam_strerror(auth_info->ph, error)); + return 1; + } + + auth_info->did_setcred = 1; + error = pam_open_session(auth_info->ph, 0); + + if (error != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_open_session failed: %s", + pam_strerror(auth_info->ph, error)); + return 1; + } + + auth_info->session_opened = 1; return 0; } @@ -112,19 +251,90 @@ auth_start_session(struct auth_info *auth_info, int display_num) int auth_stop_session(struct auth_info *auth_info) { - return 0; + int rv = 0; + int error; + + if (auth_info->session_opened) + { + error = pam_close_session(auth_info->ph, 0); + if (error != PAM_SUCCESS) + { + LOG(LOG_LEVEL_ERROR, "pam_close_session failed: %s", + pam_strerror(auth_info->ph, error)); + rv = 1; + } + else + { + auth_info->session_opened = 0; + } + } + + if (auth_info->did_setcred) + { + pam_setcred(auth_info->ph, PAM_DELETE_CRED); + auth_info->did_setcred = 0; + } + + return rv; } /******************************************************************************/ +/* returns error */ +/* cleanup */ int auth_end(struct auth_info *auth_info) { + if (auth_info != NULL) + { + if (auth_info->ph != 0) + { + auth_stop_session(auth_info); + + pam_end(auth_info->ph, PAM_SUCCESS); + auth_info->ph = 0; + } + } + + g_free(auth_info); return 0; } /******************************************************************************/ +/* returns error */ +/* set any pam env vars */ int auth_set_env(struct auth_info *auth_info) { + char **pam_envlist; + char **pam_env; + char item[256]; + char value[256]; + int eq_pos; + + if (auth_info != NULL) + { + /* export PAM environment */ + pam_envlist = pam_getenvlist(auth_info->ph); + + if (pam_envlist != NULL) + { + for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) + { + eq_pos = g_pos(*pam_env, "="); + + if (eq_pos >= 0 && eq_pos < 250) + { + g_strncpy(item, *pam_env, eq_pos); + g_strncpy(value, (*pam_env) + eq_pos + 1, 255); + g_setenv(item, value, 1); + } + + g_free(*pam_env); + } + + g_free(pam_envlist); + } + } + return 0; } From c5b6479985fc5a6768630bb02f4fdf8249420dfa Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:06:26 +0000 Subject: [PATCH 06/11] Update authtest utility This change allows the authtest utility to exercise the updated auth module interface which includes UDS authentication and improved error logging. --- sesman/tools/Makefile.am | 1 + sesman/tools/authtest.c | 93 ++++++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 17 deletions(-) diff --git a/sesman/tools/Makefile.am b/sesman/tools/Makefile.am index 8e811d75..cc32cc74 100644 --- a/sesman/tools/Makefile.am +++ b/sesman/tools/Makefile.am @@ -61,4 +61,5 @@ xrdp_xcon_LDADD = \ xrdp_authtest_LDADD = \ $(top_builddir)/common/libcommon.la \ + $(top_builddir)/libipm/libipm.la \ $(AUTHMOD_LIB) diff --git a/sesman/tools/authtest.c b/sesman/tools/authtest.c index 93e151c6..548032ec 100644 --- a/sesman/tools/authtest.c +++ b/sesman/tools/authtest.c @@ -68,14 +68,17 @@ usage(void) "etc.\n\n" "This is a DEVELOPER-ONLY tool\n"); g_printf("\nusage:\n"); - g_printf("authtest [options] username\n\n"); + g_printf("authtest [options] [username]\n\n"); g_printf("options:\n"); g_printf(" -p \n" " -F Read password from this file descriptor\n" " -c Start a session and run the\n" " specified non-interactive command\n" " in it\n"); - g_printf("Password is prompted if -p or -F are not specified\n"); + g_printf("\nIf username is omitted, the current user is used, and.\n" + "a UDS login is attempted\n" + "If username is provided, password is needed.\n" + " Password is prompted for if -p or -F are not specified\n"); } @@ -186,10 +189,15 @@ parse_program_args(int argc, char *argv[], struct authmod_params *amp) } } - if (argc <= optind) + if (argc == optind) { - LOG(LOG_LEVEL_ERROR, "No user name specified"); - params_ok = 0; + // No username was specified + if (password_set) + { + LOG(LOG_LEVEL_WARNING, "No username - ignoring specified password"); + amp->password[0] = '\0'; + } + amp->username = NULL; } else if ((argc - optind) > 1) { @@ -199,26 +207,58 @@ parse_program_args(int argc, char *argv[], struct authmod_params *amp) else { amp->username = argv[optind]; - } - - if (params_ok && !password_set) - { - const char *p = getpass("Password: "); - if (p != NULL) + if (!password_set) { - g_strcpy(amp->password, p); + const char *p = getpass("Password: "); + if (p == NULL) + { + params_ok = 0; + } + else + { + g_snprintf(amp->password, sizeof(amp->password), "%s", p); + } } } return params_ok; } +/******************************************************************************/ +/** + * Gets the current username for a UDS login + * + * Result must be freed after use. + */ +static char * +get_username() +{ + int uid = g_getuid(); + char *this_user; + int status; + + status = g_getuser_info_by_uid(uid, &this_user, NULL, NULL, NULL, NULL); + if (status != 0) + { + LOG(LOG_LEVEL_ERROR, "Can't map UID %d to a username", uid); + this_user = NULL; + } + else + { + LOG(LOG_LEVEL_INFO, "Mapped current UID %d to \"%s\"", + uid, this_user); + } + + return this_user; +} + /******************************************************************************/ int main(int argc, char **argv) { struct log_config *logging; - int rv = 1; + int rv = 0; + char *this_user = NULL; struct authmod_params amp; logging = log_config_init_for_console(LOG_LEVEL_DEBUG, @@ -229,16 +269,34 @@ main(int argc, char **argv) if (!parse_program_args(argc, argv, &)) { usage(); + rv = 1; + } + else if (amp.username == NULL && (this_user = get_username()) == NULL) + { + rv = 1; } else { struct auth_info *auth_info; - auth_info = auth_userpass(amp.username, amp.password, - NULL, &rv); + enum scp_login_status errorcode; + char errstr[64]; - LOG(LOG_LEVEL_INFO, "auth_userpass() returned %s, errorcode=%d", + if (amp.username == NULL) + { + auth_info = auth_uds(this_user, &errorcode); + } + else + { + auth_info = auth_userpass(amp.username, amp.password, + NULL, &errorcode); + } + scp_login_status_to_str(errorcode, errstr, sizeof(errstr)); + LOG(LOG_LEVEL_INFO, + "auth_userpass() returned %s, errorcode=%d [%s]", (auth_info == NULL) ? "NULL" : "non-NULL", - rv); + (int)errorcode, errstr); + + rv = (int)errorcode; if (auth_info && rv == 0 && amp.command != NULL) { int display = 10; @@ -261,6 +319,7 @@ main(int argc, char **argv) } } + g_free(this_user); log_end(); return rv; From fd99653957e4f86e59aa67d4f298e5dac1852ce2 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 14 Dec 2022 09:36:07 +0000 Subject: [PATCH 07/11] Update sesman authentication processing Update sesman to cope with separate authentication/authorization (AA) and command processing. Also, internally users are now tracked by UID rather thn username. This addresses a problem found by some users using federated naming services (e.g. Active Directory) where the same user can be referred to in more than one way. See https://github.com/neutrinolabs/xrdp/issues/1823 The separation of AA in this way allows for multiple attempts to be made on one connection to get a password right. This addresses MaxLoginRetry not working (https://github.com/neutrinolabs/xrdp/issues/1739) --- sesman/env.c | 31 ++- sesman/env.h | 4 +- sesman/scp_process.c | 531 +++++++++++++++++++++++++++++++++---------- sesman/scp_process.h | 6 +- sesman/sesman.c | 88 +++++-- sesman/sesman.h | 23 +- sesman/session.c | 150 +++++++----- sesman/session.h | 15 +- 8 files changed, 626 insertions(+), 222 deletions(-) diff --git a/sesman/env.c b/sesman/env.c index e840de31..7b5e2aba 100644 --- a/sesman/env.c +++ b/sesman/env.c @@ -92,27 +92,27 @@ env_check_password_file(const char *filename, const char *passwd) /******************************************************************************/ /* its the responsibility of the caller to free passwd_file */ int -env_set_user(const char *username, char **passwd_file, int display, +env_set_user(int uid, char **passwd_file, int display, const struct list *env_names, const struct list *env_values) { int error; - int pw_uid; int pw_gid; - int uid; int index; int len; char *name; char *value; + char *pw_username; char *pw_shell; char *pw_dir; char text[256]; char hostname[256]; + pw_username = 0; pw_shell = 0; pw_dir = 0; - error = g_getuser_info_by_name(username, &pw_uid, &pw_gid, &pw_shell, - &pw_dir, 0); + error = g_getuser_info_by_uid(uid, &pw_username, &pw_gid, &pw_shell, + &pw_dir, 0); if (error == 0) { @@ -124,7 +124,6 @@ env_set_user(const char *username, char **passwd_file, int display, if (error == 0) { - uid = pw_uid; error = g_setuid(uid); } @@ -135,8 +134,8 @@ env_set_user(const char *username, char **passwd_file, int display, g_clearenv(); g_setenv("SHELL", pw_shell, 1); g_setenv("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin", 1); - g_setenv("USER", username, 1); - g_setenv("LOGNAME", username, 1); + g_setenv("USER", pw_username, 1); + g_setenv("LOGNAME", pw_username, 1); g_sprintf(text, "%d", uid); g_setenv("UID", text, 1); g_setenv("HOME", pw_dir, 1); @@ -182,14 +181,14 @@ env_set_user(const char *username, char **passwd_file, int display, } len = g_snprintf(NULL, 0, "%s/.vnc/sesman_passwd-%s@%s:%d", - pw_dir, username, hostname, display); + pw_dir, pw_username, hostname, display); *passwd_file = (char *) g_malloc(len + 1, 1); if (*passwd_file != NULL) { /* Try legacy names first, remove if found */ g_sprintf(*passwd_file, "%s/.vnc/sesman_%s_passwd:%d", - pw_dir, username, display); + pw_dir, pw_username, display); if (g_file_exist(*passwd_file)) { LOG(LOG_LEVEL_WARNING, "Removing old " @@ -197,7 +196,7 @@ env_set_user(const char *username, char **passwd_file, int display, g_file_delete(*passwd_file); } g_sprintf(*passwd_file, "%s/.vnc/sesman_%s_passwd", - pw_dir, username); + pw_dir, pw_username); if (g_file_exist(*passwd_file)) { LOG(LOG_LEVEL_WARNING, "Removing insecure " @@ -205,18 +204,18 @@ env_set_user(const char *username, char **passwd_file, int display, g_file_delete(*passwd_file); } g_sprintf(*passwd_file, "%s/.vnc/sesman_passwd-%s@%s:%d", - pw_dir, username, hostname, display); + pw_dir, pw_username, hostname, display); } } else { /* we use auth_file_path as requested */ - len = g_snprintf(NULL, 0, g_cfg->auth_file_path, username); + len = g_snprintf(NULL, 0, g_cfg->auth_file_path, pw_username); *passwd_file = (char *) g_malloc(len + 1, 1); if (*passwd_file != NULL) { - g_sprintf(*passwd_file, g_cfg->auth_file_path, username); + g_sprintf(*passwd_file, g_cfg->auth_file_path, pw_username); } } @@ -226,6 +225,7 @@ env_set_user(const char *username, char **passwd_file, int display, } } + g_free(pw_username); g_free(pw_dir); g_free(pw_shell); } @@ -233,8 +233,7 @@ env_set_user(const char *username, char **passwd_file, int display, else { LOG(LOG_LEVEL_ERROR, - "error getting user info for user %s", - username); + "error getting user info for uid %d", uid); } return error; diff --git a/sesman/env.h b/sesman/env.h index d499b838..615285bb 100644 --- a/sesman/env.h +++ b/sesman/env.h @@ -43,14 +43,14 @@ env_check_password_file(const char *filename, const char *password); /** * * @brief Sets user environment ($PATH, $HOME, $UID, and others) - * @param username Username + * @param uid user ID * @param passwd_file VNC password file * @param display The session display * @return 0 on success, g_getuser_info() error codes on error * */ int -env_set_user(const char *username, char **passwd_file, int display, +env_set_user(int uid, char **passwd_file, int display, const struct list *env_names, const struct list *env_values); #endif diff --git a/sesman/scp_process.c b/sesman/scp_process.c index 3ff6e49f..73363671 100644 --- a/sesman/scp_process.c +++ b/sesman/scp_process.c @@ -35,7 +35,10 @@ #include "scp_process.h" #include "access.h" #include "auth.h" +#include "os_calls.h" #include "session.h" +#include "sesman.h" +#include "string_calls.h" /**************************************************************************//** * Logs an authentication failure message @@ -58,142 +61,408 @@ log_authfail_message(const char *username, const char *ip_addr) /******************************************************************************/ -static int -process_gateway_request(struct trans *trans) +/** + * Mode parameter for authenticate_and_authorize_connection() + */ +enum login_mode { - int rv; - const char *username; - const char *password; - const char *ip_addr; + AM_SYSTEM, + AM_UDS +}; - rv = scp_get_gateway_request(trans, &username, &password, &ip_addr); - if (rv == 0) +/** + * Authenticate and authorize the connection + * + * @param sc Connection to sesman + * @param login_mode Describes the type of login in use + * @param uid UID for user + * @param username Name for user + * @param password Password (AM_SYSTEM) or NULL. + * @param ip_addr Remote IP address (AM_SYSTEM) or NULL. + * @return Status for the operation + * + * @pre sc->auth_info, sc->username and sc->ip_addr must be NULL + * + * @post If E_SCP_LOGIN_OK is returned, sc->auth_info is non-NULL + * @post If E_SCP_LOGIN_OK is returned, sc->username is non-NULL + * @post If E_SCP_LOGIN_OK is returned, sc->ip_addr is non-NULL + * + */ +static enum scp_login_status +authenticate_and_authorize_connection(struct sesman_con *sc, + enum login_mode login_mode, + int uid, + const char *username, + const char *password, + const char *ip_addr) +{ + enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR; + struct auth_info *auth_info = NULL; + + /* Check preconditions */ + if (sc->auth_info != NULL || sc->username != NULL || sc->ip_addr != NULL) { - int errorcode = 0; - struct auth_info *auth_info; + LOG(LOG_LEVEL_ERROR, + "Internal error - connection already logged in"); + } + else + { + switch (login_mode) + { + case AM_SYSTEM: + auth_info = auth_userpass(username, password, ip_addr, &status); + break; + case AM_UDS: + auth_info = auth_uds(username, &status); + break; + default: + LOG(LOG_LEVEL_ERROR, "%s called with invalid mode %d", + __func__, (int)login_mode); + } - LOG(LOG_LEVEL_INFO, "Received authentication request for user: %s", - username); - - auth_info = auth_userpass(username, password, ip_addr, &errorcode); if (auth_info != NULL) { - if (1 == access_login_allowed(username)) + if (status != E_SCP_LOGIN_OK) { - /* the user is member of the correct groups. */ - LOG(LOG_LEVEL_INFO, "Access permitted for user: %s", - username); + /* This shouldn't happen */ + LOG(LOG_LEVEL_ERROR, + "Unexpected status return %d from auth call", + (int)status); } - else + else if (!access_login_allowed(username)) { - /* all first 32 are reserved for PAM errors */ - errorcode = 32 + 3; + status = E_SCP_LOGIN_NOT_AUTHORIZED; LOG(LOG_LEVEL_INFO, "Username okay but group problem for " "user: %s", username); } + + /* If all is well, put the auth_info in the sesman connection + * for later use. If not, remove the auth_info */ + if (status == E_SCP_LOGIN_OK) + { + char *dup_username = g_strdup(username); + char *dup_ip_addr = + (ip_addr == NULL) ? g_strdup("") : g_strdup(ip_addr); + + if (dup_username == NULL || dup_ip_addr == NULL) + { + LOG(LOG_LEVEL_ERROR, "%s : Memory allocation failed", + __func__); + g_free(dup_username); + g_free(dup_ip_addr); + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + LOG(LOG_LEVEL_INFO, "Access permitted for user: %s", + username); + sc->auth_info = auth_info; + sc->uid = uid; + sc->username = dup_username; + sc->ip_addr = dup_ip_addr; + } + } + + if (status != E_SCP_LOGIN_OK) + { + auth_end(auth_info); + } } - else - { - log_authfail_message(username, ip_addr); - } - rv = scp_send_gateway_response(trans, errorcode); - auth_end(auth_info); } + + return status; +} + +/******************************************************************************/ + +static int +process_set_peername_request(struct sesman_con *sc) +{ + int rv; + const char *peername; + + rv = scp_get_set_peername_request(sc->t, &peername); + if (rv == 0) + { + if (sesman_set_connection_peername(sc, peername) != 0) + { + LOG(LOG_LEVEL_WARNING, + "Failed to set connection peername from %s to %s", + sc->peername, peername); + } + } + return rv; } /******************************************************************************/ static int -process_create_session_request(struct trans *trans) +process_sys_login_request(struct sesman_con *sc) +{ + int rv; + const char *supplied_username; + const char *password; + const char *ip_addr; + + rv = scp_get_sys_login_request(sc->t, &supplied_username, + &password, &ip_addr); + if (rv == 0) + { + enum scp_login_status errorcode; + int server_closed = 1; + int uid; + char *username = NULL; + + LOG(LOG_LEVEL_INFO, + "Received system login request from %s for user: %s IP: %s", + sc->peername, supplied_username, ip_addr); + + if (sc->auth_info != NULL) + { + errorcode = E_SCP_LOGIN_ALREADY_LOGGED_IN; + LOG(LOG_LEVEL_ERROR, "Connection is already logged in for %s", + sc->username); + } + else if (g_getuser_info_by_name(supplied_username, + &uid, NULL, NULL, NULL, NULL) != 0) + { + /* we can't get a UID for the user */ + errorcode = E_SCP_LOGIN_NOT_AUTHENTICATED; + LOG(LOG_LEVEL_ERROR, "Can't get UID for user %s", + supplied_username); + log_authfail_message(username, ip_addr); + } + else if (g_getuser_info_by_uid(uid, + &username, NULL, NULL, NULL, NULL) != 0) + { + errorcode = E_SCP_LOGIN_GENERAL_ERROR; + LOG(LOG_LEVEL_ERROR, "Can't reverse lookup UID %d", uid); + } + else + { + if (g_strcmp(supplied_username, username) != 0) + { + /* + * If using a federated naming service (e.g. AD), the + * username supplied may not match that name mapped to by + * the UID. We will generate a warning in this instance so + * the user can see what is being used within sesman + */ + LOG(LOG_LEVEL_WARNING, + "Using username %s for the session (from UID %d)", + username, uid); + } + + errorcode = authenticate_and_authorize_connection( + sc, AM_SYSTEM, + uid, username, + password, ip_addr); + if (errorcode == E_SCP_LOGIN_OK) + { + server_closed = 0; + } + else if (errorcode == E_SCP_LOGIN_NOT_AUTHENTICATED) + { + log_authfail_message(username, ip_addr); + if (sc->auth_retry_count > 0) + { + /* Password problem? Invite the user to retry */ + server_closed = 0; + --sc->auth_retry_count; + } + } + + g_free(username); + } + + if (server_closed) + { + /* Expecting no more client messages. Close the connection + * after returning from this callback */ + sc->close_requested = 1; + } + + rv = scp_send_login_response(sc->t, errorcode, server_closed); + } + + return rv; +} + +/******************************************************************************/ + +static int +process_uds_login_request(struct sesman_con *sc) +{ + enum scp_login_status errorcode; + int rv; + int uid; + int pid; + char *username = NULL; + int server_closed = 1; + + rv = g_sck_get_peer_cred(sc->t->sck, &pid, &uid, NULL); + if (rv != 0) + { + LOG(LOG_LEVEL_INFO, + "Unable to get peer credentials for socket %d", + (int)sc->t->sck); + errorcode = E_SCP_LOGIN_GENERAL_ERROR; + } + else + { + LOG(LOG_LEVEL_INFO, + "Received UDS login request from %s for UID: %d from PID: %d", + sc->peername, uid, pid); + + if (sc->auth_info != NULL) + { + errorcode = E_SCP_LOGIN_ALREADY_LOGGED_IN; + LOG(LOG_LEVEL_ERROR, "Connection is already logged in for %s", + sc->username); + } + else if (g_getuser_info_by_uid(uid, &username, + NULL, NULL, NULL, NULL) != 0) + { + errorcode = E_SCP_LOGIN_GENERAL_ERROR; + LOG(LOG_LEVEL_ERROR, "Can't reverse lookup UID %d", uid); + } + else + { + errorcode = authenticate_and_authorize_connection( + sc, AM_UDS, + uid, username, + NULL, NULL); + g_free(username); + + if (errorcode == E_SCP_LOGIN_OK) + { + server_closed = 0; + } + } + } + + if (server_closed) + { + /* Close the connection after returning from this callback */ + sc->close_requested = 1; + } + + return scp_send_login_response(sc->t, errorcode, server_closed); +} + +/******************************************************************************/ + +static int +process_logout_request(struct sesman_con *sc) +{ + if (sc->auth_info != NULL) + { + LOG(LOG_LEVEL_INFO, "Logging out %s from sesman", sc->username); + auth_end(sc->auth_info); + sc->auth_info = NULL; + sc->uid = -1; + g_free(sc->username); + sc->username = NULL; + g_free(sc->ip_addr); + sc->ip_addr = NULL; + } + + return 0; +} + +/******************************************************************************/ + +static int +process_create_session_request(struct sesman_con *sc) { int rv; struct session_parameters sp; - const char *password; struct guid guid; + int display = 0; + enum scp_screate_status status = E_SCP_SCREATE_OK; guid_clear(&guid); - int display = 0; - rv = scp_get_create_session_request( - trans, - &sp.username, &password, - &sp.type, &sp.width, &sp.height, &sp.bpp, - &sp.shell, &sp.directory, &sp.ip_addr); + rv = scp_get_create_session_request(sc->t, + &sp.type, &sp.width, &sp.height, + &sp.bpp, &sp.shell, &sp.directory); if (rv == 0) { - struct auth_info *auth_info; - struct session_item *s_item; - int errorcode = 0; - bool_t do_auth_end = 1; - - LOG(LOG_LEVEL_INFO, - "Received request to create %s session for user: %s", - SCP_SESSION_TYPE_TO_STR(sp.type), - sp.username); - - auth_info = auth_userpass(sp.username, password, sp.ip_addr, &errorcode); - if (auth_info != NULL) + if (sc->auth_info == NULL) { - s_item = session_get_bydata(&sp); + status = E_SCP_SCREATE_NOT_LOGGED_IN; + } + else + { + LOG(LOG_LEVEL_INFO, + "Received request from %s to create a session for user %s", + sc->peername, sc->username); + + // Copy over the items we got from the login request, + // but which we need to describe the session + sp.uid = sc->uid; + sp.ip_addr = sc->ip_addr; // Guaranteed to be non-NULL + + struct session_item *s_item = session_get_bydata(&sp); if (s_item != 0) { + // Found an existing session display = s_item->display; guid = s_item->guid; if (sp.ip_addr[0] != '\0') { LOG( LOG_LEVEL_INFO, "++ reconnected session: username %s, " "display :%d.0, session_pid %d, ip %s", - sp.username, display, s_item->pid, sp.ip_addr); + sc->username, display, s_item->pid, sp.ip_addr); } else { LOG(LOG_LEVEL_INFO, "++ reconnected session: username %s, " - "display :%d.0, session_pid %d", sp.username, display, - s_item->pid); + "display :%d.0, session_pid %d", + sc->username, display, s_item->pid); } - session_reconnect(display, sp.username, auth_info); + session_reconnect(display, sc->uid, sc->auth_info); } else { - LOG_DEVEL(LOG_LEVEL_DEBUG, "pre auth"); - - if (1 == access_login_allowed(sp.username)) + // Need to create a new session + if (sp.ip_addr[0] != '\0') { - if (sp.ip_addr[0] != '\0') - { - LOG(LOG_LEVEL_INFO, - "++ created session (access granted): " - "username %s, ip %s", sp.username, sp.ip_addr); - } - else - { - LOG(LOG_LEVEL_INFO, - "++ created session (access granted): " - "username %s", sp.username); - } + LOG(LOG_LEVEL_INFO, + "++ created session: username %s, ip %s", + sc->username, sp.ip_addr); + } + else + { + LOG(LOG_LEVEL_INFO, + "++ created session: username %s", sc->username); + } - display = session_start(auth_info, &sp, &guid); - - /* if the session started up ok, auth_end will be called on - sig child */ - do_auth_end = display == 0; + // The new session will have a lifetime longer than + // the sesman connection, and so needs to own + // the auth_info struct. + // + // Copy the auth_info struct out of the connection and pass + // it to the session + struct auth_info *auth_info = sc->auth_info; + sc->auth_info = NULL; + status = session_start(auth_info, &sp, &display, &guid); + if (status != E_SCP_SCREATE_OK) + { + // Close the auth session down as it can't be re-used. + auth_end(auth_info); } } } - else - { - log_authfail_message(sp.username, sp.ip_addr); - } - if (do_auth_end) - { - auth_end(auth_info); - } + /* Currently a create session request is the last thing on a + * connection, and results in automatic closure */ + sc->close_requested = 1; - rv = scp_send_create_session_response(trans, errorcode, display, &guid); + rv = scp_send_create_session_response(sc->t, status, display, &guid); } return rv; @@ -202,75 +471,99 @@ process_create_session_request(struct trans *trans) /******************************************************************************/ static int -process_list_sessions_request(struct trans *trans) +process_list_sessions_request(struct sesman_con *sc) { - int rv; + int rv = 0; - const char *username; - const char *password; + struct scp_session_info *info = NULL; + unsigned int cnt = 0; + unsigned int i; - rv = scp_get_list_sessions_request(trans, &username, &password); - if (rv == 0) + if (sc->auth_info == NULL) + { + rv = scp_send_list_sessions_response(sc->t, + E_SCP_LS_NOT_LOGGED_IN, + NULL); + } + else { - enum scp_list_sessions_status status; - int errorcode = 0; - struct auth_info *auth_info; - LOG(LOG_LEVEL_INFO, - "Received request to list sessions for user %s", username); + "Received request from %s to list sessions for user %s", + sc->peername, sc->username); - auth_info = auth_userpass(username, password, NULL, &errorcode); - if (auth_info != NULL) - { - struct scp_session_info *info = NULL; - unsigned int cnt = 0; - unsigned int i; - info = session_get_byuser(username, &cnt, - SESMAN_SESSION_STATUS_ALL); + info = session_get_byuid(sc->uid, &cnt, + SESMAN_SESSION_STATUS_ALL); - for (i = 0; rv == 0 && i < cnt; ++i) - { - rv = scp_send_list_sessions_response(trans, - E_SCP_LS_SESSION_INFO, - &info[i]); - } - free_session_info_list(info, cnt); - status = E_SCP_LS_END_OF_LIST; - } - else + for (i = 0; rv == 0 && i < cnt; ++i) { - status = E_SCP_LS_AUTHENTICATION_FAIL; + rv = scp_send_list_sessions_response(sc->t, + E_SCP_LS_SESSION_INFO, + &info[i]); } - auth_end(auth_info); + free_session_info_list(info, cnt); if (rv == 0) { - rv = scp_send_list_sessions_response(trans, status, NULL); + rv = scp_send_list_sessions_response(sc->t, + E_SCP_LS_END_OF_LIST, + NULL); } } return rv; } +/******************************************************************************/ + +static int +process_close_connection_request(struct sesman_con *sc) +{ + int rv = 0; + + LOG(LOG_LEVEL_INFO, "Received request to close connection from %s", + sc->peername); + + /* Expecting no more client messages. Close the connection + * after returning from this callback */ + sc->close_requested = 1; + return rv; +} + /******************************************************************************/ int -scp_process(struct trans *t) +scp_process(struct sesman_con *sc) { enum scp_msg_code msgno; int rv = 0; - switch ((msgno = scp_msg_in_start(t))) + switch ((msgno = scp_msg_in_start(sc->t))) { - case E_SCP_GATEWAY_REQUEST: - rv = process_gateway_request(t); + case E_SCP_SET_PEERNAME_REQUEST: + rv = process_set_peername_request(sc); + break; + + case E_SCP_SYS_LOGIN_REQUEST: + rv = process_sys_login_request(sc); + break; + + case E_SCP_UDS_LOGIN_REQUEST: + rv = process_uds_login_request(sc); + break; + + case E_SCP_LOGOUT_REQUEST: + rv = process_logout_request(sc); break; case E_SCP_CREATE_SESSION_REQUEST: - rv = process_create_session_request(t); + rv = process_create_session_request(sc); break; case E_SCP_LIST_SESSIONS_REQUEST: - rv = process_list_sessions_request(t); + rv = process_list_sessions_request(sc); + break; + + case E_SCP_CLOSE_CONNECTION_REQUEST: + rv = process_close_connection_request(sc); break; default: diff --git a/sesman/scp_process.h b/sesman/scp_process.h index c52966b6..f53875c2 100644 --- a/sesman/scp_process.h +++ b/sesman/scp_process.h @@ -27,15 +27,15 @@ #ifndef SCP_PROCESS_H #define SCP_PROCESS_H -struct trans; +struct sesman_con; /** * * @brief Processes an SCP message - * @param t the connection trans + * @param sc the sesman connection * */ int -scp_process(struct trans *t); +scp_process(struct sesman_con *sc); #endif diff --git a/sesman/sesman.c b/sesman/sesman.c index 86833e51..5f06bfec 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -33,6 +33,7 @@ #include "arch.h" #include "sesman.h" +#include "auth.h" #include "config.h" #include "lock_uds.h" #include "os_calls.h" @@ -123,9 +124,11 @@ alloc_connection(struct trans *t) { struct sesman_con *result; - if ((result = g_new(struct sesman_con, 1)) != NULL) + if ((result = g_new0(struct sesman_con, 1)) != NULL) { + g_snprintf(result->peername, sizeof(result->peername), "%s", "unknown"); result->t = t; + result->auth_retry_count = g_cfg->sec.login_retry; } return result; @@ -137,15 +140,40 @@ alloc_connection(struct trans *t) * After this call, the passed-in pointer is invalid and must not be * referenced. * + * Any auth_info struct found in the sesman_con is also deallocated. + * * @param sc struct to de-allocate */ static void delete_connection(struct sesman_con *sc) { - trans_delete(sc->t); - g_free(sc); + if (sc != NULL) + { + trans_delete(sc->t); + if (sc->auth_info != NULL) + { + auth_end(sc->auth_info); + } + g_free(sc->username); + g_free(sc->ip_addr); + g_free(sc); + } } +/*****************************************************************************/ +int +sesman_set_connection_peername(struct sesman_con *sc, const char *name) +{ + int rv = 1; + + if (sc != NULL && name != NULL) + { + g_snprintf(sc->peername, sizeof(sc->peername), "%s", name); + rv = 0; + } + + return rv; +} /*****************************************************************************/ /** @@ -272,16 +300,23 @@ static int sesman_listen_test(struct config_sesman *cfg) /******************************************************************************/ int -sesman_close_all(void) +sesman_close_all(unsigned int flags) { int index; struct sesman_con *sc; LOG_DEVEL(LOG_LEVEL_TRACE, "sesman_close_all:"); + + sesman_delete_listening_transport(); for (index = 0; index < g_con_list->count; index++) { sc = (struct sesman_con *) list_get_item(g_con_list, index); + if (sc != NULL && (flags & SCA_CLOSE_AUTH_INFO) == 0) + { + // Prevent delete_connection() closing the auth_info down + sc->auth_info = NULL; + } delete_connection(sc); } return 0; @@ -298,7 +333,8 @@ sesman_data_in(struct trans *self) if (rv == 0 && available) { - if ((rv = scp_process(self)) != 0) + struct sesman_con *sc = (struct sesman_con *)self->callback_data; + if ((rv = scp_process(sc)) != 0) { LOG(LOG_LEVEL_ERROR, "sesman_data_in: scp_process_msg failed"); } @@ -324,7 +360,7 @@ sesman_listen_conn_in(struct trans *self, struct trans *new_self) { LOG(LOG_LEVEL_ERROR, "sesman_data_in: No memory to allocate " "new connection"); - trans_delete(new_self); + delete_connection(sc); } else { @@ -546,20 +582,30 @@ sesman_main_loop(void) sig_sesman_reload_cfg(); } - for (index = 0; index < g_con_list->count; index++) + index = 0; + while (index < g_con_list->count) { + int remove_con = 0; scon = (struct sesman_con *)list_get_item(g_con_list, index); - if (scon != NULL) + if (trans_check_wait_objs(scon->t) != 0) { - if (trans_check_wait_objs(scon->t) != 0) - { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_check_wait_objs failed, removing trans"); - delete_connection(scon); - list_remove_item(g_con_list, index); - index--; - continue; - } + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "trans_check_wait_objs failed, removing trans"); + remove_con = 1; + } + else if (scon->close_requested) + { + remove_con = 1; + } + + if (remove_con) + { + delete_connection(scon); + list_remove_item(g_con_list, index); + } + else + { + ++index; } } @@ -574,13 +620,9 @@ sesman_main_loop(void) } } } - for (index = 0; index < g_con_list->count; index++) - { - scon = (struct sesman_con *) list_get_item(g_con_list, index); - delete_connection(scon); - } + + sesman_close_all(SCA_CLOSE_AUTH_INFO); list_delete(g_con_list); - sesman_delete_listening_transport(); return 0; } diff --git a/sesman/sesman.h b/sesman/sesman.h index 38231ec1..7d1a2efa 100644 --- a/sesman/sesman.h +++ b/sesman/sesman.h @@ -33,6 +33,13 @@ struct sesman_con { struct trans *t; + char peername[15 + 1]; /* Name of peer, if known, for logging */ + int close_requested; /* Set to close the connection normally */ + unsigned int auth_retry_count; + struct auth_info *auth_info; /* non-NULL for an authenticated connection */ + int uid; /* User */ + char *username; /* Username from UID (at time of logon) */ + char *ip_addr; /* Connecting IP address */ }; /* Globals */ @@ -42,6 +49,15 @@ extern tintptr g_term_event; extern tintptr g_sigchld_event; extern tintptr g_reload_event; +/** + * Set the peername of a connection + * + * @param name Name to set + * @result 0 for success + */ +int +sesman_set_connection_peername(struct sesman_con *sc, const char *name); + /* * Close all file descriptors used by sesman. * @@ -50,9 +66,14 @@ extern tintptr g_reload_event; * * This call will also release all trans and SCP_SESSION objects * held by sesman + * + * @param flags Set SCA_CLOSE_AUTH_INFO to close any open auth_info + * objects. By default these are not cleared, and should + * only be done so when exiting sesman. */ +#define SCA_CLOSE_AUTH_INFO (1<<0) int -sesman_close_all(void); +sesman_close_all(unsigned int flags); /* * Remove the listening transport diff --git a/sesman/session.c b/sesman/session.c index 70ba972c..925821c7 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -112,10 +112,10 @@ session_get_bydata(const struct session_parameters *sp) config_output_policy_string(policy, policy_str, sizeof(policy_str)); LOG(LOG_LEVEL_DEBUG, - "%s: search policy=%s type=%s U=%s B=%d D=(%dx%d) I=%s", + "%s: search policy=%s type=%s U=%d B=%d D=(%dx%d) I=%s", __func__, policy_str, SCP_SESSION_TYPE_TO_STR(sp->type), - sp->username, sp->bpp, sp->width, sp->height, + sp->uid, sp->bpp, sp->width, sp->height, sp->ip_addr); /* 'Separate' policy never matches */ @@ -130,11 +130,11 @@ session_get_bydata(const struct session_parameters *sp) struct session_item *item = tmp->item; LOG(LOG_LEVEL_DEBUG, - "%s: try %p type=%s U=%s B=%d D=(%dx%d) I=%s", + "%s: try %p type=%s U=%d B=%d D=(%dx%d) I=%s", __func__, item, SCP_SESSION_TYPE_TO_STR(item->type), - item->name, + item->uid, item->bpp, item->width, item->height, item->start_ip_addr); @@ -145,11 +145,10 @@ session_get_bydata(const struct session_parameters *sp) continue; } - if ((policy & SESMAN_CFG_SESS_POLICY_U) && - g_strncmp(sp->username, item->name, sizeof(item->name) - 1) != 0) + if ((policy & SESMAN_CFG_SESS_POLICY_U) && sp->uid != item->uid) { LOG(LOG_LEVEL_DEBUG, - "%s: Username doesn't match for 'U' policy", __func__); + "%s: UID doesn't match for 'U' policy", __func__); continue; } @@ -408,7 +407,7 @@ wait_for_xserver(int display) /******************************************************************************/ static int -session_start_chansrv(const char *username, int display) +session_start_chansrv(int uid, int display) { struct list *chansrv_params; char exe_path[262]; @@ -430,7 +429,7 @@ session_start_chansrv(const char *username, int display) list_add_item(chansrv_params, (intptr_t) g_strdup(exe_path)); list_add_item(chansrv_params, 0); /* mandatory */ - env_set_user(username, 0, display, + env_set_user(uid, 0, display, g_cfg->env_names, g_cfg->env_values); @@ -445,10 +444,38 @@ session_start_chansrv(const char *username, int display) } /******************************************************************************/ +/** + * Convert a UID to a username + * + * @param uid UID + * @param uname pointer to output buffer + * @param uname_len Length of output buffer + * @return 0 for success. + */ +static int +username_from_uid(int uid, char *uname, int uname_len) +{ + char *ustr; + int rv = g_getuser_info_by_uid(uid, &ustr, NULL, NULL, NULL, NULL); -int + if (rv == 0) + { + g_snprintf(uname, uname_len, "%s", ustr); + g_free(ustr); + } + else + { + g_snprintf(uname, uname_len, ""); + } + return rv; +} + +/******************************************************************************/ + +enum scp_screate_status session_start(struct auth_info *auth_info, const struct session_parameters *s, + int *returned_display, struct guid *guid) { int display = 0; @@ -457,6 +484,7 @@ session_start(struct auth_info *auth_info, char depth[32]; char screen[32]; /* display number */ char text[256]; + char username[256]; char execvpparams[2048]; char *xserver = NULL; /* absolute/relative path to Xorg/X11rdp/Xvnc */ char *passwd_file; @@ -476,12 +504,15 @@ session_start(struct auth_info *auth_info, passwd_file = 0; + /* Get the username for display purposes */ + username_from_uid(s->uid, username, sizeof(username)); + /* check to limit concurrent sessions */ if (g_session_count >= g_cfg->sess.max_sessions) { LOG(LOG_LEVEL_ERROR, "max concurrent session limit " - "exceeded. login for user %s denied", s->username); - return 0; + "exceeded. login for user %s denied", username); + return E_SCP_SCREATE_MAX_REACHED; } temp = (struct session_chain *)g_malloc(sizeof(struct session_chain), 0); @@ -489,8 +520,8 @@ session_start(struct auth_info *auth_info, if (temp == 0) { LOG(LOG_LEVEL_ERROR, "Out of memory error: cannot create new session " - "chain element - user %s", s->username); - return 0; + "chain element - user %s", username); + return E_SCP_SCREATE_NO_MEMORY; } temp->item = (struct session_item *)g_malloc(sizeof(struct session_item), 0); @@ -499,8 +530,8 @@ session_start(struct auth_info *auth_info, { g_free(temp); LOG(LOG_LEVEL_ERROR, "Out of memory error: cannot create new session " - "item - user %s", s->username); - return 0; + "item - user %s", username); + return E_SCP_SCREATE_NO_MEMORY; } display = session_get_avail_display_from_chain(); @@ -509,11 +540,12 @@ session_start(struct auth_info *auth_info, { g_free(temp->item); g_free(temp); - return 0; + return E_SCP_SCREATE_NO_DISPLAY; } - /* Create a GUID for the new session before we work */ + /* Create a GUID for the new session before we fork */ *guid = guid_new(); + *returned_display = display; pid = g_fork(); /* parent is fork from tcp accept, child forks X and wm, then becomes scp */ @@ -524,8 +556,12 @@ session_start(struct auth_info *auth_info, "[session start] (display %d): Failed to fork for scp with " "errno: %d, description: %s", display, g_get_errno(), g_get_strerror()); + g_free(temp->item); + g_free(temp); + return E_SCP_SCREATE_GENERAL_ERROR; } - else if (pid == 0) + + if (pid == 0) { LOG(LOG_LEVEL_INFO, "[session start] (display %d): calling auth_start_session from pid %d", @@ -547,16 +583,16 @@ session_start(struct auth_info *auth_info, /* Set the secondary groups before starting the session to prevent * problems on PAM-based systems (see pam_setcred(3)) */ - if (g_initgroups(s->username) != 0) + if (g_initgroups(username) != 0) { LOG(LOG_LEVEL_ERROR, "Failed to initialise secondary groups for %s: %s", - s->username, g_get_strerror()); + username, g_get_strerror()); g_exit(1); } + sesman_close_all(0); auth_start_session(auth_info, display); - sesman_close_all(); g_sprintf(geometry, "%dx%d", s->width, s->height); g_sprintf(depth, "%d", s->bpp); g_sprintf(screen, ":%d", display); @@ -588,11 +624,11 @@ session_start(struct auth_info *auth_info, display, g_getpid()); } - if (g_setlogin(s->username) < 0) + if (g_setlogin(username) < 0) { LOG(LOG_LEVEL_WARNING, "[session start] (display %d): setlogin failed for user %s - pid %d", - display, s->username, g_getpid()); + display, username, g_getpid()); } } @@ -617,7 +653,7 @@ session_start(struct auth_info *auth_info, else if (window_manager_pid == 0) { wait_for_xserver(display); - env_set_user(s->username, + env_set_user(s->uid, 0, display, g_cfg->env_names, @@ -715,7 +751,7 @@ session_start(struct auth_info *auth_info, { if (s->type == SCP_SESSION_TYPE_XVNC) { - env_set_user(s->username, + env_set_user(s->uid, &passwd_file, display, g_cfg->env_names, @@ -723,7 +759,7 @@ session_start(struct auth_info *auth_info, } else { - env_set_user(s->username, + env_set_user(s->uid, 0, display, g_cfg->env_names, @@ -901,11 +937,11 @@ session_start(struct auth_info *auth_info, struct exit_status chansrv_exit_status; wait_for_xserver(display); - chansrv_pid = session_start_chansrv(s->username, display); + chansrv_pid = session_start_chansrv(s->uid, display); LOG(LOG_LEVEL_INFO, "Session started successfully for user %s on display %d", - s->username, display); + username, display); /* Monitor the amount of time we wait for the * window manager. This is approximately how long the window @@ -939,10 +975,12 @@ session_start(struct auth_info *auth_info, window_manager_pid, display, wm_wait_time); } LOG(LOG_LEVEL_INFO, - "Calling auth_stop_session and auth_end from pid %d", + "Calling auth_stop_session from pid %d", g_getpid()); auth_stop_session(auth_info); - auth_end(auth_info); + // auth_end() is called from the main process currently, + // as this called auth_start() + //auth_end(auth_info); LOG(LOG_LEVEL_INFO, "Terminating X server (pid %d) on display %d", @@ -978,8 +1016,8 @@ session_start(struct auth_info *auth_info, { LOG(LOG_LEVEL_INFO, "Starting session: session_pid %d, " "display :%d.0, width %d, height %d, bpp %d, client ip %s, " - "user name %s", - pid, display, s->width, s->height, s->bpp, s->ip_addr, s->username); + "UID %d", + pid, display, s->width, s->height, s->bpp, s->ip_addr, s->uid); temp->item->pid = pid; temp->item->display = display; temp->item->width = s->width; @@ -988,7 +1026,7 @@ session_start(struct auth_info *auth_info, temp->item->auth_info = auth_info; g_strncpy(temp->item->start_ip_addr, s->ip_addr, sizeof(temp->item->start_ip_addr) - 1); - g_strncpy(temp->item->name, s->username, 255); + temp->item->uid = s->uid; temp->item->guid = *guid; temp->item->start_time = g_time1(); @@ -1000,17 +1038,18 @@ session_start(struct auth_info *auth_info, g_sessions = temp; g_session_count++; - return display; + return E_SCP_SCREATE_OK; } + /* Shouldn't get here */ g_free(temp->item); g_free(temp); - return display; + return E_SCP_SCREATE_GENERAL_ERROR; } /******************************************************************************/ int -session_reconnect(int display, const char *username, +session_reconnect(int display, int uid, struct auth_info *auth_info) { int pid; @@ -1023,7 +1062,7 @@ session_reconnect(int display, const char *username, } else if (pid == 0) { - env_set_user(username, + env_set_user(uid, 0, display, g_cfg->env_names, @@ -1090,10 +1129,23 @@ session_kill(int pid) if (tmp->item->pid == pid) { + char username[256]; + username_from_uid(tmp->item->uid, username, sizeof(username)); + /* deleting the session */ + if (tmp->item->auth_info != NULL) + { + LOG(LOG_LEVEL_INFO, + "Calling auth_end for pid %d from pid %d", + pid, g_getpid()); + auth_end(tmp->item->auth_info); + tmp->item->auth_info = NULL; + } LOG(LOG_LEVEL_INFO, - "++ terminated session: username %s, display :%d.0, session_pid %d, ip %s", - tmp->item->name, tmp->item->display, tmp->item->pid, tmp->item->start_ip_addr); + "++ terminated session: UID %d (%s), display :%d.0, " + "session_pid %d, ip %s", + tmp->item->uid, username, tmp->item->display, + tmp->item->pid, tmp->item->start_ip_addr); g_free(tmp->item); if (prev == 0) @@ -1186,7 +1238,7 @@ session_get_bypid(int pid) /******************************************************************************/ struct scp_session_info * -session_get_byuser(const char *user, unsigned int *cnt, unsigned char flags) +session_get_byuid(int uid, unsigned int *cnt, unsigned char flags) { struct session_chain *tmp; struct scp_session_info *sess; @@ -1197,10 +1249,10 @@ session_get_byuser(const char *user, unsigned int *cnt, unsigned char flags) tmp = g_sessions; - LOG(LOG_LEVEL_DEBUG, "searching for session by user: %s", user); + LOG(LOG_LEVEL_DEBUG, "searching for session by UID: %d", uid); while (tmp != 0) { - if ((NULL == user) || (!g_strncasecmp(user, tmp->item->name, 256))) + if (uid == tmp->item->uid) { LOG(LOG_LEVEL_DEBUG, "session_get_byuser: status=%d, flags=%d, " "result=%d", (tmp->item->status), flags, @@ -1236,8 +1288,7 @@ session_get_byuser(const char *user, unsigned int *cnt, unsigned char flags) while (tmp != 0 && index < count) { - /* #warning FIXME: we should get only disconnected sessions! */ - if ((NULL == user) || (!g_strncasecmp(user, tmp->item->name, 256))) + if (uid == tmp->item->uid) { if ((tmp->item->status) & flags) { @@ -1248,11 +1299,11 @@ session_get_byuser(const char *user, unsigned int *cnt, unsigned char flags) (sess[index]).width = tmp->item->width; (sess[index]).bpp = tmp->item->bpp; (sess[index]).start_time = tmp->item->start_time; - (sess[index]).username = g_strdup(tmp->item->name); + (sess[index]).uid = tmp->item->uid; (sess[index]).start_ip_addr = g_strdup(tmp->item->start_ip_addr); - if ((sess[index]).username == NULL || - (sess[index]).start_ip_addr == NULL) + /* Check for string allocation failures */ + if ((sess[index]).start_ip_addr == NULL) { free_session_info_list(sess, *cnt); (*cnt) = 0; @@ -1279,7 +1330,6 @@ free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt) unsigned int i; for (i = 0 ; i < cnt ; ++i) { - g_free(sesslist[i].username); g_free(sesslist[i].start_ip_addr); } } @@ -1392,7 +1442,6 @@ clone_session_params(const struct session_parameters *sp) /* Allocate a single block of memory big enough for the structure and * all the strings it points to */ unsigned int len = sizeof(*result); - len += g_strlen(sp->username) + 1; len += g_strlen(sp->shell) + 1; len += g_strlen(sp->directory) + 1; len += g_strlen(sp->ip_addr) + 1; @@ -1412,7 +1461,6 @@ clone_session_params(const struct session_parameters *sp) strptr += len;\ } - COPY_STRING_MEMBER(sp->username, result->username); COPY_STRING_MEMBER(sp->shell, result->shell); COPY_STRING_MEMBER(sp->directory, result->directory); COPY_STRING_MEMBER(sp->ip_addr, result->ip_addr); diff --git a/sesman/session.h b/sesman/session.h index df79da78..5a712f2a 100644 --- a/sesman/session.h +++ b/sesman/session.h @@ -53,7 +53,7 @@ struct scp_session_info; struct session_item { - char name[256]; + int uid; /* UID of session */ int pid; /* pid of sesman waiting for wm to end */ int display; int width; @@ -85,11 +85,11 @@ struct session_chain */ struct session_parameters { + int uid; enum scp_session_type type; unsigned short height; unsigned short width; unsigned char bpp; - const char *username; const char *shell; const char *directory; const char *ip_addr; @@ -110,16 +110,17 @@ session_get_bydata(const struct session_parameters *params); /** * * @brief starts a session - * @return 0 on error, display number if success * + * @return Connection status. */ -int +enum scp_screate_status session_start(struct auth_info *auth_info, const struct session_parameters *params, + int *display, struct guid *guid); int -session_reconnect(int display, const char *username, +session_reconnect(int display, int uid, struct auth_info *auth_info); /** @@ -154,14 +155,14 @@ session_get_bypid(int pid); /** * * @brief retrieves session descriptions - * @param user the user for the sessions + * @param UID the UID for the descriptions * @return A block of session descriptions * * Pass the return result to free_session_info_list() after use * */ struct scp_session_info * -session_get_byuser(const char *user, unsigned int *cnt, unsigned char flags); +session_get_byuid(int uid, unsigned int *cnt, unsigned char flags); /** * From 851bed680c35f0ca5fa7228d7c46791ee211bfc5 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 8 Sep 2022 15:10:37 +0100 Subject: [PATCH 08/11] Update sesman tools for new interfaces The sesman tools sesrun and sesadmin now use the separate authentication/authorization (AA) interface introduced to sesman by the previous comment. sesrun can use either password or UDS authentication. With some limitations, this can allow for automatic creation of sessions for local users without a password being needed. sesadmin now operates using UDS logins only and so a username and password are not required. To use sesadmin for another user, use su/sudo/doas to authenticate as the other user. --- docs/man/xrdp-sesadmin.8.in | 8 +- docs/man/xrdp-sesrun.8.in | 24 ++++- sesman/tools/sesadmin.c | 92 +++++++++--------- sesman/tools/sesrun.c | 180 ++++++++++++++++++++++++++++-------- 4 files changed, 213 insertions(+), 91 deletions(-) diff --git a/docs/man/xrdp-sesadmin.8.in b/docs/man/xrdp-sesadmin.8.in index 6727772b..953f3f67 100644 --- a/docs/man/xrdp-sesadmin.8.in +++ b/docs/man/xrdp-sesadmin.8.in @@ -18,13 +18,11 @@ command. A summary of options is included below. .TP .BI \-u= username -\fIUsername\fP for authentication on the server. -Defaults to \fBroot\fP. +Retained for compatibility, but ignored. .TP .BI \-p= password -The \fIpassword\fP to authenticate with. -The default is to ask for the password interactively. +Retained for compatibility, but ignored. .TP .BI \-i= port @@ -39,7 +37,7 @@ Valid commands are: .RS 4 .TP .B list -List currently active sessions. +List active sessions for the current user. .TP .BI kill: sid Kills the session specified the given \fIsession id\fP. diff --git a/docs/man/xrdp-sesrun.8.in b/docs/man/xrdp-sesrun.8.in index 256c4f7a..96585ece 100644 --- a/docs/man/xrdp-sesrun.8.in +++ b/docs/man/xrdp-sesrun.8.in @@ -4,18 +4,28 @@ .SH "SYNTAX" .B xrdp\-sesrun -.I [ options ] username +.I --help +.br + +.B xrdp\-sesrun +.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 +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. +Default values for the options are set at compile-time. Run the utility with +the '--help' option 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. +If no username is used, the current username is used, and no password +needs to be provided. In this instance, it is important that any necessary +authentication tokens for a GUI session (e.g. a Kerberos ticket) have +already been acquired. + +If a username is provided, a password must also be provided. In this instance +the utility prompts for a password if neither \fB-p\fR or \fB-F\fR is used. .SH "OPTIONS" .TP @@ -60,6 +70,10 @@ Override the default logging level. One of "error", "warn", "info", .SH "EXAMPLES" .TP .B +xrdp-sesrun +Create a default session for the current user. +.TP +.B xrdp-sesrun -F 0 user1 #include -char user[257]; -char pass[257]; char cmnd[257]; char port[257]; @@ -47,12 +45,9 @@ int main(int argc, char **argv) //int end; int idx; //int sel; - char *pwd; struct log_config *logging; int rv = 1; - user[0] = '\0'; - pass[0] = '\0'; cmnd[0] = '\0'; port[0] = '\0'; @@ -64,11 +59,11 @@ int main(int argc, char **argv) { if (0 == g_strncmp(argv[idx], "-u=", 3)) { - g_strncpy(user, (argv[idx]) + 3, 256); + g_printf("** Ignoring unused argument '-u'"); } else if (0 == g_strncmp(argv[idx], "-p=", 3)) { - g_strncpy(pass, (argv[idx]) + 3, 256); + g_printf("** Ignoring unused argument '-p'"); } else if (0 == g_strncmp(argv[idx], "-i=", 3)) { @@ -80,48 +75,57 @@ int main(int argc, char **argv) } } - if (0 == g_strncmp(port, "", 1)) - { - g_strncpy(port, "3350", 256); - } - - if (0 == g_strncmp(user, "", 1)) - { - cmndHelp(); - return 0; - } - if (0 == g_strncmp(cmnd, "", 1)) { cmndHelp(); return 0; } - if (0 == g_strncmp(pass, "", 1)) - { - pwd = getpass("password:"); - g_strncpy(pass, pwd, 256); - - } - - t = scp_connect(port, NULL); - + t = scp_connect(port, "xrdp-sesadmin", NULL); if (t == NULL) { LOG(LOG_LEVEL_ERROR, "scp_connect() error"); } - else if (0 == g_strncmp(cmnd, "list", 5)) + else { - rv = cmndList(t); - } - else if (0 == g_strncmp(cmnd, "kill:", 5)) - { - rv = cmndKill(t); + enum scp_login_status login_result; + + /* Log in as the current user */ + if ((rv = scp_send_uds_login_request(t)) == 0 && + (rv = wait_for_sesman_reply(t, E_SCP_LOGIN_RESPONSE)) == 0) + { + rv = scp_get_login_response(t, &login_result, NULL); + 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 + } } - g_memset(pass, '\0', sizeof(pass)); + if (rv == 0) + { + if (0 == g_strncmp(cmnd, "list", 5)) + { + rv = cmndList(t); + } + else if (0 == g_strncmp(cmnd, "kill:", 5)) + { + rv = cmndKill(t); + } + } + if (rv == 0) + { + rv = scp_send_close_connection_request(t); + } trans_delete(t); log_end(); @@ -133,10 +137,7 @@ cmndHelp(void) { fprintf(stderr, "sesadmin - a console sesman administration tool\n"); fprintf(stderr, "syntax: sesadmin [] COMMAND [OPTIONS]\n\n"); - fprintf(stderr, "-u=: username to connect to sesman [MANDATORY]\n"); - fprintf(stderr, "-p=: password to connect to sesman (asked if not given)\n"); - fprintf(stderr, "-s=: sesman host (default is localhost)\n"); - fprintf(stderr, "-i= : sesman port (default 3350)\n"); + fprintf(stderr, "-i= : sesman port (can be defaulted)\n"); fprintf(stderr, "-c= : command to execute on the server [MANDATORY]\n"); fprintf(stderr, " it can be one of those:\n"); fprintf(stderr, " list\n"); @@ -146,9 +147,14 @@ cmndHelp(void) static void print_session(const struct scp_session_info *s) { + char *username; + const char *uptr; + g_getuser_info_by_uid(s->uid, &username, NULL, NULL, NULL, NULL); + uptr = (username == NULL) ? "" : username; + printf("Session ID: %d\n", s->sid); printf("\tDisplay: :%u\n", s->display); - printf("\tUser: %s\n", s->username); + printf("\tUser: %s\n", uptr); printf("\tSession type: %s\n", SCP_SESSION_TYPE_TO_STR(s->type)); printf("\tScreen size: %dx%d, color depth %d\n", s->width, s->height, s->bpp); @@ -157,6 +163,7 @@ print_session(const struct scp_session_info *s) { printf("\tStart IP address: %s\n", s->start_ip_addr); } + g_free(username); } static int @@ -168,7 +175,7 @@ cmndList(struct trans *t) enum scp_list_sessions_status status; struct scp_session_info *p; - int rv = scp_send_list_sessions_request(t, user, pass); + int rv = scp_send_list_sessions_request(t); sessions->auto_free = 1; @@ -188,11 +195,6 @@ cmndList(struct trans *t) switch (status) { - case E_SCP_LS_AUTHENTICATION_FAIL: - printf("Connection denied (authentication error)\n"); - rv = 1; - break; - case E_SCP_LS_SESSION_INFO: list_add_item(sessions, (tintptr)p); break; diff --git a/sesman/tools/sesrun.c b/sesman/tools/sesrun.c index b785f5d0..dc2d0c82 100644 --- a/sesman/tools/sesrun.c +++ b/sesman/tools/sesrun.c @@ -94,7 +94,7 @@ struct session_params const char *directory; const char *shell; - const char *connection_description; + const char *ip_addr; const char *username; char password[MAX_PASSWORD_LEN + 1]; @@ -165,7 +165,9 @@ usage(void) g_printf("xrdp session starter v" PACKAGE_VERSION "\n"); g_printf("\nusage:\n"); - g_printf("sesrun [options] username\n\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); @@ -176,9 +178,11 @@ usage(void) " -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("Supported types are %s\n", + g_printf("\nSupported types are %s\n", sesstype_list); - g_printf("Password is prompted if -p or -F are not specified\n"); + 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"); } @@ -286,7 +290,7 @@ parse_program_args(int argc, char *argv[], struct session_params *sp, sp->directory = ""; sp->shell = ""; - sp->connection_description = ""; + sp->ip_addr = ""; sp->username = NULL; sp->password[0] = '\0'; @@ -367,33 +371,109 @@ parse_program_args(int argc, char *argv[], struct session_params *sp, } } - if (argc <= optind) + if (argc == optind) { - LOG(LOG_LEVEL_ERROR, "No user name specified"); - params_ok = 0; + // 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 + else if (params_ok) { + // A username is specified sp->username = argv[optind]; - } - - if (params_ok && !password_set) - { - const char *p = getpass("Password: "); - if (p != NULL) + if (!password_set) { - g_strcpy(sp->password, p); + 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 * @@ -405,18 +485,13 @@ 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\"", + "directory:\"%s\" shell:\"%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); + sp->directory, sp->shell); 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); + t, sp->session_type, + sp->width, sp->height, sp->bpp, sp->shell, sp->directory); } /**************************************************************************//** @@ -428,21 +503,24 @@ send_create_session_request(struct trans *t, const struct session_params *sp) static int handle_create_session_response(struct trans *t) { - int auth_result; + 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, &auth_result, + rv = scp_get_create_session_response(t, &status, &display, &guid); if (rv == 0) { - if (auth_result != 0) + if (status != E_SCP_SCREATE_OK) { - g_printf("Connection denied (authentication error)\n"); + char msg[256]; + scp_screate_status_to_str(status, msg, sizeof(msg)); + g_printf("Connection failed; %s\n", msg); + rv = 1; } else { @@ -452,6 +530,7 @@ handle_create_session_response(struct trans *t) guid_to_str(&guid, guid_str)); } } + scp_msg_in_reset(t); // Done with this message } return rv; @@ -475,7 +554,12 @@ main(int argc, char **argv) log_start_from_param(logging); log_config_free(logging); - if (!parse_program_args(argc, argv, &sp, &sesman_ini)) + if (argc == 2 && g_strcmp(argv[1], "--help") == 0) + { + usage(); + rv = 0; + } + else if (!parse_program_args(argc, argv, &sp, &sesman_ini)) { usage(); } @@ -484,20 +568,44 @@ main(int argc, char **argv) LOG(LOG_LEVEL_ERROR, "error reading config file %s : %s", sesman_ini, g_get_strerror()); } - else if (!(t = scp_connect(cfg->listen_port, NULL))) + else if (!(t = scp_connect(cfg->listen_port, "xrdp-sesrun", NULL))) { LOG(LOG_LEVEL_ERROR, "connect error - %s", g_get_strerror()); } else { - rv = send_create_session_request(t, &sp); - if (rv != 0) + int server_closed = 0; + while (!server_closed) { - LOG(LOG_LEVEL_ERROR, "Error sending create session to sesman"); + 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); + } } - else + + if (rv == 0) { - rv = handle_create_session_response(t); + if ((rv = send_create_session_request(t, &sp)) == 0) + { + rv = handle_create_session_response(t); + } } trans_delete(t); } From 1a48527df9aff123212650fb423f79054ff44734 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:33:35 +0100 Subject: [PATCH 09/11] Update xrdp for separate AA and session creation xrdp is updated to use the separate authenticate/authorization (AA) and command processing interface now provided by sesman. PAM processing has been removed entirely and moved into the seman PAM module. As a result, gateway processing for proxy use-cases can be made use of by non-PAM systems. --- configure.ac | 5 - docs/man/xrdp.ini.5.in | 4 +- xrdp/xrdp_mm.c | 596 +++++++++++++---------------------------- xrdp/xrdp_types.h | 10 +- xrdp/xrdp_wm.c | 17 +- 5 files changed, 212 insertions(+), 420 deletions(-) diff --git a/configure.ac b/configure.ac index dfebca7a..94e809fc 100644 --- a/configure.ac +++ b/configure.ac @@ -424,11 +424,6 @@ then AC_DEFINE([XRDP_ENABLE_IPV6],1,[Enable IPv6]) fi -if test "x$enable_pam" = "xyes" -then - AC_DEFINE([USE_PAM],1,[Enable PAM]) -fi - AS_IF( [test "x$enable_neutrinordp" = "xyes"] , [PKG_CHECK_MODULES(FREERDP, freerdp >= 1.0.0)] ) # checking for libjpeg diff --git a/docs/man/xrdp.ini.5.in b/docs/man/xrdp.ini.5.in index f1713edb..0a1b4b9d 100644 --- a/docs/man/xrdp.ini.5.in +++ b/docs/man/xrdp.ini.5.in @@ -107,7 +107,9 @@ If not specified or set to \fB0\fP, unlimited. .TP \fBpamerrortxt\fP=\fIerror_text\fP -Specify text passed to PAM when authentication failed. The maximum length is \fB256\fP. +Specify additional text displayed to user if authentication fails. The maximum length is \fB256\fP. + +The use of 'pam' in the name of this option is historic .TP \fBport\fP=\fIport\fP diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 75573b9e..7c7845a3 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -30,15 +30,6 @@ #include "scp.h" -#ifdef USE_PAM -#if defined(HAVE__PAM_TYPES_H) -#define LINUXPAM 1 -#include -#elif defined(HAVE_PAM_CONSTANTS_H) -#define OPENPAM 1 -#include -#endif -#endif /* USE_PAM */ #include #include "xrdp_encoder.h" @@ -47,10 +38,6 @@ /* Forward declarations */ -static const char * -getPAMError(const int pamError, char *text, int text_bytes); -static const char * -getPAMAdditionalErrorInfo(const int pamError, struct xrdp_mm *self); static int xrdp_mm_chansrv_connect(struct xrdp_mm *self, const char *port); static void @@ -233,85 +220,63 @@ xrdp_mm_get_value_int(struct xrdp_mm *self, const char *aname, int def) /*****************************************************************************/ /* Send gateway login information to sesman */ static int -xrdp_mm_send_gateway_login(struct xrdp_mm *self, const char *username, - const char *password) +xrdp_mm_send_sys_login_request(struct xrdp_mm *self, const char *username, + const char *password) { xrdp_wm_log_msg(self->wm, LOG_LEVEL_DEBUG, "sending login info to session manager, please wait..."); - return scp_send_gateway_request( + return scp_send_sys_login_request( self->sesman_trans, username, password, self->wm->client_info->client_ip); } /*****************************************************************************/ -/* Send login information to sesman */ +/* Send a create session request to sesman */ static int -xrdp_mm_send_login(struct xrdp_mm *self) +xrdp_mm_create_session(struct xrdp_mm *self) { int rv = 0; int xserverbpp; - const char *username; - const char *password; + enum scp_session_type type; - username = xrdp_mm_get_value(self, "username"); - password = xrdp_mm_get_value(self, "password"); - if (username == NULL || username[0] == '\0') + /* Map the session code to an SCP session type */ + switch (self->code) { - xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, "No username is available"); - rv = 1; + case XVNC_SESSION_CODE: + type = SCP_SESSION_TYPE_XVNC; + break; + + case XRDP_SESSION_CODE: + type = SCP_SESSION_TYPE_XRDP; + break; + + case XORG_SESSION_CODE: + type = SCP_SESSION_TYPE_XORG; + break; + + default: + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, + "Unrecognised session code %d", self->code); + rv = 1; } - else if (password == NULL) + + if (rv == 0) { - /* Can't find a password definition at all - even an empty one */ - xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, - "No password field is available"); - rv = 1; - } - else - { - enum scp_session_type type; - /* Map the session code to an SCP session type */ - switch (self->code) - { - case XVNC_SESSION_CODE: - type = SCP_SESSION_TYPE_XVNC; - break; + xserverbpp = xrdp_mm_get_value_int(self, "xserverbpp", + self->wm->screen->bpp); - case XRDP_SESSION_CODE: - type = SCP_SESSION_TYPE_XRDP; - break; - - case XORG_SESSION_CODE: - type = SCP_SESSION_TYPE_XORG; - break; - - default: - xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, - "Unrecognised session code %d", self->code); - rv = 1; - } - - if (rv == 0) - { - xserverbpp = xrdp_mm_get_value_int(self, "xserverbpp", - self->wm->screen->bpp); - - xrdp_wm_log_msg(self->wm, LOG_LEVEL_DEBUG, - "sending login info to session manager. " - "Please wait..."); - rv = scp_send_create_session_request( - self->sesman_trans, - username, - password, - type, - self->wm->screen->width, - self->wm->screen->height, - xserverbpp, - self->wm->client_info->program, - self->wm->client_info->directory, - self->wm->client_info->client_ip); - } + xrdp_wm_log_msg(self->wm, LOG_LEVEL_DEBUG, + "sending create session request to session" + " manager. Please wait..."); + rv = scp_send_create_session_request( + self->sesman_trans, + type, + self->wm->screen->width, + self->wm->screen->height, + xserverbpp, + self->wm->client_info->program, + self->wm->client_info->directory); } return rv; @@ -1920,21 +1885,6 @@ xrdp_mm_chan_data_in(struct trans *trans) return error; } -/*****************************************************************************/ - -static void cleanup_sesman_connection(struct xrdp_mm *self) -{ - /* Don't delete any transports here - we may be in - * an auth callback from one of them */ - self->delete_sesman_trans = 1; - - if (self->wm->login_state != WMLS_CLEANUP) - { - xrdp_wm_set_login_state(self->wm, WMLS_INACTIVE); - xrdp_mm_module_cleanup(self); - } -} - /*****************************************************************************/ /* does the section in xrdp.ini has any channel.*=true | false */ static int @@ -2088,38 +2038,49 @@ xrdp_mm_process_channel_data(struct xrdp_mm *self, tbus param1, tbus param2, /*****************************************************************************/ static int -xrdp_mm_process_gateway_response(struct xrdp_mm *self) +xrdp_mm_process_login_response(struct xrdp_mm *self) { - int auth_result; + enum scp_login_status login_result; int rv; + int server_closed; - rv = scp_get_gateway_response(self->sesman_trans, &auth_result); + rv = scp_get_login_response(self->sesman_trans, &login_result, &server_closed); if (rv == 0) { - const char *additionalError; - char pam_error[128]; - - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, - "Reply from access control: %s", - getPAMError(auth_result, - pam_error, sizeof(pam_error))); - - if (auth_result != 0) + if (login_result != E_SCP_LOGIN_OK) { - additionalError = getPAMAdditionalErrorInfo(auth_result, self); - if (additionalError && additionalError[0]) + char buff[128]; + scp_login_status_to_str(login_result, buff, sizeof(buff)); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", buff); + + if (login_result == E_SCP_LOGIN_NOT_AUTHENTICATED && + self->wm->pamerrortxt[0] != '\0') { xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", - additionalError); + self->wm->pamerrortxt); } - /* TODO : Check this is displayed */ - cleanup_sesman_connection(self); + if (server_closed) + { + if (login_result == E_SCP_LOGIN_NOT_AUTHENTICATED) + { + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", + "Login retry limit reached"); + } + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", + "Close the log window to exit."); + self->wm->fatal_error_in_log_window = 1; + /* Transport can be deleted now */ + self->delete_sesman_trans = 1; + } + /* If the server hasn't closed, inform the window manager + * of the fail, but leave the sesman connection open for + * further login attempts */ xrdp_wm_mod_connect_done(self->wm, 1); } else { - /* Authentication successful */ + /* login successful */ xrdp_mm_connect_sm(self); } } @@ -2131,18 +2092,17 @@ xrdp_mm_process_gateway_response(struct xrdp_mm *self) static int xrdp_mm_process_create_session_response(struct xrdp_mm *self) { - int auth_result; + enum scp_screate_status status; int display; struct guid guid; int rv; - rv = scp_get_create_session_response(self->sesman_trans, &auth_result, + rv = scp_get_create_session_response(self->sesman_trans, &status, &display, &guid); if (rv == 0) { const char *username; - char displayinfo[64]; /* Sort out some logging information */ if ((username = xrdp_mm_get_value(self, "username")) == NULL) @@ -2150,38 +2110,27 @@ xrdp_mm_process_create_session_response(struct xrdp_mm *self) username = "???"; } - if (display == 0) + if (status == E_SCP_SCREATE_OK) { - /* A returned display of zero doesn't mean anything useful, and - * can confuse the user. It's most likely authentication has - * failed and no display was allocated */ - displayinfo[0] = '\0'; - } - else - { - g_snprintf(displayinfo, sizeof(displayinfo), - " on display %d", display); - } + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "session is available on display %d for user %s", + display, username); - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, - "login %s for user %s%s", - ((auth_result == 0) ? "successful" : "failed"), - username, displayinfo); - - if (auth_result != 0) - { - /* Authentication failure */ - cleanup_sesman_connection(self); - xrdp_wm_mod_connect_done(self->wm, 1); - } - else - { - /* Authentication successful - carry on with the connect - * state machine */ + /* Carry on with the connect state machine */ self->display = display; self->guid = guid; xrdp_mm_connect_sm(self); } + else + { + char buff[128]; + scp_screate_status_to_str(status, buff, sizeof(buff)); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Can't create session for user %s - %s", + username, buff); + /* Leave the sesman connection open for further login attenpts */ + xrdp_wm_mod_connect_done(self->wm, 1); + } } return rv; @@ -2203,8 +2152,8 @@ xrdp_mm_scp_data_in(struct trans *trans) switch ((msgno = scp_msg_in_start(trans))) { - case E_SCP_GATEWAY_RESPONSE: - rv = xrdp_mm_process_gateway_response(self); + case E_SCP_LOGIN_RESPONSE: + rv = xrdp_mm_process_login_response(self); break; case E_SCP_CREATE_SESSION_RESPONSE: @@ -2229,7 +2178,10 @@ xrdp_mm_scp_data_in(struct trans *trans) /*****************************************************************************/ /* This routine clears all states to make sure that our next login will be * as expected. If the user does not press ok on the log window and try to - * connect again we must make sure that no previous information is stored.*/ + * connect again we must make sure that no previous information is stored. + * + * This routine does not clear a sesman_trans if it is allocated, as this + * would break the password retry limit mechanism */ static void cleanup_states(struct xrdp_mm *self) { @@ -2238,8 +2190,8 @@ cleanup_states(struct xrdp_mm *self) self->connect_state = MMCS_CONNECT_TO_SESMAN; self->use_sesman = 0; /* true if this is a sesman session */ self->use_chansrv = 0; /* true if chansrvport is set in xrdp.ini or using sesman */ - self->use_pam_auth = 0; /* true if we're to use the PAM authentication facility */ - self->sesman_trans = NULL; /* connection to sesman */ + self->use_gw_login = 0; /* true if we're to use the gateway login facility */ + //self->sesman_trans = NULL; /* connection to sesman */ self->chan_trans = NULL; /* connection to chansrv */ self->delete_sesman_trans = 0; self->display = 0; /* 10 for :10.0, 11 for :11.0, etc */ @@ -2248,235 +2200,6 @@ cleanup_states(struct xrdp_mm *self) } } -static const char * -getPAMError(const int pamError, char *text, int text_bytes) -{ - switch (pamError) - { -#if defined(LINUXPAM) - case PAM_SUCCESS: - return "Success"; - case PAM_OPEN_ERR: - return "dlopen() failure"; - case PAM_SYMBOL_ERR: - return "Symbol not found"; - case PAM_SERVICE_ERR: - return "Error in service module"; - case PAM_SYSTEM_ERR: - return "System error"; - case PAM_BUF_ERR: - return "Memory buffer error"; - case PAM_PERM_DENIED: - return "Permission denied"; - case PAM_AUTH_ERR: - return "Authentication failure"; - case PAM_CRED_INSUFFICIENT: - return "Insufficient credentials to access authentication data"; - case PAM_AUTHINFO_UNAVAIL: - return "Authentication service cannot retrieve authentication info."; - case PAM_USER_UNKNOWN: - return "User not known to the underlying authentication module"; - case PAM_MAXTRIES: - return "Have exhausted maximum number of retries for service."; - case PAM_NEW_AUTHTOK_REQD: - return "Authentication token is no longer valid; new one required."; - case PAM_ACCT_EXPIRED: - return "User account has expired"; - case PAM_CRED_UNAVAIL: - return "Authentication service cannot retrieve user credentials"; - case PAM_CRED_EXPIRED: - return "User credentials expired"; - case PAM_CRED_ERR: - return "Failure setting user credentials"; - case PAM_NO_MODULE_DATA: - return "No module specific data is present"; - case PAM_BAD_ITEM: - return "Bad item passed to pam_*_item()"; - case PAM_CONV_ERR: - return "Conversation error"; - case PAM_AUTHTOK_ERR: - return "Authentication token manipulation error"; - case PAM_AUTHTOK_LOCK_BUSY: - return "Authentication token lock busy"; - case PAM_AUTHTOK_DISABLE_AGING: - return "Authentication token aging disabled"; - case PAM_TRY_AGAIN: - return "Failed preliminary check by password service"; - case PAM_IGNORE: - return "Please ignore underlying account module"; - case PAM_MODULE_UNKNOWN: - return "Module is unknown"; - case PAM_AUTHTOK_EXPIRED: - return "Authentication token expired"; - case PAM_CONV_AGAIN: - return "Conversation is waiting for event"; - case PAM_INCOMPLETE: - return "Application needs to call libpam again"; - case 32 + 1: - return "Error connecting to PAM"; - case 32 + 3: - return "Username okey but group problem"; -#elif defined(OPENPAM) - case PAM_SUCCESS: /* 0 */ - return "Success"; - case PAM_OPEN_ERR: - return "dlopen() failure"; - case PAM_SYMBOL_ERR: - return "Symbol not found"; - case PAM_SERVICE_ERR: - return "Error in service module"; - case PAM_SYSTEM_ERR: - return "System error"; - case PAM_BUF_ERR: - return "Memory buffer error"; - case PAM_CONV_ERR: - return "Conversation error"; - case PAM_PERM_DENIED: - return "Permission denied"; - case PAM_MAXTRIES: - return "Have exhausted maximum number of retries for service."; - case PAM_AUTH_ERR: - return "Authentication failure"; - case PAM_NEW_AUTHTOK_REQD: /* 10 */ - return "Authentication token is no longer valid; new one required."; - case PAM_CRED_INSUFFICIENT: - return "Insufficient credentials to access authentication data"; - case PAM_AUTHINFO_UNAVAIL: - return "Authentication service cannot retrieve authentication info."; - case PAM_USER_UNKNOWN: - return "User not known to the underlying authentication module"; - case PAM_CRED_UNAVAIL: - return "Authentication service cannot retrieve user credentials"; - case PAM_CRED_EXPIRED: - return "User credentials expired"; - case PAM_CRED_ERR: - return "Failure setting user credentials"; - case PAM_ACCT_EXPIRED: - return "User account has expired"; - case PAM_AUTHTOK_EXPIRED: - return "Authentication token expired"; - case PAM_SESSION_ERR: - return "Session failure"; - case PAM_AUTHTOK_ERR: /* 20 */ - return "Authentication token manipulation error"; - case PAM_AUTHTOK_RECOVERY_ERR: - return "Failed to recover old authentication token"; - case PAM_AUTHTOK_LOCK_BUSY: - return "Authentication token lock busy"; - case PAM_AUTHTOK_DISABLE_AGING: - return "Authentication token aging disabled"; - case PAM_NO_MODULE_DATA: - return "No module specific data is present"; - case PAM_IGNORE: - return "Please ignore underlying account module"; - case PAM_ABORT: - return "General failure"; - case PAM_TRY_AGAIN: - return "Failed preliminary check by password service"; - case PAM_MODULE_UNKNOWN: - return "Module is unknown"; - case PAM_DOMAIN_UNKNOWN: /* 29 */ - return "Unknown authentication domain"; -#endif - default: - g_snprintf(text, text_bytes, "Not defined PAM error:%d", pamError); - return text; - } -} - -static const char * -getPAMAdditionalErrorInfo(const int pamError, struct xrdp_mm *self) -{ - switch (pamError) - { -#if defined(LINUXPAM) - case PAM_SUCCESS: - return NULL; - case PAM_OPEN_ERR: - case PAM_SYMBOL_ERR: - case PAM_SERVICE_ERR: - case PAM_SYSTEM_ERR: - case PAM_BUF_ERR: - case PAM_PERM_DENIED: - case PAM_AUTH_ERR: - case PAM_CRED_INSUFFICIENT: - case PAM_AUTHINFO_UNAVAIL: - case PAM_USER_UNKNOWN: - case PAM_CRED_UNAVAIL: - case PAM_CRED_ERR: - case PAM_NO_MODULE_DATA: - case PAM_BAD_ITEM: - case PAM_CONV_ERR: - case PAM_AUTHTOK_ERR: - case PAM_AUTHTOK_LOCK_BUSY: - case PAM_AUTHTOK_DISABLE_AGING: - case PAM_TRY_AGAIN: - case PAM_IGNORE: - case PAM_MODULE_UNKNOWN: - case PAM_CONV_AGAIN: - case PAM_INCOMPLETE: - case _PAM_RETURN_VALUES + 1: - case _PAM_RETURN_VALUES + 3: - return NULL; - case PAM_MAXTRIES: - case PAM_NEW_AUTHTOK_REQD: - case PAM_ACCT_EXPIRED: - case PAM_CRED_EXPIRED: - case PAM_AUTHTOK_EXPIRED: - if (self->wm->pamerrortxt[0]) - { - return self->wm->pamerrortxt; - } - else - { - return "Authentication error - Verify that user/password is valid"; - } -#elif defined(OPENPAM) - case PAM_SUCCESS: /* 0 */ - return NULL; - case PAM_OPEN_ERR: - case PAM_SYMBOL_ERR: - case PAM_SERVICE_ERR: - case PAM_SYSTEM_ERR: - case PAM_BUF_ERR: - case PAM_CONV_ERR: - case PAM_PERM_DENIED: - case PAM_MAXTRIES: - case PAM_AUTH_ERR: - case PAM_NEW_AUTHTOK_REQD: /* 10 */ - case PAM_CRED_INSUFFICIENT: - case PAM_AUTHINFO_UNAVAIL: - case PAM_USER_UNKNOWN: - case PAM_CRED_UNAVAIL: - case PAM_CRED_EXPIRED: - case PAM_CRED_ERR: - case PAM_ACCT_EXPIRED: - case PAM_AUTHTOK_EXPIRED: - case PAM_SESSION_ERR: - case PAM_AUTHTOK_ERR: /* 20 */ - case PAM_AUTHTOK_RECOVERY_ERR: - case PAM_AUTHTOK_LOCK_BUSY: - case PAM_AUTHTOK_DISABLE_AGING: - case PAM_NO_MODULE_DATA: - case PAM_IGNORE: - case PAM_ABORT: - case PAM_TRY_AGAIN: - case PAM_MODULE_UNKNOWN: - case PAM_DOMAIN_UNKNOWN: /* 29 */ - if (self->wm->pamerrortxt[0]) - { - return self->wm->pamerrortxt; - } - else - { - return "Authentication error - Verify that user/password is valid"; - } -#endif - default: - return "No expected error"; - } -} - /*************************************************************************//** * Parses a chansrvport string * @@ -2539,7 +2262,7 @@ xrdp_mm_scp_connect(struct xrdp_mm *self) xrdp_wm_log_msg(self->wm, LOG_LEVEL_DEBUG, "connecting to sesman on %s", port_description); - t = scp_connect(port, g_is_term); + t = scp_connect(port, "xrdp", g_is_term); if (t != NULL) { /* fully connected */ @@ -2650,7 +2373,7 @@ void xrdp_mm_connect(struct xrdp_mm *self) { const char *port = xrdp_mm_get_value(self, "port"); - const char *gateway_username = xrdp_mm_get_value(self, "pamusername"); + const char *gw_username = xrdp_mm_get_value(self, "pamusername"); /* make sure we start in correct state */ cleanup_states(self); @@ -2680,7 +2403,7 @@ xrdp_mm_connect(struct xrdp_mm *self) } } - if (gateway_username != NULL) + if (gw_username != NULL) { /* Connecting to a remote sesman is no longer supported */ if (xrdp_mm_get_value(self, "pamsessionmng") != NULL) @@ -2690,13 +2413,7 @@ xrdp_mm_connect(struct xrdp_mm *self) "Parameter 'pamsessionmng' is obsolete." " Please remove from config"); } -#ifdef USE_PAM - self->use_pam_auth = 1; -#else - xrdp_wm_log_msg(self->wm, LOG_LEVEL_WARNING, - "pamusername parameter ignored - " - "xrdp is compiled without PAM support"); -#endif + self->use_gw_login = 1; } /* Will we need chansrv ? We use it unconditionally for a @@ -2731,7 +2448,8 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) { case MMCS_CONNECT_TO_SESMAN: { - if (self->use_sesman || self->use_pam_auth) + if (self->sesman_trans == NULL && + (self->use_sesman || self->use_gw_login)) { /* Synchronous call */ status = xrdp_mm_sesman_connect(self); @@ -2739,45 +2457,46 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) } break; - case MMCS_PAM_AUTH: + case MMCS_GATEWAY_LOGIN: { - if (self->use_pam_auth) + if (self->use_gw_login) { - const char *gateway_username; - const char *gateway_password; + const char *gw_username; + const char *gw_password; - gateway_username = xrdp_mm_get_value(self, "pamusername"); - gateway_password = xrdp_mm_get_value(self, "pampassword"); - if (!g_strcmp(gateway_username, "same")) + gw_username = xrdp_mm_get_value(self, "pamusername"); + gw_password = xrdp_mm_get_value(self, "pampassword"); + if (!g_strcmp(gw_username, "same")) { - gateway_username = xrdp_mm_get_value(self, "username"); + gw_username = xrdp_mm_get_value(self, "username"); } - if (gateway_password == NULL || - !g_strcmp(gateway_password, "same")) + if (gw_password == NULL || + !g_strcmp(gw_password, "same")) { - gateway_password = xrdp_mm_get_value(self, "password"); + gw_password = xrdp_mm_get_value(self, "password"); } - if (gateway_username == NULL || gateway_password == NULL) + if (gw_username == NULL || gw_password == NULL) { xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, "Can't determine username and/or " - "password for gateway authorization"); + "password for gateway login"); status = 1; } else { xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "Performing access control for %s", - gateway_username); + gw_username); - status = xrdp_mm_send_gateway_login(self, - gateway_username, - gateway_password); + status = xrdp_mm_send_sys_login_request(self, + gw_username, + gw_password); if (status == 0) { - /* Now waiting for a reply from sesman */ + /* Now waiting for a reply from sesman - see + xrdp_mm_process_login_response() */ waiting_for_msg = 1; } } @@ -2785,21 +2504,77 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) } break; - case MMCS_SESSION_AUTH: + case MMCS_SESSION_LOGIN: { - if (self->use_sesman) + // Finished with the gateway login + if (self->use_gw_login) { - if ((status = xrdp_mm_send_login(self)) == 0) + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "access control check was successful"); + // No reply needed for this one + status = scp_send_logout_request(self->sesman_trans); + } + + if (status == 0 && self->use_sesman) + { + const char *username; + const char *password; + + username = xrdp_mm_get_value(self, "username"); + password = xrdp_mm_get_value(self, "password"); + if (username == NULL || username[0] == '\0') { - /* Now waiting for a reply from sesman */ - waiting_for_msg = 1; + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, + "No username is available"); + status = 1; + } + else if (password == NULL) + { + /* Can't find a password definition at all - even + * an empty one */ + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, + "No password field is available"); + status = 1; + } + else + { + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Performing login request for %s", + username); + status = xrdp_mm_send_sys_login_request(self, + username, + password); + if (status == 0) + { + /* Now waiting for a reply from sesman - see + xrdp_mm_process_create_session_response() */ + waiting_for_msg = 1; + } } } } break; + case MMCS_CREATE_SESSION: + if (self->use_sesman) + { + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "login was successful - creating session"); + if ((status = xrdp_mm_create_session(self)) == 0) + { + /* Now waiting for a reply from sesman. Note that + * at this point sesman is expecting us to + * close the connection - we can do nothing else + * with it */ + waiting_for_msg = 1; + } + } + break; + case MMCS_CONNECT_TO_SESSION: { + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Connecting to session"); /* This is synchronous - no reply message expected */ status = xrdp_mm_user_session_connect(self); } @@ -2811,6 +2586,8 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) { char portbuff[256]; + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, + "Connecting to chansrv"); if (self->use_sesman) { g_snprintf(portbuff, sizeof(portbuff), @@ -2848,8 +2625,17 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) if (!waiting_for_msg) { + /* We don't need the sesman transport anymore */ + if (self->sesman_trans != NULL) + { + self->delete_sesman_trans = 1; + } xrdp_wm_mod_connect_done(self->wm, status); - cleanup_sesman_connection(self); + /* Make sure the module is cleaned up if we weren't successful */ + if (status != 0) + { + xrdp_mm_module_cleanup(self); + } } } diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index 443bca64..74e482e0 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -312,8 +312,9 @@ struct xrdp_enc_data; enum mm_connect_state { MMCS_CONNECT_TO_SESMAN, - MMCS_PAM_AUTH, - MMCS_SESSION_AUTH, + MMCS_GATEWAY_LOGIN, + MMCS_SESSION_LOGIN, + MMCS_CREATE_SESSION, MMCS_CONNECT_TO_SESSION, MMCS_CONNECT_TO_CHANSRV, MMCS_DONE @@ -348,10 +349,8 @@ struct xrdp_mm struct xrdp_wm *wm; /* owner */ enum mm_connect_state connect_state; /* State of connection */ /* Other processes we connect to */ - /* NB : When we move to UDS, the sesman and pam_auth - * connection be merged */ int use_sesman; /* true if this is a sesman session */ - int use_pam_auth; /* True if we're to authenticate using PAM */ + int use_gw_login; /* True if we're to login using a gateway */ int use_chansrv; /* true if chansrvport is set in xrdp.ini or using sesman */ struct trans *sesman_trans; /* connection to sesman */ struct trans *chan_trans; /* connection to chansrv */ @@ -495,6 +494,7 @@ struct xrdp_wm struct xrdp_font *default_font; struct xrdp_keymap keymap; int hide_log_window; + int fatal_error_in_log_window; struct xrdp_bitmap *target_surface; /* either screen or os surface */ int current_surface_index; int hints; diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c index 1232f86d..f4915247 100644 --- a/xrdp/xrdp_wm.c +++ b/xrdp/xrdp_wm.c @@ -2034,12 +2034,22 @@ xrdp_wm_login_state_changed(struct xrdp_wm *self) LOG(LOG_LEVEL_DEBUG, "xrdp_wm_login_mode_changed: login_mode is %d", self->login_state); if (self->login_state == WMLS_RESET) { - /* this is the initial state of the login window */ - xrdp_wm_set_login_state(self, WMLS_USER_PROMPT); list_clear(self->log); xrdp_wm_delete_all_children(self); self->dragging = 0; - xrdp_wm_init(self); + + if (self->fatal_error_in_log_window) + { + /* We've got here after OK is pressed in the log + * window, so the user has read the message(s) in it */ + g_set_wait_obj(self->pro_layer->self_term_event); + } + else + { + /* this is the initial state of the login window */ + xrdp_wm_set_login_state(self, WMLS_USER_PROMPT); + xrdp_wm_init(self); + } } else if (self->login_state == WMLS_START_CONNECT) { @@ -2160,7 +2170,6 @@ add_string_to_logwindow(const char *msg, struct list *log) do { new_part_message = g_strndup(current_pointer, LOG_WINDOW_CHAR_PER_LINE); - LOG(LOG_LEVEL_INFO, "%s", new_part_message); list_add_item(log, (tintptr) new_part_message); len_done += g_strlen(new_part_message); current_pointer += g_strlen(new_part_message); From 47ace4acfd8b51f4bf52d7bba5a1777b0291b173 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:45:59 +0000 Subject: [PATCH 10/11] Fix g_sck_get_peer_cred() on FreeBSD Socket level should be SOL_LOCAL rather than SOL_SOCKET - See 'man unix'. --- common/os_calls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/os_calls.c b/common/os_calls.c index a30b60f7..f631b1d1 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -671,7 +671,7 @@ g_sck_get_peer_cred(int sck, int *pid, int *uid, int *gid) unsigned int xucred_length; xucred_length = sizeof(xucred); - if (getsockopt(sck, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &xucred_length)) + if (getsockopt(sck, SOL_LOCAL, LOCAL_PEERCRED, &xucred, &xucred_length)) { return 1; } From 8a0a0249b2a1bbfc3ec2a45874ba27ce7caead9b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:31:12 +0000 Subject: [PATCH 11/11] Ignore xrdp-authtest in 'git status' --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c5026d15..7dae1232 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ NEWS README sesman/chansrv/xrdp-chansrv sesman/sessvc/xrdp-sessvc +sesman/tools/xrdp-authtest sesman/tools/xrdp-dis sesman/tools/xrdp-sesadmin sesman/tools/xrdp-sesrun