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); /** *