diff --git a/Makefile.am b/Makefile.am index 2d15c7d7..b5f26454 100644 --- a/Makefile.am +++ b/Makefile.am @@ -49,6 +49,7 @@ endif SUBDIRS = \ common \ + raptorsmiface \ vnc \ xup \ mc \ diff --git a/configure.ac b/configure.ac index 4dbcf230..5a00ed59 100644 --- a/configure.ac +++ b/configure.ac @@ -342,6 +342,7 @@ AC_CHECK_HEADERS([sys/prctl.h]) AC_CONFIG_FILES([ common/Makefile + raptorsmiface/Makefile docs/Makefile docs/man/Makefile genkeymap/Makefile diff --git a/raptorsmiface/Makefile.am b/raptorsmiface/Makefile.am new file mode 100644 index 00000000..0e4d7d1c --- /dev/null +++ b/raptorsmiface/Makefile.am @@ -0,0 +1,32 @@ +EXTRA_DIST = libraptorsmiface.h + +EXTRA_DEFINES = +EXTRA_INCLUDES = +EXTRA_LIBS = +EXTRA_FLAGS = + +EXTRA_INCLUDES += -I$(prefix)/include +EXTRA_FLAGS += -L$(prefix)/lib -Wl,-rpath -Wl,$(prefix)/lib + +AM_CFLAGS = \ + -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \ + -DXRDP_SBIN_PATH=\"${sbindir}\" \ + -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ + -DXRDP_PID_PATH=\"${localstatedir}/run\" \ + $(EXTRA_DEFINES) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/common \ + $(EXTRA_INCLUDES) + +lib_LTLIBRARIES = \ + libraptorsmiface.la + +libraptorsmiface_la_SOURCES = \ + libraptorsmiface.c + +libraptorsmiface_la_LDFLAGS = \ + $(EXTRA_FLAGS) -lmysqlclient + +libraptorsmiface_la_LIBADD = \ + $(EXTRA_LIBS) diff --git a/raptorsmiface/libraptorsmiface.c b/raptorsmiface/libraptorsmiface.c new file mode 100644 index 00000000..bb934b39 --- /dev/null +++ b/raptorsmiface/libraptorsmiface.c @@ -0,0 +1,1278 @@ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + (c) 2012 Timothy Pearson + (c) 2012 Raptor Engineering +*/ + +#define _GNU_SOURCE +#define HAVE_STDINT_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define list_delete mysql_list_delete +#include +#undef list_delete + +#include "list.h" +#include "libraptorsmiface.h" + +#define STATISTICS_SERVER_START_EVENT 0 +#define STATISTICS_SERVER_STOP_EVENT 1 +#define STATISTICS_NEW_CONNECTION_EVENT 2 +#define STATISTICS_CONNECTION_STATUS_EVENT 3 +#define STATISTICS_DISCONNECTION_EVENT 4 + +#define RAPTORSMIFACE_CFG_DATABASE "Database" +#define RAPTORSMIFACE_CFG_DATABASE_SERVER "Server" +#define RAPTORSMIFACE_CFG_DATABASE_NAME "Database" +#define RAPTORSMIFACE_CFG_DATABASE_USER "User" +#define RAPTORSMIFACE_CFG_DATABASE_PASSWORD "Password" + +#define RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION 1 + +char *server = NULL; +char *user = NULL; +char *password = NULL; +char *database = NULL; + +char *local_machine_fqdn = NULL; + +void dprint(const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + +#if 0 + vprintf(fmt, argp); +#else + char debug[512]; + vsprintf(debug, fmt, argp); + FILE *fp = fopen("/raptorsmiface.debug", "a"); + if (fp != NULL) + { + fputs(debug, fp); + fclose(fp); + } +#endif + + va_end(argp); +} + +void raptorsmiface_config_read_database(int file, struct list* param_n, struct list* param_v) { + int i; + char* buf; + char* temp_buf; + + // Set defaults + if (!server) server = strdup("localhost"); + if (!user) user = strdup("remotelab"); + if (!password) password = strdup(""); + if (!database) database = strdup("remotelab_sm"); + + list_clear(param_v); + list_clear(param_n); + + file_read_section(file, RAPTORSMIFACE_CFG_DATABASE, param_n, param_v); + for (i = 0; i < param_n->count; i++) { + buf = (char*)list_get_item(param_n, i); + if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_SERVER)) { + free(server); + server = strdup((char*)list_get_item(param_v, i)); + } + if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_NAME)) { + free(database); + database = strdup((char*)list_get_item(param_v, i)); + } + if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_USER)) { + free(user); + user = strdup((char*)list_get_item(param_v, i)); + } + if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_PASSWORD)) { + free(password); + password = strdup((char*)list_get_item(param_v, i)); + } + } + + g_printf("raptorsmiface configuration:\r\n"); + g_printf("\tServer: %s\r\n", server); + g_printf("\tDatabase: %s\r\n", database); + g_printf("\tUser: %s\r\n", user); +} + +void read_ini_configuration() { + int fd; + struct list* sec; + struct list* param_n; + struct list* param_v; + + /* set global variables */ + local_machine_fqdn = raptor_sm_get_local_machine_fqdn(); + + /* open configuration file */ + char cfg_file[256]; + g_snprintf(cfg_file, 255, "%s/xrdp.ini", XRDP_CFG_PATH); + + fd = g_file_open(cfg_file); + if (-1 == fd) { + dprint("[ERROR] Unable to open configuration file [%s]", cfg_file); + return; + } + + sec = list_create(); + sec->auto_free = 1; + file_read_sections(fd, sec); + param_n = list_create(); + param_n->auto_free = 1; + param_v = list_create(); + param_v->auto_free = 1; + + /* read database config */ + raptorsmiface_config_read_database(fd, param_n, param_v); + + /* cleanup */ + list_delete(sec); + list_delete(param_v); + list_delete(param_n); + g_file_close(fd); +} + +MYSQL * connect_if_needed() { + MYSQL *conn = mysql_init(NULL); + read_ini_configuration(); + if (!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0)) { + dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn)); + conn = 0; + } + else { + // Check schema version + char* query; + MYSQL_RES *res; + MYSQL_ROW row; + asprintf(&query, "SELECT value FROM dbschema WHERE skey='revision'"); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn)); + conn = 0; + } + else { + free(query); + res = mysql_store_result(conn); + if ((row = mysql_fetch_row(res)) == NULL) { + dprint("[ERROR] Mandatory schema version key not found\n\r"); + conn = 0; + } + else if (!row[0]) { + dprint("[ERROR] Mandatory schema version key not found\n\r"); + conn = 0; + } + else { + int schema_version = atoi(row[0]); + if (schema_version != RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION) { + dprint("[ERROR] Schema version key mismatch (%d:%d)\n\r", schema_version, RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION); + conn = 0; + } + } + } + } + return conn; +} + +char* get_mysql_escaped_string(MYSQL *sqlcn, char* rawstr) { + unsigned int minlen = strlen(rawstr); + unsigned int maxlen = ((minlen*2)+1); + char* escstr = malloc(maxlen*sizeof(char)); + mysql_real_escape_string(sqlcn, escstr, rawstr, minlen); + return escstr; +} + +char mutex; +int mysql_query_internal(MYSQL *conn, const char * query) { + // For some reason this can hang rather badly + // It might be related to concurrent access to the same conn object though + return mysql_query(conn, query); +} + +char* get_group_for_user(char* username) { + struct passwd* pwd; + pwd = getpwnam(username); + if (!pwd) { + return NULL; + } + gid_t groupid = pwd->pw_gid; + struct group* primarygroup; + primarygroup = getgrgid(groupid); + if (!primarygroup) { + return NULL; + } + + return strdup(primarygroup->gr_name); +} + +int raptor_sm_get_uid_for_user(char* username) { + struct passwd *pwd = calloc(1, sizeof(struct passwd)); + if (pwd == NULL) { + return -1; + } + size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char); + char *buffer = malloc(buffer_len); + if (buffer == NULL) { + return -2; + } + getpwnam_r(username, pwd, buffer, buffer_len, &pwd); + if (pwd == NULL) { + return -3; + } + uid_t uid = pwd->pw_uid; + free(buffer); + free(pwd); + return uid; +} + +char raptor_sm_deallocate_session(char* username) { + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_RES *svr_res; + MYSQL_ROW svr_row; + MYSQL_RES *cnt_res; + MYSQL_ROW cnt_row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return 1; + } + + int display = raptor_sm_get_display_for_username(username); + char* hostname = raptor_sm_get_hostname_for_username(username, false); + pid_t serverpid = raptor_sm_get_pid_for_username(username, RAPTOR_SM_SERVER_PID_FIELD); + if (serverpid >= 0) { + // Verify non-existence of PID on remote server before removing session information from the database + char* ip = raptor_sm_get_ip_for_hostname(hostname, 0); + char* command_string; + asprintf(&command_string, "ssh root@%s \'ps -p %d | grep %d\'", ip, serverpid, serverpid); + FILE *fp; + char output[1024]; + // Open the command for reading + fp = popen(command_string, "r"); + if (fp == NULL) { + free(ip); + mysql_close(conn); + return -1; + } + // Read the output a line at a time + fgets(output, sizeof(output)-1, fp); + // Close output + pclose(fp); + free(command_string); + free(ip); + if (strcmp(output, "") != 0) { + mysql_free_result(res); + mysql_close(conn); + return 0; + } + } + +#ifndef RAPTOR_SM_DISABLE_KERBEROS + char* command_string; + char* ip = raptor_sm_get_ip_for_hostname(hostname, 0); + asprintf(&command_string, "ssh root@%s \'rm -f /tmp/krb5cc_%d\'", ip, raptor_sm_get_uid_for_user(username)); + dprint("Running command %s...\n\r", command_string); + system(command_string); + free(command_string); + free(ip); +#endif + + // Remove the user from the system + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "DELETE FROM sessions WHERE username='%s'", safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(hostname); + free(query); + mysql_close(conn); + return 2; + } + else { + free(query); + + // Insert connection information into the statistics database + char* safe_servername = get_mysql_escaped_string(conn, hostname); + char* safe_username = get_mysql_escaped_string(conn, username); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + long long timestamp = time(NULL); + asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_DISCONNECTION_EVENT, safe_local_machine_fqdn, safe_servername, display, -1, safe_username); + free(safe_servername); + free(safe_username); + free(safe_local_machine_fqdn); + free(hostname); + if (mysql_query_internal(conn, query)) { + // Server error + dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn)); + } + free(query); + + mysql_close(conn); + return 0; + } +} + +char* raptor_sm_allocate_session(char* username) { + MYSQL_RES *res; + MYSQL_ROW row; + MYSQL_RES *svr_res; + MYSQL_ROW svr_row; + MYSQL_RES *per_res; + MYSQL_ROW per_row; + MYSQL_RES *cnt_res; + MYSQL_ROW cnt_row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return strdup("ERROR"); + } + + // Verify that this user is not already on the system + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "SELECT servername FROM sessions WHERE username='%s'", safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return strdup("ERROR"); + } + else { + free(query); + res = mysql_store_result(conn); + if ((row = mysql_fetch_row(res)) == NULL) { + // User is not on a system + // Find the least utilized node + if (mysql_query_internal(conn, "SELECT name FROM servers WHERE online='1'")) { + // Server error + mysql_free_result(res); + mysql_close(conn); + return strdup("ERROR"); + } + else { + svr_res = mysql_store_result(conn); + + // Get group for user + char* groupname = get_group_for_user(username); + if (!groupname) { + return strdup("ERROR"); + } + char* safe_groupname = get_mysql_escaped_string(conn, groupname); + free(groupname); + // Get the list of allowed nodes for this group + asprintf(&query, "SELECT server FROM allowed_servers WHERE groupname='%s'", safe_groupname); + free(safe_groupname); + if (mysql_query_internal(conn, query)) { + // Server error + mysql_free_result(res); + mysql_free_result(svr_res); + mysql_close(conn); + return strdup("ERROR"); + } + else { + per_res = mysql_store_result(conn); + char* bestserver = strdup(""); + int bestusage = INT_MAX; + while ((svr_row = mysql_fetch_row(svr_res)) != NULL) { + // Am I allowed to use this server? + bool can_use_server = false; + while ((per_row = mysql_fetch_row(per_res)) != NULL) { + if (strcmp(per_row[0], svr_row[0]) == 0) { + can_use_server = true; + } + } + mysql_data_seek(per_res, 0); + if (can_use_server) { + char* safe_servername = get_mysql_escaped_string(conn, svr_row[0]); + asprintf(&query, "SELECT username FROM sessions WHERE servername='%s'", safe_servername); + free(safe_servername); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + free(bestserver); + mysql_free_result(res); + mysql_free_result(svr_res); + mysql_close(conn); + return strdup("ERROR"); + } + else { + free(query); + cnt_res = mysql_store_result(conn); + int usagecount = 0; + while ((cnt_row = mysql_fetch_row(cnt_res)) != NULL) { + usagecount++; + } + mysql_free_result(cnt_res); + if (usagecount < bestusage) { + free(bestserver); + bestserver = strdup(svr_row[0]); + bestusage = usagecount; + } + } + } + } + mysql_free_result(res); + mysql_free_result(svr_res); + mysql_free_result(per_res); + + if (strcmp(bestserver, "") != 0) { + // Insert new information into the sessions database and set status to ALLOCATED + char* safe_servername = get_mysql_escaped_string(conn, bestserver); + char* safe_username = get_mysql_escaped_string(conn, username); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + asprintf(&query, "INSERT INTO sessions (username, arbiter, servername, state) VALUES ('%s', '%s', '%s', '%d')", safe_username, safe_local_machine_fqdn, safe_servername, SM_STATUS_ALLOCATED); + free(safe_local_machine_fqdn); + free(safe_servername); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return strdup("ERROR"); + } + else { + free(query); + + // Insert connection information into the statistics database + char* safe_servername = get_mysql_escaped_string(conn, bestserver); + char* safe_username = get_mysql_escaped_string(conn, username); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + long long timestamp = time(NULL); + asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_NEW_CONNECTION_EVENT, safe_local_machine_fqdn, safe_servername, -1, SM_STATUS_ALLOCATED, safe_username); + free(safe_local_machine_fqdn); + free(safe_servername); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn)); + } + free(query); + + mysql_close(conn); + return strdup(bestserver); + } + } + else { + // No usable server found! + mysql_close(conn); + return strdup("ERROR"); + } + } + } + } + else { + char* ret = strdup(row[0]); + mysql_free_result(res); + mysql_close(conn); + return ret; + } + } +} + +char* raptor_sm_get_local_machine_fqdn() { + struct addrinfo hints, *res; + int err; + + char hostname[1024]; + hostname[1023] = '\0'; + gethostname(hostname, 1023); + + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { + return strdup(""); + } + + char* ret = strdup(res->ai_canonname); + freeaddrinfo(res); + return ret; +} + +char* raptor_sm_get_ip_for_hostname(char* hostname, char* error) { + struct addrinfo hints, *res; + struct in_addr addr; + int err; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_INET; + + if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { + if (error) *error = 1; + return strdup(""); + } + + addr.s_addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr; + + char* ret = strdup(inet_ntoa(addr)); + freeaddrinfo(res); + if (error) *error = 0; + return ret; +} + +char* raptor_sm_get_hostname_for_username(char* username, bool create) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return strdup("SQLERR100"); + } + + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "SELECT servername FROM sessions WHERE username='%s'", safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return strdup("SQLERR101"); + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + char* ret = strdup(row[0]); + mysql_free_result(res); + mysql_close(conn); + return ret; + } + // Nothing in the DB + mysql_free_result(res); + if (create) { + // Try to allocate a new session on a node + mysql_close(conn); + return raptor_sm_allocate_session(username); + } + else { + mysql_close(conn); + return strdup(""); + } + } +} + +char* raptor_sm_get_hostname_for_display(int display) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return strdup("SQLERR100"); + } + + asprintf(&query, "SELECT servername FROM sessions WHERE display='%d'", display); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return strdup("SQLERR101"); + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + char* ret = strdup(row[0]); + mysql_free_result(res); + mysql_close(conn); + return ret; + } + // Nothing in the DB + mysql_free_result(res); + mysql_close(conn); + return strdup(""); + } +} + +char* raptor_sm_get_ip_for_username(char* username, bool create) { + char* hostname = raptor_sm_get_hostname_for_username(username, create); + char err; + char* ip = raptor_sm_get_ip_for_hostname(hostname, &err); + free(hostname); + if (err) { + raptor_sm_deallocate_session(username); + return strdup("ERROR"); + } + return ip; +} + +bool raptor_sm_sesslimit_reached(char* username) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return true; + } + + // Respect maximum session number for the group for this user + int sesslimit = 0; // Default to denying all sessions + + // Get group for user + char* groupname = get_group_for_user(username); + if (!groupname) { + return true; + } + char* safe_groupname = get_mysql_escaped_string(conn, groupname); + asprintf(&query, "SELECT sesslimit FROM groups WHERE groupname='%s'", safe_groupname); + free(safe_groupname); + if (mysql_query_internal(conn, query)) { + // Server error + free(groupname); + free(query); + mysql_close(conn); + return true; + } + else { + free(query); + res = mysql_store_result(conn); + row = mysql_fetch_row(res); + if (row[0]) { + sesslimit = atoi(row[0]); + } + mysql_free_result(res); + } + + // Figure out how many users are online from this group + int sesscount = 0; + asprintf(&query, "SELECT username FROM sessions WHERE state<>'%d'", SM_STATUS_ALLOCATED); + if (mysql_query_internal(conn, query)) { + // Server error + free(groupname); + free(query); + mysql_close(conn); + return true; + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + if (row[0]) { + char* test_groupname = get_group_for_user(row[0]); + if (!test_groupname) { + free(groupname); + return true; + } + if (strcmp(groupname, test_groupname) == 0) { + sesscount++; + } + free(test_groupname); + } + } + mysql_free_result(res); + + if (sesscount < sesslimit) { + free(groupname); + mysql_close(conn); + return false; + } + free(groupname); + mysql_close(conn); + return true; + } + + // We should never end up here! + free(groupname); + mysql_close(conn); + return true; +} + +pid_t raptor_sm_run_remote_server(char* username, char *const argv[], char* dbfield, int display) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) { + // Respect maximum session number for the group for this user + if (raptor_sm_sesslimit_reached(username)) { + mysql_close(conn); + return -5; + } + } + + // Make sure a server is not already running for this user + // Return the existing PID if it is + char* safe_username = get_mysql_escaped_string(conn, username); + if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) { + asprintf(&query, "SELECT %s,servername FROM sessions WHERE username='%s' AND state<>'%d'", dbfield, safe_username, SM_STATUS_ALLOCATED); + } + else { + asprintf(&query, "SELECT %s,servername FROM sessions WHERE username='%s'", dbfield, safe_username); + } + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + if (row[0]) { + int ret = atoi(row[0]); + if (ret >= 0) { + // Verify existence of PID on remote server + dprint("Verifying process %d on %s...\n\r", ret, row[1]); + char* ip = raptor_sm_get_ip_for_hostname(row[1], 0); + char* command_string; + asprintf(&command_string, "ssh root@%s \'ps -p %d | grep %d\'", ip, ret, ret); + FILE *fp; + char output[1024]; + // Open the command for reading + fp = popen(command_string, "r"); + if (fp == NULL) { + free(ip); + mysql_close(conn); + return -1; + } + // Read the output a line at a time + fgets(output, sizeof(output)-1, fp); + // Close output + pclose(fp); + free(command_string); + free(ip); + dprint("...result was %s\n\r", output); + if (strcmp(output, "") != 0) { + mysql_free_result(res); + mysql_close(conn); + return ret; + } + } + } + } + mysql_free_result(res); + } + + int i; + int n_commands; + + n_commands = 0; + while (argv[n_commands] != NULL) { + n_commands++; + } + + char* ipaddr = raptor_sm_get_ip_for_username(username, true); + + // This is HORRIBLY inefficient + char* command_string = strdup(""); + for (i=0; i= 0) { + uid_t uid = raptor_sm_get_uid_for_user(username); + asprintf(&command_string, "rsync -a /tmp/krb5cc_%d root@%s:/tmp/krb5cc_%d", uid, ipaddr, uid); + dprint("Running command %s...\n\r", command_string); + system(command_string); + free(command_string); + asprintf(&command_string, "rm -f /tmp/krb5cc_%d", uid); + dprint("Running command %s...\n\r", command_string); + system(command_string); + free(command_string); + } +#endif + +#ifndef RAPTOR_SM_DISABLE_PULSEAUDIO + if (display >= 0) { + asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && pulseaudio -D --load=\\\"module-native-protocol-tcp listen=0.0.0.0 auth-ip-acl=%s port=%d\\\"\' &> /dev/null\" &", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), RAPTOR_SM_MANAGEMENT_SERVER_IP_NETRANGE, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display)); + dprint("Running command %s...\n\r", command_string); + system(command_string); + free(command_string); + } +#endif + + if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) { + asprintf(&command_string, "ssh root@%s \'%s & echo $! &\'", ipaddr, origstr); + } + else { +#if RAPTOR_SM_DISABLE_PULSEAUDIO + asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && %s\' &> /dev/null & echo \\$!\"", ipaddr, username, display, origstr); +#else + //asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s\' &> /dev/null & echo \\$!\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), origstr); + asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s\' &> /var/log/%s_wm_session.log & echo \\$!\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), origstr, username); +#endif + } + dprint("Running command %s...\n\r", command_string); + free(origstr); + free(ipaddr); + + FILE *fp; + char output[1024]; + + // Open the command for reading + fp = popen(command_string, "r"); + if (fp == NULL) { + mysql_close(conn); + return -1; + } + + // Read the output a line at a time + fgets(output, sizeof(output)-1, fp); + + // Close output + pclose(fp); + + free(command_string); + + mysql_close(conn); + return atoi(output); +} + +pid_t raptor_sm_get_pid_for_username(char* username, char* dbfield) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Make sure a server is not already running for this user + // Return the existing PID if it is + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "SELECT %s FROM sessions WHERE username='%s'", dbfield, safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + if (row[0]) { + int ret = atoi(row[0]); + if (ret >= 0) { + mysql_free_result(res); + mysql_close(conn); + return ret; + } + } + } + mysql_free_result(res); + } + + mysql_close(conn); + return -3; +} + +int raptor_sm_server_started(char* username, pid_t pid, int display, char* dbfield) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + long long timestamp = time(NULL); + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Update new information into the sessions database and set status to ALLOCATED + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "UPDATE sessions SET %s='%d', stamp_start='%lld', state='%d', display='%d', stamp_statechange='%lld' WHERE username='%s' AND state='%d'", dbfield, pid, timestamp, SM_STATUS_RUNNING, display, timestamp, safe_username, SM_STATUS_ALLOCATED); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + + // Insert connection information into the statistics database + char* current_server = raptor_sm_get_hostname_for_display(display); + char* safe_servername = get_mysql_escaped_string(conn, current_server); + char* safe_username = get_mysql_escaped_string(conn, username); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + long long timestamp = time(NULL); + asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_CONNECTION_STATUS_EVENT, safe_local_machine_fqdn, safe_servername, display, SM_STATUS_RUNNING, safe_username); + free(safe_servername); + free(safe_username); + free(safe_local_machine_fqdn); + free(current_server); + if (mysql_query_internal(conn, query)) { + // Server error + dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn)); + } + free(query); + + mysql_close(conn); + return 0; + } +} + +int raptor_sm_wm_started(char* username, pid_t pid, char* dbfield) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Update new information into the sessions database and set status to ALLOCATED + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "UPDATE sessions SET %s='%d' WHERE username='%s'", dbfield, pid, safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + mysql_close(conn); + return 0; + } +} + +int raptor_sm_get_display_for_username(char* username) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "SELECT display FROM sessions WHERE username='%s'", safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + if (row[0]) { + int ret = atoi(row[0]); + mysql_free_result(res); + mysql_close(conn); + return ret; + } + else { + mysql_free_result(res); + mysql_close(conn); + return -3; + } + } + // Nothing in the DB + mysql_free_result(res); + mysql_close(conn); + return -4; + } +} + +char* raptor_sm_get_username_for_display_and_hostname(int display, char* hostname) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return strdup(""); + } + + char* safe_hostname = get_mysql_escaped_string(conn, hostname); + asprintf(&query, "SELECT username FROM sessions WHERE display='%d' AND servername='%s'", display, safe_hostname); + free(safe_hostname); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return strdup(""); + } + else { + free(query); + res = mysql_store_result(conn); + while ((row = mysql_fetch_row(res)) != NULL) { + if (row[0]) { + char* ret = strdup(row[0]); + mysql_free_result(res); + mysql_close(conn); + return ret; + } + else { + mysql_free_result(res); + mysql_close(conn); + return strdup(""); + } + } + // Nothing in the DB + mysql_free_result(res); + mysql_close(conn); + return strdup(""); + } +} + +void raptor_sm_wait_for_pid_exit(char* username, pid_t pid) { + char* ipaddr = raptor_sm_get_ip_for_username(username, false); + + char* command_string; + asprintf(&command_string, "ssh root@%s \'while [[ `ps -p %d | grep %d` != \"\" ]]; do sleep 1; done\'", ipaddr, pid, pid); + system(command_string); + free(command_string); +} + +void raptor_sm_session_terminated(char* username) { + raptor_sm_deallocate_session(username); +} + +int raptor_sm_wm_terminated(char* username) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + long long timestamp = time(NULL); + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Update new information into the sessions database + char* safe_username = get_mysql_escaped_string(conn, username); + asprintf(&query, "UPDATE sessions SET %s=NULL WHERE username='%s'", RAPTOR_SM_WM_PID_FIELD, safe_username); + free(safe_username); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + mysql_close(conn); + return -2; + } + else { + free(query); + mysql_close(conn); + return 0; + } +} + +int raptor_sm_get_new_unique_display(int mindisplay, int maxdisplay) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + asprintf(&query, "SELECT display FROM sessions"); + if (mysql_query_internal(conn, query)) { + // Server error + free(query); + dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn)); + mysql_close(conn); + return -2; + } + else { + res = mysql_store_result(conn); + int freedisp; + bool dispinuse; + for (freedisp=mindisplay; freedisp /dev/null\"", ipaddr, username, display, executable); +#else + asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s && exit\' &> /dev/null\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), executable); +#endif + system(command_string); + free(command_string); +} + +void raptor_sm_terminate_server(char* username) { + char* ipaddr = raptor_sm_get_ip_for_username(username, true); + char* command_string; + + // Terminate remote X server + pid_t pid = raptor_sm_get_pid_for_username(username, RAPTOR_SM_SERVER_PID_FIELD); + if (pid > 0) { + asprintf(&command_string, "ssh root@%s \'kill -9 %ld\'", ipaddr, pid); + system(command_string); + free(command_string); + } +} + +int raptor_sm_stats_report_server_start(char* hostname) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Insert information into the statistics database + char* safe_servername = get_mysql_escaped_string(conn, hostname); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + long long timestamp = time(NULL); + asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d')", timestamp, STATISTICS_SERVER_START_EVENT, safe_local_machine_fqdn, safe_servername, -1, -1); + free(safe_servername); + free(safe_local_machine_fqdn); + if (mysql_query_internal(conn, query)) { + // Server error + dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn)); + } + free(query); + mysql_close(conn); + + return 0; +} + +int raptor_sm_stats_report_server_stop(char* hostname) { + MYSQL_RES *res; + MYSQL_ROW row; + char* query; + + MYSQL *conn = connect_if_needed(); + if (!conn) { + return -1; + } + + // Insert information into the statistics database + char* safe_servername = get_mysql_escaped_string(conn, hostname); + char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn); + long long timestamp = time(NULL); + asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d')", timestamp, STATISTICS_SERVER_STOP_EVENT, safe_local_machine_fqdn, safe_servername, -1, -1); + free(safe_servername); + free(safe_local_machine_fqdn); + if (mysql_query_internal(conn, query)) { + // Server error + dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn)); + } + free(query); + mysql_close(conn); + + return 0; +} diff --git a/raptorsmiface/libraptorsmiface.h b/raptorsmiface/libraptorsmiface.h new file mode 100644 index 00000000..79e4c2e1 --- /dev/null +++ b/raptorsmiface/libraptorsmiface.h @@ -0,0 +1,66 @@ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + (c) 2012 Timothy Pearson + (c) 2012 Raptor Engineering +*/ + +#include + +#include + +typedef unsigned char bool; +#define true 1 +#define false 0 + +// SM_STATUS_ALLOCATED: Server is not yet started +// SM_STATUS_RUNNING: Server is up, but client is not connected +// SM_STATUS_CONNECTED: Server is up and client is connected +enum raptor_sm_status { + SM_STATUS_ALLOCATED, + SM_STATUS_RUNNING, + SM_STATUS_CONNECTED, + SM_STATUS_FORCEKILL +}; + +#define RAPTOR_SM_SERVER_PID_FIELD "server_pid" +#define RAPTOR_SM_WM_PID_FIELD "wm_pid" + +#define RAPTOR_SM_BASE_PULSEAUDIO_PORT 2000 +#define RAPTOR_SM_MANAGEMENT_SERVER_IP_NETRANGE "10.0.0.0/8" + +char* raptor_sm_get_local_machine_fqdn(); + +char* raptor_sm_get_ip_for_hostname(char* hostname, char* err); +char* raptor_sm_get_hostname_for_username(char* username, bool create); + +char* raptor_sm_get_ip_for_username(char* username, bool create); +pid_t raptor_sm_run_remote_server(char* username, char *const argv[], char* dbfield, int display); +pid_t raptor_sm_get_pid_for_username(char* username, char* dbfield); +int raptor_sm_server_started(char* username, pid_t pid, int display, char* dbfield); +int raptor_sm_wm_started(char* username, pid_t pid, char* dbfield); +int raptor_sm_get_display_for_username(char* username); +void raptor_sm_wait_for_pid_exit(char* username, pid_t pid); +char* raptor_sm_get_username_for_display_and_hostname(int display, char* hostname); +void raptor_sm_session_terminated(char* username); +int raptor_sm_wm_terminated(char* username); +int raptor_sm_get_new_unique_display(int mindisplay, int maxdisplay); +bool raptor_sm_sesslimit_reached(char* username); +char raptor_sm_set_session_state(int display, int state); +void raptor_sm_run_remote_desktop(char* username, int display, char* executable); +void raptor_sm_terminate_server(char* username); +char* raptor_sm_get_hostname_for_display(int display); +int raptor_sm_stats_report_server_start(char* hostname); +int raptor_sm_stats_report_server_stop(char* hostname); diff --git a/sesman/Makefile.am b/sesman/Makefile.am index 50425e63..50a9fafc 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -9,7 +9,8 @@ AM_CPPFLAGS = \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ -DXRDP_SOCKET_PATH=\"${socketdir}\" \ -I$(top_srcdir)/common \ - -I$(top_srcdir)/sesman/libscp + -I$(top_srcdir)/sesman/libscp \ + -I$(top_srcdir)/raptorsmiface if XRDP_DEBUG AM_CPPFLAGS += -DXRDP_DEBUG @@ -70,6 +71,7 @@ xrdp_sesman_SOURCES = \ xrdp_sesman_LDADD = \ $(top_builddir)/common/libcommon.la \ $(top_builddir)/sesman/libscp/libscp.la \ + $(top_builddir)/raptorsmiface/libraptorsmiface.la \ $(AUTH_LIB) \ -lpthread diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index 05007757..2b22312c 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -9,7 +9,8 @@ AM_CPPFLAGS = \ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \ -DXRDP_SOCKET_PATH=\"${socketdir}\" \ - -I$(top_srcdir)/common + -I$(top_srcdir)/common \ + -I$(top_srcdir)/raptorsmiface if XRDP_DEBUG AM_CPPFLAGS += -DXRDP_DEBUG @@ -77,5 +78,6 @@ xrdp_chansrv_LDFLAGS = \ xrdp_chansrv_LDADD = \ $(top_builddir)/common/libcommon.la \ + $(top_builddir)/raptorsmiface/libraptorsmiface.la \ $(X_PRE_LIBS) -lXfixes -lXrandr -lX11 $(X_EXTRA_LIBS) \ $(CHANSRV_EXTRA_LIBS) diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 7a0de556..12614f51 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -1,6 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * + * Copyright (C) Timothy Pearson 2012-2019 * Copyright (C) Jay Sorg 2009-2013 * Copyright (C) Laxmikant Rashinkar 2009-2012 * @@ -38,6 +39,8 @@ #include "chansrv_fuse.h" #include "xrdp_sockets.h" +#include "libraptorsmiface.h" + static struct trans *g_lis_trans = 0; static struct trans *g_con_trans = 0; static struct trans *g_api_lis_trans = 0; @@ -60,6 +63,32 @@ int g_rdpsnd_chan_id = -1; /* rdpsnd */ int g_rdpdr_chan_id = -1; /* rdpdr */ int g_rail_chan_id = -1; /* rail */ +#if 0 +#include +#include +#include +void dprint(const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + char debug[1024]; + vsprintf(debug, fmt, argp); + FILE *fp = fopen("/chansrv.debug", "a"); + if (fp != NULL) + { + fputs(debug, fp); + fclose(fp); + } + va_end(argp); +} +#undef LOG +#define LOG(_a, _params) \ +{ \ + dprint _params; \ + dprint("\n"); \ +} +#endif + char *g_exec_name; tbus g_exec_event; tbus g_exec_mutex; @@ -414,6 +443,8 @@ process_message_channel_setup(struct stream *s) rail_init(); } + // Use the display number to mark session connected in the Raptor session management database + raptor_sm_set_session_state(g_display_num, SM_STATUS_CONNECTED); return rv; } @@ -1435,6 +1466,9 @@ channel_thread_loop(void *in_val) /* delete g_con_trans */ trans_delete(g_con_trans); g_con_trans = 0; + /* use the display number to mark session disconnected in the Raptor session management database */ + raptor_sm_set_session_state(g_display_num, SM_STATUS_RUNNING); + exit(0); // RAPTOR session management /* create new listener */ error = setup_listen(); @@ -1904,6 +1938,8 @@ main(int argc, char **argv) } } + /* use the display number to mark session disconnected in the Raptor session management database */ + raptor_sm_set_session_state(g_display_num, SM_STATUS_RUNNING); /* cleanup */ main_cleanup(); LOGM((LOG_LEVEL_INFO, "main: app exiting pid %d(0x%8.8x)", pid, pid)); diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 9af7a100..d8d8ea94 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -29,7 +29,7 @@ X11DisplayOffset=10 ;; MaxSessions - maximum number of connections to an xrdp server ; Type: integer ; Default: 0 -MaxSessions=50 +MaxSessions=1000000 ;; KillDisconnected - kill disconnected sessions ; Type: boolean diff --git a/sesman/session.c b/sesman/session.c index 0d9fdc70..6fa63c3a 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -42,6 +42,8 @@ #include "xauth.h" #include "xrdp_sockets.h" +#include "libraptorsmiface.h" + #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 #endif @@ -448,7 +450,21 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, return 0; } - display = session_get_avail_display_from_chain(); + char session_was_already_running = 0; + int allocdisplay = raptor_sm_get_display_for_username(s->username); + if (allocdisplay >= 0) { + session_was_already_running = 1; + display = allocdisplay; + } + else { + int allocdisplay = raptor_sm_get_new_unique_display(g_cfg->sess.x11_display_offset, g_cfg->sess.max_sessions); + if (allocdisplay < 0) { + display = 0; + } + else { + display = allocdisplay; + } + } if (display == 0) { @@ -533,6 +549,9 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, display, g_cfg->env_names, g_cfg->env_values); + if (session_was_already_running) { + g_exit(0); + } if (x_server_running(display)) { auth_set_env(data); @@ -705,7 +724,29 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, g_setenv("XRDP_START_HEIGHT", geometry, 1); /* fire up Xorg */ - g_execvp(xserver, pp1); + pid_t serverpid; + serverpid = raptor_sm_run_remote_server(s->username, pp1); + + if (serverpid >= 0) { + if (!session_was_already_running) { + char *friendlyscreen = g_strdup(screen); + friendlyscreen[0] = ' '; + raptor_sm_server_started(s->username, serverpid, atoi(friendlyscreen)); + g_free(friendlyscreen); + + // Wait for PID exit and remove information from the session database + raptor_sm_wait_for_pid_exit(s->username, serverpid); + raptor_sm_session_terminated(s->username); + } + } + else { + raptor_sm_session_terminated(s->username); + log_message(LOG_LEVEL_WARNING, "max concurrent session limit " + "exceeded in group. login for user %s denied", s->username); + g_exit(1); + } + + g_exit(0); } else if (type == SESMAN_SESSION_TYPE_XVNC) { diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am index fe14d88d..5179ef68 100644 --- a/xrdp/Makefile.am +++ b/xrdp/Makefile.am @@ -12,7 +12,8 @@ AM_CPPFLAGS = \ -DXRDP_SOCKET_PATH=\"${socketdir}\" \ -I$(top_builddir) \ -I$(top_srcdir)/common \ - -I$(top_srcdir)/libxrdp + -I$(top_srcdir)/libxrdp \ + -I$(top_srcdir)/raptorsmiface XRDP_EXTRA_LIBS = @@ -63,6 +64,7 @@ xrdp_SOURCES = \ xrdp_LDADD = \ $(top_builddir)/common/libcommon.la \ $(top_builddir)/libxrdp/libxrdp.la \ + $(top_builddir)/raptorsmiface/libraptorsmiface.la \ $(XRDP_EXTRA_LIBS) xrdpsysconfdir=$(sysconfdir)/xrdp diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 7d204b5b..1bb7ea15 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -24,6 +24,8 @@ #include "xrdp.h" #include "log.h" +#include "libraptorsmiface.h" + #ifndef USE_NOPAM #if defined(HAVE__PAM_TYPES_H) #define LINUXPAM 1 @@ -59,6 +61,7 @@ xrdp_mm_create(struct xrdp_wm *owner) self->wm = owner; self->login_names = list_create(); self->login_names->auto_free = 1; + self->login_username = 0; self->login_values = list_create(); self->login_values->auto_free = 1; @@ -190,6 +193,7 @@ xrdp_mm_send_login(struct xrdp_mm *self) if (g_strcasecmp(name, "username") == 0) { username = value; + self->login_username = g_strdup(username); } else if (g_strcasecmp(name, "password") == 0) { @@ -521,16 +525,30 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self, tui8 *guid) } else if (self->code == 10 || self->code == 20) /* X11rdp/Xorg */ { - use_uds = 1; - - if (xrdp_mm_get_value(self, "ip", text, 255) == 0) - { - if (g_strcmp(text, "127.0.0.1") != 0) - { + char* rsmip = raptor_sm_get_ip_for_username(self->login_username, true); + int allocdisplay = raptor_sm_get_display_for_username(self->login_username); + if ((raptor_sm_sesslimit_reached(self->login_username)) && (allocdisplay < 0)) { + g_snprintf(text, 255, "[LICENSE] Maximum concurrent session"); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text); + g_snprintf(text, 255, "[LICENSE] limit exceeded for group."); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text); + g_snprintf(text, 255, "[LICENSE] Login for user %s denied.", self->login_username); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text); + raptor_sm_session_terminated(self->login_username); + return 1; + } + else { + if (allocdisplay >= 0) { + self->display = allocdisplay; + } + self->mod->mod_set_param(self->mod, "ip", rsmip); + use_uds = 1; + if (g_strcmp(rsmip, "127.0.0.1") != 0) { use_uds = 0; } } + g_free(rsmip); if (use_uds) { g_snprintf(text, 255, XRDP_X11RDP_STR, self->display); @@ -576,7 +594,9 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self, tui8 *guid) { name = (const char *) list_get_item(self->login_names, i); value = (const char *) list_get_item(self->login_values, i); - self->mod->mod_set_param(self->mod, name, value); + if (strcmp(name, "ip") != 0) { + self->mod->mod_set_param(self->mod, name, value); + } } /* connect */ @@ -1546,8 +1566,7 @@ xrdp_mm_process_login_response(struct xrdp_mm *self, struct stream *s) if (ok) { self->display = display; - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, - "login successful for display %d", display); + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "login successful on display %d", display); if (xrdp_mm_setup_mod1(self) == 0) { diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index 7e416125..2cb51b80 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -294,6 +294,7 @@ struct xrdp_mm int chan_trans_up; /* true once connected to chansrv */ int delete_chan_trans; /* boolean set when done with channel connection */ int usechansrv; /* true if chansrvport is set in xrdp.ini or using sesman */ + char* login_username; /* RAPTOR */ struct xrdp_encoder *encoder; int cs2xr_cid_map[256]; int xr2cr_cid_map[256];