/* * Remote Laboratory Authentication Server * * 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 3 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (c) 2012-2019 Timothy Pearson * Raptor Engineering * http://www.raptorengineeringinc.com */ #include // getLocalMachineFQDN #include #include #include #include #include #include #include #include #include #include #include "auth_conn.h" #define STATISTICS_SERVER_START_EVENT 0 #define STATISTICS_SERVER_STOP_EVENT 1 #define STATISTICS_NEW_CONNECTION_EVENT 2 #define STATISTICS_DISCONNECTION_EVENT 3 #define DB_SCHEMA_VERSION 1 /* exception handling */ struct exit_exception { int c; exit_exception(int c):c(c) { } }; /* The AuthSocket class provides a socket that is connected with a client. For every client that connects to the server, the server creates a new instance of this class. */ AuthSocket::AuthSocket(int sock, TQString localMachineFQDN, TQObject *parent, const char *name) : TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_stationID(-1), m_bound(false), m_serviceID(0), m_localMachineFQDN(localMachineFQDN), m_pollInterval(10), m_terminationStamp(0), m_servActive(false), m_servState(0), m_servClientSocket(NULL), m_servClientTimeout(NULL), m_loopTimer(NULL), m_pollTimer(NULL), m_config(static_cast(parent)->m_config), m_database(NULL), m_databaseStationsCursor(NULL), m_databaseServicesCursor(NULL), m_databaseServiceTypesCursor(NULL), m_databasePermissionsCursor(NULL), m_databaseActivityCursor(NULL), m_databaseStatisticsCursor(NULL), m_databaseStatusCursor(NULL) { // Read settings m_config->setGroup("Tuning"); m_pollInterval = m_config->readNumEntry("pollInterval", m_pollInterval); // Initialize timers m_kerberosInitTimer = new TQTimer(); connect(m_kerberosInitTimer, SIGNAL(timeout()), this, SLOT(finishKerberosHandshake())); setServiceName("ulab"); m_loopBuffer.resize(8192); // 8kB line = 0; connect(this, SIGNAL(connectionClosed()), SLOT(connectionClosedHandler())); setSocket(sock); if (connectToDatabase() != 0) { exit(1); } m_pollTimer = new TQTimer(); connect(m_pollTimer, SIGNAL(timeout()), this, SLOT(pollFlags())); m_pollTimer->start(0, TRUE); } AuthSocket::~AuthSocket() { if (m_kerberosInitTimer) { m_kerberosInitTimer->stop(); delete m_kerberosInitTimer; m_kerberosInitTimer = NULL; } if (m_pollTimer) { m_pollTimer->stop(); delete m_pollTimer; m_pollTimer = NULL; } if (m_loopTimer) { m_loopTimer->stop(); delete m_loopTimer; m_loopTimer = NULL; } if (m_databaseStationsCursor) { delete m_databaseStationsCursor; } if (m_databaseServicesCursor) { delete m_databaseServicesCursor; } if (m_databaseServiceTypesCursor) { delete m_databaseServiceTypesCursor; } if (m_databasePermissionsCursor) { delete m_databasePermissionsCursor; } if (m_databaseActivityCursor) { delete m_databaseActivityCursor; } if (m_databaseStatisticsCursor) { delete m_databaseStatisticsCursor; } if (m_databaseStatusCursor) { delete m_databaseStatusCursor; } if (m_servClientSocket) { delete m_servClientSocket; } } void AuthSocket::close() { if (state() == TQSocket::Connected) { TDEKerberosServerSocket::close(); connectionClosedHandler(); } } void AuthSocket::connectionClosedHandler() { printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii()); updateStatistics(STATISTICS_DISCONNECTION_EVENT); // Update database m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND serviceid=%4").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_serviceID)); if (m_databaseActivityCursor->next()) { m_databaseActivityCursor->primeDelete(); m_databaseActivityCursor->del(true); } if (m_criticalSection > 0) { throw exit_exception(-1); } } void AuthSocket::initiateKerberosHandshake() { setUsingKerberos(true); m_kerberosInitTimer->start(100, TRUE); } void AuthSocket::finishKerberosHandshake() { if (kerberosStatus() == TDEKerberosServerSocket::KerberosInitializing) { m_kerberosInitTimer->start(100, TRUE); return; } if (kerberosStatus() == TDEKerberosServerSocket::KerberosInUse) { TQ_UINT32 magicnum = MAGIC_NUMBER; TQ_UINT32 protover = PROTOCOL_VERSION; TQDataStream ds(this); ds.setPrintableData(true); ds << magicnum; ds << protover; writeEndOfFrame(); enterCommandLoop(); return; } else { printf("[DEBUG] Connection from %s closed due to Kerberos failure\n\r", m_remoteHost.ascii()); fflush(stdout); close(); return; } } int AuthSocket::servLoop() { bool transferred_data; transferred_data = false; if (m_servActive) { TQString command; TQDataStream ds(this); ds.setPrintableData(true); TDEKerberosClientSocket::KerberosStatus krbstat; switch (m_servState) { case 0: if (!m_servClientTimeout) { m_servClientTimeout = new TQTimer(); m_servClientTimeout->start(5000, TRUE); } if ((m_servClientSocket->state() == TQSocket::Connecting) || (m_servClientSocket->state() == TQSocket::HostLookup)) { if (!m_servClientTimeout->isActive()) { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } } else { if (m_servClientTimeout) { m_servClientTimeout->stop(); delete m_servClientTimeout; m_servClientTimeout = NULL; } transferred_data = true; m_servState = 1; } break; case 1: if (m_servClientSocket->state() == TQSocket::Connected) { m_servClientSocket->setUsingKerberos(true); m_servState = 2; } else { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } break; case 2: krbstat = m_servClientSocket->kerberosStatus(); if ((krbstat == TDEKerberosClientSocket::KerberosInitializing) || (krbstat == TDEKerberosClientSocket::KerberosInUse)) { if (krbstat == TDEKerberosClientSocket::KerberosInUse) { transferred_data = true; m_servState = 3; } } else { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s due to Kerberos failure\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } break; case 3: if (!m_servClientTimeout) { m_servClientTimeout = new TQTimer(); m_servClientTimeout->start(5000, TRUE); } if (m_servClientSocket->state() == TQSocket::Connected) { if (m_servClientSocket->canReadFrame()) { TQDataStream clientDS(m_servClientSocket); clientDS.setPrintableData(true); TQString server_reply; clientDS >> server_reply; m_servClientSocket->clearFrameTail(); if (server_reply == "OK") { ds << TQString("OK"); writeEndOfFrame(); transferred_data = true; m_servState = 4; } else { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s due to remote server returning %s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii(), server_reply.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } } else { if (!m_servClientTimeout->isActive()) { // Timeout! m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } } } else { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection failed to %s:%d for user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; delete m_servClientTimeout; m_servClientTimeout = NULL; close(); } break; case 4: if (m_servClientSocket->state() == TQSocket::Connected) { TQ_LONG reclen; if (canReadData()) { reclen = readBlock(m_loopBuffer.data(), m_loopBuffer.size()); if (reclen > 0) { m_servClientSocket->writeBlock(m_loopBuffer.data(), reclen); m_servClientSocket->flush(); transferred_data = true; } else { printf("[WARNING] Remote server advertised data available but no data was able to be read!\n\r"); } } if (m_servClientSocket->canReadData()) { reclen = m_servClientSocket->readBlock(m_loopBuffer.data(), m_loopBuffer.size()); if (reclen > 0) { writeBlock(m_loopBuffer.data(), reclen); flush(); transferred_data = true; } else { printf("[WARNING] Remote client advertised data available but no data was able to be read!\n\r"); } } } else { m_servClientSocket->close(); ds << TQString("ERRNOTAVL"); writeEndOfFrame(); printf("[DEBUG] Connection terminated by remote host %s:%d for user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_servActive = false; close(); } break; } } if (transferred_data) { return 1; } else { return 0; } } void AuthSocket::pollFlags() { if ((m_bound) || (m_servActive)) { unsigned long long timestamp = TQDateTime::currentDateTime().toTime_t(); m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND serviceid=%4").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_serviceID)); if (m_databaseActivityCursor->next()) { m_terminationStamp = m_databaseActivityCursor->value("terminate").toLongLong(); if (m_terminationStamp > 0) { if (m_terminationStamp <= timestamp) { printf("[DEBUG] Got termination request from database (%lld <= %lld)\n\r", m_terminationStamp, timestamp); fflush(stdout); close(); } } } } m_pollTimer->start(1000, TRUE); } void AuthSocket::updateStatistics(int eventType) { // Update statistics long long sessionID = -1; m_databaseActivityCursor->select(TQString("station='%1' AND username='%2' AND realmname='%3' AND arbiter='%4' AND serviceid='%5'").arg(m_stationID).arg(m_authenticatedUserName).arg(m_authenticatedRealmName).arg(m_localMachineFQDN).arg(m_serviceID)); if (m_databaseActivityCursor->next()) { sessionID = m_databaseActivityCursor->value("pk").toInt(); } TQString userID = m_authenticatedUserName + "@" + m_authenticatedRealmName; TQSqlRecord *buffer = m_databaseStatisticsCursor->primeInsert(); buffer->setValue("timestamp", (TQ_ULLONG)TQDateTime::currentDateTime().toTime_t()); buffer->setValue("eventtypeid", eventType); buffer->setValue("arbiter", m_localMachineFQDN); buffer->setValue("stationid", m_stationID); buffer->setValue("sessionid", sessionID); buffer->setValue("typeid", m_serviceID); buffer->setValue("userid", userID); m_databaseStatisticsCursor->insert(); } void AuthSocket::commandLoop() { bool transferred_data; if (m_servActive) { transferred_data = false; if (servLoop() == 1) { transferred_data = true; } if (transferred_data) { if (m_loopTimer) m_loopTimer->start(0, TRUE); } else { if (m_loopTimer) m_loopTimer->start(m_pollInterval, TRUE); } return; } m_criticalSection++; try { transferred_data = false; if (state() == TQSocket::Connected) { if (canReadFrame()) { TQString command; TQDataStream ds(this); ds.setPrintableData(true); transferred_data = true; ds >> command; clearFrameTail(); if (command != "") { printf("[DEBUG] Got command %s from user %s@%s\n\r", command.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); if (command == "LIST") { // Send list of available servers... m_slist.clear(); // Get all stations from the database m_databaseStationsCursor->select(); while (m_databaseStationsCursor->next()) { bool authorized = false; bool in_use = false; bool multi_use = false; int ngroups = 0; gid_t *groups; struct passwd *pw; struct group *gr; groups = (gid_t*)malloc(ngroups*sizeof(gid_t)); if (groups == NULL) { printf("[ERROR] Unable to allocate memory\n\r"); fflush(stdout); exit(EXIT_FAILURE); } pw = getpwnam(m_authenticatedUserName.ascii()); if (pw == NULL) { printf("[WARNING] Unable to get detailed information for user '%s', ignoring\n\r", m_authenticatedUserName.ascii()); fflush(stdout); continue; } if (getgrouplist(m_authenticatedUserName.ascii(), pw->pw_gid, groups, &ngroups) == -1) { free(groups); groups = (gid_t*)malloc(ngroups*sizeof(gid_t)); if (getgrouplist(m_authenticatedUserName.ascii(), pw->pw_gid, groups, &ngroups) == -1) { printf("[WARNING] Unable to get groups for user '%s', ignoring\n\r", m_authenticatedUserName.ascii()); fflush(stdout); continue; } } int i; m_databasePermissionsCursor->select(TQString("station=%1").arg(m_databaseStationsCursor->value("pk").toInt())); while (m_databasePermissionsCursor->next()) { for (i = 0; i < ngroups; i++) { gr = getgrgid(groups[i]); if (gr != NULL) { if (m_databasePermissionsCursor->value("groupname").toString() == TQString(gr->gr_name)) { authorized = true; } } } } m_databaseActivityCursor->select(TQString("station=%1").arg(m_databaseStationsCursor->value("pk").toInt())); while (m_databaseActivityCursor->next()) { if (m_databaseActivityCursor->value("username").toString() != "") { in_use = true; } } if (authorized) { StationType st; st.id = m_databaseStationsCursor->value("pk").toInt(); st.name = m_databaseStationsCursor->value("name").toString(); st.description = m_databaseStationsCursor->value("description").toString(); m_databaseServicesCursor->select(TQString("station=%1").arg(m_databaseStationsCursor->value("pk").toInt())); while (m_databaseServicesCursor->next()) { m_databaseServiceTypesCursor->select(TQString("serviceid=%1").arg(m_databaseServicesCursor->value("servicetype").toInt())); ServiceType svt; if (m_databaseServiceTypesCursor->next()) { svt.type = m_databaseServiceTypesCursor->value("serviceid").toInt(); svt.name = m_databaseServiceTypesCursor->value("name").toString(); svt.description = m_databaseServiceTypesCursor->value("description").toString(); svt.clientLibrary = m_databaseServiceTypesCursor->value("client_library").toString(); svt.version = m_databaseServiceTypesCursor->value("version").toInt(); char tempchar; tempchar = m_databaseServiceTypesCursor->value("single_instance").toInt(); svt.singleInstance = (tempchar != 0); if (!svt.singleInstance) multi_use = true; } if (svt.name == "") { svt.name = i18n(""); } if (svt.description == "") { svt.description = i18n(""); } st.services.append(svt); } if ((!in_use) || (multi_use)) { m_slist.append(st); } } } ds << m_slist; writeEndOfFrame(); } else if (command == "BIND") { // Get desired Station Type from client StationType st; ds >> st; clearFrameTail(); // Attempt to bind to station matching desired Service Type list... m_stationID = -1; // Ensure that this user is not already connected int activeID = -1; m_databaseActivityCursor->select(TQString("username='%1' AND realmname='%2'").arg(m_authenticatedUserName).arg(m_authenticatedRealmName)); if (m_databaseActivityCursor->next()) { activeID = m_databaseActivityCursor->value("station").toInt(); } if (activeID < 0) { for (StationList::Iterator it(m_slist.begin()); it != m_slist.end(); ++it) { if ((*it).services == st.services) { // Verify that all services provided by this candidate workspace are online before allocating the workspace to a user bool online = true; m_databaseServicesCursor->select(TQString("station=%1").arg((*it).id)); while (m_databaseServicesCursor->next()) { m_databaseStatusCursor->select(TQString("hostname='%1'").arg(m_databaseServicesCursor->value("hostname").toString())); if (m_databaseStatusCursor->next()) { if (m_databaseStatusCursor->value("online").toInt() == 0) { online = false; } } } if (online) { m_stationID = (*it).id; break; } } } if (m_stationID < 0) { ds << TQString("ERRUNAVAL"); writeEndOfFrame(); } else { m_bound = true; m_serviceID = 0; // Update database TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert(); buffer->setValue("station", m_stationID); buffer->setValue("arbiter", m_localMachineFQDN); buffer->setValue("username", m_authenticatedUserName); buffer->setValue("realmname", m_authenticatedRealmName); buffer->setValue("logontime", (TQ_ULLONG)TQDateTime::currentDateTime().toTime_t()); buffer->setValue("serviceid", m_serviceID); buffer->setValue("terminate", 0); m_databaseActivityCursor->insert(); updateStatistics(STATISTICS_NEW_CONNECTION_EVENT); ds << TQString("OK"); writeEndOfFrame(); } } else { ds << TQString("ERRPREVCN"); writeEndOfFrame(); } } else if (command == "SERV") { // Get client library name from the client TQString libname; ds >> libname; clearFrameTail(); printf("[DEBUG] SERV command parameter was %s from user %s@%s\n\r", libname.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); m_databaseActivityCursor->select(TQString("username='%1' AND realmname='%2' AND serviceid=0").arg(m_authenticatedUserName).arg(m_authenticatedRealmName)); if (m_databaseActivityCursor->next()) { m_stationID = m_databaseActivityCursor->value("station").toInt(); } if (m_bound == true) { ds << TQString("ERRINVCMD"); writeEndOfFrame(); } else { if (m_stationID < 0) { ds << TQString("ERRNOCONN"); writeEndOfFrame(); } else { // Find the service ID for the specified client library name TQInt32List sidList; m_databaseServiceTypesCursor->select(TQString("client_library='%1'").arg(libname)); while (m_databaseServiceTypesCursor->next()) { sidList.append(m_databaseServiceTypesCursor->value("serviceid").toInt()); } if (sidList.empty()) { ds << TQString("ERRNOSERV"); writeEndOfFrame(); } else { // Attempt to connect to the backend server TQ_INT32 sid = -1; while (1) { m_databaseServicesCursor->select(TQString("servicetype=%1 AND station=%2").arg(sidList[0]).arg(m_stationID)); if (m_databaseServicesCursor->next()) { sid = sidList[0]; break; } sidList.remove(sidList.at(0)); if (sidList.isEmpty()) { break; } } if (sid != -1) { // Enforce single instance flags bool available = true; m_databaseServiceTypesCursor->select(TQString("serviceid=%1").arg(sid)); if (m_databaseServiceTypesCursor->next()) { char tempchar; tempchar = m_databaseServiceTypesCursor->value("single_instance").toInt(); if (tempchar != 0) { // Verify that service is not already in use m_databaseActivityCursor->select(TQString("station=%1 AND serviceid=%2").arg(m_stationID).arg(sid)); while (m_databaseActivityCursor->next()) { if (m_databaseActivityCursor->value("username").toString() != "") { available = false; } } } } if (available) { m_srvServiceHostName = m_databaseServicesCursor->value("hostname").toString(); m_srvServicePort = m_databaseServicesCursor->value("port").toInt(); // Update database m_serviceID = sid; TQSqlRecord *buffer = m_databaseActivityCursor->primeInsert(); buffer->setValue("station", m_stationID); buffer->setValue("arbiter", m_localMachineFQDN); buffer->setValue("username", m_authenticatedUserName); buffer->setValue("realmname", m_authenticatedRealmName); buffer->setValue("logontime", (TQ_ULLONG)TQDateTime::currentDateTime().toTime_t()); buffer->setValue("serviceid", m_serviceID); buffer->setValue("terminate", 0); m_databaseActivityCursor->insert(); updateStatistics(STATISTICS_NEW_CONNECTION_EVENT); printf("[DEBUG] SERV request using %s:%d for %s and user %s@%s\n\r", m_srvServiceHostName.ascii(), m_srvServicePort, libname.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout); if (!m_servClientSocket) m_servClientSocket = new TDEKerberosClientSocket; m_servClientSocket->setServiceName("ulab"); TQStringList mechList; m_config->setGroup("SASL"); mechList = TQStringList::split(" ", m_config->readEntry("allowed_mechanisms"), false); if (mechList.count() > 0) { m_servClientSocket->setAllowedMechanisms(mechList); } mechList = TQStringList::split(" ", m_config->readEntry("disallowed_mechanisms"), false); if (mechList.count() > 0) { m_servClientSocket->setDisallowedMechanisms(mechList); } mechList = TQStringList::split(" ", m_config->readEntry("override_mechanism_list"), false); if (mechList.count() > 0) { m_servClientSocket->setMechanismOverrideList(mechList); } m_servClientSocket->setServerFQDN(m_srvServiceHostName); m_servClientSocket->connectToHost(m_srvServiceHostName, m_srvServicePort); m_servState = 0; m_servActive = true; } else { ds << TQString("ERRNOSERV"); writeEndOfFrame(); } } else { ds << TQString("ERRNOSERV"); writeEndOfFrame(); } } } } } else if (command == "TSTP") { ds << m_terminationStamp; writeEndOfFrame(); } else { ds << TQString("ERRINVCMD"); writeEndOfFrame(); } } } m_criticalSection--; if (transferred_data) { if (m_loopTimer) m_loopTimer->start(0, TRUE); } else { if (m_loopTimer) m_loopTimer->start(m_pollInterval, TRUE); } return; } } catch (...) { m_criticalSection--; return; } } int AuthSocket::enterCommandLoop() { if (!m_loopTimer) { m_loopTimer = new TQTimer(); connect(m_loopTimer, SIGNAL(timeout()), this, SLOT(commandLoop())); } if (m_loopTimer) m_loopTimer->start(0, TRUE); return 0; } int AuthSocket::connectToDatabase() { if (m_database) { return -2; } m_database = TQSqlDatabase::database(); if (!m_database) { printf("[ERROR] Database was not constructed by the application\n\r"); fflush(stdout); return -1; } m_databaseStationsCursor = new TQSqlCursor("stations", TRUE, m_database); m_databaseServicesCursor = new TQSqlCursor("services", TRUE, m_database); m_databaseServiceTypesCursor = new TQSqlCursor("servicetypes", TRUE, m_database); m_databasePermissionsCursor = new TQSqlCursor("permissions", TRUE, m_database); m_databaseActivityCursor = new TQSqlCursor("activity", TRUE, m_database); m_databaseStatisticsCursor = new TQSqlCursor("statistics", TRUE, m_database); m_databaseStatusCursor = new TQSqlCursor("status", TRUE, m_database); return 0; } /* The AuthServer class handles new connections to the server. For every client that connects, it creates a new AuthSocket -- that instance is now responsible for the communication with that client. */ AuthServer::AuthServer(TQObject* parent) : TQServerSocket( 4004, 1, parent ), m_database(NULL) { m_localMachineFQDN = getLocalMachineFQDN(); m_config = new KSimpleConfig("ulab_authserver.conf", false); if (connectToDatabase() != 0) { exit(1); } // Verify schema version bool schemaValid = 0; uint schemaVersion = 0; TQSqlCursor databaseDBSchemaCursor("dbschema", TRUE, m_database); databaseDBSchemaCursor.select(TQString("skey='revision'")); if (databaseDBSchemaCursor.next()) { schemaVersion = databaseDBSchemaCursor.value("value").toUInt(); if (schemaVersion == DB_SCHEMA_VERSION) { schemaValid = 1; } } if (!schemaValid) { printf("[ERROR] Schema version not present or incorrect (got %d expected %d)\n\r", schemaVersion, DB_SCHEMA_VERSION); exit(1); } // Delete existing activity entries for this server ID TQSqlCursor databaseActivityCursor("activity", TRUE, m_database); databaseActivityCursor.select(TQString("arbiter='%1'").arg(m_localMachineFQDN)); while (databaseActivityCursor.next()) { databaseActivityCursor.primeDelete(); databaseActivityCursor.del(false); } // Update statistics TQSqlCursor databaseStatisticsCursor("statistics", TRUE, m_database); TQSqlRecord *buffer = databaseStatisticsCursor.primeInsert(); buffer->setValue("timestamp", (TQ_ULLONG)TQDateTime::currentDateTime().toTime_t()); buffer->setValue("eventtypeid", STATISTICS_SERVER_START_EVENT); databaseStatisticsCursor.insert(); if ( !ok() ) { printf("[ERROR] Failed to bind to port 4004\n\r"); exit(1); } printf("[INFO] Server started on port 4004\n\r"); fflush(stdout); } AuthServer::~AuthServer() { if (m_sqlPingTimer) { m_sqlPingTimer->stop(); delete m_sqlPingTimer; m_sqlPingTimer = NULL; } // Update statistics TQSqlCursor databaseStatisticsCursor("statistics", TRUE, m_database); TQSqlRecord *buffer = databaseStatisticsCursor.primeInsert(); buffer->setValue("timestamp", (TQ_ULLONG)TQDateTime::currentDateTime().toTime_t()); buffer->setValue("eventtypeid", STATISTICS_SERVER_STOP_EVENT); databaseStatisticsCursor.insert(); if (m_database) { TQSqlDatabase::removeDatabase(m_database); m_database = NULL; } delete m_config; } int AuthServer::connectToDatabase() { m_config->setGroup("Database"); TQString databaseDriver = m_config->readEntry("driver"); m_database = TQSqlDatabase::addDatabase(databaseDriver); m_database->setDatabaseName(m_config->readEntry("database")); m_database->setUserName(m_config->readEntry("username")); m_database->setPassword(m_config->readEntry("password")); m_database->setHostName(m_config->readEntry("server")); if (databaseDriver.contains("MYSQL")) { m_database->setConnectOptions("MYSQL_OPT_RECONNECT"); } if(!m_database->open()) { printf("[ERROR] Failed to connect to control database on server '%s' [%s]\n\r", m_database->hostName().ascii(), m_database->lastError().text().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } TQSqlCursor* testCursor = NULL; testCursor = new TQSqlCursor("stations", TRUE, m_database); testCursor->select(); if (!testCursor->isActive()) { m_database->close(); printf("[ERROR] Control database '%s' on '%s' does not contain the required 'stations' table\n\r", m_database->databaseName().ascii(), m_database->hostName().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } delete testCursor; testCursor = new TQSqlCursor("services", TRUE, m_database); testCursor->select(); if (!testCursor->isActive()) { m_database->close(); printf("[ERROR] Control database '%s' on '%s' does not contain the required 'services' table\n\r", m_database->databaseName().ascii(), m_database->hostName().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } delete testCursor; testCursor = new TQSqlCursor("servicetypes", TRUE, m_database); testCursor->select(); if (!testCursor->isActive()) { m_database->close(); printf("[ERROR] Control database '%s' on '%s' does not contain the required 'servicetypes' table\n\r", m_database->databaseName().ascii(), m_database->hostName().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } delete testCursor; testCursor = new TQSqlCursor("permissions", TRUE, m_database); testCursor->select(); if (!testCursor->isActive()) { m_database->close(); printf("[ERROR] Control database '%s' on '%s' does not contain the required 'permissions' table\n\r", m_database->databaseName().ascii(), m_database->hostName().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } delete testCursor; testCursor = new TQSqlCursor("activity", TRUE, m_database); testCursor->select(); if (!testCursor->isActive()) { m_database->close(); printf("[ERROR] Control database '%s' on '%s' does not contain the required 'activity' table\n\r", m_database->databaseName().ascii(), m_database->hostName().ascii()); fflush(stdout); TQSqlDatabase::removeDatabase(m_database); m_database = NULL; return -1; } delete testCursor; // Start database ping process // When combined with the MYSQL_OPT_RECONNECT flag passed above, this will keep the connection open even if the database server goes offline and then comes back online m_sqlPingTimer = new TQTimer(); connect(m_sqlPingTimer, SIGNAL(timeout()), this, SLOT(pingSQLServer())); m_sqlPingTimer->start(60*1000); return 0; } void AuthServer::pingSQLServer() { if (m_database) { m_database->ping(); } } TQString AuthServer::getLocalMachineFQDN() { 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 TQString::null; } TQString ret(res->ai_canonname); freeaddrinfo(res); return ret; } void AuthServer::newConnection(int socket) { AuthSocket *s = new AuthSocket(socket, m_localMachineFQDN, this); s->m_remoteHost = s->peerAddress().toString(); printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii()); connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater())); s->initiateKerberosHandshake(); emit newConnect(s); }