/*************************************************************************** * Copyright (C) 2012-2019 by Timothy Pearson * * kb9vqf@pearsoncomputing.net * * * * 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., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "tdekrbclientsocket.h" #if SASL_VERSION_FULL < 0x020119 typedef int (*sasl_callback_ft)(void); #endif // SASL_VERSION_FULL #define NET_SEC_BUF_SIZE (2048) // When control comes back from processEvents() my object may be completely gone! This attempts to mitigate the risk #define SAFELY_PROCESS_EVENTS if (!m_canary) { \ m_canary = new bool; \ *m_canary = false; \ } \ bool* canary = m_canary; \ tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput); \ if (*canary == true) { \ delete canary; \ return -1; \ } \ delete m_canary; \ m_canary = NULL; static bool tde_krb_sasl_client_initialized = false; static sasl_callback_t tde_krb_sasl_client_callbacks[N_CALLBACKS]; /* exception handling */ struct exit_exception { int c; exit_exception(int c):c(c) { } }; class SASLDataPrivate { public: sasl_conn_t *m_krbConnection; }; static const char * safe_sasl_errdetail(sasl_conn_t *conn) { const char * str = sasl_errdetail(conn); if (str) { return str; } else { return "unknown error"; } } static int logSASLMessages(void *context __attribute__((unused)), int priority, const char *message) { const char *label; if (!message) { return SASL_BADPARAM; } switch (priority) { case SASL_LOG_ERR: label = "Error"; break; case SASL_LOG_NOTE: label = "Info"; break; default: label = "Other"; break; } printf("[SASL %s] %s\n\r", label, message); return SASL_OK; } TDEKerberosClientSocket::TDEKerberosClientSocket(TQObject *parent, const char *name) : TQSocket(parent, name), m_kerberosRequested(false), m_criticalSection(0), m_readBufferLength(0), m_readBufferReadPointer(0), m_writeBufferLength(0), m_krbInitRunning(false), m_krbInitState(-1), m_dataTimeout(-1), kerberosInitLoopTimer(NULL), m_canary(NULL), m_negotiatedMaxBufferSize(NET_SEC_BUF_SIZE) { saslData = new SASLDataPrivate; saslData->m_krbConnection = NULL; m_readBuffer = new TQBuffer(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_writeBuffer = new TQBuffer(); m_writeBuffer->open(IO_ReadWrite|IO_Truncate); } TDEKerberosClientSocket::~TDEKerberosClientSocket() { if (m_canary) { *m_canary = true; } if (kerberosInitLoopTimer) { kerberosInitLoopTimer->stop(); delete kerberosInitLoopTimer; kerberosInitLoopTimer = NULL; } setUsingKerberos(false); m_writeBuffer->close(); m_readBuffer->close(); delete m_writeBuffer; delete m_readBuffer; delete saslData; } void TDEKerberosClientSocket::setDataTimeout(int timeoutms) { m_dataTimeout = timeoutms; } bool TDEKerberosClientSocket::open(int mode) { setStatusMessage(i18n("Establishing initial connection to server")); bool ret = TQSocket::open(mode); if (m_kerberosRequested) { initializeKerberosInterface(); } return ret; } void TDEKerberosClientSocket::close() { TQSocket::close(); setStatusMessage(i18n("Disconnected")); } void TDEKerberosClientSocket::flush(int hidebasehack) { Q_UNUSED(hidebasehack); if (kerberosStatus() == KerberosInUse) { writeBufferedData(); TQSocket::flush(); } else { TQSocket::flush(); } } TQIODevice::Offset TDEKerberosClientSocket::size() const { TQIODevice::Offset ret; if (kerberosStatus() == KerberosInUse) { ret = m_readBufferLength; } else { ret = TQSocket::size(); } return ret; } TQIODevice::Offset TDEKerberosClientSocket::at() const { return TQSocket::at(); } bool TDEKerberosClientSocket::at(TQIODevice::Offset off, int hidebasehack) { bool ret; Q_UNUSED(hidebasehack); if (kerberosStatus() == KerberosInUse) { if (off > 0) { // Prevent overflow if (off > (unsigned long)m_readBufferLength) { off = m_readBufferLength; } // Remove the specified bytes from the buffer m_readBufferLength = m_readBufferLength-off; m_readBufferReadPointer = m_readBufferReadPointer+off; if (m_readBufferLength < 1) { // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferReadPointer = 0; } } return true; } else { ret = TQSocket::at(off); } return ret; } bool TDEKerberosClientSocket::atEnd() const { bool ret; if (kerberosStatus() == KerberosInUse) { ret = ((m_readBufferLength < 1) && TQSocket::atEnd()); } else { ret = TQSocket::atEnd(); } return ret; } int TDEKerberosClientSocket::getch() { int ret; if (kerberosStatus() == KerberosInUse) { char data[1]; if (readBlock(data, 1) < 0) { ret = -1; } else { ret = data[0]; } } else { ret = TQSocket::getch(); } return ret; } int TDEKerberosClientSocket::putch(int ch) { int ret; if (kerberosStatus() == KerberosInUse) { char data[1]; data[0] = ch; if (writeBlock(data, 1) < 1) { ret = -1; } else { ret = ch; } } else { ret = TQSocket::putch(ch); } return ret; } int TDEKerberosClientSocket::ungetch(int ch) { int ret; if (kerberosStatus() == KerberosInUse) { // FIXME // UNIMPLEMENTED // This feature, if supported, will be very expensive, requiring a full allocation+copy/shift+deallocation of the buffer array, // followed by insertion of the new character to the head of the array ret = -1; } else { ret = TQSocket::ungetch(ch); } return ret; } TQ_ULONG TDEKerberosClientSocket::bytesAvailable() const { TQ_ULONG ret; if (kerberosStatus() == KerberosInUse) { ret = m_readBufferLength; } else { ret = TQSocket::bytesAvailable(); } return ret; } int TDEKerberosClientSocket::processPendingData() { if (kerberosStatus() == KerberosInUse) { while (TQSocket::canReadLine() && (TQSocket::state() == TQSocket::Connected)) { int reclen; int wrlen; int bytesAvailable = TQSocket::bytesAvailable(); char* buf = (char*)malloc(bytesAvailable); reclen = receiveEncryptedData(buf, bytesAvailable, false); if (reclen < 0) { free(buf); return -1; } if (reclen > 0) { m_readBuffer->at(m_readBufferLength+m_readBufferReadPointer); wrlen = m_readBuffer->writeBlock(buf, reclen); if (wrlen > 0) { m_readBufferLength = m_readBufferLength + wrlen; emit(newDataReceived()); } } free(buf); } } return 0; } int TDEKerberosClientSocket::setUsingKerberos(bool krbactive) { int ret = 0; if (m_serviceName == "") { printf("[ERROR] No service name set!\n\r"); fflush(stdout); return -1; } if (krbactive) { m_kerberosRequested = true; if ((!saslData->m_krbConnection) && (state() == TQSocket::Connected)) { initializeKerberosInterface(); } } else { m_kerberosRequested = false; if (saslData->m_krbConnection) { freeKerberosConnection(); } } return ret; } void TDEKerberosClientSocket::setServiceName(TQString name) { m_serviceName = name; } void TDEKerberosClientSocket::setServerFQDN(TQString name) { m_serverFQDN = name; } TQ_LONG TDEKerberosClientSocket::readBlock(char *data, TQ_ULONG maxlen) { TQ_LONG ret; if (kerberosStatus() == KerberosInUse) { int reclen; int wrlen; if (m_readBufferLength <= 0) { int bytesAvailable = TQSocket::bytesAvailable(); char* buf = (char*)malloc(bytesAvailable); reclen = receiveEncryptedData(buf, bytesAvailable, false); if (reclen < 0) { free(buf); return -1; } if (reclen > 0) { m_readBuffer->at(m_readBufferLength+m_readBufferReadPointer); wrlen = m_readBuffer->writeBlock(buf, reclen); if (wrlen > 0) { m_readBufferLength = m_readBufferLength + wrlen; emit(newDataReceived()); } } free(buf); } if (maxlen > (unsigned int)m_readBufferLength) { maxlen = m_readBufferLength; } m_readBuffer->at(m_readBufferReadPointer); ret = m_readBuffer->readBlock(data, maxlen); if (ret > 0) { // Remove the read bytes from the buffer m_readBufferLength = m_readBufferLength-ret; m_readBufferReadPointer = m_readBufferReadPointer+ret; if (m_readBufferLength < 1) { // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferReadPointer = 0; } } } else { ret = TQSocket::readBlock(data, maxlen); } return ret; } TQ_LONG TDEKerberosClientSocket::writeBlock(const char *data, TQ_ULONG len) { TQ_LONG ret; if (kerberosStatus() == KerberosInUse) { int wrlen; m_writeBuffer->at(m_writeBufferLength); wrlen = m_writeBuffer->writeBlock(data, len); if (wrlen > 0) { m_writeBufferLength = m_writeBufferLength + wrlen; } ret = wrlen; } else { ret = TQSocket::writeBlock(data, len); } return ret; } void TDEKerberosClientSocket::writeBufferedData() { if (kerberosStatus() == KerberosInUse) { if (m_writeBufferLength > 0) { if (transmitEncryptedData(m_writeBuffer->buffer().data(), m_writeBufferLength) < 0) { printf("[WARNING] Attempt to transmit buffered data resulted in a short write\n\r"); fflush(stdout); } // Clear the buffer from memory m_writeBuffer->close(); m_writeBuffer->open(IO_ReadWrite|IO_Truncate); m_writeBufferLength = 0; } } } TQ_LONG TDEKerberosClientSocket::readLine(char *data, TQ_ULONG maxlen) { TQ_LONG ret; if (kerberosStatus() == KerberosInUse) { int reclen; int wrlen; if (m_readBufferLength <= 0) { int bytesAvailable = TQSocket::bytesAvailable(); char* buf = (char*)malloc(bytesAvailable); reclen = receiveEncryptedData(buf, bytesAvailable, false); if (reclen < 0) { free(buf); return -1; } if (reclen > 0) { m_readBuffer->at(m_readBufferLength+m_readBufferReadPointer); wrlen = m_readBuffer->writeBlock(buf, reclen); if (wrlen > 0) { m_readBufferLength = m_readBufferLength + wrlen; emit(newDataReceived()); } } free(buf); } if (maxlen > (unsigned int)m_readBufferLength) { maxlen = m_readBufferLength; } m_readBuffer->at(m_readBufferReadPointer); ret = m_readBuffer->readLine(data, maxlen); if (ret > 0) { // Remove the read bytes from the buffer m_readBufferLength = m_readBufferLength-ret; m_readBufferReadPointer = m_readBufferReadPointer+ret; if (m_readBufferLength < 1) { // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferReadPointer = 0; } } } else { ret = TQSocket::readLine(data, maxlen); } return ret; } TQString TDEKerberosClientSocket::readLine() { TQString ret; long maxlen; if (kerberosStatus() == KerberosInUse) { int reclen; int wrlen; int readlen; char* buf; maxlen = m_negotiatedMaxBufferSize; if (m_readBufferLength <= 0) { int bytesAvailable = TQSocket::bytesAvailable(); buf = (char*)malloc(bytesAvailable); reclen = receiveEncryptedData(buf, bytesAvailable, false); if (reclen < 0) { free(buf); return TQString::null; } if (reclen > 0) { m_readBuffer->at(m_readBufferLength+m_readBufferReadPointer); wrlen = m_readBuffer->writeBlock(buf, reclen); if (wrlen > 0) { m_readBufferLength = m_readBufferLength + wrlen; emit(newDataReceived()); } } free(buf); } if (maxlen > m_readBufferLength) { maxlen = m_readBufferLength; } m_readBuffer->at(m_readBufferReadPointer); buf = (char*)malloc(maxlen); readlen = m_readBuffer->readLine(buf, maxlen); if (readlen > 0) { // Remove the read bytes from the buffer m_readBufferLength = m_readBufferLength-readlen; m_readBufferReadPointer = m_readBufferReadPointer+readlen; if (m_readBufferLength < 1) { // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferReadPointer = 0; } ret = TQString(buf); } else { ret == TQString::null; } free(buf); } else { ret = TQSocket::readLine(); } return ret; } void TDEKerberosClientSocket::writeLine(TQString str) { if (kerberosStatus() == KerberosInUse) { transmitEncryptedData(str.ascii(), str.length()); } else { TQSocket::writeBlock(str.ascii(), str.length()); } } void TDEKerberosClientSocket::setAllowedMechanisms(TQStringList mechanisms) { m_allowedMechanisms = mechanisms; } void TDEKerberosClientSocket::setDisallowedMechanisms(TQStringList mechanisms) { m_disallowedMechanisms = mechanisms; } void TDEKerberosClientSocket::setMechanismOverrideList(TQStringList mechanisms) { m_overrideMechanisms = mechanisms; } void TDEKerberosClientSocket::freeKerberosConnection(void) { if (saslData->m_krbConnection) { sasl_dispose(&saslData->m_krbConnection); } saslData->m_krbConnection = 0; } void TDEKerberosClientSocket::sendSASLDataToNetwork(const char *buffer, unsigned length) { char *buf; unsigned int len = 0; unsigned int alloclen = 0; int result; alloclen = (((length / 3) + 1) * 4) + 1; buf = (char*)malloc(alloclen+1); if (!buf) { printf("[ERROR] Unable to malloc()!\n\r"); return; } result = sasl_encode64(buffer, length, buf, alloclen, &len); if (result != SASL_OK) { printf("[ERROR] Encoding data in base64 returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); return; } buf[len] = '\n'; buf[len+1] = 0; unsigned int ret = TQSocket::writeBlock(buf, len+1); if (ret < (len+1)) { printf("[WARNING] Transmitting data in base64 failed due to short write [wanted: %d wrote: %d]\n\r", len+1, ret); } free(buf); } int TDEKerberosClientSocket::getSASLDataFromNetwork(char *buf, int trunclen, bool shouldblock) { m_criticalSection++; try { unsigned int len; int result; TQCString ba; if (!shouldblock) { if ((!TQSocket::canReadLine()) || (state() != TQSocket::Connected)) { return 0; } } len = 0; TQTimer dataTimeoutTimer; if (m_dataTimeout > 0) { dataTimeoutTimer.start(m_dataTimeout, TRUE); } while (dataTimeoutTimer.isActive() || (m_dataTimeout < 0)) { if (!TQSocket::canReadLine()) { if ((shouldblock) && (dataTimeoutTimer.isActive() || (m_dataTimeout < 0))) { SAFELY_PROCESS_EVENTS } } if (state() != TQSocket::Connected) { m_criticalSection--; return -1; } if (TQSocket::canReadLine()) { TQString base64string = TQSocket::readLine(); base64string.truncate(base64string.length()-1); ba = base64string; break; } else { if (shouldblock) { usleep(1000); } else { break; } } } if (!ba.isNull()) { len = strlen(ba.data()); result = sasl_decode64(ba.data(), len, buf, trunclen, &len); if (result != SASL_OK) { printf("[ERROR] Decoding data from base64 returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); m_criticalSection--; return -1; } buf[len] = '\0'; } else { buf[0] = '\0'; } m_criticalSection--; return len; } catch(exit_exception& e) { m_criticalSection--; return -1; } } int TDEKerberosClientSocket::transmitEncryptedData(const char* readbuf, int cc) { int result = 0; unsigned int len; const char *data; long data_remaining; long remnant_position; TQTimer dataTimeoutTimer; if (m_dataTimeout > 0) { dataTimeoutTimer.start(m_dataTimeout, TRUE); } data_remaining = cc; remnant_position = 0; while ((data_remaining > 0) && (dataTimeoutTimer.isActive() || (m_dataTimeout < 0))) { int data_to_write_len; if ((unsigned long)data_remaining > (m_negotiatedMaxBufferSize/2)) { data_to_write_len = m_negotiatedMaxBufferSize/2; } else { data_to_write_len = data_remaining; } result=sasl_encode(saslData->m_krbConnection, readbuf+remnant_position, data_to_write_len, &data, &len); if (result != SASL_OK) { printf("[ERROR] Encrypting data returned %s (%d)\n\r", safe_sasl_errdetail(saslData->m_krbConnection), result); return -1; } sendSASLDataToNetwork(data, len); data_remaining = data_remaining - data_to_write_len; remnant_position = remnant_position + data_to_write_len; if ((data_remaining > 0) && (dataTimeoutTimer.isActive() || (m_dataTimeout < 0))) { SAFELY_PROCESS_EVENTS } } return cc; } int TDEKerberosClientSocket::receiveEncryptedData(char *buf, unsigned int trunclen, bool shouldblock) { unsigned int recv_len; const char *recv_data; int result; int len; int bytesAvailable = TQSocket::bytesAvailable(); char *encbuf = (char*)malloc(bytesAvailable); len = getSASLDataFromNetwork(encbuf, bytesAvailable, shouldblock); if (len < 0) { return -1; } if (len >= 0) { result=sasl_decode(saslData->m_krbConnection, encbuf, len, &recv_data, &recv_len); if (result != SASL_OK) { free(encbuf); printf("[ERROR] Decrypting data returned %s (%d)\n\r", safe_sasl_errdetail(saslData->m_krbConnection), result); return -1; } if (recv_len > trunclen) { recv_len = trunclen; } memcpy(buf, recv_data, recv_len); } free(encbuf); return recv_len; } TDEKerberosClientSocket::KerberosStatus TDEKerberosClientSocket::kerberosStatus() const { if (!m_kerberosRequested) { return KerberosNotRequested; } if (m_krbInitRunning) { return KerberosInitializing; } if (m_krbInitState < 0) { return KerberosFailure; } return KerberosInUse; } bool TDEKerberosClientSocket::canReadData() { return (TQSocket::canReadLine() || (m_readBufferLength > 0)); } void TDEKerberosClientSocket::clearIncomingData() { char data[64]; processPendingData(); while (canReadData()) { readBlock(data, 64); } } int TDEKerberosClientSocket::writeEndOfFrame() { int ret; char data[1]; data[0] = 255; ret = writeBlock(data, 1); writeBufferedData(); return ret; } bool TDEKerberosClientSocket::canReadFrame(bool callProcessPendingData) { if (callProcessPendingData) { processPendingData(); } if (m_readBufferLength > 0) { if (m_readBuffer->buffer().find(255, m_readBufferReadPointer) >= 0) { return true; } else { return false; } } else { return false; } } void TDEKerberosClientSocket::clearFrameTail() { int eofLoc; if (m_readBufferLength > 0) { eofLoc = m_readBuffer->buffer().find(255, m_readBufferReadPointer) + 1; if ((eofLoc > 0) && (eofLoc <= (m_readBufferLength+m_readBufferReadPointer))) { // Remove the remaining frame bytes (including the End of Frame marker) from the buffer m_readBufferLength = m_readBufferLength-(eofLoc-m_readBufferReadPointer); m_readBufferReadPointer = m_readBufferReadPointer+(eofLoc-m_readBufferReadPointer); if (m_readBufferLength < 1) { // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferReadPointer = 0; } } } } void TDEKerberosClientSocket::setStatusMessage(TQString message) { if (message != m_prevStatusMessage) { emit(statusMessageUpdated(message)); m_prevStatusMessage = message; } } void TDEKerberosClientSocket::continueKerberosInitialization() { int slen = 0; char buf[NET_SEC_BUF_SIZE]; unsigned int len = 0; const char *data = 0; const char *chosenmech = 0; sasl_ssf_t *ssf = 0; const void *sasl_prop_ptr; if (m_krbInitRunning) { switch (m_krbInitState) { case 0: if (state() == TQSocket::Connected) { setStatusMessage(i18n("Waiting for mechanism list from server")); if (canReadLine()) { printf("[DEBUG] Waiting for mechanism list from server...\n\r"); slen = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); if (slen < 0) { m_krbInitState = -2; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } len = slen; printf("[DEBUG] Server and client support mechanisms: %s\n", buf); TQStringList krbMechList = TQStringList::split(" ", buf, false); // If a mechanism override list is set then use it! if (m_overrideMechanisms.count() > 0) { printf("[DEBUG] Overriding mechanisms list: %s\n", m_overrideMechanisms.join(" ").ascii()); TQStringList supportedMechanisms = krbMechList; krbMechList = m_overrideMechanisms; // Remove all mechanisms not supported by the client and server for (TQStringList::Iterator it = krbMechList.begin(); it != krbMechList.end(); ++it ) { if (supportedMechanisms.find(*it) == supportedMechanisms.end()) { krbMechList.remove(*it); it = krbMechList.begin(); } } } else { // Remove all mechanisms not listed in the allowed list if (m_allowedMechanisms.count() > 0) { for (TQStringList::Iterator it = krbMechList.begin(); it != krbMechList.end(); ++it ) { if (m_allowedMechanisms.find(*it) == m_allowedMechanisms.end()) { printf("[DEBUG] Removing implicitly disallowed mechanism %s from list\n", (*it).ascii()); krbMechList.remove(*it); it = krbMechList.begin(); } } } // Remove all mechanisms listed in the disallowed list if (m_disallowedMechanisms.count() > 0) { for (TQStringList::Iterator it = m_disallowedMechanisms.begin(); it != m_disallowedMechanisms.end(); ++it ) { printf("[DEBUG] Removing explicitly disallowed mechanism %s from list\n", (*it).ascii()); krbMechList.remove(*it); } } } TQString krbMechListString = krbMechList.join(" "); printf("Choosing best mechanism from: %s\n", krbMechListString.ascii()); m_krbInitResult = sasl_client_start(saslData->m_krbConnection, krbMechListString.ascii(), NULL, &data, &len, &chosenmech); if (m_krbInitResult != SASL_OK && m_krbInitResult != SASL_CONTINUE) { printf("[ERROR] Starting SASL negotiation returned %s (%d)\n\r", sasl_errstring(m_krbInitResult, NULL, NULL), m_krbInitResult); freeKerberosConnection(); m_krbInitState = -1; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } printf("[DEBUG] Using mechanism %s\n\r", chosenmech); strcpy(buf, chosenmech); if (data) { if (NET_SEC_BUF_SIZE - strlen(buf) - 1 < len) { printf("[ERROR] Insufficient buffer space to construct initial response!\n\r"); freeKerberosConnection(); m_krbInitState = -1; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } printf("[DEBUG] Preparing initial response...\n\r"); memcpy(buf + strlen(buf) + 1, data, len); len += (unsigned) strlen(buf) + 1; data = NULL; } else { len = (unsigned) strlen(buf); } printf("[DEBUG] Sending initial response...\n\r"); sendSASLDataToNetwork(buf, len); m_krbInitState = 1; } } else { m_krbInitState = -3; m_krbInitRunning = false; } break; case 1: if (state() == TQSocket::Connected) { if (m_krbInitResult == SASL_CONTINUE) { setStatusMessage(i18n("Waiting for server reply")); if (canReadLine()) { printf("[DEBUG] Waiting for server reply...\n\r"); slen = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); if (slen < 0) { m_krbInitState = -2; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } len = slen; m_krbInitResult = sasl_client_step(saslData->m_krbConnection, buf, len, NULL, &data, &len); if (m_krbInitResult != SASL_OK && m_krbInitResult != SASL_CONTINUE) { printf("[ERROR] Performing SASL negotiation returned %s (%d)\n\r", sasl_errstring(m_krbInitResult, NULL, NULL), m_krbInitResult); freeKerberosConnection(); m_krbInitState = -1; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } if (data && len) { printf("[DEBUG] Sending response...\n\r"); sendSASLDataToNetwork(data, len); } else if (m_krbInitResult != SASL_OK || !m_krbInitServerLast) { sendSASLDataToNetwork("", 0); } } } else { printf("[DEBUG] Negotiation complete!\n\r"); m_krbInitState = 2; } } else { m_krbInitState = -3; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } break; case 2: if (state() == TQSocket::Connected) { m_krbInitResult = sasl_getprop(saslData->m_krbConnection, SASL_USERNAME, &sasl_prop_ptr); data = (const char *)sasl_prop_ptr; if (m_krbInitResult != SASL_OK) { printf("[WARNING] Unable to determine authenticated username!\n\r"); } else { printf("[DEBUG] Authenticated username: %s\n\r", data ? data : "(NULL)"); } #if 0 m_krbInitResult = sasl_getprop(saslData->m_krbConnection, SASL_DEFUSERREALM, &sasl_prop_ptr); data = (const char *)sasl_prop_ptr; if (m_krbInitResult != SASL_OK) { printf("[WARNING] Unable to determine authenticated realm!\n\r"); } else { printf("[DEBUG] Authenticated realm: %s\n\r", data ? data : "(NULL)"); } #endif m_krbInitResult = sasl_getprop(saslData->m_krbConnection, SASL_SSF, &sasl_prop_ptr); ssf = (sasl_ssf_t *)sasl_prop_ptr; if (m_krbInitResult != SASL_OK) { printf("[WARNING] Unable to determine SSF!\n\r"); } else { printf("[DEBUG] Authenticated SSF: %d\n", *ssf); } m_krbInitResult = sasl_getprop(saslData->m_krbConnection, SASL_MAXOUTBUF, &sasl_prop_ptr); m_negotiatedMaxBufferSize = *((unsigned*)sasl_prop_ptr); if (m_krbInitResult != SASL_OK) { printf("[WARNING] Unable to determine maximum buffer size!\n\r"); m_negotiatedMaxBufferSize = NET_SEC_BUF_SIZE; } else { // For some reason m_negotiatedMaxBufferSize can be set negative under certain circumstances // Prevent that from happening! if (m_negotiatedMaxBufferSize < NET_SEC_BUF_SIZE) { m_negotiatedMaxBufferSize = NET_SEC_BUF_SIZE; } printf("[DEBUG] Maximum buffer size: %d\n", m_negotiatedMaxBufferSize); } m_krbInitState = 3; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection established")); return; } else { m_krbInitState = -3; m_krbInitRunning = false; setStatusMessage(i18n("Kerberos connection failed")); return; } break; } if (kerberosInitLoopTimer) kerberosInitLoopTimer->start(0, TRUE); } } int TDEKerberosClientSocket::initializeKerberosInterface() { if (state() != TQSocket::Connected) { freeKerberosConnection(); return -1; } sasl_callback_t *callback; m_krbInitResult = 0; m_krbInitServerLast = 0; sasl_security_properties_t secprops; char *iplocal = NULL; char *ipremote = NULL; const char *service = m_serviceName.ascii(); const char *fqdn = m_serverFQDN.ascii(); callback = tde_krb_sasl_client_callbacks; // log callback->id = SASL_CB_LOG; callback->proc = (sasl_callback_ft)&logSASLMessages; callback->context = NULL; ++callback; // end of callback list callback->id = SASL_CB_LIST_END; callback->proc = NULL; callback->context = NULL; ++callback; // Clear the buffer from memory m_readBuffer->close(); m_readBuffer->open(IO_ReadWrite|IO_Truncate); m_readBufferLength = 0; m_readBufferReadPointer = 0; m_writeBuffer->close(); m_writeBuffer->open(IO_ReadWrite|IO_Truncate); m_writeBufferLength = 0; // Initialize default data structures memset(&secprops, 0L, sizeof(secprops)); secprops.maxbufsize = NET_SEC_BUF_SIZE; secprops.max_ssf = UINT_MAX; if (!tde_krb_sasl_client_initialized) { m_krbInitResult = sasl_client_init(tde_krb_sasl_client_callbacks); if (m_krbInitResult != SASL_OK) { printf("[ERROR] Initializing libsasl returned %s (%d)\n\r", sasl_errstring(m_krbInitResult, NULL, NULL), m_krbInitResult); return -1; } tde_krb_sasl_client_initialized = true; } m_krbInitResult = sasl_client_new(service, fqdn, iplocal, ipremote, NULL, m_krbInitServerLast, &saslData->m_krbConnection); if (m_krbInitResult != SASL_OK) { printf("[ERROR] Allocating sasl connection state returned %s (%d)\n\r", sasl_errstring(m_krbInitResult, NULL, NULL), m_krbInitResult); return -1; } m_krbInitResult = sasl_setprop(saslData->m_krbConnection, SASL_SEC_PROPS, &secprops); if (m_krbInitResult != SASL_OK) { printf("[ERROR] Setting security properties returned %s (%d)\n\r", sasl_errstring(m_krbInitResult, NULL, NULL), m_krbInitResult); freeKerberosConnection(); return -1; } m_krbInitRunning = true; m_krbInitState = 0; if (!kerberosInitLoopTimer) { kerberosInitLoopTimer = new TQTimer(); connect(kerberosInitLoopTimer, SIGNAL(timeout()), this, SLOT(continueKerberosInitialization())); } if (kerberosInitLoopTimer) kerberosInitLoopTimer->start(0, TRUE); return 0; }